blob: 6cf72a45a06be8c2873124def5710c03ee343ef2 [file] [log] [blame]
Milo Sredkov22b8d9e2018-11-27 15:52:10 +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 android.ext.services.notification;
18
19import android.content.ContentResolver;
20import android.database.ContentObserver;
21import android.net.Uri;
22import android.os.Handler;
Gustav Sennton13cc6eb2019-01-17 18:05:46 +000023import android.provider.DeviceConfig;
Milo Sredkov22b8d9e2018-11-27 15:52:10 +000024import android.provider.Settings;
Gustav Sennton13cc6eb2019-01-17 18:05:46 +000025import android.text.TextUtils;
Gustav Sennton13cc6eb2019-01-17 18:05:46 +000026import android.util.Log;
Milo Sredkov22b8d9e2018-11-27 15:52:10 +000027
28import com.android.internal.annotations.VisibleForTesting;
29
30/**
31 * Observes the settings for {@link Assistant}.
32 */
33final class AssistantSettings extends ContentObserver {
Gustav Sennton13cc6eb2019-01-17 18:05:46 +000034 private static final String LOG_TAG = "AssistantSettings";
Milo Sredkov22b8d9e2018-11-27 15:52:10 +000035 public static Factory FACTORY = AssistantSettings::createAndRegister;
36 private static final boolean DEFAULT_GENERATE_REPLIES = true;
37 private static final boolean DEFAULT_GENERATE_ACTIONS = true;
38 private static final int DEFAULT_NEW_INTERRUPTION_MODEL_INT = 1;
Tony Make1a27ac2019-01-31 16:32:19 +000039 private static final int DEFAULT_MAX_MESSAGES_TO_EXTRACT = 5;
40 @VisibleForTesting
41 static final int DEFAULT_MAX_SUGGESTIONS = 3;
Milo Sredkov22b8d9e2018-11-27 15:52:10 +000042
43 private static final Uri STREAK_LIMIT_URI =
44 Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT);
45 private static final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI =
46 Settings.Global.getUriFor(
47 Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT);
Milo Sredkov22b8d9e2018-11-27 15:52:10 +000048 private static final Uri NOTIFICATION_NEW_INTERRUPTION_MODEL_URI =
49 Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL);
50
Milo Sredkov22b8d9e2018-11-27 15:52:10 +000051 private final ContentResolver mResolver;
52 private final int mUserId;
53
Gustav Sennton13cc6eb2019-01-17 18:05:46 +000054 private final Handler mHandler;
55
Milo Sredkov22b8d9e2018-11-27 15:52:10 +000056 @VisibleForTesting
57 protected final Runnable mOnUpdateRunnable;
58
Tony Make1a27ac2019-01-31 16:32:19 +000059 // Actual configuration settings.
Milo Sredkov22b8d9e2018-11-27 15:52:10 +000060 float mDismissToViewRatioLimit;
61 int mStreakLimit;
62 boolean mGenerateReplies = DEFAULT_GENERATE_REPLIES;
63 boolean mGenerateActions = DEFAULT_GENERATE_ACTIONS;
64 boolean mNewInterruptionModel;
Tony Make1a27ac2019-01-31 16:32:19 +000065 int mMaxMessagesToExtract = DEFAULT_MAX_MESSAGES_TO_EXTRACT;
66 int mMaxSuggestions = DEFAULT_MAX_SUGGESTIONS;
Milo Sredkov22b8d9e2018-11-27 15:52:10 +000067
68 private AssistantSettings(Handler handler, ContentResolver resolver, int userId,
69 Runnable onUpdateRunnable) {
70 super(handler);
Gustav Sennton13cc6eb2019-01-17 18:05:46 +000071 mHandler = handler;
Milo Sredkov22b8d9e2018-11-27 15:52:10 +000072 mResolver = resolver;
73 mUserId = userId;
74 mOnUpdateRunnable = onUpdateRunnable;
75 }
76
77 private static AssistantSettings createAndRegister(
78 Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable) {
79 AssistantSettings assistantSettings =
80 new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
81 assistantSettings.register();
Gustav Sennton13cc6eb2019-01-17 18:05:46 +000082 assistantSettings.registerDeviceConfigs();
Milo Sredkov22b8d9e2018-11-27 15:52:10 +000083 return assistantSettings;
84 }
85
86 /**
87 * Creates an instance but doesn't register it as an observer.
88 */
89 @VisibleForTesting
90 protected static AssistantSettings createForTesting(
91 Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable) {
92 return new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
93 }
94
95 private void register() {
96 mResolver.registerContentObserver(
97 DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, mUserId);
98 mResolver.registerContentObserver(STREAK_LIMIT_URI, false, this, mUserId);
Milo Sredkov22b8d9e2018-11-27 15:52:10 +000099
100 // Update all uris on creation.
101 update(null);
102 }
103
Gustav Sennton13cc6eb2019-01-17 18:05:46 +0000104 private void registerDeviceConfigs() {
105 DeviceConfig.addOnPropertyChangedListener(
Gustav Sennton73a8c1b2019-01-23 18:15:39 +0000106 DeviceConfig.NotificationAssistant.NAMESPACE,
Gustav Sennton13cc6eb2019-01-17 18:05:46 +0000107 this::postToHandler,
108 this::onDeviceConfigPropertyChanged);
109
110 // Update the fields in this class from the current state of the device config.
111 updateFromDeviceConfigFlags();
112 }
113
114 private void postToHandler(Runnable r) {
115 this.mHandler.post(r);
116 }
117
118 @VisibleForTesting
119 void onDeviceConfigPropertyChanged(String namespace, String name, String value) {
Gustav Sennton73a8c1b2019-01-23 18:15:39 +0000120 if (!DeviceConfig.NotificationAssistant.NAMESPACE.equals(namespace)) {
Gustav Sennton13cc6eb2019-01-17 18:05:46 +0000121 Log.e(LOG_TAG, "Received update from DeviceConfig for unrelated namespace: "
122 + namespace + " " + name + "=" + value);
123 return;
124 }
125
126 updateFromDeviceConfigFlags();
127 }
128
129 private void updateFromDeviceConfigFlags() {
Tony Make1a27ac2019-01-31 16:32:19 +0000130 mGenerateReplies = DeviceConfigHelper.getBoolean(
131 DeviceConfig.NotificationAssistant.GENERATE_REPLIES, DEFAULT_GENERATE_REPLIES);
Gustav Sennton13cc6eb2019-01-17 18:05:46 +0000132
Tony Make1a27ac2019-01-31 16:32:19 +0000133 mGenerateActions = DeviceConfigHelper.getBoolean(
134 DeviceConfig.NotificationAssistant.GENERATE_ACTIONS, DEFAULT_GENERATE_ACTIONS);
135
136 mMaxMessagesToExtract = DeviceConfigHelper.getInteger(
137 DeviceConfig.NotificationAssistant.MAX_MESSAGES_TO_EXTRACT,
138 DEFAULT_MAX_MESSAGES_TO_EXTRACT);
139
140 mMaxSuggestions = DeviceConfigHelper.getInteger(
141 DeviceConfig.NotificationAssistant.MAX_SUGGESTIONS, DEFAULT_MAX_SUGGESTIONS);
Gustav Sennton13cc6eb2019-01-17 18:05:46 +0000142
143 mOnUpdateRunnable.run();
144 }
145
Milo Sredkov22b8d9e2018-11-27 15:52:10 +0000146 @Override
147 public void onChange(boolean selfChange, Uri uri) {
148 update(uri);
149 }
150
151 private void update(Uri uri) {
152 if (uri == null || DISMISS_TO_VIEW_RATIO_LIMIT_URI.equals(uri)) {
153 mDismissToViewRatioLimit = Settings.Global.getFloat(
154 mResolver, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
155 ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT);
156 }
157 if (uri == null || STREAK_LIMIT_URI.equals(uri)) {
158 mStreakLimit = Settings.Global.getInt(
159 mResolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT,
160 ChannelImpressions.DEFAULT_STREAK_LIMIT);
161 }
Milo Sredkov22b8d9e2018-11-27 15:52:10 +0000162 if (uri == null || NOTIFICATION_NEW_INTERRUPTION_MODEL_URI.equals(uri)) {
163 int mNewInterruptionModelInt = Settings.Secure.getInt(
164 mResolver, Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL,
165 DEFAULT_NEW_INTERRUPTION_MODEL_INT);
166 mNewInterruptionModel = mNewInterruptionModelInt == 1;
167 }
168
169 mOnUpdateRunnable.run();
170 }
171
Tony Make1a27ac2019-01-31 16:32:19 +0000172 static class DeviceConfigHelper {
173
174 static int getInteger(String key, int defaultValue) {
175 String value = getValue(key);
176 if (TextUtils.isEmpty(value)) {
177 return defaultValue;
178 }
179 try {
180 return Integer.parseInt(value);
181 } catch (NumberFormatException ex) {
182 return defaultValue;
183 }
184 }
185
186 static boolean getBoolean(String key, boolean defaultValue) {
187 String value = getValue(key);
188 if (TextUtils.isEmpty(value)) {
189 return defaultValue;
190 }
191 return Boolean.parseBoolean(value);
192 }
193
194 private static String getValue(String key) {
195 return DeviceConfig.getProperty(
196 DeviceConfig.NotificationAssistant.NAMESPACE,
197 key);
198 }
199 }
200
Milo Sredkov22b8d9e2018-11-27 15:52:10 +0000201 public interface Factory {
202 AssistantSettings createAndRegister(Handler handler, ContentResolver resolver, int userId,
203 Runnable onUpdateRunnable);
204 }
Tony Make1a27ac2019-01-31 16:32:19 +0000205}