blob: a692ab968395ff00c299708ffef31e11da14f00a [file] [log] [blame]
Brian Attwellc1b73282015-06-23 23:26:27 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.contacts.common.activity;
18
19import com.android.contacts.common.R;
Walter Jang60951f82015-07-23 11:05:30 -070020import com.android.contacts.common.model.AccountTypeManager;
Brian Attwellc1b73282015-06-23 23:26:27 -070021
22import android.app.Activity;
23import android.content.Context;
24import android.content.Intent;
25import android.content.pm.PackageManager;
26import android.os.Bundle;
27import android.os.Trace;
28import android.widget.Toast;
29
30import java.util.ArrayList;
31import java.util.Arrays;
32
33/**
34 * Activity that asks the user for all {@link #getDesiredPermissions} if any of
35 * {@link #getRequiredPermissions} are missing.
36 *
37 * NOTE: As a result of b/22095159, this can behave oddly in the case where the final permission
38 * you are requesting causes an application restart.
39 */
40public abstract class RequestPermissionsActivityBase extends Activity {
41 public static final String PREVIOUS_ACTIVITY_INTENT = "previous_intent";
Walter Jang6b87ef12016-11-04 14:03:39 -070042
43 protected static final String EXTRA_IS_CALLER_SELF = "is_caller_self";
44
Brian Attwellc1b73282015-06-23 23:26:27 -070045 private static final int PERMISSIONS_REQUEST_ALL_PERMISSIONS = 1;
46
47 /**
48 * @return list of permissions that are needed in order for {@link #PREVIOUS_ACTIVITY_INTENT} to
49 * operate. You only need to return a single permission per permission group you care about.
50 */
51 protected abstract String[] getRequiredPermissions();
52
53 /**
54 * @return list of permissions that would be useful for {@link #PREVIOUS_ACTIVITY_INTENT} to
55 * operate. You only need to return a single permission per permission group you care about.
56 */
57 protected abstract String[] getDesiredPermissions();
58
59 private Intent mPreviousActivityIntent;
60
Walter Jang6b87ef12016-11-04 14:03:39 -070061 /** If true then start the target activity "for result" after permissions are granted. */
62 protected boolean mIsCallerSelf;
63
Brian Attwellc1b73282015-06-23 23:26:27 -070064 @Override
65 protected void onCreate(Bundle savedInstanceState) {
66 super.onCreate(savedInstanceState);
67 mPreviousActivityIntent = (Intent) getIntent().getExtras().get(PREVIOUS_ACTIVITY_INTENT);
Walter Jang6b87ef12016-11-04 14:03:39 -070068 mIsCallerSelf = getIntent().getBooleanExtra(EXTRA_IS_CALLER_SELF, false);
Brian Attwellc1b73282015-06-23 23:26:27 -070069
70 // Only start a requestPermissions() flow when first starting this activity the first time.
71 // The process is likely to be restarted during the permission flow (necessary to enable
72 // permissions) so this is important to track.
73 if (savedInstanceState == null) {
74 requestPermissions();
75 }
76 }
77
78 /**
79 * If any permissions the Contacts app needs are missing, open an Activity
80 * to prompt the user for these permissions. Moreover, finish the current activity.
81 *
82 * This is designed to be called inside {@link android.app.Activity#onCreate}
83 */
84 protected static boolean startPermissionActivity(Activity activity,
85 String[] requiredPermissions, Class<?> newActivityClass) {
Walter Jang6b87ef12016-11-04 14:03:39 -070086 return startPermissionActivity(activity, requiredPermissions, /* isCallerSelf */ false,
87 newActivityClass);
88 }
89
90 protected static boolean startPermissionActivity(Activity activity,
91 String[] requiredPermissions, boolean isCallerSelf, Class<?> newActivityClass) {
Brian Attwellc1b73282015-06-23 23:26:27 -070092 if (!RequestPermissionsActivity.hasPermissions(activity, requiredPermissions)) {
93 final Intent intent = new Intent(activity, newActivityClass);
94 intent.putExtra(PREVIOUS_ACTIVITY_INTENT, activity.getIntent());
Walter Jang6b87ef12016-11-04 14:03:39 -070095 intent.putExtra(EXTRA_IS_CALLER_SELF, isCallerSelf);
Brian Attwellc1b73282015-06-23 23:26:27 -070096 activity.startActivity(intent);
97 activity.finish();
98 return true;
99 }
Walter Jang60951f82015-07-23 11:05:30 -0700100
101 // Account type initialization must be delayed until the Contacts permission group
102 // has been granted (since GET_ACCOUNTS) falls under that groups. Previously it
103 // was initialized in ContactApplication which would cause problems as
104 // AccountManager.getAccounts would return an empty array. See b/22690336
105 AccountTypeManager.getInstance(activity);
106
Brian Attwellc1b73282015-06-23 23:26:27 -0700107 return false;
108 }
109
110 @Override
111 public void onRequestPermissionsResult(int requestCode, String permissions[],
112 int[] grantResults) {
Yorke Lee957cbe22015-08-11 18:02:18 -0700113 if (permissions != null && permissions.length > 0
114 && isAllGranted(permissions, grantResults)) {
Brian Attwellc1b73282015-06-23 23:26:27 -0700115 mPreviousActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
Walter Jang6b87ef12016-11-04 14:03:39 -0700116 if (mIsCallerSelf) {
117 startActivityForResult(mPreviousActivityIntent, 0);
118 } else {
119 startActivity(mPreviousActivityIntent);
120 }
Brian Attwellc1b73282015-06-23 23:26:27 -0700121 finish();
122 overridePendingTransition(0, 0);
123 } else {
124 Toast.makeText(this, R.string.missing_required_permission, Toast.LENGTH_SHORT).show();
125 finish();
126 }
127 }
128
129 private boolean isAllGranted(String permissions[], int[] grantResult) {
130 for (int i = 0; i < permissions.length; i++) {
131 if (grantResult[i] != PackageManager.PERMISSION_GRANTED
132 && isPermissionRequired(permissions[i])) {
133 return false;
134 }
135 }
136 return true;
137 }
138
139 private boolean isPermissionRequired(String p) {
140 return Arrays.asList(getRequiredPermissions()).contains(p);
141 }
142
143 private void requestPermissions() {
144 Trace.beginSection("requestPermissions");
145 try {
146 // Construct a list of missing permissions
147 final ArrayList<String> unsatisfiedPermissions = new ArrayList<>();
148 for (String permission : getDesiredPermissions()) {
149 if (checkSelfPermission(permission)
150 != PackageManager.PERMISSION_GRANTED) {
151 unsatisfiedPermissions.add(permission);
152 }
153 }
154 if (unsatisfiedPermissions.size() == 0) {
155 throw new RuntimeException("Request permission activity was called even"
156 + " though all permissions are satisfied.");
157 }
158 requestPermissions(
159 unsatisfiedPermissions.toArray(new String[unsatisfiedPermissions.size()]),
160 PERMISSIONS_REQUEST_ALL_PERMISSIONS);
161 } finally {
162 Trace.endSection();
163 }
164 }
165
166 protected static boolean hasPermissions(Context context, String[] permissions) {
167 Trace.beginSection("hasPermission");
168 try {
169 for (String permission : permissions) {
170 if (context.checkSelfPermission(permission)
171 != PackageManager.PERMISSION_GRANTED) {
172 return false;
173 }
174 }
175 return true;
176 } finally {
177 Trace.endSection();
178 }
179 }
180}