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