Handle runtime permissions

For now, we handle runtime permissions in the most
hamfisted and maintainable way possible: don't let
any Activity's be fully created unless we get all the
runtime permissions.

Bug: 20066216
Change-Id: Iaab9ad2a8106d34b5e20a1eae1ef7a5560dc49a2
diff --git a/src/com/android/contacts/common/activity/RequestPermissionsActivity.java b/src/com/android/contacts/common/activity/RequestPermissionsActivity.java
new file mode 100644
index 0000000..994f9f5
--- /dev/null
+++ b/src/com/android/contacts/common/activity/RequestPermissionsActivity.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.contacts.common.activity;
+
+import android.Manifest.permission;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Trace;
+
+/**
+ * Repeatedly ask the user for runtime permissions, until they grant all the permissions.
+ * For now only handles activities that are only designed for use in Contacts.
+ */
+public class RequestPermissionsActivity extends Activity {
+    public static final String PREVIOUS_ACTIVITY_INTENT = "previous_intent";
+
+    private static final int PERMISSIONS_REQUEST_ALL_PERMISSIONS = 1;
+    private static String[] permissions = new String[]{
+            permission.CALL_PHONE,
+            permission.READ_CONTACTS,
+            permission.WRITE_CONTACTS,
+            permission.MANAGE_ACCOUNTS,
+            permission.GET_ACCOUNTS,
+            permission.ACCESS_FINE_LOCATION,
+            permission.ACCESS_COARSE_LOCATION,
+            permission.READ_PROFILE,
+            permission.WRITE_PROFILE,
+            permission.INTERNET,
+            permission.NFC,
+            permission.READ_PHONE_STATE,
+            permission.WAKE_LOCK,
+            permission.WRITE_EXTERNAL_STORAGE,
+            permission.WRITE_SETTINGS,
+            permission.USE_CREDENTIALS,
+            permission.VIBRATE,
+            permission.READ_SYNC_SETTINGS,
+            permission.INSTALL_SHORTCUT,
+            permission.READ_CALL_LOG,
+            permission.READ_SMS,
+            permission.READ_CALENDAR,
+            // This following permission can't be requested as a runtime permission.
+            //permission.READ_VOICEMAIL
+    };
+
+    private Intent mPreviousActivityIntent;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mPreviousActivityIntent = (Intent) getIntent().getExtras().get(PREVIOUS_ACTIVITY_INTENT);
+        requestPermissions();
+    }
+
+    /**
+     * If any permissions the Contacts app needs are missing, open an Activity
+     * to prompt the user for these permissions. Moreover, finish the current activity.
+     *
+     * This is designed to be called inside {@link android.app.Activity#onCreate}
+     */
+    public static boolean startPermissionActivity(Activity activity) {
+        if (!RequestPermissionsActivity.hasPermissions(activity)) {
+            final Intent intent = new Intent(activity,  RequestPermissionsActivity.class);
+            intent.putExtra(PREVIOUS_ACTIVITY_INTENT, activity.getIntent());
+            activity.startActivity(intent);
+            activity.finish();
+            return true;
+        }
+        return false;
+    }
+
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String permissions[],
+            int[] grantResults) {
+        if (isAllGranted(grantResults)) {
+            mPreviousActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+            startActivity(mPreviousActivityIntent);
+            finish();
+            overridePendingTransition(0, 0);
+        } else {
+            requestPermissions();
+        }
+    }
+
+    private boolean isAllGranted(int[] grantResult) {
+        for (int result : grantResult) {
+            if (result != PackageManager.PERMISSION_GRANTED) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void requestPermissions() {
+        Trace.beginSection("requestPermissions");
+        requestPermissions(permissions, PERMISSIONS_REQUEST_ALL_PERMISSIONS);
+        Trace.endSection();
+    }
+
+    public static boolean hasPermissions(Context context) {
+        Trace.beginSection("hasPermission");
+        try {
+            for (String permission : permissions) {
+                if (context.checkSelfPermission(permission)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    return false;
+                }
+            }
+            return true;
+        } finally {
+            Trace.endSection();
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/common/vcard/ImportVCardActivity.java b/src/com/android/contacts/common/vcard/ImportVCardActivity.java
index 62e8431..0f12ded 100644
--- a/src/com/android/contacts/common/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/common/vcard/ImportVCardActivity.java
@@ -46,6 +46,7 @@
 import android.widget.Toast;
 
 import com.android.contacts.common.R;
+import com.android.contacts.common.activity.RequestPermissionsActivity;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.common.util.AccountSelectionUtil;
@@ -824,6 +825,10 @@
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
 
+        if (RequestPermissionsActivity.startPermissionActivity(this)) {
+            return;
+        }
+
         String accountName = null;
         String accountType = null;
         String dataSet = null;
diff --git a/src/com/android/contacts/common/vcard/NfcImportVCardActivity.java b/src/com/android/contacts/common/vcard/NfcImportVCardActivity.java
index 0837fbc..0634df4 100644
--- a/src/com/android/contacts/common/vcard/NfcImportVCardActivity.java
+++ b/src/com/android/contacts/common/vcard/NfcImportVCardActivity.java
@@ -32,6 +32,7 @@
 import android.util.Log;
 
 import com.android.contacts.common.R;
+import com.android.contacts.common.activity.RequestPermissionsActivity;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.common.util.ImplicitIntentsUtil;
@@ -150,6 +151,10 @@
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
 
+        if (RequestPermissionsActivity.startPermissionActivity(this)) {
+            return;
+        }
+
         Intent intent = getIntent();
         if (!NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
             Log.w(TAG, "Unknowon intent " + intent);