blob: 72f4fc7820a53866454c398899b81fe6935ab70e [file] [log] [blame]
Petr Cermak10011fa2018-02-05 19:00:54 +00001/*
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
17package com.android.systemui.statusbar.policy;
18
Jason Monk27d01a622018-12-10 15:57:09 -050019import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
20
Milo Sredkov41dc4ba2018-12-27 12:03:45 +000021import android.app.RemoteInput;
Petr Cermak10011fa2018-02-05 19:00:54 +000022import android.content.Context;
23import android.content.res.Resources;
Petr Cermak10011fa2018-02-05 19:00:54 +000024import android.os.Handler;
Gustav Sennton8fa7e952019-01-29 19:37:00 +000025import android.provider.DeviceConfig;
26import android.text.TextUtils;
Petr Cermak10011fa2018-02-05 19:00:54 +000027import android.util.KeyValueListParser;
28import android.util.Log;
29
Gustav Sennton8fa7e952019-01-29 19:37:00 +000030import com.android.internal.annotations.VisibleForTesting;
31import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
Petr Cermak10011fa2018-02-05 19:00:54 +000032import com.android.systemui.R;
33
Jason Monk27d01a622018-12-10 15:57:09 -050034import javax.inject.Inject;
35import javax.inject.Named;
36import javax.inject.Singleton;
37
38@Singleton
Gustav Sennton8fa7e952019-01-29 19:37:00 +000039public final class SmartReplyConstants {
Petr Cermak10011fa2018-02-05 19:00:54 +000040
41 private static final String TAG = "SmartReplyConstants";
42
Petr Cermak10011fa2018-02-05 19:00:54 +000043 private final boolean mDefaultEnabled;
Richard Ledley28944cb2018-02-26 10:36:00 +000044 private final boolean mDefaultRequiresP;
Petr Cermak10011fa2018-02-05 19:00:54 +000045 private final int mDefaultMaxSqueezeRemeasureAttempts;
Milo Sredkov41dc4ba2018-12-27 12:03:45 +000046 private final boolean mDefaultEditChoicesBeforeSending;
Gustav Sennton3f3eaff2019-01-08 09:39:51 +000047 private final boolean mDefaultShowInHeadsUp;
Gustav Senntona31f6ae2019-01-08 11:20:49 +000048 private final int mDefaultMinNumSystemGeneratedReplies;
Gustav Sennton4bf5ff52019-01-16 14:27:25 +000049 private final int mDefaultMaxNumActions;
Petr Cermak10011fa2018-02-05 19:00:54 +000050
Gustav Senntoneb93c402019-02-25 18:44:01 +000051 // These fields are updated on the UI thread but can be accessed on both the UI thread and
52 // background threads. We use the volatile keyword here instead of synchronization blocks since
53 // we only care about variable updates here being visible to other threads (and not for example
54 // whether the variables we are reading were updated in the same go).
55 private volatile boolean mEnabled;
56 private volatile boolean mRequiresTargetingP;
57 private volatile int mMaxSqueezeRemeasureAttempts;
58 private volatile boolean mEditChoicesBeforeSending;
59 private volatile boolean mShowInHeadsUp;
60 private volatile int mMinNumSystemGeneratedReplies;
61 private volatile int mMaxNumActions;
Petr Cermak10011fa2018-02-05 19:00:54 +000062
Gustav Sennton8fa7e952019-01-29 19:37:00 +000063 private final Handler mHandler;
Petr Cermak10011fa2018-02-05 19:00:54 +000064 private final Context mContext;
65 private final KeyValueListParser mParser = new KeyValueListParser(',');
66
Jason Monk27d01a622018-12-10 15:57:09 -050067 @Inject
68 public SmartReplyConstants(@Named(MAIN_HANDLER_NAME) Handler handler, Context context) {
Gustav Sennton8fa7e952019-01-29 19:37:00 +000069 mHandler = handler;
Petr Cermak10011fa2018-02-05 19:00:54 +000070 mContext = context;
71 final Resources resources = mContext.getResources();
72 mDefaultEnabled = resources.getBoolean(
73 R.bool.config_smart_replies_in_notifications_enabled);
Richard Ledley28944cb2018-02-26 10:36:00 +000074 mDefaultRequiresP = resources.getBoolean(
75 R.bool.config_smart_replies_in_notifications_requires_targeting_p);
Petr Cermak10011fa2018-02-05 19:00:54 +000076 mDefaultMaxSqueezeRemeasureAttempts = resources.getInteger(
77 R.integer.config_smart_replies_in_notifications_max_squeeze_remeasure_attempts);
Milo Sredkov41dc4ba2018-12-27 12:03:45 +000078 mDefaultEditChoicesBeforeSending = resources.getBoolean(
79 R.bool.config_smart_replies_in_notifications_edit_choices_before_sending);
Gustav Sennton3f3eaff2019-01-08 09:39:51 +000080 mDefaultShowInHeadsUp = resources.getBoolean(
81 R.bool.config_smart_replies_in_notifications_show_in_heads_up);
Gustav Senntona31f6ae2019-01-08 11:20:49 +000082 mDefaultMinNumSystemGeneratedReplies = resources.getInteger(
83 R.integer.config_smart_replies_in_notifications_min_num_system_generated_replies);
Gustav Sennton4bf5ff52019-01-16 14:27:25 +000084 mDefaultMaxNumActions = resources.getInteger(
85 R.integer.config_smart_replies_in_notifications_max_num_actions);
Petr Cermak10011fa2018-02-05 19:00:54 +000086
Gustav Sennton8fa7e952019-01-29 19:37:00 +000087 registerDeviceConfigListener();
Petr Cermak10011fa2018-02-05 19:00:54 +000088 updateConstants();
89 }
90
Gustav Sennton8fa7e952019-01-29 19:37:00 +000091 private void registerDeviceConfigListener() {
92 DeviceConfig.addOnPropertyChangedListener(
93 DeviceConfig.NAMESPACE_SYSTEMUI,
94 this::postToHandler,
95 this::onDeviceConfigPropertyChanged);
96 }
97
98 private void postToHandler(Runnable r) {
99 this.mHandler.post(r);
100 }
101
102 @VisibleForTesting
103 void onDeviceConfigPropertyChanged(String namespace, String name, String value) {
104 if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) {
105 Log.e(TAG, "Received update from DeviceConfig for unrelated namespace: "
106 + namespace + " " + name + "=" + value);
107 return;
108 }
109
Petr Cermak10011fa2018-02-05 19:00:54 +0000110 updateConstants();
111 }
112
113 private void updateConstants() {
114 synchronized (SmartReplyConstants.this) {
Gustav Sennton8fa7e952019-01-29 19:37:00 +0000115 mEnabled = readDeviceConfigBooleanOrDefaultIfEmpty(
116 SystemUiDeviceConfigFlags.SSIN_ENABLED,
117 mDefaultEnabled);
118 mRequiresTargetingP = readDeviceConfigBooleanOrDefaultIfEmpty(
119 SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P,
120 mDefaultRequiresP);
121 mMaxSqueezeRemeasureAttempts = readDeviceConfigIntegerOrDefaultIfEmpty(
122 SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS,
123 mDefaultMaxSqueezeRemeasureAttempts);
124 mEditChoicesBeforeSending = readDeviceConfigBooleanOrDefaultIfEmpty(
125 SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING,
126 mDefaultEditChoicesBeforeSending);
127 mShowInHeadsUp = readDeviceConfigBooleanOrDefaultIfEmpty(
128 SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP,
129 mDefaultShowInHeadsUp);
130 mMinNumSystemGeneratedReplies = readDeviceConfigIntegerOrDefaultIfEmpty(
131 SystemUiDeviceConfigFlags.SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES,
132 mDefaultMinNumSystemGeneratedReplies);
133 mMaxNumActions = readDeviceConfigIntegerOrDefaultIfEmpty(
134 SystemUiDeviceConfigFlags.SSIN_MAX_NUM_ACTIONS,
135 mDefaultMaxNumActions);
136 }
137 }
138
139 private static boolean readDeviceConfigBooleanOrDefaultIfEmpty(String propertyName,
140 boolean defaultValue) {
141 String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI, propertyName);
142 if (TextUtils.isEmpty(value)) {
143 return defaultValue;
144 }
145 if ("true".equals(value)) {
146 return true;
147 }
148 if ("false".equals(value)) {
149 return false;
150 }
151 // For invalid configs we return the default value.
152 return defaultValue;
153 }
154
155 private static int readDeviceConfigIntegerOrDefaultIfEmpty(String propertyName,
156 int defaultValue) {
157 String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI, propertyName);
158 if (TextUtils.isEmpty(value)) {
159 return defaultValue;
160 }
161 try {
162 return Integer.parseInt(value);
163 } catch (NumberFormatException e) {
164 Log.e(TAG, "Tried to read an integer flag, property name="
165 + propertyName + ", value=" + value);
166 return defaultValue;
Petr Cermak10011fa2018-02-05 19:00:54 +0000167 }
168 }
169
170 /** Returns whether smart replies in notifications are enabled. */
171 public boolean isEnabled() {
172 return mEnabled;
173 }
174
175 /**
Richard Ledley28944cb2018-02-26 10:36:00 +0000176 * Returns whether smart replies in notifications should be disabled when the app targets a
177 * version of Android older than P.
178 */
179 public boolean requiresTargetingP() {
180 return mRequiresTargetingP;
181 }
182
183 /**
Petr Cermak10011fa2018-02-05 19:00:54 +0000184 * Returns the maximum number of times {@link SmartReplyView#onMeasure(int, int)} will try to
185 * find a better (narrower) line-break for a double-line smart reply button.
186 */
187 public int getMaxSqueezeRemeasureAttempts() {
188 return mMaxSqueezeRemeasureAttempts;
189 }
Milo Sredkov41dc4ba2018-12-27 12:03:45 +0000190
191 /**
192 * Returns whether by tapping on a choice should let the user edit the input before it
193 * is sent to the app.
194 *
195 * @param remoteInputEditChoicesBeforeSending The value from
196 * {@link RemoteInput#getEditChoicesBeforeSending()}
197 */
198 public boolean getEffectiveEditChoicesBeforeSending(
199 @RemoteInput.EditChoicesBeforeSending int remoteInputEditChoicesBeforeSending) {
200 switch (remoteInputEditChoicesBeforeSending) {
201 case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED:
202 return false;
203 case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED:
204 return true;
205 case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO:
206 default:
207 return mEditChoicesBeforeSending;
208 }
209 }
Gustav Sennton3f3eaff2019-01-08 09:39:51 +0000210
211 /**
212 * Returns whether smart suggestions should be enabled in heads-up notifications.
213 */
214 public boolean getShowInHeadsUp() {
215 return mShowInHeadsUp;
216 }
Gustav Senntona31f6ae2019-01-08 11:20:49 +0000217
218 /**
219 * Returns the minimum number of system generated replies to show in a notification.
220 * If we cannot show at least this many system generated replies we should show none.
221 */
222 public int getMinNumSystemGeneratedReplies() {
223 return mMinNumSystemGeneratedReplies;
224 }
Gustav Sennton4bf5ff52019-01-16 14:27:25 +0000225
226 /**
227 * Returns the maximum number smart actions to show in a notification, or -1 if there shouldn't
228 * be a limit.
229 */
230 public int getMaxNumActions() {
231 return mMaxNumActions;
232 }
Petr Cermak10011fa2018-02-05 19:00:54 +0000233}