Started code to check DND policy on incoming call

Added dialog to ask user to grant us read permission on DND settings
This commit is contained in:
Sylvain Berfini 2019-05-02 14:30:24 +02:00
parent b0b8284fc7
commit 4d58d71dbe
9 changed files with 198 additions and 2 deletions

View file

@ -46,6 +46,8 @@
<!-- Needed for kill application yourself --> <!-- Needed for kill application yourself -->
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" /> <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- Needed to get the current Do Not Disturb policy -->
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<supports-screens <supports-screens
android:anyDensity="true" android:anyDensity="true"

View file

@ -35,6 +35,7 @@ import android.os.Bundle;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
@ -232,6 +233,11 @@ public abstract class MainActivity extends LinphoneGenericActivity
LinphoneManager.getInstance().isAccountWithAlias(); LinphoneManager.getInstance().isAccountWithAlias();
} }
} }
if (!Compatibility.isDoNotDisturbSettingsAccessGranted(
MainActivity.this)) {
displayDNDSettingsDialog();
}
} }
} }
@ -667,6 +673,50 @@ public abstract class MainActivity extends LinphoneGenericActivity
dialog.show(); dialog.show();
} }
private void displayDNDSettingsDialog() {
if (!LinphonePreferences.instance().isDNDSettingsPopupEnabled()) return;
final Dialog dialog =
displayDialog(getString(R.string.pref_grant_read_dnd_settings_permission_desc));
dialog.findViewById(R.id.dialog_do_not_ask_again_layout).setVisibility(View.VISIBLE);
final CheckBox doNotAskAgain = dialog.findViewById(R.id.doNotAskAgain);
dialog.findViewById(R.id.doNotAskAgainLabel)
.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
doNotAskAgain.setChecked(!doNotAskAgain.isChecked());
}
});
Button cancel = dialog.findViewById(R.id.dialog_cancel_button);
cancel.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
if (doNotAskAgain.isChecked()) {
LinphonePreferences.instance().enableDNDSettingsPopup(false);
}
dialog.dismiss();
}
});
Button ok = dialog.findViewById(R.id.dialog_ok_button);
ok.setVisibility(View.VISIBLE);
ok.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.w(
"[Permission] Asking user to grant us permission to read DND settings");
startActivity(
new Intent("android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS"));
dialog.dismiss();
}
});
Button delete = dialog.findViewById(R.id.dialog_delete_button);
delete.setVisibility(View.GONE);
dialog.show();
}
// Logs // Logs
private void shareUploadedLogsUrl(String info) { private void shareUploadedLogsUrl(String info) {

View file

@ -20,8 +20,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.os.PowerManager; import android.os.PowerManager;
import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact;
import org.linphone.core.Address;
import org.linphone.core.tools.Log;
@TargetApi(23) @TargetApi(23)
class ApiTwentyThreePlus { class ApiTwentyThreePlus {
@ -29,4 +34,82 @@ class ApiTwentyThreePlus {
public static boolean isAppIdleMode(Context context) { public static boolean isAppIdleMode(Context context) {
return ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).isDeviceIdleMode(); return ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).isDeviceIdleMode();
} }
public static boolean isDoNotDisturbSettingsAccessGranted(Context context) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
boolean accessGranted = notificationManager.isNotificationPolicyAccessGranted();
return accessGranted;
}
public static boolean isDoNotDisturbPolicyAllowingRinging(
Context context, Address remoteAddress) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
int filter = notificationManager.getCurrentInterruptionFilter();
if (filter == NotificationManager.INTERRUPTION_FILTER_PRIORITY) {
Log.w("[Audio Manager] Priority interruption filter detected");
boolean accessGranted = notificationManager.isNotificationPolicyAccessGranted();
if (!accessGranted) {
Log.e(
"[Audio Manager] Access to policy is denied, let's assume it is not safe for ringing");
return false;
}
NotificationManager.Policy policy = notificationManager.getNotificationPolicy();
int callPolicy = policy.priorityCallSenders;
if (callPolicy == NotificationManager.Policy.PRIORITY_SENDERS_ANY) {
Log.i("[Audio Manager] Priority for calls is Any, we can ring");
} else {
if (remoteAddress == null) {
Log.e(
"[Audio Manager] Remote address is null, let's assume it is not safe for ringing");
return false;
}
LinphoneContact contact =
ContactsManager.getInstance().findContactFromAddress(remoteAddress);
if (callPolicy == NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS) {
Log.i("[Audio Manager] Priority for calls is Contacts, let's check");
if (contact == null) {
Log.w(
"[Audio Manager] Couldn't find a contact for address "
+ remoteAddress.asStringUriOnly());
return false;
} else {
Log.i(
"[Audio Manager] Contact found for address "
+ remoteAddress.asStringUriOnly()
+ ", we can ring");
}
} else if (callPolicy == NotificationManager.Policy.PRIORITY_SENDERS_STARRED) {
Log.i("[Audio Manager] Priority for calls is Starred Contacts, let's check");
if (contact == null) {
Log.w(
"[Audio Manager] Couldn't find a contact for address "
+ remoteAddress.asStringUriOnly());
return false;
} else if (!contact.isFavourite()) {
Log.w(
"[Audio Manager] Contact found for address "
+ remoteAddress.asStringUriOnly()
+ ", but it isn't starred");
return false;
} else {
Log.i(
"[Audio Manager] Starred contact found for address "
+ remoteAddress.asStringUriOnly()
+ ", we can ring");
}
}
}
} else if (filter == NotificationManager.INTERRUPTION_FILTER_ALARMS) {
Log.w("[Audio Manager] Alarms interruption filter detected");
return false;
} else {
Log.i("[Audio Manager] Interruption filter is " + filter + ", we can ring");
}
return true;
}
} }

