• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.systemui.statusbar.policy;
18 
19 import android.app.RemoteInput;
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.os.Handler;
23 import android.provider.DeviceConfig;
24 import android.text.TextUtils;
25 import android.util.KeyValueListParser;
26 import android.util.Log;
27 
28 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
29 import com.android.systemui.R;
30 import com.android.systemui.dagger.SysUISingleton;
31 import com.android.systemui.dagger.qualifiers.Main;
32 import com.android.systemui.util.DeviceConfigProxy;
33 
34 import javax.inject.Inject;
35 
36 @SysUISingleton
37 public final class SmartReplyConstants {
38 
39     private static final String TAG = "SmartReplyConstants";
40 
41     private final boolean mDefaultEnabled;
42     private final boolean mDefaultRequiresP;
43     private final int mDefaultMaxSqueezeRemeasureAttempts;
44     private final boolean mDefaultEditChoicesBeforeSending;
45     private final boolean mDefaultShowInHeadsUp;
46     private final int mDefaultMinNumSystemGeneratedReplies;
47     private final int mDefaultMaxNumActions;
48     private final int mDefaultOnClickInitDelay;
49 
50     // These fields are updated on the UI thread but can be accessed on both the UI thread and
51     // background threads. We use the volatile keyword here instead of synchronization blocks since
52     // we only care about variable updates here being visible to other threads (and not for example
53     // whether the variables we are reading were updated in the same go).
54     private volatile boolean mEnabled;
55     private volatile boolean mRequiresTargetingP;
56     private volatile int mMaxSqueezeRemeasureAttempts;
57     private volatile boolean mEditChoicesBeforeSending;
58     private volatile boolean mShowInHeadsUp;
59     private volatile int mMinNumSystemGeneratedReplies;
60     private volatile int mMaxNumActions;
61     private volatile long mOnClickInitDelay;
62 
63     private final Handler mHandler;
64     private final Context mContext;
65     private final DeviceConfigProxy mDeviceConfig;
66     private final KeyValueListParser mParser = new KeyValueListParser(',');
67 
68     @Inject
SmartReplyConstants( @ain Handler handler, Context context, DeviceConfigProxy deviceConfig )69     public SmartReplyConstants(
70             @Main Handler handler,
71             Context context,
72             DeviceConfigProxy deviceConfig
73     ) {
74         mHandler = handler;
75         mContext = context;
76         final Resources resources = mContext.getResources();
77         mDefaultEnabled = resources.getBoolean(
78                 R.bool.config_smart_replies_in_notifications_enabled);
79         mDefaultRequiresP = resources.getBoolean(
80                 R.bool.config_smart_replies_in_notifications_requires_targeting_p);
81         mDefaultMaxSqueezeRemeasureAttempts = resources.getInteger(
82                 R.integer.config_smart_replies_in_notifications_max_squeeze_remeasure_attempts);
83         mDefaultEditChoicesBeforeSending = resources.getBoolean(
84                 R.bool.config_smart_replies_in_notifications_edit_choices_before_sending);
85         mDefaultShowInHeadsUp = resources.getBoolean(
86                 R.bool.config_smart_replies_in_notifications_show_in_heads_up);
87         mDefaultMinNumSystemGeneratedReplies = resources.getInteger(
88                 R.integer.config_smart_replies_in_notifications_min_num_system_generated_replies);
89         mDefaultMaxNumActions = resources.getInteger(
90                 R.integer.config_smart_replies_in_notifications_max_num_actions);
91         mDefaultOnClickInitDelay = resources.getInteger(
92                 R.integer.config_smart_replies_in_notifications_onclick_init_delay);
93 
94         mDeviceConfig = deviceConfig;
95         registerDeviceConfigListener();
96         updateConstants();
97     }
98 
registerDeviceConfigListener()99     private void registerDeviceConfigListener() {
100         mDeviceConfig.addOnPropertiesChangedListener(
101                 DeviceConfig.NAMESPACE_SYSTEMUI,
102                 this::postToHandler,
103                 mOnPropertiesChangedListener);
104     }
105 
postToHandler(Runnable r)106     private void postToHandler(Runnable r) {
107         this.mHandler.post(r);
108     }
109 
110     private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
111             new DeviceConfig.OnPropertiesChangedListener() {
112                 @Override
113                 public void onPropertiesChanged(DeviceConfig.Properties properties) {
114                     if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())) {
115                         Log.e(TAG,
116                                 "Received update from DeviceConfig for unrelated namespace: "
117                                         + properties.getNamespace());
118                         return;
119                     }
120                     updateConstants();
121                 }
122             };
123 
updateConstants()124     private void updateConstants() {
125         synchronized (SmartReplyConstants.this) {
126             mEnabled = readDeviceConfigBooleanOrDefaultIfEmpty(
127                     SystemUiDeviceConfigFlags.SSIN_ENABLED,
128                     mDefaultEnabled);
129             mRequiresTargetingP = readDeviceConfigBooleanOrDefaultIfEmpty(
130                     SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P,
131                     mDefaultRequiresP);
132             mMaxSqueezeRemeasureAttempts = mDeviceConfig.getInt(
133                     DeviceConfig.NAMESPACE_SYSTEMUI,
134                     SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS,
135                     mDefaultMaxSqueezeRemeasureAttempts);
136             mEditChoicesBeforeSending = readDeviceConfigBooleanOrDefaultIfEmpty(
137                     SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING,
138                     mDefaultEditChoicesBeforeSending);
139             mShowInHeadsUp = readDeviceConfigBooleanOrDefaultIfEmpty(
140                     SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP,
141                     mDefaultShowInHeadsUp);
142             mMinNumSystemGeneratedReplies = mDeviceConfig.getInt(
143                     DeviceConfig.NAMESPACE_SYSTEMUI,
144                     SystemUiDeviceConfigFlags.SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES,
145                     mDefaultMinNumSystemGeneratedReplies);
146             mMaxNumActions = mDeviceConfig.getInt(
147                     DeviceConfig.NAMESPACE_SYSTEMUI,
148                     SystemUiDeviceConfigFlags.SSIN_MAX_NUM_ACTIONS,
149                     mDefaultMaxNumActions);
150             mOnClickInitDelay = mDeviceConfig.getInt(
151                     DeviceConfig.NAMESPACE_SYSTEMUI,
152                     SystemUiDeviceConfigFlags.SSIN_ONCLICK_INIT_DELAY,
153                     mDefaultOnClickInitDelay);
154         }
155     }
156 
readDeviceConfigBooleanOrDefaultIfEmpty(String propertyName, boolean defaultValue)157     private boolean readDeviceConfigBooleanOrDefaultIfEmpty(String propertyName,
158             boolean defaultValue) {
159         String value = mDeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI, propertyName);
160         if (TextUtils.isEmpty(value)) {
161             return defaultValue;
162         }
163         if ("true".equals(value)) {
164             return true;
165         }
166         if ("false".equals(value)) {
167             return false;
168         }
169         // For invalid configs we return the default value.
170         return defaultValue;
171     }
172 
173     /** Returns whether smart replies in notifications are enabled. */
isEnabled()174     public boolean isEnabled() {
175         return mEnabled;
176     }
177 
178     /**
179      * Returns whether smart replies in notifications should be disabled when the app targets a
180      * version of Android older than P.
181      */
requiresTargetingP()182     public boolean requiresTargetingP() {
183         return mRequiresTargetingP;
184     }
185 
186     /**
187      * Returns the maximum number of times {@link SmartReplyView#onMeasure(int, int)} will try to
188      * find a better (narrower) line-break for a double-line smart reply button.
189      */
getMaxSqueezeRemeasureAttempts()190     public int getMaxSqueezeRemeasureAttempts() {
191         return mMaxSqueezeRemeasureAttempts;
192     }
193 
194     /**
195      * Returns whether by tapping on a choice should let the user edit the input before it
196      * is sent to the app.
197      *
198      * @param remoteInputEditChoicesBeforeSending The value from
199      *         {@link RemoteInput#getEditChoicesBeforeSending()}
200      */
getEffectiveEditChoicesBeforeSending( @emoteInput.EditChoicesBeforeSending int remoteInputEditChoicesBeforeSending)201     public boolean getEffectiveEditChoicesBeforeSending(
202             @RemoteInput.EditChoicesBeforeSending int remoteInputEditChoicesBeforeSending) {
203         switch (remoteInputEditChoicesBeforeSending) {
204             case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED:
205                 return false;
206             case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED:
207                 return true;
208             case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO:
209             default:
210                 return mEditChoicesBeforeSending;
211         }
212     }
213 
214     /**
215      * Returns whether smart suggestions should be enabled in heads-up notifications.
216      */
getShowInHeadsUp()217     public boolean getShowInHeadsUp() {
218         return mShowInHeadsUp;
219     }
220 
221     /**
222      * Returns the minimum number of system generated replies to show in a notification.
223      * If we cannot show at least this many system generated replies we should show none.
224      */
getMinNumSystemGeneratedReplies()225     public int getMinNumSystemGeneratedReplies() {
226         return mMinNumSystemGeneratedReplies;
227     }
228 
229     /**
230      * Returns the maximum number smart actions to show in a notification, or -1 if there shouldn't
231      * be a limit.
232      */
getMaxNumActions()233     public int getMaxNumActions() {
234         return mMaxNumActions;
235     }
236 
237     /**
238      * Returns the amount of time (ms) before smart suggestions are clickable, since the suggestions
239      * were added.
240      */
getOnClickInitDelay()241     public long getOnClickInitDelay() {
242         return mOnClickInitDelay;
243     }
244 }
245