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