Move Android permission handling out of K9Activity

This commit is contained in:
cketti 2020-05-18 20:44:43 +02:00
parent 4cb7e46494
commit f393c3a00f
6 changed files with 117 additions and 71 deletions

View file

@ -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;
}
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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
);
}

View file

@ -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)

View file

@ -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"
}
}