1 /* 2 * Copyright (C) 2017 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.settings.slices; 18 19 import static com.android.settings.bluetooth.BluetoothSliceBuilder.ACTION_BLUETOOTH_SLICE_CHANGED; 20 import static com.android.settings.network.telephony.Enhanced4gLteSliceHelper.ACTION_ENHANCED_4G_LTE_CHANGED; 21 import static com.android.settings.notification.ZenModeSliceBuilder.ACTION_ZEN_MODE_SLICE_CHANGED; 22 import static com.android.settings.slices.SettingsSliceProvider.ACTION_COPY; 23 import static com.android.settings.slices.SettingsSliceProvider.ACTION_SLIDER_CHANGED; 24 import static com.android.settings.slices.SettingsSliceProvider.ACTION_TOGGLE_CHANGED; 25 import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY; 26 import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_PLATFORM_DEFINED; 27 import static com.android.settings.wifi.calling.WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED; 28 import static com.android.settings.wifi.calling.WifiCallingSliceHelper.ACTION_WIFI_CALLING_PREFERENCE_CELLULAR_PREFERRED; 29 import static com.android.settings.wifi.calling.WifiCallingSliceHelper.ACTION_WIFI_CALLING_PREFERENCE_WIFI_ONLY; 30 import static com.android.settings.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.settings.bluetooth.BluetoothSliceBuilder; 44 import com.android.settings.core.BasePreferenceController; 45 import com.android.settings.core.SliderPreferenceController; 46 import com.android.settings.core.TogglePreferenceController; 47 import com.android.settings.notification.ZenModeSliceBuilder; 48 import com.android.settings.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 minValue = sliderController.getMin(); 168 final int maxValue = sliderController.getMax(); 169 if (newPosition < minValue || newPosition > maxValue) { 170 throw new IllegalArgumentException( 171 "Invalid position passed to Slider controller. Expected between " + minValue 172 + " and " + maxValue + " but found " + newPosition); 173 } 174 175 sliderController.setSliderPosition(newPosition); 176 logSliceValueChange(context, key, newPosition); 177 updateUri(context, key, isPlatformSlice); 178 } 179 handleCopyAction(Context context, String key, boolean isPlatformSlice)180 private void handleCopyAction(Context context, String key, boolean isPlatformSlice) { 181 if (TextUtils.isEmpty(key)) { 182 throw new IllegalArgumentException("No key passed to Intent for controller"); 183 } 184 185 final BasePreferenceController controller = getPreferenceController(context, key); 186 187 if (!(controller instanceof Sliceable)) { 188 throw new IllegalArgumentException( 189 "Copyable action passed for a non-copyable key:" + key); 190 } 191 192 if (!controller.isAvailable()) { 193 Log.w(TAG, "Can't update " + key + " since the setting is unavailable"); 194 if (!controller.hasAsyncUpdate()) { 195 updateUri(context, key, isPlatformSlice); 196 } 197 return; 198 } 199 200 controller.copy(); 201 } 202 203 /** 204 * Log Slice value update events into MetricsFeatureProvider. The logging schema generally 205 * follows the pattern in SharedPreferenceLogger. 206 */ logSliceValueChange(Context context, String sliceKey, int newValue)207 private void logSliceValueChange(Context context, String sliceKey, int newValue) { 208 FeatureFactory.getFactory(context).getMetricsFeatureProvider() 209 .action(SettingsEnums.PAGE_UNKNOWN, 210 SettingsEnums.ACTION_SETTINGS_SLICE_CHANGED, 211 SettingsEnums.PAGE_UNKNOWN, 212 sliceKey, newValue); 213 } 214 getPreferenceController(Context context, String key)215 private BasePreferenceController getPreferenceController(Context context, String key) { 216 final SlicesDatabaseAccessor accessor = new SlicesDatabaseAccessor(context); 217 final SliceData sliceData = accessor.getSliceDataFromKey(key); 218 return SliceBuilderUtils.getPreferenceController(context, sliceData); 219 } 220 updateUri(Context context, String key, boolean isPlatformDefined)221 private void updateUri(Context context, String key, boolean isPlatformDefined) { 222 final String authority = isPlatformDefined 223 ? SettingsSlicesContract.AUTHORITY 224 : SettingsSliceProvider.SLICE_AUTHORITY; 225 final Uri uri = new Uri.Builder() 226 .scheme(ContentResolver.SCHEME_CONTENT) 227 .authority(authority) 228 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) 229 .appendPath(key) 230 .build(); 231 context.getContentResolver().notifyChange(uri, null /* observer */); 232 } 233 } 234