diff --git a/app/src/main/java/dummydomain/yetanothercallblocker/PermissionHelper.java b/app/src/main/java/dummydomain/yetanothercallblocker/PermissionHelper.java index f652ba9..1fc9bdb 100644 --- a/app/src/main/java/dummydomain/yetanothercallblocker/PermissionHelper.java +++ b/app/src/main/java/dummydomain/yetanothercallblocker/PermissionHelper.java @@ -22,6 +22,7 @@ import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; import com.android.settings.applications.PreferredSettingsUtils; @@ -65,29 +66,46 @@ public class PermissionHelper { CONTACTS_PERMISSIONS.add(Manifest.permission.READ_CONTACTS); } - public static void checkPermissions(Activity activity, boolean info, - boolean block, boolean contacts) { + public static List getMissingPermissions(Context context, boolean info, + boolean block, boolean contacts) { Set requiredPermissions = new HashSet<>(); if (info) requiredPermissions.addAll(INFO_PERMISSIONS); if (block) requiredPermissions.addAll(BLOCKING_PERMISSIONS); if (contacts) requiredPermissions.addAll(CONTACTS_PERMISSIONS); - List missingPermissions = new ArrayList<>(requiredPermissions.size()); + List missingPermissions = new ArrayList<>(); for (String permission : requiredPermissions) { - if (ContextCompat.checkSelfPermission(activity, permission) + if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) { missingPermissions.add(permission); } } + return missingPermissions; + } + + public static void checkPermissions(Activity activity, boolean info, + boolean block, boolean contacts) { + List missingPermissions = getMissingPermissions(activity, info, block, contacts); + if (!missingPermissions.isEmpty()) { ActivityCompat.requestPermissions(activity, missingPermissions.toArray(new String[0]), REQUEST_CODE_PERMISSIONS); } } + public static void checkPermissions(Context context, Fragment fragment, + boolean info, boolean block, boolean contacts) { + List missingPermissions = getMissingPermissions(context, info, block, contacts); + + if (!missingPermissions.isEmpty()) { + fragment.requestPermissions( + missingPermissions.toArray(new String[0]), REQUEST_CODE_PERMISSIONS); + } + } + public static void handlePermissionsResult(@NonNull Context context, int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults, @@ -166,11 +184,15 @@ public class PermissionHelper { } public static RequestToken requestCallScreening(Activity activity) { + return requestCallScreening(activity, null); + } + + public static RequestToken requestCallScreening(Activity activity, Fragment fragment) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - RoleManagerHelper.requestCallScreeningRole(activity); + RoleManagerHelper.requestCallScreeningRole(activity, fragment); return null; } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - return setAsDefaultDialer(activity); + return setAsDefaultDialer(activity, fragment); } return null; } @@ -183,36 +205,36 @@ public class PermissionHelper { return isDefaultDialer(context); } - public static void disableCallScreening(Context context) { + public static void disableCallScreening(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - if (RoleManagerHelper.hasCallScreeningRole(context)) { - new AlertDialog.Builder(context) + if (RoleManagerHelper.hasCallScreeningRole(activity)) { + new AlertDialog.Builder(activity) .setTitle(R.string.default_caller_id_app) .setMessage(R.string.default_caller_id_app_unset) .setPositiveButton(R.string.open_system_settings, - (d, w) -> openDefaultAppsSettings(context)) + (d, w) -> openDefaultAppsSettings(activity)) .show(); return; } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - if (isDefaultDialer(context)) { - showDefaultDialerDialog(context, false); + if (isDefaultDialer(activity)) { + showDefaultDialerDialog(activity, false); } } } - public static boolean handleCallScreeningResult(Context context, + public static boolean handleCallScreeningResult(Activity activity, int requestCode, int resultCode, RequestToken requestToken) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - if (RoleManagerHelper.handleCallScreeningResult(context, requestCode, resultCode)) { + if (RoleManagerHelper.handleCallScreeningResult(activity, requestCode, resultCode)) { return true; } } - return handleDefaultDialerResult(context, requestCode, resultCode, requestToken); + return handleDefaultDialerResult(activity, requestCode, resultCode, requestToken); } public static boolean isDefaultDialer(Context context) { @@ -226,6 +248,11 @@ public class PermissionHelper { @RequiresApi(Build.VERSION_CODES.N) public static RequestToken setAsDefaultDialer(Activity activity) { + return setAsDefaultDialer(activity, null); + } + + @RequiresApi(Build.VERSION_CODES.N) + public static RequestToken setAsDefaultDialer(Activity activity, Fragment fragment) { if (isDefaultDialer(activity)) return null; Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER); @@ -233,7 +260,12 @@ public class PermissionHelper { BuildConfig.APPLICATION_ID); try { - activity.startActivityForResult(intent, REQUEST_CODE_DEFAULT_DIALER); + if (fragment != null) { + fragment.startActivityForResult(intent, REQUEST_CODE_DEFAULT_DIALER); + } else { + activity.startActivityForResult(intent, REQUEST_CODE_DEFAULT_DIALER); + } + return new RequestToken(System.nanoTime()); } catch (Exception e) { LOG.warn("setAsDefaultDialer()", e); @@ -245,17 +277,17 @@ public class PermissionHelper { } @RequiresApi(Build.VERSION_CODES.N) - private static void setAsDefaultDialerFallback(Context context) { - showDefaultDialerDialog(context, true); + private static void setAsDefaultDialerFallback(Activity activity) { + showDefaultDialerDialog(activity, true); } @RequiresApi(Build.VERSION_CODES.N) - private static void showDefaultDialerDialog(Context context, boolean set) { - new AlertDialog.Builder(context) + private static void showDefaultDialerDialog(Activity activity, boolean set) { + new AlertDialog.Builder(activity) .setTitle(R.string.default_phone_app) .setMessage(set ? R.string.default_phone_app_set : R.string.default_phone_app_unset) .setPositiveButton(R.string.open_system_settings, - (d, w) -> openDefaultDialerSettings(context)) + (d, w) -> openDefaultDialerSettings(activity)) .show(); } @@ -333,7 +365,7 @@ public class PermissionHelper { return intent; } - public static boolean handleDefaultDialerResult(Context context, + public static boolean handleDefaultDialerResult(Activity activity, int requestCode, int resultCode, RequestToken requestToken) { if (requestCode != REQUEST_CODE_DEFAULT_DIALER) return false; @@ -343,9 +375,9 @@ public class PermissionHelper { if (requestToken != null && System.nanoTime() - requestToken.timestamp < TimeUnit.MILLISECONDS.toNanos(500)) { // probably the request is not supported, try workarounds - setAsDefaultDialerFallback(context); + setAsDefaultDialerFallback(activity); } else { - Toast.makeText(context, R.string.denied_default_dialer_message, Toast.LENGTH_LONG) + Toast.makeText(activity, R.string.denied_default_dialer_message, Toast.LENGTH_LONG) .show(); } diff --git a/app/src/main/java/dummydomain/yetanothercallblocker/RoleManagerHelper.java b/app/src/main/java/dummydomain/yetanothercallblocker/RoleManagerHelper.java index f4bef67..f89b5da 100644 --- a/app/src/main/java/dummydomain/yetanothercallblocker/RoleManagerHelper.java +++ b/app/src/main/java/dummydomain/yetanothercallblocker/RoleManagerHelper.java @@ -9,6 +9,7 @@ import android.os.Build; import android.widget.Toast; import androidx.annotation.RequiresApi; +import androidx.fragment.app.Fragment; import static android.content.Context.ROLE_SERVICE; import static java.util.Objects.requireNonNull; @@ -25,10 +26,19 @@ public class RoleManagerHelper { public static void requestCallScreeningRole(Activity activity) { if (hasCallScreeningRole(activity)) return; - Intent intent = getRoleManager(activity) - .createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING); + activity.startActivityForResult( + getCallScreeningRequestIntent(activity), REQUEST_CODE_CALL_SCREENING); + } - activity.startActivityForResult(intent, REQUEST_CODE_CALL_SCREENING); + public static void requestCallScreeningRole(Context context, Fragment fragment) { + if (hasCallScreeningRole(context)) return; + + fragment.startActivityForResult( + getCallScreeningRequestIntent(context), REQUEST_CODE_CALL_SCREENING); + } + + private static Intent getCallScreeningRequestIntent(Context context) { + return getRoleManager(context).createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING); } public static boolean handleCallScreeningResult(Context context, diff --git a/app/src/main/java/dummydomain/yetanothercallblocker/SettingsActivity.java b/app/src/main/java/dummydomain/yetanothercallblocker/SettingsActivity.java index ea29dda..ae7c016 100644 --- a/app/src/main/java/dummydomain/yetanothercallblocker/SettingsActivity.java +++ b/app/src/main/java/dummydomain/yetanothercallblocker/SettingsActivity.java @@ -14,7 +14,6 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceGroup; @@ -37,19 +36,12 @@ import dummydomain.yetanothercallblocker.work.UpdateScheduler; public class SettingsActivity extends AppCompatActivity implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback { - private static final String STATE_REQUEST_TOKEN = "STATE_REQUEST_TOKEN"; - - private PermissionHelper.RequestToken requestToken; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.settings_activity); - if (savedInstanceState != null) { - requestToken = PermissionHelper.RequestToken - .fromSavedInstanceState(savedInstanceState, STATE_REQUEST_TOKEN); - } else { + if (savedInstanceState == null) { getSupportFragmentManager() .beginTransaction() .replace(R.id.settings, new RootSettingsFragment()) @@ -62,22 +54,6 @@ public class SettingsActivity extends AppCompatActivity } } - @Override - protected void onResume() { - super.onResume(); - - updateCallScreeningPreference(); - } - - @Override - protected void onSaveInstanceState(@NonNull Bundle outState) { - super.onSaveInstanceState(outState); - - if (requestToken != null) { - requestToken.onSaveInstanceState(outState, STATE_REQUEST_TOKEN); - } - } - @Override public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat, PreferenceScreen preferenceScreen) { @@ -93,36 +69,6 @@ public class SettingsActivity extends AppCompatActivity return true; } - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - - Settings settings = App.getSettings(); - - PermissionHelper.handlePermissionsResult(this, requestCode, permissions, grantResults, - settings.getIncomingCallNotifications(), settings.getCallBlockingEnabled(), - settings.getUseContacts()); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if (PermissionHelper.handleCallScreeningResult( - this, requestCode, resultCode, requestToken)) { - updateCallScreeningPreference(); - } - } - - private void updateCallScreeningPreference() { - for (Fragment fragment : getSupportFragmentManager().getFragments()) { - if (fragment instanceof BaseSettingsFragment) { - ((RootSettingsFragment) fragment).updateCallScreeningPreference(); - } - } - } - public static abstract class BaseSettingsFragment extends PreferenceFragmentCompat implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback { @@ -225,12 +171,61 @@ public class SettingsActivity extends AppCompatActivity private static final String PREF_NOTIFICATIONS_BLOCKED_NON_PERSISTENT = "showNotificationsForBlockedCallsNonPersistent"; private static final String PREF_SCREEN_ADVANCED = "screenAdvanced"; + private static final String STATE_REQUEST_TOKEN = "STATE_REQUEST_TOKEN"; + private final UpdateScheduler updateScheduler = UpdateScheduler.get(App.getInstance()); + private PermissionHelper.RequestToken requestToken; + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + + Settings settings = App.getSettings(); + + PermissionHelper.handlePermissionsResult(requireContext(), + requestCode, permissions, grantResults, + settings.getIncomingCallNotifications(), settings.getCallBlockingEnabled(), + settings.getUseContacts()); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (PermissionHelper.handleCallScreeningResult( + requireActivity(), requestCode, resultCode, requestToken)) { + updateCallScreeningPreference(); + } + } + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + super.onCreatePreferences(savedInstanceState, rootKey); + + requestToken = PermissionHelper.RequestToken + .fromSavedInstanceState(savedInstanceState, STATE_REQUEST_TOKEN); + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + + if (requestToken != null) { + requestToken.onSaveInstanceState(outState, STATE_REQUEST_TOKEN); + } + } + @Override public void onStart() { super.onStart(); + // may be changed externally + updateCallScreeningPreference(); + + // needs to be updated after the confirmation dialog was closed + // due to activity recreation (orientation change, etc.) updateBlockedCallNotificationsPreference(); } @@ -243,14 +238,16 @@ public class SettingsActivity extends AppCompatActivity protected void initScreen() { setPrefChangeListener(Settings.PREF_INCOMING_CALL_NOTIFICATIONS, (pref, newValue) -> { if (Boolean.TRUE.equals(newValue)) { - PermissionHelper.checkPermissions(requireActivity(), true, false, false); + PermissionHelper.checkPermissions(requireContext(), this, + true, false, false); } return true; }); Preference.OnPreferenceChangeListener callBlockingListener = (preference, newValue) -> { if (Boolean.TRUE.equals(newValue)) { - PermissionHelper.checkPermissions(requireActivity(), false, true, false); + PermissionHelper.checkPermissions(requireContext(), this, + false, true, false); } return true; }; @@ -263,16 +260,9 @@ public class SettingsActivity extends AppCompatActivity callScreeningPref.setChecked(PermissionHelper.isCallScreeningHeld(requireContext())); callScreeningPref.setOnPreferenceChangeListener((preference, newValue) -> { if (Boolean.TRUE.equals(newValue)) { - FragmentActivity activity = requireActivity(); - - PermissionHelper.RequestToken requestToken - = PermissionHelper.requestCallScreening(activity); - - if (activity instanceof SettingsActivity) { - ((SettingsActivity) activity).requestToken = requestToken; - } + requestToken = PermissionHelper.requestCallScreening(requireActivity(), this); } else { - PermissionHelper.disableCallScreening(requireContext()); + PermissionHelper.disableCallScreening(requireActivity()); return false; } return true; @@ -310,7 +300,8 @@ public class SettingsActivity extends AppCompatActivity setPrefChangeListener(Settings.PREF_USE_CONTACTS, (preference, newValue) -> { if (Boolean.TRUE.equals(newValue)) { - PermissionHelper.checkPermissions(requireActivity(), false, false, true); + PermissionHelper.checkPermissions(requireContext(), this, + false, false, true); } return true; }); @@ -357,24 +348,18 @@ public class SettingsActivity extends AppCompatActivity } } + private void updateCallScreeningPreference() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return; + + this.requirePreference(PREF_USE_CALL_SCREENING_SERVICE) + .setChecked(PermissionHelper.isCallScreeningHeld(requireContext())); + } + private void updateBlockedCallNotificationsPreference() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) return; - SwitchPreferenceCompat notificationsForBlockedPref = - findPreference(PREF_NOTIFICATIONS_BLOCKED_NON_PERSISTENT); - - if (notificationsForBlockedPref != null) { - notificationsForBlockedPref.setChecked( - App.getSettings().getNotificationsForBlockedCalls()); - } - } - - public void updateCallScreeningPreference() { - SwitchPreferenceCompat callScreeningPref - = findPreference(PREF_USE_CALL_SCREENING_SERVICE); - if (callScreeningPref != null) { - callScreeningPref.setChecked(PermissionHelper.isCallScreeningHeld(requireContext())); - } + this.requirePreference(PREF_NOTIFICATIONS_BLOCKED_NON_PERSISTENT) + .setChecked(App.getSettings().getNotificationsForBlockedCalls()); } @Override