• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.google.phonenumbers.demoapp.contacts;
2 
3 import static android.content.Context.MODE_PRIVATE;
4 
5 import android.Manifest.permission;
6 import android.app.Activity;
7 import android.content.Context;
8 import android.content.Intent;
9 import android.content.SharedPreferences;
10 import android.content.pm.PackageManager;
11 import android.net.Uri;
12 import androidx.core.app.ActivityCompat;
13 import androidx.core.content.ContextCompat;
14 
15 /**
16  * Handles everything related to the contacts permissions ({@link permission#READ_CONTACTS} and
17  * {@link permission#WRITE_CONTACTS}) and the requesting process to grant the permissions.
18  */
19 public class ContactsPermissionManagement {
20 
21   public static final int CONTACTS_PERMISSION_REQUEST_CODE = 0;
22 
23   private static final String SHARED_PREFS_NAME = "contacts-permission-management";
24   private static final String NUMBER_OF_CONTACTS_PERMISSION_DENIALS_KEY =
25       "NUMBER_OF_CONTACTS_PERMISSION_DENIALS";
26 
ContactsPermissionManagement()27   private ContactsPermissionManagement() {}
28 
29   /**
30    * Returns the current state of the permissions granting as {@link PermissionState}.
31    *
32    * @param activity Activity of the app
33    * @return {@link PermissionState} of the permissions granting
34    */
getState(Activity activity)35   public static PermissionState getState(Activity activity) {
36     if (isGranted(activity.getApplicationContext())) {
37       return PermissionState.ALREADY_GRANTED;
38     }
39     if (!shouldPermissionBeRequestedInApp(activity.getApplicationContext())) {
40       return PermissionState.NEEDS_GRANT_IN_SETTINGS;
41     }
42     if (shouldShowRationale(activity)) {
43       return PermissionState.SHOW_RATIONALE;
44     }
45     return PermissionState.NEEDS_REQUEST;
46   }
47 
48   /**
49    * Returns whether the contacts permissions ({@link permission#READ_CONTACTS} and {@link
50    * permission#WRITE_CONTACTS}) are granted for the param {@code context}.
51    *
52    * @param context Context of the app
53    * @return boolean whether contacts permissions are granted
54    */
isGranted(Context context)55   public static boolean isGranted(Context context) {
56     if (ContextCompat.checkSelfPermission(context, permission.READ_CONTACTS)
57         == PackageManager.PERMISSION_DENIED) {
58       return false;
59     }
60     return ContextCompat.checkSelfPermission(context, permission.WRITE_CONTACTS)
61         != PackageManager.PERMISSION_DENIED;
62   }
63 
64   /**
65    * Returns whether the permissions should be requested directly in the app or not. Specifically
66    * returns true if less than 2 denials happened since the app installation.
67    *
68    * @param context Context of the app
69    * @return boolean whether the permissions should be requested directly in the app
70    */
shouldPermissionBeRequestedInApp(Context context)71   private static boolean shouldPermissionBeRequestedInApp(Context context) {
72     return getNumberOfDenials(context) < 2;
73   }
74 
75   /**
76    * Returns the number of times the permission dialog has been denied since the app installation.
77    * Dismissing the permission dialog instead of answering is considered a denial.
78    *
79    * @param context Context of the app
80    * @return int number of times the permission has been denied
81    */
getNumberOfDenials(Context context)82   private static int getNumberOfDenials(Context context) {
83     SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);
84     return preferences.getInt(NUMBER_OF_CONTACTS_PERMISSION_DENIALS_KEY, 0);
85   }
86 
87   /**
88    * Adds 1 to the number of denials since the app installation. Should be called every time the
89    * user denies the permission (in the dialog). Dismissing the permission dialog instead of
90    * answering is considered a denial.
91    *
92    * @param context Context of the app
93    */
addOneToNumberOfDenials(Context context)94   public static void addOneToNumberOfDenials(Context context) {
95     SharedPreferences.Editor editor =
96         context.getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE).edit();
97     editor.putInt(NUMBER_OF_CONTACTS_PERMISSION_DENIALS_KEY, getNumberOfDenials(context) + 1);
98     editor.apply();
99   }
100 
101   /**
102    * Returns whether a rational should be shown explaining why the app requests these permissions
103    * (before requesting them).
104    *
105    * @param activity Activity of the app
106    * @return boolean whether a rational should be shown
107    */
shouldShowRationale(Activity activity)108   private static boolean shouldShowRationale(Activity activity) {
109     if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission.READ_CONTACTS)) {
110       return true;
111     }
112     return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission.WRITE_CONTACTS);
113   }
114 
115   /**
116    * Requests the contact permissions ({@link permission#READ_CONTACTS} and {@link
117    * permission#WRITE_CONTACTS}) in the param {@code activity} with the request code {@link
118    * ContactsPermissionManagement#CONTACTS_PERMISSION_REQUEST_CODE}.
119    *
120    * @param activity Activity of the app
121    */
request(Activity activity)122   public static void request(Activity activity) {
123     activity.requestPermissions(
124         new String[] {permission.READ_CONTACTS, permission.WRITE_CONTACTS},
125         CONTACTS_PERMISSION_REQUEST_CODE);
126   }
127 
128   /**
129    * Opens the system settings (app details page) if the app can. Special cases that can not open
130    * the system settings are for example emulators without Play Store installed.
131    *
132    * @param activity Activity of the app
133    */
openSystemSettings(Activity activity)134   public static void openSystemSettings(Activity activity) {
135     Intent intent =
136         new Intent(
137             android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
138             Uri.parse("package:" + activity.getPackageName()));
139     activity.startActivity(intent);
140   }
141 
142   /** Represents the different states the permissions granting process can be at. */
143   public enum PermissionState {
144     /** The permissions are already granted. The action requiring the permissions can be started. */
145     ALREADY_GRANTED,
146     /**
147      * The permissions are not granted, but can be requested directly (without showing a rationale).
148      */
149     NEEDS_REQUEST,
150     /**
151      * The permissions are not granted and a rationale should be shown explaining why the app
152      * requests the permissions before requesting them (directly in the app).
153      */
154     SHOW_RATIONALE,
155     /**
156      * The permissions are not granted and can not be granted directly in the app. The user has to
157      * grant permissions in the system settings instead.
158      */
159     NEEDS_GRANT_IN_SETTINGS
160   }
161 }
162