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