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