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 import android.widget.Toast; 31 32 import java.util.ArrayList; 33 import java.util.Arrays; 34 35 /** 36 * Activity that asks the user for all {@link #getDesiredPermissions} if any of 37 * {@link #getRequiredPermissions} are missing. 38 * 39 * NOTE: As a result of b/22095159, this can behave oddly in the case where the final permission 40 * you are requesting causes an application restart. 41 */ 42 public abstract class RequestPermissionsActivityBase extends Activity 43 implements ActivityCompat.OnRequestPermissionsResultCallback { 44 45 public static final String PREVIOUS_ACTIVITY_INTENT = "previous_intent"; 46 47 /** Whether the permissions activity was already started. */ 48 protected static final String STARTED_PERMISSIONS_ACTIVITY = "started_permissions_activity"; 49 50 protected static final String EXTRA_IS_CALLER_SELF = "is_caller_self"; 51 52 private static final int PERMISSIONS_REQUEST_ALL_PERMISSIONS = 1; 53 54 /** 55 * @return list of permissions that are needed in order for {@link #PREVIOUS_ACTIVITY_INTENT} to 56 * operate. You only need to return a single permission per permission group you care about. 57 */ getRequiredPermissions()58 protected abstract String[] getRequiredPermissions(); 59 60 /** 61 * @return list of permissions that would be useful for {@link #PREVIOUS_ACTIVITY_INTENT} to 62 * operate. You only need to return a single permission per permission group you care about. 63 */ getDesiredPermissions()64 protected abstract String[] getDesiredPermissions(); 65 66 protected Intent mPreviousActivityIntent; 67 68 /** If true then start the target activity "for result" after permissions are granted. */ 69 protected boolean mIsCallerSelf; 70 71 @Override onCreate(Bundle savedInstanceState)72 protected void onCreate(Bundle savedInstanceState) { 73 super.onCreate(savedInstanceState); 74 mPreviousActivityIntent = (Intent) getIntent().getExtras().get(PREVIOUS_ACTIVITY_INTENT); 75 mIsCallerSelf = getIntent().getBooleanExtra(EXTRA_IS_CALLER_SELF, false); 76 77 // Only start a requestPermissions() flow when first starting this activity the first time. 78 // The process is likely to be restarted during the permission flow (necessary to enable 79 // permissions) so this is important to track. 80 if (savedInstanceState == null) { 81 requestPermissions(); 82 } 83 } 84 85 /** 86 * If any permissions the Contacts app needs are missing, open an Activity 87 * to prompt the user for these permissions. Moreover, finish the current activity. 88 * 89 * This is designed to be called inside {@link android.app.Activity#onCreate} 90 */ startPermissionActivity(Activity activity, String[] requiredPermissions, Class<?> newActivityClass)91 protected static boolean startPermissionActivity(Activity activity, 92 String[] requiredPermissions, Class<?> newActivityClass) { 93 return startPermissionActivity(activity, requiredPermissions, /* isCallerSelf */ false, 94 newActivityClass); 95 } 96 startPermissionActivity(Activity activity, String[] requiredPermissions, boolean isCallerSelf, Class<?> newActivityClass)97 protected static boolean startPermissionActivity(Activity activity, 98 String[] requiredPermissions, boolean isCallerSelf, Class<?> newActivityClass) { 99 if (!hasPermissions(activity, requiredPermissions)) { 100 final Intent intent = new Intent(activity, newActivityClass); 101 activity.getIntent().putExtra(STARTED_PERMISSIONS_ACTIVITY, true); 102 intent.putExtra(PREVIOUS_ACTIVITY_INTENT, activity.getIntent()); 103 intent.putExtra(EXTRA_IS_CALLER_SELF, isCallerSelf); 104 activity.startActivity(intent); 105 activity.finish(); 106 return true; 107 } 108 109 // Account type initialization must be delayed until the Contacts permission group 110 // has been granted (since GET_ACCOUNTS) falls under that groups. Previously it 111 // was initialized in ContactApplication which would cause problems as 112 // AccountManager.getAccounts would return an empty array. See b/22690336 113 AccountTypeManager.getInstance(activity); 114 115 return false; 116 } 117 isAllGranted(String permissions[], int[] grantResult)118 protected boolean isAllGranted(String permissions[], int[] grantResult) { 119 for (int i = 0; i < permissions.length; i++) { 120 if (grantResult[i] != PackageManager.PERMISSION_GRANTED 121 && isPermissionRequired(permissions[i])) { 122 return false; 123 } 124 } 125 return true; 126 } 127 isPermissionRequired(String p)128 private boolean isPermissionRequired(String p) { 129 return Arrays.asList(getRequiredPermissions()).contains(p); 130 } 131 requestPermissions()132 private void requestPermissions() { 133 Trace.beginSection("requestPermissions"); 134 try { 135 // Construct a list of missing permissions 136 final ArrayList<String> unsatisfiedPermissions = new ArrayList<>(); 137 for (String permission : getDesiredPermissions()) { 138 if (checkSelfPermission(permission) 139 != PackageManager.PERMISSION_GRANTED) { 140 unsatisfiedPermissions.add(permission); 141 } 142 } 143 if (unsatisfiedPermissions.size() == 0) { 144 throw new RuntimeException("Request permission activity was called even" 145 + " though all permissions are satisfied."); 146 } 147 ActivityCompat.requestPermissions( 148 this, 149 unsatisfiedPermissions.toArray(new String[unsatisfiedPermissions.size()]), 150 PERMISSIONS_REQUEST_ALL_PERMISSIONS); 151 } finally { 152 Trace.endSection(); 153 } 154 } 155 156 @Override checkSelfPermission(String permission)157 public int checkSelfPermission(String permission) { 158 return ContextCompat.checkSelfPermission(this, permission); 159 } 160 hasPermissions(Context context, String[] permissions)161 protected static boolean hasPermissions(Context context, String[] permissions) { 162 Trace.beginSection("hasPermission"); 163 try { 164 for (String permission : permissions) { 165 if (ContextCompat.checkSelfPermission(context, permission) 166 != PackageManager.PERMISSION_GRANTED) { 167 return false; 168 } 169 } 170 return true; 171 } finally { 172 Trace.endSection(); 173 } 174 } 175 } 176