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 android.ext.services.notification;
18 
19 import android.content.ContentResolver;
20 import android.database.ContentObserver;
21 import android.net.Uri;
22 import android.os.Handler;
23 import android.provider.DeviceConfig;
24 import android.provider.Settings;
25 import android.util.Log;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
29 
30 /**
31  * Observes the settings for {@link Assistant}.
32  */
33 final class AssistantSettings extends ContentObserver {
34     private static final String LOG_TAG = "AssistantSettings";
35     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;
39     private static final int DEFAULT_MAX_MESSAGES_TO_EXTRACT = 5;
40     @VisibleForTesting
41     static final int DEFAULT_MAX_SUGGESTIONS = 3;
42 
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);
48     private static final Uri NOTIFICATION_NEW_INTERRUPTION_MODEL_URI =
49             Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL);
50 
51     private final ContentResolver mResolver;
52     private final int mUserId;
53 
54     private final Handler mHandler;
55 
56     @VisibleForTesting
57     protected final Runnable mOnUpdateRunnable;
58 
59     // Actual configuration settings.
60     float mDismissToViewRatioLimit;
61     int mStreakLimit;
62     boolean mGenerateReplies = DEFAULT_GENERATE_REPLIES;
63     boolean mGenerateActions = DEFAULT_GENERATE_ACTIONS;
64     boolean mNewInterruptionModel;
65     int mMaxMessagesToExtract = DEFAULT_MAX_MESSAGES_TO_EXTRACT;
66     int mMaxSuggestions = DEFAULT_MAX_SUGGESTIONS;
67 
AssistantSettings(Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable)68     private AssistantSettings(Handler handler, ContentResolver resolver, int userId,
69             Runnable onUpdateRunnable) {
70         super(handler);
71         mHandler = handler;
72         mResolver = resolver;
73         mUserId = userId;
74         mOnUpdateRunnable = onUpdateRunnable;
75     }
76 
createAndRegister( Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable)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();
82         assistantSettings.registerDeviceConfigs();
83         return assistantSettings;
84     }
85 
86     /**
87      * Creates an instance but doesn't register it as an observer.
88      */
89     @VisibleForTesting
createForTesting( Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable)90     protected static AssistantSettings createForTesting(
91             Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable) {
92         return new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
93     }
94 
register()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);
99 
100         // Update all uris on creation.
101         update(null);
102     }
103 
registerDeviceConfigs()104     private void registerDeviceConfigs() {
105         DeviceConfig.addOnPropertiesChangedListener(
106                 DeviceConfig.NAMESPACE_SYSTEMUI,
107                 this::postToHandler,
108                 (properties) -> onDeviceConfigPropertiesChanged(properties.getNamespace()));
109 
110         // Update the fields in this class from the current state of the device config.
111         updateFromDeviceConfigFlags();
112     }
113 
postToHandler(Runnable r)114     private void postToHandler(Runnable r) {
115         this.mHandler.post(r);
116     }
117 
118     @VisibleForTesting
onDeviceConfigPropertiesChanged(String namespace)119     void onDeviceConfigPropertiesChanged(String namespace) {
120         if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) {
121             Log.e(LOG_TAG, "Received update from DeviceConfig for unrelated namespace: "
122                     + namespace);
123             return;
124         }
125 
126         updateFromDeviceConfigFlags();
127     }
128 
updateFromDeviceConfigFlags()129     private void updateFromDeviceConfigFlags() {
130         mGenerateReplies = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
131                 SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, DEFAULT_GENERATE_REPLIES);
132 
133         mGenerateActions = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
134                 SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, DEFAULT_GENERATE_ACTIONS);
135 
136         mMaxMessagesToExtract = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
137                 SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT,
138                 DEFAULT_MAX_MESSAGES_TO_EXTRACT);
139 
140         mMaxSuggestions = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
141                 SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS, DEFAULT_MAX_SUGGESTIONS);
142 
143         mOnUpdateRunnable.run();
144     }
145 
146     @Override
onChange(boolean selfChange, Uri uri)147     public void onChange(boolean selfChange, Uri uri) {
148         update(uri);
149     }
150 
update(Uri uri)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         }
162         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 
172     public interface Factory {
createAndRegister(Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable)173         AssistantSettings createAndRegister(Handler handler, ContentResolver resolver, int userId,
174                 Runnable onUpdateRunnable);
175     }
176 }
177