• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.settings.gestures;
18 
19 import static com.android.internal.accessibility.AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME;
20 
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.database.ContentObserver;
24 import android.net.Uri;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.os.SystemProperties;
28 import android.os.UserHandle;
29 import android.provider.Settings;
30 import android.text.TextUtils;
31 
32 import androidx.annotation.VisibleForTesting;
33 
34 /**
35  * The Util to query one-handed mode settings config
36  */
37 public class OneHandedSettingsUtils {
38 
39     static final String ONE_HANDED_MODE_TARGET_NAME =
40             ONE_HANDED_COMPONENT_NAME.getShortClassName();
41 
42     static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
43     static final int OFF = 0;
44     static final int ON = 1;
45     static final Uri ONE_HANDED_MODE_ENABLED_URI =
46             Settings.Secure.getUriFor(Settings.Secure.ONE_HANDED_MODE_ENABLED);
47     static final Uri SHOW_NOTIFICATION_ENABLED_URI =
48             Settings.Secure.getUriFor(Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
49     static final Uri SOFTWARE_SHORTCUT_ENABLED_URI =
50             Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
51     static final Uri HARDWARE_SHORTCUT_ENABLED_URI =
52             Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
53     static final Uri QS_SHORTCUT_ENABLED_URI =
54             Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
55 
56     public enum OneHandedTimeout {
57         NEVER(0), SHORT(4), MEDIUM(8), LONG(12);
58 
59         private final int mValue;
60 
OneHandedTimeout(int value)61         OneHandedTimeout(int value) {
62             this.mValue = value;
63         }
64 
getValue()65         public int getValue() {
66             return mValue;
67         }
68     }
69 
70     private final Context mContext;
71     private final SettingsObserver mSettingsObserver;
72 
73     private static int sCurrentUserId;
74 
OneHandedSettingsUtils(Context context)75     OneHandedSettingsUtils(Context context) {
76         mContext = context;
77         sCurrentUserId = UserHandle.myUserId();
78         mSettingsObserver = new SettingsObserver(new Handler(Looper.getMainLooper()));
79     }
80 
81     /**
82      * Gets One-Handed mode support flag.
83      */
isSupportOneHandedMode()84     public static boolean isSupportOneHandedMode() {
85         return SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
86     }
87 
88     /**
89      * Gets one-handed mode feature enable or disable flag from Settings provider.
90      *
91      * @param context App context
92      * @return enable or disable one-handed mode flag.
93      */
isOneHandedModeEnabled(Context context)94     public static boolean isOneHandedModeEnabled(Context context) {
95         return Settings.Secure.getIntForUser(context.getContentResolver(),
96                 Settings.Secure.ONE_HANDED_MODE_ENABLED, OFF, sCurrentUserId) == ON;
97     }
98 
99     /**
100      * Sets one-handed mode enable or disable flag to Settings provider.
101      *
102      * @param context App context
103      * @param enable  enable or disable one-handed mode.
104      */
setOneHandedModeEnabled(Context context, boolean enable)105     public static void setOneHandedModeEnabled(Context context, boolean enable) {
106         Settings.Secure.putIntForUser(context.getContentResolver(),
107                 Settings.Secure.ONE_HANDED_MODE_ENABLED, enable ? ON : OFF, sCurrentUserId);
108     }
109 
110     /**
111      * Gets enabling taps app to exit one-handed mode flag from Settings provider.
112      *
113      * @param context App context
114      * @return enable or disable taps app to exit.
115      */
isTapsAppToExitEnabled(Context context)116     public static boolean isTapsAppToExitEnabled(Context context) {
117         return Settings.Secure.getIntForUser(context.getContentResolver(),
118                 Settings.Secure.TAPS_APP_TO_EXIT, OFF, sCurrentUserId) == ON;
119     }
120 
121     /**
122      * Sets enabling taps app to exit one-handed mode flag to Settings provider.
123      *
124      * @param context App context
125      * @param enable  enable or disable when taping app to exit one-handed mode.
126      */
setTapsAppToExitEnabled(Context context, boolean enable)127     public static boolean setTapsAppToExitEnabled(Context context, boolean enable) {
128         return Settings.Secure.putIntForUser(context.getContentResolver(),
129                 Settings.Secure.TAPS_APP_TO_EXIT, enable ? ON : OFF, sCurrentUserId);
130     }
131 
132     /**
133      * Gets one-handed mode timeout value from Settings provider.
134      *
135      * @param context App context
136      * @return timeout value in seconds.
137      */
getTimeoutValue(Context context)138     public static int getTimeoutValue(Context context) {
139         return Settings.Secure.getIntForUser(context.getContentResolver(),
140                 Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
141                 OneHandedTimeout.MEDIUM.getValue() /* default MEDIUM(8) by UX */,
142                 sCurrentUserId);
143     }
144 
145     /**
146      * Gets current user id from OneHandedSettingsUtils
147      *
148      * @return the current user id in OneHandedSettingsUtils
149      */
getUserId()150     public static int getUserId() {
151         return sCurrentUserId;
152     }
153 
154     /**
155      * Sets specific user id for OneHandedSettingsUtils
156      *
157      * @param userId the user id to be updated
158      */
setUserId(int userId)159     public static void setUserId(int userId) {
160         sCurrentUserId = userId;
161     }
162 
163     /**
164      * Sets one-handed mode timeout value to Settings provider.
165      *
166      * @param context App context
167      * @param timeout timeout in seconds for exiting one-handed mode.
168      */
setTimeoutValue(Context context, int timeout)169     public static void setTimeoutValue(Context context, int timeout) {
170         Settings.Secure.putIntForUser(context.getContentResolver(),
171                 Settings.Secure.ONE_HANDED_MODE_TIMEOUT, timeout, sCurrentUserId);
172     }
173 
174     /**
175      * Gets Swipe-down-notification enable or disable flag from Settings provider.
176      *
177      * @param context App context
178      * @return enable or disable Swipe-down-notification flag.
179      */
isSwipeDownNotificationEnabled(Context context)180     public static boolean isSwipeDownNotificationEnabled(Context context) {
181         return Settings.Secure.getIntForUser(context.getContentResolver(),
182                 Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, OFF, sCurrentUserId) == ON;
183     }
184 
185     /**
186      * Sets Swipe-down-notification enable or disable flag to Settings provider.
187      *
188      * @param context App context
189      * @param enable enable or disable Swipe-down-notification.
190      */
setSwipeDownNotificationEnabled(Context context, boolean enable)191     public static void setSwipeDownNotificationEnabled(Context context, boolean enable) {
192         Settings.Secure.putIntForUser(context.getContentResolver(),
193                 Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, enable ? ON : OFF,
194                 sCurrentUserId);
195     }
196 
197     /**
198      * Set NavigationBar mode flag to Settings provider.
199      * @param context App context
200      * @param value Navigation bar mode:
201      *  0 = 3 button
202      *  1 = 2 button
203      *  2 = fully gestural
204      * @return true if the value was set, false on database errors.
205      */
206     @VisibleForTesting
setNavigationBarMode(Context context, String value)207     public boolean setNavigationBarMode(Context context, String value) {
208         return Settings.Secure.putStringForUser(context.getContentResolver(),
209                 Settings.Secure.NAVIGATION_MODE, value, UserHandle.myUserId());
210     }
211 
212     /**
213      * Get NavigationBar mode flag from Settings provider.
214      * @param context App context
215      * @return Navigation bar mode:
216      *  0 = 3 button
217      *  1 = 2 button
218      *  2 = fully gestural
219      */
getNavigationBarMode(Context context)220     public static int getNavigationBarMode(Context context) {
221         return Settings.Secure.getIntForUser(context.getContentResolver(),
222                 Settings.Secure.NAVIGATION_MODE, 2 /* fully gestural */, sCurrentUserId);
223     }
224 
225     /**
226      * Check if One-handed mode settings controllers can enabled or disabled.
227      * @param context App context
228      * @return true if controllers are able to enabled, false otherwise.
229      *
230      * Note: For better UX experience, just disabled controls that let users know to use
231      * this feature, they need to make sure gesture navigation is turned on in system
232      * navigation settings.
233      */
canEnableController(Context context)234     public static boolean canEnableController(Context context) {
235         return ((OneHandedSettingsUtils.isOneHandedModeEnabled(context)
236                 && getNavigationBarMode(context) != 0 /* 3-button */)
237                 || getShortcutEnabled(context));
238     }
239 
240     /**
241      * Queries one-handed mode shortcut enabled in settings or not.
242      *
243      * @return true if user enabled one-handed shortcut in settings, false otherwise.
244      */
getShortcutEnabled(Context context)245     public static boolean getShortcutEnabled(Context context) {
246         // Checks SOFTWARE_SHORTCUT_KEY
247         final String targetsSW = Settings.Secure.getStringForUser(context.getContentResolver(),
248                 Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, sCurrentUserId);
249         if (!TextUtils.isEmpty(targetsSW) && targetsSW.contains(ONE_HANDED_MODE_TARGET_NAME)) {
250             return true;
251         }
252 
253         // Checks HARDWARE_SHORTCUT_KEY
254         final String targetsHW = Settings.Secure.getStringForUser(context.getContentResolver(),
255                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, sCurrentUserId);
256         if (!TextUtils.isEmpty(targetsHW) && targetsHW.contains(ONE_HANDED_MODE_TARGET_NAME)) {
257             return true;
258         }
259 
260         // Checks QS_SHORTCUT_KEY
261         final String targetsQs = Settings.Secure.getStringForUser(context.getContentResolver(),
262                 Settings.Secure.ACCESSIBILITY_QS_TARGETS, sCurrentUserId);
263         return !TextUtils.isEmpty(targetsQs) && targetsQs.contains(ONE_HANDED_MODE_TARGET_NAME);
264     }
265 
266     /**
267      * This is a test only API for set Shortcut enabled or not.
268      */
269     @VisibleForTesting
setShortcutEnabled(Context context, boolean enabled)270     public void setShortcutEnabled(Context context, boolean enabled) {
271         final String targetName = enabled ? ONE_HANDED_MODE_TARGET_NAME : "";
272         Settings.Secure.putStringForUser(context.getContentResolver(),
273                 Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, targetName, sCurrentUserId);
274     }
275 
276     /**
277      * Registers callback for observing Settings.Secure.ONE_HANDED_MODE_ENABLED state.
278      * @param callback for state changes
279      */
registerToggleAwareObserver(TogglesCallback callback)280     public void registerToggleAwareObserver(TogglesCallback callback) {
281         mSettingsObserver.observe();
282         mSettingsObserver.setCallback(callback);
283     }
284 
285     /**
286      * Unregisters callback for observing Settings.Secure.ONE_HANDED_MODE_ENABLED state.
287      */
unregisterToggleAwareObserver()288     public void unregisterToggleAwareObserver() {
289         final ContentResolver resolver = mContext.getContentResolver();
290         resolver.unregisterContentObserver(mSettingsObserver);
291     }
292 
293     private final class SettingsObserver extends ContentObserver {
294         private TogglesCallback mCallback;
295 
SettingsObserver(Handler handler)296         SettingsObserver(Handler handler) {
297             super(handler);
298         }
299 
setCallback(TogglesCallback callback)300         private void setCallback(TogglesCallback callback) {
301             mCallback = callback;
302         }
303 
observe()304         public void observe() {
305             final ContentResolver resolver = mContext.getContentResolver();
306             resolver.registerContentObserver(ONE_HANDED_MODE_ENABLED_URI, true, this);
307             resolver.registerContentObserver(SHOW_NOTIFICATION_ENABLED_URI, true, this);
308             resolver.registerContentObserver(SOFTWARE_SHORTCUT_ENABLED_URI, true, this);
309             resolver.registerContentObserver(HARDWARE_SHORTCUT_ENABLED_URI, true, this);
310             if (android.view.accessibility.Flags.a11yQsShortcut()) {
311                 resolver.registerContentObserver(QS_SHORTCUT_ENABLED_URI, true, this);
312             }
313         }
314 
315         @Override
onChange(boolean selfChange, Uri uri)316         public void onChange(boolean selfChange, Uri uri) {
317             if (mCallback != null) mCallback.onChange(uri);
318         }
319     }
320 
321     /**
322      * An interface for when Settings.Secure key state changes.
323      */
324     public interface TogglesCallback {
325         /**
326          * Callback method for Settings.Secure key state changes.
327          *
328          * @param uri The Uri of the changed content.
329          */
onChange(Uri uri)330         void onChange(Uri uri);
331     }
332 }
333