Home | History | Annotate | Download | only in com.example.android.system.runtimepermissions
      1 /*
      2 * Copyright 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.example.android.system.runtimepermissions;
     18 
     19 import com.example.android.common.logger.Log;
     20 import com.example.android.common.logger.LogFragment;
     21 import com.example.android.common.logger.LogWrapper;
     22 import com.example.android.common.logger.MessageOnlyLogFilter;
     23 import com.example.android.system.runtimepermissions.camera.CameraPreviewFragment;
     24 import com.example.android.system.runtimepermissions.contacts.ContactsFragment;
     25 
     26 import android.Manifest;
     27 import android.app.Activity;
     28 import android.content.Context;
     29 import android.content.pm.PackageManager;
     30 import android.os.Bundle;
     31 import android.support.annotation.NonNull;
     32 import android.support.design.widget.Snackbar;
     33 import android.support.v4.app.ActivityCompat;
     34 import android.support.v4.app.FragmentTransaction;
     35 import android.view.Menu;
     36 import android.view.MenuItem;
     37 import android.view.View;
     38 import android.widget.ViewAnimator;
     39 
     40 import common.activities.SampleActivityBase;
     41 
     42 /**
     43  * Launcher Activity that demonstrates the use of runtime permissions for Android M.
     44  * It contains a summary sample description, sample log and a Fragment that calls callbacks on this
     45  * Activity to illustrate parts of the runtime permissions API.
     46  * <p>
     47  * This Activity requests permissions to access the camera ({@link android.Manifest.permission#CAMERA})
     48  * when the 'Show Camera' button is clicked to display the camera preview.
     49  * Contacts permissions (({@link android.Manifest.permission#READ_CONTACTS} and ({@link
     50  * android.Manifest.permission#WRITE_CONTACTS})) are requested when the 'Show and Add Contacts'
     51  * button is
     52  * clicked to display the first contact in the contacts database and to add a dummy contact
     53  * directly to it. Permissions are verified and requested through compat helpers in the support v4
     54  * library, in this Activity using {@link ActivityCompat}.
     55  * First, permissions are checked if they have already been granted through {@link
     56  * ActivityCompat#checkSelfPermission(Context, String)}.
     57  * If permissions have not been granted, they are requested through
     58  * {@link ActivityCompat#requestPermissions(Activity, String[], int)} and the return value checked
     59  * in
     60  * a callback to the {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback}
     61  * interface.
     62  * <p>
     63  * Before requesting permissions, {@link ActivityCompat#shouldShowRequestPermissionRationale(Activity,
     64  * String)}
     65  * should be called to provide the user with additional context for the use of permissions if they
     66  * have been denied previously.
     67  * <p>
     68  * If this sample is executed on a device running a platform version below M, all permissions
     69  * declared
     70  * in the Android manifest file are always granted at install time and cannot be requested at run
     71  * time.
     72  * <p>
     73  * This sample targets the M platform and must therefore request permissions at runtime. Change the
     74  * targetSdk in the file 'Application/build.gradle' to 22 to run the application in compatibility
     75  * mode.
     76  * Now, if a permission has been disable by the system through the application settings, disabled
     77  * APIs provide compatibility data.
     78  * For example the camera cannot be opened or an empty list of contacts is returned. No special
     79  * action is required in this case.
     80  * <p>
     81  * (This class is based on the MainActivity used in the SimpleFragment sample template.)
     82  */
     83 public class MainActivity extends SampleActivityBase
     84         implements ActivityCompat.OnRequestPermissionsResultCallback {
     85 
     86     public static final String TAG = "MainActivity";
     87 
     88     /**
     89      * Id to identify a camera permission request.
     90      */
     91     private static final int REQUEST_CAMERA = 0;
     92 
     93     /**
     94      * Id to identify a contacts permission request.
     95      */
     96     private static final int REQUEST_CONTACTS = 1;
     97 
     98     /**
     99      * Permissions required to read and write contacts. Used by the {@link ContactsFragment}.
    100      */
    101     private static String[] PERMISSIONS_CONTACT = {Manifest.permission.READ_CONTACTS,
    102             Manifest.permission.WRITE_CONTACTS};
    103 
    104     // Whether the Log Fragment is currently shown.
    105     private boolean mLogShown;
    106 
    107     /**
    108      * Root of the layout of this Activity.
    109      */
    110     private View mLayout;
    111 
    112     /**
    113      * Called when the 'show camera' button is clicked.
    114      * Callback is defined in resource layout definition.
    115      */
    116     public void showCamera(View view) {
    117         Log.i(TAG, "Show camera button pressed. Checking permission.");
    118         // BEGIN_INCLUDE(camera_permission)
    119         // Check if the Camera permission is already available.
    120         if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
    121                 != PackageManager.PERMISSION_GRANTED) {
    122             // Camera permission has not been granted.
    123 
    124             requestCameraPermission();
    125 
    126         } else {
    127 
    128             // Camera permissions is already available, show the camera preview.
    129             Log.i(TAG,
    130                     "CAMERA permission has already been granted. Displaying camera preview.");
    131             showCameraPreview();
    132         }
    133         // END_INCLUDE(camera_permission)
    134 
    135     }
    136 
    137     /**
    138      * Requests the Camera permission.
    139      * If the permission has been denied previously, a SnackBar will prompt the user to grant the
    140      * permission, otherwise it is requested directly.
    141      */
    142     private void requestCameraPermission() {
    143         Log.i(TAG, "CAMERA permission has NOT been granted. Requesting permission.");
    144 
    145         // BEGIN_INCLUDE(camera_permission_request)
    146         if (ActivityCompat.shouldShowRequestPermissionRationale(this,
    147                 Manifest.permission.CAMERA)) {
    148             // Provide an additional rationale to the user if the permission was not granted
    149             // and the user would benefit from additional context for the use of the permission.
    150             // For example if the user has previously denied the permission.
    151             Log.i(TAG,
    152                     "Displaying camera permission rationale to provide additional context.");
    153             Snackbar.make(mLayout, R.string.permission_camera_rationale,
    154                     Snackbar.LENGTH_INDEFINITE)
    155                     .setAction(R.string.ok, new View.OnClickListener() {
    156                         @Override
    157                         public void onClick(View view) {
    158                             ActivityCompat.requestPermissions(MainActivity.this,
    159                                     new String[]{Manifest.permission.CAMERA},
    160                                     REQUEST_CAMERA);
    161                         }
    162                     })
    163                     .show();
    164         } else {
    165 
    166             // Camera permission has not been granted yet. Request it directly.
    167             ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
    168                     REQUEST_CAMERA);
    169         }
    170         // END_INCLUDE(camera_permission_request)
    171     }
    172 
    173     /**
    174      * Called when the 'show camera' button is clicked.
    175      * Callback is defined in resource layout definition.
    176      */
    177     public void showContacts(View v) {
    178         Log.i(TAG, "Show contacts button pressed. Checking permissions.");
    179 
    180         // Verify that all required contact permissions have been granted.
    181         if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
    182                 != PackageManager.PERMISSION_GRANTED
    183                 || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS)
    184                 != PackageManager.PERMISSION_GRANTED) {
    185             // Contacts permissions have not been granted.
    186             Log.i(TAG, "Contact permissions has NOT been granted. Requesting permissions.");
    187             requestContactsPermissions();
    188 
    189         } else {
    190 
    191             // Contact permissions have been granted. Show the contacts fragment.
    192             Log.i(TAG,
    193                     "Contact permissions have already been granted. Displaying contact details.");
    194             showContactDetails();
    195         }
    196     }
    197 
    198     /**
    199      * Requests the Contacts permissions.
    200      * If the permission has been denied previously, a SnackBar will prompt the user to grant the
    201      * permission, otherwise it is requested directly.
    202      */
    203     private void requestContactsPermissions() {
    204         // BEGIN_INCLUDE(contacts_permission_request)
    205         if (ActivityCompat.shouldShowRequestPermissionRationale(this,
    206                 Manifest.permission.READ_CONTACTS)
    207                 || ActivityCompat.shouldShowRequestPermissionRationale(this,
    208                 Manifest.permission.WRITE_CONTACTS)) {
    209 
    210             // Provide an additional rationale to the user if the permission was not granted
    211             // and the user would benefit from additional context for the use of the permission.
    212             // For example, if the request has been denied previously.
    213             Log.i(TAG,
    214                     "Displaying contacts permission rationale to provide additional context.");
    215 
    216             // Display a SnackBar with an explanation and a button to trigger the request.
    217             Snackbar.make(mLayout, R.string.permission_contacts_rationale,
    218                     Snackbar.LENGTH_INDEFINITE)
    219                     .setAction(R.string.ok, new View.OnClickListener() {
    220                         @Override
    221                         public void onClick(View view) {
    222                             ActivityCompat
    223                                     .requestPermissions(MainActivity.this, PERMISSIONS_CONTACT,
    224                                             REQUEST_CONTACTS);
    225                         }
    226                     })
    227                     .show();
    228         } else {
    229             // Contact permissions have not been granted yet. Request them directly.
    230             ActivityCompat.requestPermissions(this, PERMISSIONS_CONTACT, REQUEST_CONTACTS);
    231         }
    232         // END_INCLUDE(contacts_permission_request)
    233     }
    234 
    235 
    236     /**
    237      * Display the {@link CameraPreviewFragment} in the content area if the required Camera
    238      * permission has been granted.
    239      */
    240     private void showCameraPreview() {
    241         getSupportFragmentManager().beginTransaction()
    242                 .replace(R.id.sample_content_fragment, CameraPreviewFragment.newInstance())
    243                 .addToBackStack("contacts")
    244                 .commit();
    245     }
    246 
    247     /**
    248      * Display the {@link ContactsFragment} in the content area if the required contacts
    249      * permissions
    250      * have been granted.
    251      */
    252     private void showContactDetails() {
    253         getSupportFragmentManager().beginTransaction()
    254                 .replace(R.id.sample_content_fragment, ContactsFragment.newInstance())
    255                 .addToBackStack("contacts")
    256                 .commit();
    257     }
    258 
    259 
    260     /**
    261      * Callback received when a permissions request has been completed.
    262      */
    263     @Override
    264     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
    265             @NonNull int[] grantResults) {
    266 
    267         if (requestCode == REQUEST_CAMERA) {
    268             // BEGIN_INCLUDE(permission_result)
    269             // Received permission result for camera permission.
    270             Log.i(TAG, "Received response for Camera permission request.");
    271 
    272             // Check if the only required permission has been granted
    273             if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    274                 // Camera permission has been granted, preview can be displayed
    275                 Log.i(TAG, "CAMERA permission has now been granted. Showing preview.");
    276                 Snackbar.make(mLayout, R.string.permision_available_camera,
    277                         Snackbar.LENGTH_SHORT).show();
    278             } else {
    279                 Log.i(TAG, "CAMERA permission was NOT granted.");
    280                 Snackbar.make(mLayout, R.string.permissions_not_granted,
    281                         Snackbar.LENGTH_SHORT).show();
    282 
    283             }
    284             // END_INCLUDE(permission_result)
    285 
    286         } else if (requestCode == REQUEST_CONTACTS) {
    287             Log.i(TAG, "Received response for contact permissions request.");
    288 
    289             // We have requested multiple permissions for contacts, so all of them need to be
    290             // checked.
    291             if (PermissionUtil.verifyPermissions(grantResults)) {
    292                 // All required permissions have been granted, display contacts fragment.
    293                 Snackbar.make(mLayout, R.string.permision_available_contacts,
    294                         Snackbar.LENGTH_SHORT)
    295                         .show();
    296             } else {
    297                 Log.i(TAG, "Contacts permissions were NOT granted.");
    298                 Snackbar.make(mLayout, R.string.permissions_not_granted,
    299                         Snackbar.LENGTH_SHORT)
    300                         .show();
    301             }
    302 
    303         } else {
    304             super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    305         }
    306     }
    307 
    308     /* Note: Methods and definitions below are only used to provide the UI for this sample and are
    309     not relevant for the execution of the runtime permissions API. */
    310 
    311 
    312     @Override
    313     public boolean onCreateOptionsMenu(Menu menu) {
    314         getMenuInflater().inflate(R.menu.main, menu);
    315         return true;
    316     }
    317 
    318     @Override
    319     public boolean onPrepareOptionsMenu(Menu menu) {
    320         MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
    321         logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
    322         logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
    323 
    324         return super.onPrepareOptionsMenu(menu);
    325     }
    326 
    327     @Override
    328     public boolean onOptionsItemSelected(MenuItem item) {
    329         switch (item.getItemId()) {
    330             case R.id.menu_toggle_log:
    331                 mLogShown = !mLogShown;
    332                 ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
    333                 if (mLogShown) {
    334                     output.setDisplayedChild(1);
    335                 } else {
    336                     output.setDisplayedChild(0);
    337                 }
    338                 supportInvalidateOptionsMenu();
    339                 return true;
    340         }
    341         return super.onOptionsItemSelected(item);
    342     }
    343 
    344     /** Create a chain of targets that will receive log data */
    345     @Override
    346     public void initializeLogging() {
    347         // Wraps Android's native log framework.
    348         LogWrapper logWrapper = new LogWrapper();
    349         // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
    350         Log.setLogNode(logWrapper);
    351 
    352         // Filter strips out everything except the message text.
    353         MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
    354         logWrapper.setNext(msgFilter);
    355 
    356         // On screen logging via a fragment with a TextView.
    357         LogFragment logFragment = (LogFragment) getSupportFragmentManager()
    358                 .findFragmentById(R.id.log_fragment);
    359         msgFilter.setNext(logFragment.getLogView());
    360     }
    361 
    362     public void onBackClick(View view) {
    363         getSupportFragmentManager().popBackStack();
    364     }
    365 
    366     @Override
    367     protected void onCreate(Bundle savedInstanceState) {
    368         super.onCreate(savedInstanceState);
    369         setContentView(R.layout.activity_main);
    370         mLayout = findViewById(R.id.sample_main_layout);
    371 
    372         if (savedInstanceState == null) {
    373             FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    374             RuntimePermissionsFragment fragment = new RuntimePermissionsFragment();
    375             transaction.replace(R.id.sample_content_fragment, fragment);
    376             transaction.commit();
    377         }
    378 
    379         // This method sets up our custom logger, which will print all log messages to the device
    380         // screen, as well as to adb logcat.
    381         initializeLogging();
    382     }
    383 }
    384