diff options
author | zafir <zafir@google.com> | 2015-07-07 02:21:44 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2015-07-07 02:21:44 +0000 |
commit | b03e914a5e889fb140553d0a33a69a5c1ddbf896 (patch) | |
tree | 1b43ca91c0d06ab9ae3f95a5d257551592b39bbd | |
parent | cfa12ed54554c0dd7c96d404e6279f0fd7b4d04b (diff) | |
parent | 2b4101fe49818cc4b71105386820c9d753ca5aaa (diff) | |
download | android_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.xml | 12 | ||||
-rw-r--r-- | res/values/strings.xml | 3 | ||||
-rw-r--r-- | src/com/android/camera/CameraActivity.java | 149 | ||||
-rw-r--r-- | src/com/android/camera/PermissionsActivity.java | 183 | ||||
-rw-r--r-- | src/com/android/camera/settings/Keys.java | 1 |
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. |