1 /*
2  * Copyright (C) 2020 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.wm.shell.onehanded;
18 
19 import static com.android.internal.accessibility.AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME;
20 
21 import android.annotation.IntDef;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.database.ContentObserver;
25 import android.net.Uri;
26 import android.provider.Settings;
27 import android.text.TextUtils;
28 
29 import androidx.annotation.Nullable;
30 
31 import com.android.wm.shell.R;
32 
33 import java.io.PrintWriter;
34 import java.lang.annotation.Retention;
35 import java.lang.annotation.RetentionPolicy;
36 
37 /**
38  * APIs for querying or updating one handed settings.
39  */
40 public final class OneHandedSettingsUtil {
41     private static final String TAG = "OneHandedSettingsUtil";
42     private static final String ONE_HANDED_MODE_TARGET_NAME =
43             ONE_HANDED_COMPONENT_NAME.getShortClassName();
44 
45     @IntDef(prefix = {"ONE_HANDED_TIMEOUT_"}, value = {
46             ONE_HANDED_TIMEOUT_NEVER,
47             ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS,
48             ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS,
49             ONE_HANDED_TIMEOUT_LONG_IN_SECONDS,
50     })
51     @Retention(RetentionPolicy.SOURCE)
52     public @interface OneHandedTimeout {
53     }
54 
55     /**
56      * Never stop one handed automatically
57      */
58     public static final int ONE_HANDED_TIMEOUT_NEVER = 0;
59     /**
60      * Auto stop one handed in {@link OneHandedSettingsUtil#ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS}
61      */
62     public static final int ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS = 4;
63     /**
64      * Auto stop one handed in {@link OneHandedSettingsUtil#ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS}
65      */
66     public static final int ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS = 8;
67     /**
68      * Auto stop one handed in {@link OneHandedSettingsUtil#ONE_HANDED_TIMEOUT_LONG_IN_SECONDS}
69      */
70     public static final int ONE_HANDED_TIMEOUT_LONG_IN_SECONDS = 12;
71 
72     /**
73      * Registers one handed preference settings observer
74      *
75      * @param key       Setting key to monitor in observer
76      * @param resolver  ContentResolver of context
77      * @param observer  Observer from caller
78      * @param newUserId New user id to be registered
79      * @return uri key for observing
80      */
81     @Nullable
registerSettingsKeyObserver(String key, ContentResolver resolver, ContentObserver observer, int newUserId)82     public Uri registerSettingsKeyObserver(String key, ContentResolver resolver,
83             ContentObserver observer, int newUserId) {
84         Uri uriKey = null;
85         uriKey = Settings.Secure.getUriFor(key);
86         if (resolver != null && uriKey != null) {
87             resolver.registerContentObserver(uriKey, false, observer, newUserId);
88         }
89         return uriKey;
90     }
91 
92     /**
93      * Unregisters one handed preference settings observer.
94      *
95      * @param resolver  ContentResolver of context
96      * @param observer  preference key change observer
97      */
unregisterSettingsKeyObserver(ContentResolver resolver, ContentObserver observer)98     public void unregisterSettingsKeyObserver(ContentResolver resolver,
99             ContentObserver observer) {
100         if (resolver != null) {
101             resolver.unregisterContentObserver(observer);
102         }
103     }
104 
105     /**
106      * Queries one handed enable or disable flag from Settings provider.
107      *
108      * @return enable or disable one handed mode flag.
109      */
getSettingsOneHandedModeEnabled(ContentResolver resolver, int userId)110     public boolean getSettingsOneHandedModeEnabled(ContentResolver resolver, int userId) {
111         return Settings.Secure.getIntForUser(resolver,
112                 Settings.Secure.ONE_HANDED_MODE_ENABLED, 0 /* Disabled */, userId) == 1;
113     }
114 
115     /**
116      * Sets one handed enable or disable flag from Settings provider.
117      *
118      * @return true if the value was set, false on database errors
119      */
setOneHandedModeEnabled(ContentResolver resolver, int enabled, int userId)120     public boolean setOneHandedModeEnabled(ContentResolver resolver, int enabled, int userId) {
121         return Settings.Secure.putIntForUser(resolver,
122                 Settings.Secure.ONE_HANDED_MODE_ENABLED, enabled, userId);
123     }
124 
125 
126     /**
127      * Queries taps app to exit config from Settings provider.
128      *
129      * @return enable or disable taps app exit.
130      */
getSettingsTapsAppToExit(ContentResolver resolver, int userId)131     public boolean getSettingsTapsAppToExit(ContentResolver resolver, int userId) {
132         return Settings.Secure.getIntForUser(resolver,
133                 Settings.Secure.TAPS_APP_TO_EXIT, 1, userId) == 1;
134     }
135 
136     /**
137      * Queries timeout value from Settings provider. Default is.
138      * {@link OneHandedSettingsUtil#ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS}
139      *
140      * @return timeout value in seconds.
141      */
getSettingsOneHandedModeTimeout(ContentResolver resolver, int userId)142     public @OneHandedTimeout int getSettingsOneHandedModeTimeout(ContentResolver resolver,
143             int userId) {
144         return Settings.Secure.getIntForUser(resolver,
145                 Settings.Secure.ONE_HANDED_MODE_TIMEOUT, ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS,
146                 userId);
147     }
148 
149     /**
150      * Returns whether swipe bottom to notification gesture enabled or not.
151      */
getSettingsSwipeToNotificationEnabled(ContentResolver resolver, int userId)152     public boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver, int userId) {
153         return Settings.Secure.getIntForUser(resolver,
154                 Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0, userId) == 1;
155     }
156 
157 
158     /**
159      * Queries tutorial shown counts from Settings provider. Default is 0.
160      *
161      * @return counts tutorial shown counts.
162      */
getTutorialShownCounts(ContentResolver resolver, int userId)163     public int getTutorialShownCounts(ContentResolver resolver, int userId) {
164         return Settings.Secure.getIntForUser(resolver,
165                 Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0, userId);
166     }
167 
168     /**
169      * Queries one-handed mode shortcut enabled in settings or not.
170      *
171      * @return true if user enabled one-handed shortcut in settings, false otherwise.
172      */
getShortcutEnabled(ContentResolver resolver, int userId)173     public boolean getShortcutEnabled(ContentResolver resolver, int userId) {
174         // Checks SOFTWARE_SHORTCUT_KEY
175         final String targetsSwKey = Settings.Secure.getStringForUser(resolver,
176                 Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
177         if (!TextUtils.isEmpty(targetsSwKey) && targetsSwKey.contains(
178                 ONE_HANDED_MODE_TARGET_NAME)) {
179             return true;
180         }
181 
182         // Checks HARDWARE_SHORTCUT_KEY
183         final String targetsHwKey = Settings.Secure.getStringForUser(resolver,
184                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId);
185         if (!TextUtils.isEmpty(targetsHwKey) && targetsHwKey.contains(
186                 ONE_HANDED_MODE_TARGET_NAME)) {
187             return true;
188         }
189         return false;
190     }
191 
192     /**
193      * Sets tutorial shown counts.
194      *
195      * @return true if the value was set, false on database errors.
196      */
setTutorialShownCounts(ContentResolver resolver, int shownCounts, int userId)197     public boolean setTutorialShownCounts(ContentResolver resolver, int shownCounts, int userId) {
198         return Settings.Secure.putIntForUser(resolver,
199                 Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, shownCounts, userId);
200     }
201 
202     /**
203      * Sets one handed activated or not to notify state for shortcut.
204      *
205      * @return true if one handed mode is activated.
206      */
getOneHandedModeActivated(ContentResolver resolver, int userId)207     public boolean getOneHandedModeActivated(ContentResolver resolver, int userId) {
208         return Settings.Secure.getIntForUser(resolver,
209                 Settings.Secure.ONE_HANDED_MODE_ACTIVATED, 0, userId) == 1;
210     }
211 
212     /**
213      * Sets one handed activated or not to notify state for shortcut.
214      *
215      * @return true if the value was set, false on database errors.
216      */
setOneHandedModeActivated(ContentResolver resolver, int state, int userId)217     public boolean setOneHandedModeActivated(ContentResolver resolver, int state, int userId) {
218         return Settings.Secure.putIntForUser(resolver,
219                 Settings.Secure.ONE_HANDED_MODE_ACTIVATED, state, userId);
220     }
221 
222     /**
223      * Obtains one-handed mode transition duration from resource config.
224      *
225      * @return durationMs The duration in milli-seconds
226      */
getTransitionDuration(Context context)227     public int getTransitionDuration(Context context) {
228         return context.getResources().getInteger(
229                 R.integer.config_one_handed_translate_animation_duration);
230     }
231 
232     /**
233      * Obtains one-handed mode offset fraction from resource config.
234      *
235      * @return fraction The fraction of offset of the whole screen.
236      */
getTranslationFraction(Context context)237     public float getTranslationFraction(Context context) {
238         return context.getResources().getFraction(R.fraction.config_one_handed_offset, 1, 1);
239     }
240 
dump(PrintWriter pw, String prefix, ContentResolver resolver, int userId)241     void dump(PrintWriter pw, String prefix, ContentResolver resolver,
242             int userId) {
243         final String innerPrefix = "  ";
244         pw.println(TAG);
245         pw.print(innerPrefix + "isOneHandedModeEnable=");
246         pw.println(getSettingsOneHandedModeEnabled(resolver, userId));
247         pw.print(innerPrefix + "isSwipeToNotificationEnabled=");
248         pw.println(getSettingsSwipeToNotificationEnabled(resolver, userId));
249         pw.print(innerPrefix + "oneHandedTimeOut=");
250         pw.println(getSettingsOneHandedModeTimeout(resolver, userId));
251         pw.print(innerPrefix + "tapsAppToExit=");
252         pw.println(getSettingsTapsAppToExit(resolver, userId));
253         pw.print(innerPrefix + "shortcutActivated=");
254         pw.println(getOneHandedModeActivated(resolver, userId));
255         pw.print(innerPrefix + "tutorialShownCounts=");
256         pw.println(getTutorialShownCounts(resolver, userId));
257     }
258 
OneHandedSettingsUtil()259     public OneHandedSettingsUtil() {
260     }
261 }
262