1 /*
2  * Copyright (C) 2014 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.inputmethod.latin.utils;
18 
19 import android.Manifest;
20 import android.content.Context;
21 import android.content.SharedPreferences;
22 import android.provider.Settings;
23 import android.provider.Settings.SettingNotFoundException;
24 import android.text.TextUtils;
25 import android.util.Log;
26 
27 import com.android.inputmethod.annotations.UsedForTesting;
28 import com.android.inputmethod.latin.R;
29 import com.android.inputmethod.latin.permissions.PermissionsUtil;
30 import com.android.inputmethod.latin.settings.SettingsValues;
31 
32 import java.util.concurrent.TimeUnit;
33 
34 public final class ImportantNoticeUtils {
35     private static final String TAG = ImportantNoticeUtils.class.getSimpleName();
36 
37     // {@link SharedPreferences} name to save the last important notice version that has been
38     // displayed to users.
39     private static final String PREFERENCE_NAME = "important_notice_pref";
40 
41     private static final String KEY_SUGGEST_CONTACTS_NOTICE = "important_notice_suggest_contacts";
42 
43     @UsedForTesting
44     static final String KEY_TIMESTAMP_OF_CONTACTS_NOTICE = "timestamp_of_suggest_contacts_notice";
45 
46     @UsedForTesting
47     static final long TIMEOUT_OF_IMPORTANT_NOTICE = TimeUnit.HOURS.toMillis(23);
48 
49     // Copy of the hidden {@link Settings.Secure#USER_SETUP_COMPLETE} settings key.
50     // The value is zero until each multiuser completes system setup wizard.
51     // Caveat: This is a hidden API.
52     private static final String Settings_Secure_USER_SETUP_COMPLETE = "user_setup_complete";
53     private static final int USER_SETUP_IS_NOT_COMPLETE = 0;
54 
ImportantNoticeUtils()55     private ImportantNoticeUtils() {
56         // This utility class is not publicly instantiable.
57     }
58 
59     @UsedForTesting
isInSystemSetupWizard(final Context context)60     static boolean isInSystemSetupWizard(final Context context) {
61         try {
62             final int userSetupComplete = Settings.Secure.getInt(
63                     context.getContentResolver(), Settings_Secure_USER_SETUP_COMPLETE);
64             return userSetupComplete == USER_SETUP_IS_NOT_COMPLETE;
65         } catch (final SettingNotFoundException e) {
66             Log.w(TAG, "Can't find settings in Settings.Secure: key="
67                     + Settings_Secure_USER_SETUP_COMPLETE);
68             return false;
69         }
70     }
71 
72     @UsedForTesting
getImportantNoticePreferences(final Context context)73     static SharedPreferences getImportantNoticePreferences(final Context context) {
74         return context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
75     }
76 
77     @UsedForTesting
hasContactsNoticeShown(final Context context)78     static boolean hasContactsNoticeShown(final Context context) {
79         return getImportantNoticePreferences(context).getBoolean(
80                 KEY_SUGGEST_CONTACTS_NOTICE, false);
81     }
82 
shouldShowImportantNotice(final Context context, final SettingsValues settingsValues)83     public static boolean shouldShowImportantNotice(final Context context,
84             final SettingsValues settingsValues) {
85         // Check to see whether "Use Contacts" is enabled by the user.
86         if (!settingsValues.mUseContactsDict) {
87             return false;
88         }
89 
90         if (hasContactsNoticeShown(context)) {
91             return false;
92         }
93 
94         // Don't show the dialog if we have all the permissions.
95         if (PermissionsUtil.checkAllPermissionsGranted(
96                 context, Manifest.permission.READ_CONTACTS)) {
97             return false;
98         }
99 
100         final String importantNoticeTitle = getSuggestContactsNoticeTitle(context);
101         if (TextUtils.isEmpty(importantNoticeTitle)) {
102             return false;
103         }
104         if (isInSystemSetupWizard(context)) {
105             return false;
106         }
107         if (hasContactsNoticeTimeoutPassed(context, System.currentTimeMillis())) {
108             updateContactsNoticeShown(context);
109             return false;
110         }
111         return true;
112     }
113 
getSuggestContactsNoticeTitle(final Context context)114     public static String getSuggestContactsNoticeTitle(final Context context) {
115         return context.getResources().getString(R.string.important_notice_suggest_contact_names);
116     }
117 
118     @UsedForTesting
hasContactsNoticeTimeoutPassed( final Context context, final long currentTimeInMillis)119     static boolean hasContactsNoticeTimeoutPassed(
120             final Context context, final long currentTimeInMillis) {
121         final SharedPreferences prefs = getImportantNoticePreferences(context);
122         if (!prefs.contains(KEY_TIMESTAMP_OF_CONTACTS_NOTICE)) {
123             prefs.edit()
124                     .putLong(KEY_TIMESTAMP_OF_CONTACTS_NOTICE, currentTimeInMillis)
125                     .apply();
126         }
127         final long firstDisplayTimeInMillis = prefs.getLong(
128                 KEY_TIMESTAMP_OF_CONTACTS_NOTICE, currentTimeInMillis);
129         final long elapsedTime = currentTimeInMillis - firstDisplayTimeInMillis;
130         return elapsedTime >= TIMEOUT_OF_IMPORTANT_NOTICE;
131     }
132 
updateContactsNoticeShown(final Context context)133     public static void updateContactsNoticeShown(final Context context) {
134         getImportantNoticePreferences(context)
135                 .edit()
136                 .putBoolean(KEY_SUGGEST_CONTACTS_NOTICE, true)
137                 .remove(KEY_TIMESTAMP_OF_CONTACTS_NOTICE)
138                 .apply();
139     }
140 }
141