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.activities;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.PackageManager;
23 import android.os.Bundle;
24 import android.os.Trace;
25 import androidx.core.app.ActivityCompat;
26 
27 import com.android.contacts.model.AccountTypeManager;
28 import com.android.contacts.util.PermissionsUtil;
29 
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 
33 /**
34  * Activity that asks the user for all {@link #getPermissions} if any are missing.
35  *
36  * NOTE: As a result of b/22095159, this can behave oddly in the case where the final permission
37  * you are requesting causes an application restart.
38  */
39 public abstract class RequestPermissionsActivityBase extends Activity
40         implements ActivityCompat.OnRequestPermissionsResultCallback {
41 
42     public static final String PREVIOUS_ACTIVITY_INTENT = "previous_intent";
43 
44     /** Whether the permissions activity was already started. */
45     protected static final String EXTRA_STARTED_PERMISSIONS_ACTIVITY =
46             "started_permissions_activity";
47 
48     protected static final String EXTRA_IS_CALLER_SELF = "is_caller_self";
49 
50     private static final int PERMISSIONS_REQUEST_ALL_PERMISSIONS = 1;
51 
52     /**
53      * @return list of permissions that are needed in order for {@link #PREVIOUS_ACTIVITY_INTENT}
54      * to operate. You only need to return a single permission per permission group you care about.
55      */
getPermissions()56     protected abstract String[] getPermissions();
57 
58     protected Intent mPreviousActivityIntent;
59 
60     /** If true then start the target activity "for result" after permissions are granted. */
61     protected boolean mIsCallerSelf;
62 
63     @Override
onCreate(Bundle savedInstanceState)64     protected void onCreate(Bundle savedInstanceState) {
65         super.onCreate(savedInstanceState);
66         mPreviousActivityIntent = (Intent) getIntent().getExtras().get(PREVIOUS_ACTIVITY_INTENT);
67         mIsCallerSelf = getIntent().getBooleanExtra(EXTRA_IS_CALLER_SELF, false);
68 
69         // Only start a requestPermissions() flow when first starting this activity the first time.
70         // The process is likely to be restarted during the permission flow (necessary to enable
71         // permissions) so this is important to track.
72         if (savedInstanceState == null) {
73             requestPermissions();
74         }
75     }
76 
77     /**
78      * If any permissions the Contacts app needs are missing, open an Activity
79      * to prompt the user for these permissions. Moreover, finish the current activity.
80      *
81      * This is designed to be called inside {@link android.app.Activity#onCreate}
82      */
startPermissionActivity(Activity activity, String[] requiredPermissions, Class<?> newActivityClass)83     protected static boolean startPermissionActivity(Activity activity,
84             String[] requiredPermissions, Class<?> newActivityClass) {
85         return startPermissionActivity(activity, requiredPermissions, /* isCallerSelf */ false,
86                 newActivityClass);
87     }
88 
startPermissionActivity(Activity activity, String[] requiredPermissions, boolean isCallerSelf, Class<?> newActivityClass)89     protected static boolean startPermissionActivity(Activity activity,
90                 String[] requiredPermissions, boolean isCallerSelf, Class<?> newActivityClass) {
91         if (!hasPermissions(activity, requiredPermissions)) {
92             final Intent intent = new Intent(activity,  newActivityClass);
93             activity.getIntent().putExtra(EXTRA_STARTED_PERMISSIONS_ACTIVITY, true);
94             intent.putExtra(PREVIOUS_ACTIVITY_INTENT, activity.getIntent());
95             intent.putExtra(EXTRA_IS_CALLER_SELF, isCallerSelf);
96             activity.startActivity(intent);
97             activity.finish();
98             return true;
99         }
100 
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 
107         return false;
108     }
109 
isAllGranted(String permissions[], int[] grantResult)110     protected boolean isAllGranted(String permissions[], int[] grantResult) {
111         for (int i = 0; i < permissions.length; i++) {
112             if (grantResult[i] != PackageManager.PERMISSION_GRANTED
113                     && isPermissionRequired(permissions[i])) {
114                 return false;
115             }
116         }
117         return true;
118     }
119 
isPermissionRequired(String p)120     private boolean isPermissionRequired(String p) {
121         return Arrays.asList(getPermissions()).contains(p);
122     }
123 
requestPermissions()124     private void requestPermissions() {
125         Trace.beginSection("requestPermissions");
126         try {
127             // Construct a list of missing permissions
128             final ArrayList<String> unsatisfiedPermissions = new ArrayList<>();
129             for (String permission : getPermissions()) {
130                 if (!PermissionsUtil.hasPermission(this, permission)) {
131                     unsatisfiedPermissions.add(permission);
132                 }
133             }
134             if (unsatisfiedPermissions.size() == 0) {
135                 throw new RuntimeException("Request permission activity was called even"
136                         + " though all permissions are satisfied.");
137             }
138             ActivityCompat.requestPermissions(
139                     this,
140                     unsatisfiedPermissions.toArray(new String[unsatisfiedPermissions.size()]),
141                     PERMISSIONS_REQUEST_ALL_PERMISSIONS);
142         } finally {
143             Trace.endSection();
144         }
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 (!PermissionsUtil.hasPermission(context, permission)) {
152                     return false;
153                 }
154             }
155             return true;
156         } finally {
157             Trace.endSection();
158         }
159     }
160 }
161