summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzafir <zafir@google.com>2015-07-07 02:21:44 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-07-07 02:21:44 +0000
commitb03e914a5e889fb140553d0a33a69a5c1ddbf896 (patch)
tree1b43ca91c0d06ab9ae3f95a5d257551592b39bbd
parentcfa12ed54554c0dd7c96d404e6279f0fd7b4d04b (diff)
parent2b4101fe49818cc4b71105386820c9d753ca5aaa (diff)
downloadandroid_packages_apps_Camera2-b03e914a5e889fb140553d0a33a69a5c1ddbf896.tar.gz
android_packages_apps_Camera2-b03e914a5e889fb140553d0a33a69a5c1ddbf896.tar.bz2
android_packages_apps_Camera2-b03e914a5e889fb140553d0a33a69a5c1ddbf896.zip
am 2b4101fe: am ad44cda8: Minimum viable Android M runtime permissions handling for H.
* commit '2b4101fe49818cc4b71105386820c9d753ca5aaa': Minimum viable Android M runtime permissions handling for H.
-rw-r--r--AndroidManifest.xml12
-rw-r--r--res/values/strings.xml3
-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
5 files changed, 296 insertions, 52 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 07647ae45..0498a26f8 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -6,7 +6,7 @@
<uses-sdk
android:minSdkVersion="19"
- android:targetSdkVersion="21" />
+ android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
@@ -19,7 +19,6 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.NFC" />
- <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
@@ -72,6 +71,15 @@
android:resource="@layout/keyguard_widget" />
</activity>
+ <activity
+ android:name="com.android.camera.PermissionsActivity"
+ android:label="@string/app_name"
+ android:parentActivityName="com.android.camera.CameraActivity" >
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="com.android.camera.CameraActivity" />
+ </activity>
+
<activity-alias
android:name="com.android.camera.CameraLauncher"
android:label="@string/app_name"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1df88030f..4e98a7a48 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -136,6 +136,9 @@
<!-- message for the dialog showing that the user's photo could not be saved [CHAR LIMIT=NONE] -->
<string name="error_media_storage_failure">There was a problem saving your photo or video.</string>
+ <!-- message for the dialog showing that the app does not have sufficient permissions [CHAR LIMIT=NONE] -->
+ <string name="error_permissions">The app does not have critical permissions needed to run. Please check your permissions settings.</string>
+
<!-- Reason used for session failure which is visible in UI [CHAR LIMIT=NONE] -->
<string name="reason_storage_failure">Photo storage failure.</string>
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.