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