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