diff --git a/app/build.gradle b/app/build.gradle index d2f5b7e49..4a1848c55 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -62,6 +62,7 @@ android { def supportVersion = "25.4.0" def spongycastleVersion = "1.58.0.0" +def permissionDispatcherVersion = "3.1.0" dependencies { androidTestCompile "junit:junit:4.12" @@ -72,7 +73,14 @@ dependencies { compile "com.android.support:cardview-v7:$supportVersion" compile "com.madgag.spongycastle:core:$spongycastleVersion" compile "com.madgag.spongycastle:prov:$spongycastleVersion" + // Time compile "joda-time:joda-time:2.9.9" compile "org.sufficientlysecure:html-textview:3.5" compile "com.nononsenseapps:filepicker:4.1.0" + // Permissions + compile ("com.github.hotchemi:permissionsdispatcher:$permissionDispatcherVersion") { + // if you don't use android.app.Fragment you can exclude support for them + exclude module: "support-v13" + } + annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:$permissionDispatcherVersion" } diff --git a/app/src/main/java/com/keepassdroid/fileselect/FileSelectActivity.java b/app/src/main/java/com/keepassdroid/fileselect/FileSelectActivity.java index e675eeefd..8a4e0eb69 100644 --- a/app/src/main/java/com/keepassdroid/fileselect/FileSelectActivity.java +++ b/app/src/main/java/com/keepassdroid/fileselect/FileSelectActivity.java @@ -22,15 +22,14 @@ package com.keepassdroid.fileselect; import android.Manifest; import android.content.ActivityNotFoundException; import android.content.ContentResolver; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.preference.PreferenceManager; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; +import android.support.v7.app.AlertDialog; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; @@ -41,19 +40,19 @@ import android.view.View; import android.widget.EditText; import android.widget.Toast; -import com.keepassdroid.fragments.AssignMasterKeyDialogFragment; -import com.keepassdroid.fragments.CreateFileDialogFragment; import com.keepassdroid.activities.GroupActivity; -import com.keepassdroid.password.PasswordActivity; -import com.keepassdroid.tasks.ProgressTask; import com.keepassdroid.app.App; import com.keepassdroid.compat.ContentResolverCompat; import com.keepassdroid.compat.StorageAF; import com.keepassdroid.database.edit.CreateDB; import com.keepassdroid.database.edit.FileOnFinish; import com.keepassdroid.database.exception.ContentFileNotFoundException; +import com.keepassdroid.fragments.AssignMasterKeyDialogFragment; +import com.keepassdroid.fragments.CreateFileDialogFragment; import com.keepassdroid.intents.Intents; +import com.keepassdroid.password.PasswordActivity; import com.keepassdroid.stylish.StylishActivity; +import com.keepassdroid.tasks.ProgressTask; import com.keepassdroid.utils.EmptyUtils; import com.keepassdroid.utils.Interaction; import com.keepassdroid.utils.MenuUtil; @@ -68,6 +67,14 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.net.URLDecoder; +import permissions.dispatcher.NeedsPermission; +import permissions.dispatcher.OnNeverAskAgain; +import permissions.dispatcher.OnPermissionDenied; +import permissions.dispatcher.OnShowRationale; +import permissions.dispatcher.PermissionRequest; +import permissions.dispatcher.RuntimePermissions; + +@RuntimePermissions public class FileSelectActivity extends StylishActivity implements CreateFileDialogFragment.DefinePathDialogListener , AssignMasterKeyDialogFragment.AssignPasswordDialogListener, @@ -77,7 +84,6 @@ public class FileSelectActivity extends StylishActivity implements private static final String TAG = "FileSelectActivity"; - private static final int MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE = 111; private RecyclerView mListFiles; private FileSelectAdapter mAdapter; private View fileListTitle; @@ -501,59 +507,37 @@ public class FileSelectActivity extends StylishActivity implements mAdapter.notifyDataSetChanged(); } - private void checkStoragePermission() { - // Here, thisActivity is the current activity - if (ContextCompat.checkSelfPermission(FileSelectActivity.this, - Manifest.permission.WRITE_EXTERNAL_STORAGE) - != PackageManager.PERMISSION_GRANTED) { + @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void checkStoragePermission() {} - // Should we show an explanation? - //if (ActivityCompat.shouldShowRequestPermissionRationale(FileSelectActivity.this, - // Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + @OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void showRationaleForExternalStorage(final PermissionRequest request) { + new AlertDialog.Builder(this) + .setMessage(R.string.permission_external_storage_rationale) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + request.proceed(); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + request.cancel(); + } + }) + .show(); + } - // Show an explanation to the user *asynchronously* -- don't block - // this thread waiting for the user's response! After the user - // sees the explanation, try again to request the permission. + @OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void showDeniedForExternalStorage() { + Toast.makeText(this, R.string.permission_external_storage_denied, Toast.LENGTH_SHORT).show(); + } - //} else { - - // No explanation needed, we can request the permission. - - ActivityCompat.requestPermissions(FileSelectActivity.this, - new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, - MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE); - - // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an - // app-defined int constant. The callback method gets the - // result of the request. - //} - } - } - - @Override - public void onRequestPermissionsResult(int requestCode, - String permissions[], int[] grantResults) { - switch (requestCode) { - case MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE: { - // If request is cancelled, the result arrays are empty. - if (grantResults.length > 0 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - - // permission was granted, yay! Do the - // contacts-related task you need to do. - - } else { - - // permission denied, boo! Disable the - // functionality that depends on this permission. - } - return; - } - - // other 'case' lines to check for other - // permissions this app might request - } - } + @OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void showNeverAskForExternalStorage() { + Toast.makeText(this, R.string.permission_external_storage_neverask, Toast.LENGTH_SHORT).show(); + } @Override public boolean onCreateOptionsMenu(Menu menu) { diff --git a/app/src/main/java/com/keepassdroid/password/PasswordActivity.java b/app/src/main/java/com/keepassdroid/password/PasswordActivity.java index 0a38451a6..7862c679b 100644 --- a/app/src/main/java/com/keepassdroid/password/PasswordActivity.java +++ b/app/src/main/java/com/keepassdroid/password/PasswordActivity.java @@ -19,6 +19,7 @@ */ package com.keepassdroid.password; +import android.Manifest; import android.app.Activity; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; @@ -32,6 +33,7 @@ import android.os.Handler; import android.preference.PreferenceManager; import android.support.annotation.RequiresApi; import android.support.v4.hardware.fingerprint.FingerprintManagerCompat; +import android.support.v7.app.AlertDialog; import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.TextWatcher; @@ -76,6 +78,14 @@ import static com.keepassdroid.fingerprint.FingerPrintHelper.Mode.NOT_CONFIGURED import static com.keepassdroid.fingerprint.FingerPrintHelper.Mode.OPEN_MODE; import static com.keepassdroid.fingerprint.FingerPrintHelper.Mode.STORE_MODE; +import permissions.dispatcher.NeedsPermission; +import permissions.dispatcher.OnNeverAskAgain; +import permissions.dispatcher.OnPermissionDenied; +import permissions.dispatcher.OnShowRationale; +import permissions.dispatcher.PermissionRequest; +import permissions.dispatcher.RuntimePermissions; + +@RuntimePermissions public class PasswordActivity extends LockingActivity implements FingerPrintHelper.FingerPrintCallback, UriIntentInitTaskCallback { @@ -261,6 +271,9 @@ public class PasswordActivity extends LockingActivity // For check shutdown super.onResume(); + // Check the storage permission + checkStoragePermission(); + new UriIntentInitTask(this, mRememberKeyfile) .execute(getIntent()); } @@ -843,4 +856,36 @@ public class PasswordActivity extends LockingActivity } } } + + @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void checkStoragePermission() {} + + @OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void showRationaleForExternalStorage(final PermissionRequest request) { + new AlertDialog.Builder(this) + .setMessage(R.string.permission_external_storage_rationale) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + request.proceed(); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + request.cancel(); + } + }) + .show(); + } + + @OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void showDeniedForExternalStorage() { + Toast.makeText(this, R.string.permission_external_storage_denied, Toast.LENGTH_SHORT).show(); + } + + @OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE) + void showNeverAskForExternalStorage() { + Toast.makeText(this, R.string.permission_external_storage_neverask, Toast.LENGTH_SHORT).show(); + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5ea88ee3e..a05193c51 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -253,6 +253,9 @@ View the full file path Use Recycle Bin Move a group or entry to the Recycle Bin before deleting + Application need external storage permission + Permission_external_storage_denied + Permission_external_storage_neverask 5 seconds