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.notification.ZenModeSliceBuilder.ACTION_ZEN_MODE_SLICE_CHANGED; 21 import static com.android.settings.slices.SettingsSliceProvider.ACTION_SLIDER_CHANGED; 22 import static com.android.settings.slices.SettingsSliceProvider.ACTION_TOGGLE_CHANGED; 23 import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY; 24 import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_PLATFORM_DEFINED; 25 import static com.android.settings.wifi.calling.WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED; 26 import static com.android.settings.wifi.WifiSliceBuilder.ACTION_WIFI_SLICE_CHANGED; 27 28 import android.app.slice.Slice; 29 import android.content.BroadcastReceiver; 30 import android.content.ContentResolver; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.net.Uri; 34 import android.provider.SettingsSlicesContract; 35 import android.text.TextUtils; 36 import android.util.Log; 37 import android.util.Pair; 38 39 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 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.ZenModeSliceBuilder; 45 import com.android.settings.overlay.FeatureFactory; 46 import com.android.settings.wifi.WifiSliceBuilder; 47 48 /** 49 * Responds to actions performed on slices and notifies slices of updates in state changes. 50 */ 51 public class SliceBroadcastReceiver extends BroadcastReceiver { 52 53 private static String TAG = "SettSliceBroadcastRec"; 54 55 @Override onReceive(Context context, Intent intent)56 public void onReceive(Context context, Intent intent) { 57 final String action = intent.getAction(); 58 final String key = intent.getStringExtra(EXTRA_SLICE_KEY); 59 final boolean isPlatformSlice = intent.getBooleanExtra(EXTRA_SLICE_PLATFORM_DEFINED, 60 false /* default */); 61 62 switch (action) { 63 case ACTION_TOGGLE_CHANGED: 64 final boolean isChecked = intent.getBooleanExtra(Slice.EXTRA_TOGGLE_STATE, false); 65 handleToggleAction(context, key, isChecked, isPlatformSlice); 66 break; 67 case ACTION_SLIDER_CHANGED: 68 final int newPosition = intent.getIntExtra(Slice.EXTRA_RANGE_VALUE, -1); 69 handleSliderAction(context, key, newPosition, isPlatformSlice); 70 break; 71 case ACTION_BLUETOOTH_SLICE_CHANGED: 72 BluetoothSliceBuilder.handleUriChange(context, intent); 73 break; 74 case ACTION_WIFI_SLICE_CHANGED: 75 WifiSliceBuilder.handleUriChange(context, intent); 76 break; 77 case ACTION_WIFI_CALLING_CHANGED: 78 FeatureFactory.getFactory(context) 79 .getSlicesFeatureProvider() 80 .getNewWifiCallingSliceHelper(context) 81 .handleWifiCallingChanged(intent); 82 break; 83 case ACTION_ZEN_MODE_SLICE_CHANGED: 84 ZenModeSliceBuilder.handleUriChange(context, intent); 85 break; 86 } 87 } 88 handleToggleAction(Context context, String key, boolean isChecked, boolean isPlatformSlice)89 private void handleToggleAction(Context context, String key, boolean isChecked, 90 boolean isPlatformSlice) { 91 if (TextUtils.isEmpty(key)) { 92 throw new IllegalStateException("No key passed to Intent for toggle controller"); 93 } 94 95 final BasePreferenceController controller = getPreferenceController(context, key); 96 97 if (!(controller instanceof TogglePreferenceController)) { 98 throw new IllegalStateException("Toggle action passed for a non-toggle key: " + key); 99 } 100 101 if (!controller.isAvailable()) { 102 Log.w(TAG, "Can't update " + key + " since the setting is unavailable"); 103 if (!controller.hasAsyncUpdate()) { 104 updateUri(context, key, isPlatformSlice); 105 } 106 return; 107 } 108 109 // TODO post context.getContentResolver().notifyChanged(uri, null) in the Toggle controller 110 // so that it's automatically broadcast to any slice. 111 final TogglePreferenceController toggleController = (TogglePreferenceController) controller; 112 toggleController.setChecked(isChecked); 113 logSliceValueChange(context, key, isChecked ? 1 : 0); 114 if (!controller.hasAsyncUpdate()) { 115 updateUri(context, key, isPlatformSlice); 116 } 117 } 118 handleSliderAction(Context context, String key, int newPosition, boolean isPlatformSlice)119 private void handleSliderAction(Context context, String key, int newPosition, 120 boolean isPlatformSlice) { 121 if (TextUtils.isEmpty(key)) { 122 throw new IllegalArgumentException( 123 "No key passed to Intent for slider controller. Use extra: " + EXTRA_SLICE_KEY); 124 } 125 126 if (newPosition == -1) { 127 throw new IllegalArgumentException("Invalid position passed to Slider controller"); 128 } 129 130 final BasePreferenceController controller = getPreferenceController(context, key); 131 132 if (!(controller instanceof SliderPreferenceController)) { 133 throw new IllegalArgumentException("Slider action passed for a non-slider key: " + key); 134 } 135 136 if (!controller.isAvailable()) { 137 Log.w(TAG, "Can't update " + key + " since the setting is unavailable"); 138 updateUri(context, key, isPlatformSlice); 139 return; 140 } 141 142 final SliderPreferenceController sliderController = (SliderPreferenceController) controller; 143 final int maxSteps = sliderController.getMaxSteps(); 144 if (newPosition < 0 || newPosition > maxSteps) { 145 throw new IllegalArgumentException( 146 "Invalid position passed to Slider controller. Expected between 0 and " 147 + maxSteps + " but found " + newPosition); 148 } 149 150 sliderController.setSliderPosition(newPosition); 151 logSliceValueChange(context, key, newPosition); 152 updateUri(context, key, isPlatformSlice); 153 } 154 155 /** 156 * Log Slice value update events into MetricsFeatureProvider. The logging schema generally 157 * follows the pattern in SharedPreferenceLogger. 158 */ logSliceValueChange(Context context, String sliceKey, int newValue)159 private void logSliceValueChange(Context context, String sliceKey, int newValue) { 160 final Pair<Integer, Object> namePair = Pair.create( 161 MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, sliceKey); 162 final Pair<Integer, Object> valuePair = Pair.create( 163 MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, newValue); 164 FeatureFactory.getFactory(context).getMetricsFeatureProvider() 165 .action(context, MetricsEvent.ACTION_SETTINGS_SLICE_CHANGED, namePair, valuePair); 166 } 167 getPreferenceController(Context context, String key)168 private BasePreferenceController getPreferenceController(Context context, String key) { 169 final SlicesDatabaseAccessor accessor = new SlicesDatabaseAccessor(context); 170 final SliceData sliceData = accessor.getSliceDataFromKey(key); 171 return SliceBuilderUtils.getPreferenceController(context, sliceData); 172 } 173 updateUri(Context context, String key, boolean isPlatformDefined)174 private void updateUri(Context context, String key, boolean isPlatformDefined) { 175 final String authority = isPlatformDefined 176 ? SettingsSlicesContract.AUTHORITY 177 : SettingsSliceProvider.SLICE_AUTHORITY; 178 final Uri uri = new Uri.Builder() 179 .scheme(ContentResolver.SCHEME_CONTENT) 180 .authority(authority) 181 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) 182 .appendPath(key) 183 .build(); 184 context.getContentResolver().notifyChange(uri, null /* observer */); 185 } 186 } 187