View file

@ -29,6 +29,7 @@ import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Build; import android.os.Build;
import android.provider.Settings; import android.provider.Settings;
import org.linphone.core.Address;
import org.linphone.mediastream.Version; import org.linphone.mediastream.Version;
import org.linphone.notifications.Notifiable; import org.linphone.notifications.Notifiable;
@ -228,4 +229,19 @@ public class Compatibility {
ApiTwentyOnePlus.setTurnScreenOn(activity, enable); ApiTwentyOnePlus.setTurnScreenOn(activity, enable);
} }
} }
public static boolean isDoNotDisturbSettingsAccessGranted(Context context) {
if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) {
return ApiTwentyThreePlus.isDoNotDisturbSettingsAccessGranted(context);
}
return true;
}
public static boolean isDoNotDisturbPolicyAllowingRinging(
Context context, Address remoteAddress) {
if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) {
return ApiTwentyThreePlus.isDoNotDisturbPolicyAllowingRinging(context, remoteAddress);
}
return true;
}
} }

View file

@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import android.Manifest; import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.text.InputType; import android.text.InputType;
@ -34,6 +35,8 @@ import org.linphone.R;
import org.linphone.core.Core; import org.linphone.core.Core;
import org.linphone.core.MediaEncryption; import org.linphone.core.MediaEncryption;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.mediastream.Version;
import org.linphone.settings.widget.BasicSetting;
import org.linphone.settings.widget.ListSetting; import org.linphone.settings.widget.ListSetting;
import org.linphone.settings.widget.SettingListenerBase; import org.linphone.settings.widget.SettingListenerBase;
import org.linphone.settings.widget.SwitchSetting; import org.linphone.settings.widget.SwitchSetting;
@ -50,6 +53,7 @@ public class CallSettingsFragment extends SettingsFragment {
mAutoAnswer; mAutoAnswer;
private ListSetting mMediaEncryption; private ListSetting mMediaEncryption;
private TextSetting mAutoAnswerTime, mIncomingCallTimeout, mVoiceMailUri; private TextSetting mAutoAnswerTime, mIncomingCallTimeout, mVoiceMailUri;
private BasicSetting mDndPermissionSettings;
@Nullable @Nullable
@Override @Override
@ -93,6 +97,9 @@ public class CallSettingsFragment extends SettingsFragment {
mVoiceMailUri = mRootView.findViewById(R.id.pref_voice_mail); mVoiceMailUri = mRootView.findViewById(R.id.pref_voice_mail);
mAutoAnswerTime.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI); mAutoAnswerTime.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI);
mDndPermissionSettings =
mRootView.findViewById(R.id.pref_grant_read_dnd_settings_permission);
} }
private void setListeners() { private void setListeners() {
@ -196,6 +203,15 @@ public class CallSettingsFragment extends SettingsFragment {
mPrefs.setVoiceMailUri(newValue); mPrefs.setVoiceMailUri(newValue);
} }
}); });
mDndPermissionSettings.setListener(
new SettingListenerBase() {
@Override
public void onClicked() {
startActivity(
new Intent("android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS"));
}
});
} }
private void updateValues() { private void updateValues() {
@ -218,6 +234,9 @@ public class CallSettingsFragment extends SettingsFragment {
mVoiceMailUri.setValue(mPrefs.getVoiceMailUri()); mVoiceMailUri.setValue(mPrefs.getVoiceMailUri());
mDndPermissionSettings.setVisibility(
Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60) ? View.VISIBLE : View.GONE);
setListeners(); setListeners();
} }

