summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorzafir <zafir@google.com>2015-06-29 00:08:22 -0500
committerzafir <zafir@google.com>2015-07-06 18:20:51 -0700
commitad44cda82fe6ec5ee090115129223c6314f9e1bb (patch)
treec5d9c85d389b19b3ed7334c3d7149d5475202725 /src
parentfd6f316adc5abfa071534642688bdf46f47a3925 (diff)
downloadandroid_packages_apps_Camera2-ad44cda82fe6ec5ee090115129223c6314f9e1bb.tar.gz
android_packages_apps_Camera2-ad44cda82fe6ec5ee090115129223c6314f9e1bb.tar.bz2
android_packages_apps_Camera2-ad44cda82fe6ec5ee090115129223c6314f9e1bb.zip
Minimum viable Android M runtime permissions handling for H.
Creates new activity for permissions handling: both checking for permissions and handling error condition when critical permissions are not present. The reason for creating a new activity is so the app does not attempt to continue executing OnCreate, OnResume etc, which opens the camera while the dialogs are showing. This should not slow the app down because the permissions activity will only run when a) the first time the app has insufficient permissions and b) when a critical permission is missing and the app needs to shut down. Bug: 21273463 Change-Id: I603acfb3057ba26b9cfa7935eb4cb24b5d547cb5
Diffstat (limited to 'src')
-rw-r--r--src/com/android/camera/CameraActivity.java149
-rw-r--r--src/com/android/camera/PermissionsActivity.java183
-rw-r--r--src/com/android/camera/settings/Keys.java1
3 files changed, 283 insertions, 50 deletions
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index a2c49c50b..586a66dd5 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -17,6 +17,7 @@
package com.android.camera;
+import android.Manifest;
import android.animation.Animator;
import android.app.ActionBar;
import android.app.Activity;
@@ -28,6 +29,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Matrix;
@@ -190,6 +192,9 @@ public class CameraActivity extends QuickActivity
private static final long SCREEN_DELAY_MS = 2 * 60 * 1000; // 2 mins.
/** Load metadata for 10 items ahead of our current. */
private static final int FILMSTRIP_PRELOAD_AHEAD_ITEMS = 10;
+ private static final int PERMISSIONS_ACTIVITY_REQUEST_CODE = 1;
+ private static final int PERMISSIONS_RESULT_CODE_OK = 0;
+ private static final int PERMISSIONS_RESULT_CODE_FAILED = 1;
/** Should be used wherever a context is needed. */
private Context mAppContext;
@@ -245,6 +250,7 @@ public class CameraActivity extends QuickActivity
private boolean mIsUndoingDeletion = false;
private boolean mIsActivityRunning = false;
private FatalErrorHandler mFatalErrorHandler;
+ private boolean mHasCriticalPermissions;
private final Uri[] mNfcPushUris = new Uri[1];
@@ -1434,7 +1440,7 @@ public class CameraActivity extends QuickActivity
mFeatureConfig = OneCameraFeatureConfigCreator.createDefault(getContentResolver(),
getServices().getMemoryManager());
mFatalErrorHandler = new FatalErrorHandlerImpl(this);
-
+ checkPermissions();
profile.mark();
if (!Glide.isSetup()) {
Context context = getAndroidContext();
@@ -1602,13 +1608,6 @@ public class CameraActivity extends QuickActivity
new PhotoDataFactory());
mVideoItemFactory = new VideoItemFactory(mAppContext, glideManager, appContentResolver,
new VideoDataFactory());
- mDataAdapter = new CameraFilmstripDataAdapter(mAppContext,
- mPhotoItemFactory, mVideoItemFactory);
- mDataAdapter.setLocalDataListener(mFilmstripItemListener);
-
- mPreloader = new Preloader<Integer, AsyncTask>(FILMSTRIP_PRELOAD_AHEAD_ITEMS, mDataAdapter,
- mDataAdapter);
-
mCameraAppUI.getFilmstripContentPanel().setFilmstripListener(mFilmstripListener);
if (mSettingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
Keys.KEY_SHOULD_SHOW_REFOCUS_VIEWER_CLING)) {
@@ -1623,45 +1622,6 @@ public class CameraActivity extends QuickActivity
mCurrentModule.init(this, isSecureCamera(), isCaptureIntent());
profile.mark("Init CurrentModule");
- if (!mSecureCamera) {
- mFilmstripController.setDataAdapter(mDataAdapter);
- if (!isCaptureIntent()) {
- mDataAdapter.requestLoad(new Callback<Void>() {
- @Override
- public void onCallback(Void result) {
- fillTemporarySessions();
- }
- });
- }
- } else {
- // Put a lock placeholder as the last image by setting its date to
- // 0.
- ImageView v = (ImageView) getLayoutInflater().inflate(
- R.layout.secure_album_placeholder, null);
- v.setTag(R.id.mediadata_tag_viewtype, FilmstripItemType.SECURE_ALBUM_PLACEHOLDER.ordinal());
- v.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- UsageStatistics.instance().changeScreen(NavigationChange.Mode.GALLERY,
- NavigationChange.InteractionCause.BUTTON);
- startGallery();
- finish();
- }
- });
- v.setContentDescription(getString(R.string.accessibility_unlock_to_camera));
- mDataAdapter = new FixedLastProxyAdapter(
- mAppContext,
- mDataAdapter,
- new PlaceholderItem(
- v,
- FilmstripItemType.SECURE_ALBUM_PLACEHOLDER,
- v.getDrawable().getIntrinsicWidth(),
- v.getDrawable().getIntrinsicHeight()));
- // Flush out all the original data.
- mDataAdapter.clear();
- mFilmstripController.setDataAdapter(mDataAdapter);
- }
-
setupNfcBeamPush();
mLocalImagesObserver = new FilmstripContentObserver();
@@ -1857,7 +1817,9 @@ public class CameraActivity extends QuickActivity
mLocalImagesObserver.setForegroundChangeListener(null);
mLocalImagesObserver.setActivityPaused(true);
mLocalVideosObserver.setActivityPaused(true);
- mPreloader.cancelAllLoads();
+ if (mPreloader != null) {
+ mPreloader.cancelAllLoads();
+ }
resetScreenOn();
mMotionManager.stop();
@@ -1887,7 +1849,6 @@ public class CameraActivity extends QuickActivity
@Override
public void onResumeTasks() {
mPaused = false;
-
if (!mSecureCamera) {
// Show the dialog if necessary. The rest resume logic will be invoked
// at the onFirstRunStateReady() callback.
@@ -1905,11 +1866,99 @@ public class CameraActivity extends QuickActivity
}
}
+ /**
+ * Checks if any of the needed Android runtime permissions are missing.
+ * If they are, then launch the permissions activity under one of the following conditions:
+ * a) The permissions dialogs have not run yet. We will ask for permission only once.
+ * b) If the missing permissions are critical to the app running, we will display a fatal error dialog.
+ * Critical permissions are: camera, microphone and storage. The app cannot run without them.
+ * Non-critical permission is location.
+ */
+ private void checkPermissions() {
+
+ if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED &&
+ checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED &&
+ checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
+ mHasCriticalPermissions = true;
+ } else {
+ mHasCriticalPermissions = false;
+ }
+
+ if ((checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
+ !mSettingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL, Keys.KEY_HAS_SEEN_PERMISSIONS_DIALOGS)) ||
+ !mHasCriticalPermissions) {
+ Intent intent = new Intent(this, PermissionsActivity.class);
+ startActivityForResult(intent, PERMISSIONS_ACTIVITY_REQUEST_CODE);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ // Close the app if critical permissions are missing.
+ if (requestCode == PERMISSIONS_ACTIVITY_REQUEST_CODE && resultCode == PERMISSIONS_RESULT_CODE_FAILED) {
+ finish();
+ }
+ }
+
+ private void preloadFilmstripItems() {
+ if (mDataAdapter == null) {
+ mDataAdapter = new CameraFilmstripDataAdapter(mAppContext,
+ mPhotoItemFactory, mVideoItemFactory);
+ mDataAdapter.setLocalDataListener(mFilmstripItemListener);
+ mPreloader = new Preloader<Integer, AsyncTask>(FILMSTRIP_PRELOAD_AHEAD_ITEMS, mDataAdapter,
+ mDataAdapter);
+ if (!mSecureCamera) {
+ mFilmstripController.setDataAdapter(mDataAdapter);
+ if (!isCaptureIntent()) {
+ mDataAdapter.requestLoad(new Callback<Void>() {
+ @Override
+ public void onCallback(Void result) {
+ fillTemporarySessions();
+ }
+ });
+ }
+ } else {
+ // Put a lock placeholder as the last image by setting its date to
+ // 0.
+ ImageView v = (ImageView) getLayoutInflater().inflate(
+ R.layout.secure_album_placeholder, null);
+ v.setTag(R.id.mediadata_tag_viewtype, FilmstripItemType.SECURE_ALBUM_PLACEHOLDER.ordinal());
+ v.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ UsageStatistics.instance().changeScreen(NavigationChange.Mode.GALLERY,
+ NavigationChange.InteractionCause.BUTTON);
+ startGallery();
+ finish();
+ }
+ });
+ v.setContentDescription(getString(R.string.accessibility_unlock_to_camera));
+ mDataAdapter = new FixedLastProxyAdapter(
+ mAppContext,
+ mDataAdapter,
+ new PlaceholderItem(
+ v,
+ FilmstripItemType.SECURE_ALBUM_PLACEHOLDER,
+ v.getDrawable().getIntrinsicWidth(),
+ v.getDrawable().getIntrinsicHeight()));
+ // Flush out all the original data.
+ mDataAdapter.clear();
+ mFilmstripController.setDataAdapter(mDataAdapter);
+ }
+ }
+ }
+
private void resume() {
Profile profile = mProfiler.create("CameraActivity.resume").start();
CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_RESUME);
Log.v(TAG, "Build info: " + Build.DISPLAY);
-
+ if (!mHasCriticalPermissions) {
+ Log.v(TAG, "Missing critical permissions.");
+ return;
+ }
+ preloadFilmstripItems();
updateStorageSpaceAndHint(null);
mLastLayoutOrientation = getResources().getConfiguration().orientation;
diff --git a/src/com/android/camera/PermissionsActivity.java b/src/com/android/camera/PermissionsActivity.java
new file mode 100644
index 000000000..af223d53f
--- /dev/null
+++ b/src/com/android/camera/PermissionsActivity.java
@@ -0,0 +1,183 @@
+package com.android.camera;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import com.android.camera.app.CameraServicesImpl;
+import com.android.camera.debug.Log;
+import com.android.camera.settings.Keys;
+import com.android.camera.settings.SettingsManager;
+import com.android.camera2.R;
+
+/**
+ * Activity that shows permissions request dialogs and handles lack of critical permissions.
+ */
+public class PermissionsActivity extends Activity {
+ private static final Log.Tag TAG = new Log.Tag("PermissionsActivity");
+
+ private static int PERMISSION_REQUEST_CODE = 1;
+ private static int RESULT_CODE_OK = 0;
+ private static int RESULT_CODE_FAILED = 1;
+
+ private int mIndexPermissionRequestCamera;
+ private int mIndexPermissionRequestMicrophone;
+ private int mIndexPermissionRequestLocation;
+ private int mIndexPermissionRequestStorage;
+ private boolean mShouldRequestCameraPermission;
+ private boolean mShouldRequestMicrophonePermission;
+ private boolean mShouldRequestLocationPermission;
+ private boolean mShouldRequestStoragePermission;
+ private int mNumPermissionsToRequest;
+ private boolean mFlagHasCameraPermission;
+ private boolean mFlagHasMicrophonePermission;
+ private boolean mFlagHasStoragePermission;
+ private SettingsManager mSettingsManager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mSettingsManager = CameraServicesImpl.instance().getSettingsManager();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mNumPermissionsToRequest = 0;
+ checkPermissions();
+ }
+
+ private void checkPermissions() {
+ if (checkSelfPermission(Manifest.permission.CAMERA)
+ != PackageManager.PERMISSION_GRANTED) {
+ mNumPermissionsToRequest++;
+ mShouldRequestCameraPermission = true;
+ } else {
+ mFlagHasCameraPermission = true;
+ }
+
+ if (checkSelfPermission(Manifest.permission.RECORD_AUDIO)
+ != PackageManager.PERMISSION_GRANTED) {
+ mNumPermissionsToRequest++;
+ mShouldRequestMicrophonePermission = true;
+ } else {
+ mFlagHasMicrophonePermission = true;
+ }
+
+ if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+ mNumPermissionsToRequest++;
+ mShouldRequestStoragePermission = true;
+ } else {
+ mFlagHasStoragePermission = true;
+ }
+
+ if (checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED) {
+ mNumPermissionsToRequest++;
+ mShouldRequestLocationPermission = true;
+ }
+
+ if (mNumPermissionsToRequest != 0) {
+ if (!mSettingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
+ Keys.KEY_HAS_SEEN_PERMISSIONS_DIALOGS)) {
+ buildPermissionsRequest();
+ } else {
+ //Permissions dialog has already been shown, and we're still missing permissions.
+ handlePermissionsFailure();
+ }
+ } else {
+ handlePermissionsSuccess();
+ }
+ }
+
+ private void buildPermissionsRequest() {
+ String[] permissionsToRequest = new String[mNumPermissionsToRequest];
+ int permissionsRequestIndex = 0;
+
+ if (mShouldRequestCameraPermission) {
+ permissionsToRequest[permissionsRequestIndex] = Manifest.permission.CAMERA;
+ mIndexPermissionRequestCamera = permissionsRequestIndex;
+ permissionsRequestIndex++;
+ }
+ if (mShouldRequestMicrophonePermission) {
+ permissionsToRequest[permissionsRequestIndex] = Manifest.permission.RECORD_AUDIO;
+ mIndexPermissionRequestMicrophone = permissionsRequestIndex;
+ permissionsRequestIndex++;
+ }
+ if (mShouldRequestStoragePermission) {
+ permissionsToRequest[permissionsRequestIndex] = Manifest.permission.READ_EXTERNAL_STORAGE;
+ mIndexPermissionRequestStorage = permissionsRequestIndex;
+ permissionsRequestIndex++;
+ }
+ if (mShouldRequestLocationPermission) {
+ permissionsToRequest[permissionsRequestIndex] = Manifest.permission.ACCESS_COARSE_LOCATION;
+ mIndexPermissionRequestLocation = permissionsRequestIndex;
+ }
+
+ requestPermissions(permissionsToRequest, PERMISSION_REQUEST_CODE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[], int[] grantResults) {
+ mSettingsManager.set(
+ SettingsManager.SCOPE_GLOBAL,
+ Keys.KEY_HAS_SEEN_PERMISSIONS_DIALOGS,
+ true);
+
+ if (mShouldRequestCameraPermission) {
+ if (grantResults[mIndexPermissionRequestCamera] == PackageManager.PERMISSION_GRANTED) {
+ mFlagHasCameraPermission = true;
+ } else {
+ handlePermissionsFailure();
+ }
+ }
+ if (mShouldRequestMicrophonePermission) {
+ if (grantResults[mIndexPermissionRequestMicrophone] == PackageManager.PERMISSION_GRANTED) {
+ mFlagHasMicrophonePermission = true;
+ } else {
+ handlePermissionsFailure();
+ }
+ }
+ if (mShouldRequestStoragePermission) {
+ if (grantResults[mIndexPermissionRequestStorage] == PackageManager.PERMISSION_GRANTED) {
+ mFlagHasStoragePermission = true;
+ } else {
+ handlePermissionsFailure();
+ }
+ }
+
+ if (mShouldRequestLocationPermission) {
+ if (grantResults[mIndexPermissionRequestLocation] == PackageManager.PERMISSION_GRANTED) {
+ // Do nothing
+ } else {
+ // Do nothing
+ }
+ }
+
+ if (mFlagHasCameraPermission && mFlagHasMicrophonePermission && mFlagHasStoragePermission) {
+ handlePermissionsSuccess();
+ }
+ }
+
+ private void handlePermissionsSuccess() {
+ setResult(RESULT_CODE_OK, null);
+ finish();
+ }
+
+ private void handlePermissionsFailure() {
+ new AlertDialog.Builder(this).setTitle(getResources().getString(R.string.camera_error_title))
+ .setMessage(getResources().getString(R.string.error_permissions))
+ .setPositiveButton(getResources().getString(R.string.dialog_dismiss), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ setResult(RESULT_CODE_FAILED, null);
+ finish();
+ }
+ })
+ .show();
+ }
+}
diff --git a/src/com/android/camera/settings/Keys.java b/src/com/android/camera/settings/Keys.java
index 8712d4ef7..0339ea6c7 100644
--- a/src/com/android/camera/settings/Keys.java
+++ b/src/com/android/camera/settings/Keys.java
@@ -80,6 +80,7 @@ public class Keys {
public static final String KEY_HDR_PLUS_FLASH_MODE = "pref_hdr_plus_flash_mode";
public static final String KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING =
"pref_should_show_settings_button_cling";
+ public static final String KEY_HAS_SEEN_PERMISSIONS_DIALOGS = "pref_has_seen_permissions_dialogs";
/**
* Set some number of defaults for the defined keys.