• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.car.developeroptions.slices;
18 
19 import static com.android.car.developeroptions.bluetooth.BluetoothSliceBuilder.ACTION_BLUETOOTH_SLICE_CHANGED;
20 import static com.android.car.developeroptions.network.telephony.Enhanced4gLteSliceHelper.ACTION_ENHANCED_4G_LTE_CHANGED;
21 import static com.android.car.developeroptions.notification.ZenModeSliceBuilder.ACTION_ZEN_MODE_SLICE_CHANGED;
22 import static com.android.car.developeroptions.slices.SettingsSliceProvider.ACTION_COPY;
23 import static com.android.car.developeroptions.slices.SettingsSliceProvider.ACTION_SLIDER_CHANGED;
24 import static com.android.car.developeroptions.slices.SettingsSliceProvider.ACTION_TOGGLE_CHANGED;
25 import static com.android.car.developeroptions.slices.SettingsSliceProvider.EXTRA_SLICE_KEY;
26 import static com.android.car.developeroptions.slices.SettingsSliceProvider.EXTRA_SLICE_PLATFORM_DEFINED;
27 import static com.android.car.developeroptions.wifi.calling.WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED;
28 import static com.android.car.developeroptions.wifi.calling.WifiCallingSliceHelper.ACTION_WIFI_CALLING_PREFERENCE_CELLULAR_PREFERRED;
29 import static com.android.car.developeroptions.wifi.calling.WifiCallingSliceHelper.ACTION_WIFI_CALLING_PREFERENCE_WIFI_ONLY;
30 import static com.android.car.developeroptions.wifi.calling.WifiCallingSliceHelper.ACTION_WIFI_CALLING_PREFERENCE_WIFI_PREFERRED;
31 
32 import android.app.settings.SettingsEnums;
33 import android.app.slice.Slice;
34 import android.content.BroadcastReceiver;
35 import android.content.ContentResolver;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.net.Uri;
39 import android.provider.SettingsSlicesContract;
40 import android.text.TextUtils;
41 import android.util.Log;
42 
43 import com.android.car.developeroptions.bluetooth.BluetoothSliceBuilder;
44 import com.android.car.developeroptions.core.BasePreferenceController;
45 import com.android.car.developeroptions.core.SliderPreferenceController;
46 import com.android.car.developeroptions.core.TogglePreferenceController;
47 import com.android.car.developeroptions.notification.ZenModeSliceBuilder;
48 import com.android.car.developeroptions.overlay.FeatureFactory;
49 
50 /**
51  * Responds to actions performed on slices and notifies slices of updates in state changes.
52  */
53 public class SliceBroadcastReceiver extends BroadcastReceiver {
54 
55     private static String TAG = "SettSliceBroadcastRec";
56 
57     @Override
onReceive(Context context, Intent intent)58     public void onReceive(Context context, Intent intent) {
59         final String action = intent.getAction();
60         final String key = intent.getStringExtra(EXTRA_SLICE_KEY);
61         final boolean isPlatformSlice = intent.getBooleanExtra(EXTRA_SLICE_PLATFORM_DEFINED,
62                 false /* default */);
63 
64         if (CustomSliceRegistry.isValidAction(action)) {
65             final CustomSliceable sliceable =
66                     CustomSliceable.createInstance(context,
67                             CustomSliceRegistry.getSliceClassByUri(Uri.parse(action)));
68             sliceable.onNotifyChange(intent);
69             return;
70         }
71 
72         switch (action) {
73             case ACTION_TOGGLE_CHANGED:
74                 final boolean isChecked = intent.getBooleanExtra(Slice.EXTRA_TOGGLE_STATE, false);
75                 handleToggleAction(context, key, isChecked, isPlatformSlice);
76                 break;
77             case ACTION_SLIDER_CHANGED:
78                 final int newPosition = intent.getIntExtra(Slice.EXTRA_RANGE_VALUE, -1);
79                 handleSliderAction(context, key, newPosition, isPlatformSlice);
80                 break;
81             case ACTION_BLUETOOTH_SLICE_CHANGED:
82                 BluetoothSliceBuilder.handleUriChange(context, intent);
83                 break;
84             case ACTION_WIFI_CALLING_CHANGED:
85                 FeatureFactory.getFactory(context)
86                         .getSlicesFeatureProvider()
87                         .getNewWifiCallingSliceHelper(context)
88                         .handleWifiCallingChanged(intent);
89                 break;
90             case ACTION_ZEN_MODE_SLICE_CHANGED:
91                 ZenModeSliceBuilder.handleUriChange(context, intent);
92                 break;
93             case ACTION_ENHANCED_4G_LTE_CHANGED:
94                 FeatureFactory.getFactory(context)
95                         .getSlicesFeatureProvider()
96                         .getNewEnhanced4gLteSliceHelper(context)
97                         .handleEnhanced4gLteChanged(intent);
98                 break;
99             case ACTION_WIFI_CALLING_PREFERENCE_WIFI_ONLY:
100             case ACTION_WIFI_CALLING_PREFERENCE_WIFI_PREFERRED:
101             case ACTION_WIFI_CALLING_PREFERENCE_CELLULAR_PREFERRED:
102                 FeatureFactory.getFactory(context)
103                         .getSlicesFeatureProvider()
104                         .getNewWifiCallingSliceHelper(context)
105                         .handleWifiCallingPreferenceChanged(intent);
106                 break;
107             case ACTION_COPY:
108                 handleCopyAction(context, key, isPlatformSlice);
109                 break;
110         }
111     }
112 
handleToggleAction(Context context, String key, boolean isChecked, boolean isPlatformSlice)113     private void handleToggleAction(Context context, String key, boolean isChecked,
114             boolean isPlatformSlice) {
115         if (TextUtils.isEmpty(key)) {
116             throw new IllegalStateException("No key passed to Intent for toggle controller");
117         }
118 
119         final BasePreferenceController controller = getPreferenceController(context, key);
120 
121         if (!(controller instanceof TogglePreferenceController)) {
122             throw new IllegalStateException("Toggle action passed for a non-toggle key: " + key);
123         }
124 
125         if (!controller.isAvailable()) {
126             Log.w(TAG, "Can't update " + key + " since the setting is unavailable");
127             if (!controller.hasAsyncUpdate()) {
128                 updateUri(context, key, isPlatformSlice);
129             }
130             return;
131         }
132 
133         // TODO post context.getContentResolver().notifyChanged(uri, null) in the Toggle controller
134         // so that it's automatically broadcast to any slice.
135         final TogglePreferenceController toggleController = (TogglePreferenceController) controller;
136         toggleController.setChecked(isChecked);
137         logSliceValueChange(context, key, isChecked ? 1 : 0);
138         if (!controller.hasAsyncUpdate()) {
139             updateUri(context, key, isPlatformSlice);
140         }
141     }
142 
handleSliderAction(Context context, String key, int newPosition, boolean isPlatformSlice)143     private void handleSliderAction(Context context, String key, int newPosition,
144             boolean isPlatformSlice) {
145         if (TextUtils.isEmpty(key)) {
146             throw new IllegalArgumentException(
147                     "No key passed to Intent for slider controller. Use extra: " + EXTRA_SLICE_KEY);
148         }
149 
150         if (newPosition == -1) {
151             throw new IllegalArgumentException("Invalid position passed to Slider controller");
152         }
153 
154         final BasePreferenceController controller = getPreferenceController(context, key);
155 
156         if (!(controller instanceof SliderPreferenceController)) {
157             throw new IllegalArgumentException("Slider action passed for a non-slider key: " + key);
158         }
159 
160         if (!controller.isAvailable()) {
161             Log.w(TAG, "Can't update " + key + " since the setting is unavailable");
162             updateUri(context, key, isPlatformSlice);
163             return;
164         }
165 
166         final SliderPreferenceController sliderController = (SliderPreferenceController) controller;
167         final int maxSteps = sliderController.getMaxSteps();
168         if (newPosition < 0 || newPosition > maxSteps) {
169             throw new IllegalArgumentException(
170                     "Invalid position passed to Slider controller. Expected between 0 and "
171                             + maxSteps + " but found " + newPosition);
172         }
173 
174         sliderController.setSliderPosition(newPosition);
175         logSliceValueChange(context, key, newPosition);
176         updateUri(context, key, isPlatformSlice);
177     }
178 
handleCopyAction(Context context, String key, boolean isPlatformSlice)179     private void handleCopyAction(Context context, String key, boolean isPlatformSlice) {
180         if (TextUtils.isEmpty(key)) {
181             throw new IllegalArgumentException("No key passed to Intent for controller");
182         }
183 
184         final BasePreferenceController controller = getPreferenceController(context, key);
185 
186         if (!(controller instanceof Sliceable)) {
187             throw new IllegalArgumentException(
188                     "Copyable action passed for a non-copyable key:" + key);
189         }
190 
191         if (!controller.isAvailable()) {
192             Log.w(TAG, "Can't update " + key + " since the setting is unavailable");
193             if (!controller.hasAsyncUpdate()) {
194                 updateUri(context, key, isPlatformSlice);
195             }
196             return;
197         }
198 
199         controller.copy();
200     }
201 
202     /**
203      * Log Slice value update events into MetricsFeatureProvider. The logging schema generally
204      * follows the pattern in SharedPreferenceLogger.
205      */
logSliceValueChange(Context context, String sliceKey, int newValue)206     private void logSliceValueChange(Context context, String sliceKey, int newValue) {
207         FeatureFactory.getFactory(context).getMetricsFeatureProvider()
208                 .action(SettingsEnums.PAGE_UNKNOWN,
209                         SettingsEnums.ACTION_SETTINGS_SLICE_CHANGED,
210                         SettingsEnums.PAGE_UNKNOWN,
211                         sliceKey, newValue);
212     }
213 
getPreferenceController(Context context, String key)214     private BasePreferenceController getPreferenceController(Context context, String key) {
215         final SlicesDatabaseAccessor accessor = new SlicesDatabaseAccessor(context);
216         final SliceData sliceData = accessor.getSliceDataFromKey(key);
217         return SliceBuilderUtils.getPreferenceController(context, sliceData);
218     }
219 
updateUri(Context context, String key, boolean isPlatformDefined)220     private void updateUri(Context context, String key, boolean isPlatformDefined) {
221         final String authority = isPlatformDefined
222                 ? SettingsSlicesContract.AUTHORITY
223                 : SettingsSliceProvider.SLICE_AUTHORITY;
224         final Uri uri = new Uri.Builder()
225                 .scheme(ContentResolver.SCHEME_CONTENT)
226                 .authority(authority)
227                 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
228                 .appendPath(key)
229                 .build();
230         context.getContentResolver().notifyChange(uri, null /* observer */);
231     }
232 }
233