1 /*
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 
17 package com.android.contacts.common.activity;
18 
19 import com.android.contacts.common.R;
20 import com.android.contacts.common.model.AccountTypeManager;
21 
22 import android.app.Activity;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.PackageManager;
26 import android.os.Bundle;
27 import android.os.Trace;
28 import android.support.v4.app.ActivityCompat;
29 import android.support.v4.content.ContextCompat;
30 
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 
34 /**
35  * Activity that asks the user for all {@link #getDesiredPermissions} if any of
36  * {@link #getRequiredPermissions} are missing.
37  *
38  * NOTE: As a result of b/22095159, this can behave oddly in the case where the final permission
39  * you are requesting causes an application restart.
40  */
41 public abstract class RequestPermissionsActivityBase extends Activity
42         implements ActivityCompat.OnRequestPermissionsResultCallback {
43 
44     public static final String PREVIOUS_ACTIVITY_INTENT = "previous_intent";
45 
46     /** Whether the permissions activity was already started. */
47     protected static final String STARTED_PERMISSIONS_ACTIVITY = "started_permissions_activity";
48 
49     private static final int PERMISSIONS_REQUEST_ALL_PERMISSIONS = 1;
50 
51     /**
52      * @return list of permissions that are needed in order for {@link #PREVIOUS_ACTIVITY_INTENT} to
53      * operate. You only need to return a single permission per permission group you care about.
54      */
getRequiredPermissions()55     protected abstract String[] getRequiredPermissions();
56 
57     /**
58      * @return list of permissions that would be useful for {@link #PREVIOUS_ACTIVITY_INTENT} to
59      * operate. You only need to return a single permission per permission group you care about.
60      */
getDesiredPermissions()61     protected abstract String[] getDesiredPermissions();
62 
63     protected Intent mPreviousActivityIntent;
64 
65     @Override
onCreate(Bundle savedInstanceState)66     protected void onCreate(Bundle savedInstanceState) {
67         super.onCreate(savedInstanceState);
68         mPreviousActivityIntent = (Intent) getIntent().getExtras().get(PREVIOUS_ACTIVITY_INTENT);
69 
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      */
startPermissionActivity(Activity activity, String[] requiredPermissions, Class<?> newActivityClass)84     protected static boolean startPermissionActivity(Activity activity,
85             String[] requiredPermissions, Class<?> newActivityClass) {
86         if (!hasPermissions(activity, requiredPermissions)) {
87             final Intent intent = new Intent(activity,  newActivityClass);
88             activity.getIntent().putExtra(STARTED_PERMISSIONS_ACTIVITY, true);
89             intent.putExtra(PREVIOUS_ACTIVITY_INTENT, activity.getIntent());
90             activity.startActivity(intent);
91             activity.finish();
92             return true;
93         }
94 
95         // Account type initialization must be delayed until the Contacts permission group
96         // has been granted (since GET_ACCOUNTS) falls under that groups.  Previously it
97         // was initialized in ContactApplication which would cause problems as
98         // AccountManager.getAccounts would return an empty array. See b/22690336
99         AccountTypeManager.getInstance(activity);
100 
101         return false;
102     }
103 
isAllGranted(String permissions[], int[] grantResult)104     protected boolean isAllGranted(String permissions[], int[] grantResult) {
105         for (int i = 0; i < permissions.length; i++) {
106             if (grantResult[i] != PackageManager.PERMISSION_GRANTED
107                     && isPermissionRequired(permissions[i])) {
108                 return false;
109             }
110         }
111         return true;
112     }
113 
isPermissionRequired(String p)114     private boolean isPermissionRequired(String p) {
115         return Arrays.asList(getRequiredPermissions()).contains(p);
116     }
117 
requestPermissions()118     private void requestPermissions() {
119         Trace.beginSection("requestPermissions");
120         try {
121             // Construct a list of missing permissions
122             final ArrayList<String> unsatisfiedPermissions = new ArrayList<>();
123             for (String permission : getDesiredPermissions()) {
124                 if (checkSelfPermission(permission)
125                         != PackageManager.PERMISSION_GRANTED) {
126                     unsatisfiedPermissions.add(permission);
127                 }
128             }
129             if (unsatisfiedPermissions.size() == 0) {
130                 throw new RuntimeException("Request permission activity was called even"
131                         + " though all permissions are satisfied.");
132             }
133             ActivityCompat.requestPermissions(
134                     this,
135                     unsatisfiedPermissions.toArray(new String[unsatisfiedPermissions.size()]),
136                     PERMISSIONS_REQUEST_ALL_PERMISSIONS);
137         } finally {
138             Trace.endSection();
139         }
140     }
141 
142     @Override
checkSelfPermission(String permission)143     public int checkSelfPermission(String permission) {
144         return ContextCompat.checkSelfPermission(this, permission);
145     }
146 
hasPermissions(Context context, String[] permissions)147     protected static boolean hasPermissions(Context context, String[] permissions) {
148         Trace.beginSection("hasPermission");
149         try {
150             for (String permission : permissions) {
151                 if (ContextCompat.checkSelfPermission(context, permission)
152                         != PackageManager.PERMISSION_GRANTED) {
153                     return false;
154                 }
155             }
156             return true;
157         } finally {
158             Trace.endSection();
159         }
160     }
161 }
162