View file

@ -889,6 +889,14 @@ public class LinphonePreferences {
getConfig().setBool("app", "link_popup_enabled", enable); getConfig().setBool("app", "link_popup_enabled", enable);
} }
public boolean isDNDSettingsPopupEnabled() {
return getConfig().getBool("app", "dnd_settings_popup_enabled", true);
}
public void enableDNDSettingsPopup(boolean enable) {
getConfig().setBool("app", "dnd_settings_popup_enabled", enable);
}
public boolean isLimeSecurityPopupEnabled() { public boolean isLimeSecurityPopupEnabled() {
return getConfig().getBool("app", "lime_security_popup_enabled", true); return getConfig().getBool("app", "lime_security_popup_enabled", true);
} }

View file

@ -35,6 +35,8 @@ import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.compatibility.Compatibility;
import org.linphone.core.Address;
import org.linphone.core.Call; import org.linphone.core.Call;
import org.linphone.core.Core; import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub; import org.linphone.core.CoreListenerStub;
@ -82,7 +84,7 @@ public class AndroidAudioManager {
requestAudioFocus(STREAM_RING); requestAudioFocus(STREAM_RING);
mRingingCall = call; mRingingCall = call;
startRinging(); startRinging(call.getRemoteAddress());
// otherwise there is the beep // otherwise there is the beep
} }
} else if (call == mRingingCall && mIsRinging) { } else if (call == mRingingCall && mIsRinging) {
@ -281,13 +283,20 @@ public class AndroidAudioManager {
} }
} }
private synchronized void startRinging() { private synchronized void startRinging(Address remoteAddress) {
if (!LinphonePreferences.instance().isDeviceRingtoneEnabled()) { if (!LinphonePreferences.instance().isDeviceRingtoneEnabled()) {
// Enable speaker audio route, linphone library will do the ringing itself automatically // Enable speaker audio route, linphone library will do the ringing itself automatically
routeAudioToSpeaker(); routeAudioToSpeaker();
return; return;
} }
boolean doNotDisturbPolicyAllowsRinging =
Compatibility.isDoNotDisturbPolicyAllowingRinging(mContext, remoteAddress);
if (!doNotDisturbPolicyAllowsRinging) {
Log.e("[Audio Manager] Do not ring as Android Do Not Disturb Policy forbids it");
return;
}
if (mContext.getResources().getBoolean(R.bool.allow_ringing_while_early_media)) { if (mContext.getResources().getBoolean(R.bool.allow_ringing_while_early_media)) {
routeAudioToSpeaker(); // Need to be able to ear the ringtone during the early media routeAudioToSpeaker(); // Need to be able to ear the ringtone during the early media
} }

View file

@ -23,6 +23,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
linphone:title="@string/pref_vibrate_on_incoming_calls" /> linphone:title="@string/pref_vibrate_on_incoming_calls" />
<org.linphone.settings.widget.BasicSetting
android:id="@+id/pref_grant_read_dnd_settings_permission"
android:layout_width="match_parent"
android:layout_height="wrap_content"
linphone:title="@string/pref_grant_read_dnd_settings_permission_title"
linphone:subtitle="@string/pref_grant_read_dnd_settings_permission_desc"/>
<org.linphone.settings.widget.ListSetting <org.linphone.settings.widget.ListSetting
android:id="@+id/pref_media_encryption" android:id="@+id/pref_media_encryption"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -441,6 +441,8 @@
<string name="pref_voice_mail">Voice mail URI</string> <string name="pref_voice_mail">Voice mail URI</string>
<string name="pref_dialer_call">Use Linphone as default phone app</string> <string name="pref_dialer_call">Use Linphone as default phone app</string>
<string name="pref_accept_early_media">Accept early-media</string> <string name="pref_accept_early_media">Accept early-media</string>
<string name="pref_grant_read_dnd_settings_permission_title">Do Not Disturb settings</string>
<string name="pref_grant_read_dnd_settings_permission_desc">We need you to grant use the access to the Do Not Disturb settings to correctly ring or not depending on the current policy</string>
<!-- Chat settings --> <!-- Chat settings -->
<string name="pref_chat_title">Chat</string> <string name="pref_chat_title">Chat</string>