From f393c3a00fb0a48744076983209df1a0020e4417 Mon Sep 17 00:00:00 2001 From: cketti Date: Mon, 18 May 2020 20:44:43 +0200 Subject: [PATCH] Move Android permission handling out of K9Activity --- .../java/com/fsck/k9/activity/K9Activity.java | 56 +------------------ .../com/fsck/k9/activity/MessageCompose.java | 24 +++++++- .../com/fsck/k9/activity/MessageList.java | 25 ++++++++- .../com/fsck/k9/ui/permissions/Permission.kt | 21 +++++++ .../PermissionRationaleDialogFragment.kt | 25 ++++----- .../k9/ui/permissions/PermissionUiHelper.kt | 37 ++++++++++++ 6 files changed, 117 insertions(+), 71 deletions(-) create mode 100644 app/ui/src/main/java/com/fsck/k9/ui/permissions/Permission.kt create mode 100644 app/ui/src/main/java/com/fsck/k9/ui/permissions/PermissionUiHelper.kt diff --git a/app/ui/src/main/java/com/fsck/k9/activity/K9Activity.java b/app/ui/src/main/java/com/fsck/k9/activity/K9Activity.java index 1df533874..77570f372 100644 --- a/app/ui/src/main/java/com/fsck/k9/activity/K9Activity.java +++ b/app/ui/src/main/java/com/fsck/k9/activity/K9Activity.java @@ -1,28 +1,17 @@ package com.fsck.k9.activity; -import android.Manifest; -import android.content.pm.PackageManager; import android.os.Bundle; + import androidx.annotation.LayoutRes; -import androidx.annotation.StringRes; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; -import android.view.View; import com.fsck.k9.ui.R; import com.fsck.k9.ui.ThemeManager; -import com.fsck.k9.ui.permissions.PermissionRationaleDialogFragment; -import timber.log.Timber; public abstract class K9Activity extends AppCompatActivity { - public static final int PERMISSIONS_REQUEST_READ_CONTACTS = 1; - private static final String FRAGMENT_TAG_RATIONALE = "rationale"; - - private final K9ActivityCommon base = new K9ActivityCommon(this, ThemeType.DEFAULT); public ThemeManager getThemeManager() { @@ -49,47 +38,4 @@ public abstract class K9Activity extends AppCompatActivity { } setSupportActionBar(toolbar); } - - public boolean hasPermission(Permission permission) { - return ContextCompat.checkSelfPermission(this, permission.permission) == PackageManager.PERMISSION_GRANTED; - } - - public void requestPermissionOrShowRationale(Permission permission) { - if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permission)) { - PermissionRationaleDialogFragment dialogFragment = - PermissionRationaleDialogFragment.newInstance(permission); - - dialogFragment.show(getSupportFragmentManager(), FRAGMENT_TAG_RATIONALE); - } else { - requestPermission(permission); - } - } - - public void requestPermission(Permission permission) { - Timber.i("Requesting permission: " + permission.permission); - ActivityCompat.requestPermissions(this, new String[] { permission.permission }, permission.requestCode); - } - - - public enum Permission { - READ_CONTACTS( - Manifest.permission.READ_CONTACTS, - PERMISSIONS_REQUEST_READ_CONTACTS, - R.string.permission_contacts_rationale_title, - R.string.permission_contacts_rationale_message - ); - - - public final String permission; - public final int requestCode; - public final int rationaleTitle; - public final int rationaleMessage; - - Permission(String permission, int requestCode, @StringRes int rationaleTitle, @StringRes int rationaleMessage) { - this.permission = permission; - this.requestCode = requestCode; - this.rationaleTitle = rationaleTitle; - this.rationaleMessage = rationaleMessage; - } - } } diff --git a/app/ui/src/main/java/com/fsck/k9/activity/MessageCompose.java b/app/ui/src/main/java/com/fsck/k9/activity/MessageCompose.java index 7f298b318..3569b838a 100644 --- a/app/ui/src/main/java/com/fsck/k9/activity/MessageCompose.java +++ b/app/ui/src/main/java/com/fsck/k9/activity/MessageCompose.java @@ -110,7 +110,11 @@ import com.fsck.k9.ui.compose.QuotedMessageMvpView; import com.fsck.k9.ui.compose.QuotedMessagePresenter; import com.fsck.k9.ui.helper.SizeFormatter; import com.fsck.k9.ui.messagelist.DefaultFolderProvider; +import com.fsck.k9.ui.permissions.K9PermissionUiHelper; +import com.fsck.k9.ui.permissions.Permission; +import com.fsck.k9.ui.permissions.PermissionUiHelper; +import org.jetbrains.annotations.NotNull; import org.openintents.openpgp.OpenPgpApiManager; import org.openintents.openpgp.util.OpenPgpApi; import timber.log.Timber; @@ -120,7 +124,7 @@ import timber.log.Timber; public class MessageCompose extends K9Activity implements OnClickListener, CancelListener, AttachmentDownloadCancelListener, OnFocusChangeListener, OnOpenPgpInlineChangeListener, OnOpenPgpSignOnlyChangeListener, MessageBuilder.Callback, - AttachmentPresenter.AttachmentsChangedListener, OnOpenPgpDisableListener { + AttachmentPresenter.AttachmentsChangedListener, OnOpenPgpDisableListener, PermissionUiHelper { private static final int DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE = 1; private static final int DIALOG_CONFIRM_DISCARD_ON_BACK = 2; @@ -177,6 +181,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, private final MessageLoaderHelperFactory messageLoaderHelperFactory = DI.get(MessageLoaderHelperFactory.class); private final DefaultFolderProvider defaultFolderProvider = DI.get(DefaultFolderProvider.class); + private final PermissionUiHelper permissionUiHelper = new K9PermissionUiHelper(this); + private QuotedMessagePresenter quotedMessagePresenter; private MessageLoaderHelper messageLoaderHelper; private AttachmentPresenter attachmentPresenter; @@ -1883,4 +1889,20 @@ public class MessageCompose extends K9Activity implements OnClickListener, return titleResource; } } + + @Override + public boolean hasPermission(@NotNull Permission permission) { + return permissionUiHelper.hasPermission(permission); + } + + @Override + public void requestPermissionOrShowRationale(@NotNull Permission permission) { + permissionUiHelper.requestPermissionOrShowRationale(permission); + } + + @Override + public void requestPermission(@NotNull Permission permission) { + permissionUiHelper.requestPermission(permission); + } + } diff --git a/app/ui/src/main/java/com/fsck/k9/activity/MessageList.java b/app/ui/src/main/java/com/fsck/k9/activity/MessageList.java index 38ce2a501..954b231f5 100644 --- a/app/ui/src/main/java/com/fsck/k9/activity/MessageList.java +++ b/app/ui/src/main/java/com/fsck/k9/activity/MessageList.java @@ -60,9 +60,15 @@ import com.fsck.k9.ui.messageview.MessageViewFragment; import com.fsck.k9.ui.messageview.MessageViewFragment.MessageViewFragmentListener; import com.fsck.k9.ui.messageview.PlaceholderFragment; import com.fsck.k9.ui.onboarding.OnboardingActivity; +import com.fsck.k9.ui.permissions.K9PermissionUiHelper; +import com.fsck.k9.ui.permissions.Permission; +import com.fsck.k9.ui.permissions.PermissionUiHelper; import com.fsck.k9.view.ViewSwitcher; import com.fsck.k9.view.ViewSwitcher.OnSwitchCompleteListener; import com.mikepenz.materialdrawer.Drawer.OnDrawerListener; + +import org.jetbrains.annotations.NotNull; + import timber.log.Timber; @@ -72,7 +78,7 @@ import timber.log.Timber; * From this Activity the user can perform all standard message operations. */ public class MessageList extends K9Activity implements MessageListFragmentListener, - MessageViewFragmentListener, OnBackStackChangedListener, OnSwitchCompleteListener { + MessageViewFragmentListener, OnBackStackChangedListener, OnSwitchCompleteListener, PermissionUiHelper { private static final String EXTRA_SEARCH = "search_bytes"; private static final String EXTRA_NO_THREADING = "no_threading"; @@ -178,6 +184,8 @@ public class MessageList extends K9Activity implements MessageListFragmentListen private final NotificationChannelManager channelUtils = DI.get(NotificationChannelManager.class); private final DefaultFolderProvider defaultFolderProvider = DI.get(DefaultFolderProvider.class); + private final PermissionUiHelper permissionUiHelper = new K9PermissionUiHelper(this); + private ActionBar actionBar; private ActionBarDrawerToggle drawerToggle; private K9Drawer drawer; @@ -1622,4 +1630,19 @@ public class MessageList extends K9Activity implements MessageListFragmentListen drawer.deselect(); } } + + @Override + public boolean hasPermission(@NotNull Permission permission) { + return permissionUiHelper.hasPermission(permission); + } + + @Override + public void requestPermissionOrShowRationale(@NotNull Permission permission) { + permissionUiHelper.requestPermissionOrShowRationale(permission); + } + + @Override + public void requestPermission(@NotNull Permission permission) { + permissionUiHelper.requestPermission(permission); + } } diff --git a/app/ui/src/main/java/com/fsck/k9/ui/permissions/Permission.kt b/app/ui/src/main/java/com/fsck/k9/ui/permissions/Permission.kt new file mode 100644 index 000000000..d3e4595b9 --- /dev/null +++ b/app/ui/src/main/java/com/fsck/k9/ui/permissions/Permission.kt @@ -0,0 +1,21 @@ +package com.fsck.k9.ui.permissions + +import android.Manifest +import androidx.annotation.StringRes +import com.fsck.k9.ui.R + +private const val PERMISSIONS_REQUEST_READ_CONTACTS = 1 + +enum class Permission( + val permission: String, + val requestCode: Int, + @param:StringRes val rationaleTitle: Int, + @param:StringRes val rationaleMessage: Int +) { + READ_CONTACTS( + Manifest.permission.READ_CONTACTS, + PERMISSIONS_REQUEST_READ_CONTACTS, + R.string.permission_contacts_rationale_title, + R.string.permission_contacts_rationale_message + ); +} diff --git a/app/ui/src/main/java/com/fsck/k9/ui/permissions/PermissionRationaleDialogFragment.kt b/app/ui/src/main/java/com/fsck/k9/ui/permissions/PermissionRationaleDialogFragment.kt index b4e867259..91b41b187 100644 --- a/app/ui/src/main/java/com/fsck/k9/ui/permissions/PermissionRationaleDialogFragment.kt +++ b/app/ui/src/main/java/com/fsck/k9/ui/permissions/PermissionRationaleDialogFragment.kt @@ -5,15 +5,13 @@ import android.app.Dialog import android.os.Bundle import androidx.core.os.bundleOf import androidx.fragment.app.DialogFragment -import com.fsck.k9.activity.K9Activity -import com.fsck.k9.activity.K9Activity.Permission import com.fsck.k9.ui.R /** * A dialog displaying a message to explain why the app requests a certain permission. * - * Closing the dialog triggers a permission request. For this to work the Activity needs to be a subclass of - * [K9Activity]. + * Closing the dialog triggers a permission request. For this to work the Activity needs to implement + * [PermissionUiHelper]. */ class PermissionRationaleDialogFragment : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { @@ -22,22 +20,21 @@ class PermissionRationaleDialogFragment : DialogFragment() { val permission = Permission.valueOf(permissionName) - return AlertDialog.Builder(requireContext()).apply { - setTitle(permission.rationaleTitle) - setMessage(permission.rationaleMessage) - setPositiveButton(R.string.okay_action) { _, _ -> - val activity = requireActivity() as? K9Activity - ?: throw AssertionError("PermissionRationaleDialogFragment can only be used with K9Activity") + return AlertDialog.Builder(requireContext()) + .setTitle(permission.rationaleTitle) + .setMessage(permission.rationaleMessage) + .setPositiveButton(R.string.okay_action) { _, _ -> + val permissionUiHelper = requireActivity() as? PermissionUiHelper + ?: throw AssertionError("Activities using PermissionRationaleDialogFragment need to " + + "implement PermissionUiHelper") - activity.requestPermission(permission) - } - }.create() + permissionUiHelper.requestPermission(permission) + }.create() } companion object { private const val ARG_PERMISSION = "permission" - @JvmStatic fun newInstance(permission: Permission): PermissionRationaleDialogFragment { return PermissionRationaleDialogFragment().apply { arguments = bundleOf(ARG_PERMISSION to permission.name) diff --git a/app/ui/src/main/java/com/fsck/k9/ui/permissions/PermissionUiHelper.kt b/app/ui/src/main/java/com/fsck/k9/ui/permissions/PermissionUiHelper.kt new file mode 100644 index 000000000..31dc080a7 --- /dev/null +++ b/app/ui/src/main/java/com/fsck/k9/ui/permissions/PermissionUiHelper.kt @@ -0,0 +1,37 @@ +package com.fsck.k9.ui.permissions + +import android.content.pm.PackageManager +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import timber.log.Timber + +interface PermissionUiHelper { + fun hasPermission(permission: Permission): Boolean + fun requestPermissionOrShowRationale(permission: Permission) + fun requestPermission(permission: Permission) +} + +class K9PermissionUiHelper(private val activity: AppCompatActivity) : PermissionUiHelper { + override fun hasPermission(permission: Permission): Boolean { + return ContextCompat.checkSelfPermission(activity, permission.permission) == PackageManager.PERMISSION_GRANTED + } + + override fun requestPermissionOrShowRationale(permission: Permission) { + if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission.permission)) { + val dialogFragment = PermissionRationaleDialogFragment.newInstance(permission) + dialogFragment.show(activity.supportFragmentManager, FRAGMENT_TAG_RATIONALE) + } else { + requestPermission(permission) + } + } + + override fun requestPermission(permission: Permission) { + Timber.i("Requesting permission: " + permission.permission) + ActivityCompat.requestPermissions(activity, arrayOf(permission.permission), permission.requestCode) + } + + companion object { + private const val FRAGMENT_TAG_RATIONALE = "rationale" + } +}