1 /* 2 * Copyright (C) 2018 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 android.app.PendingIntent; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.net.Uri; 23 24 import androidx.slice.Slice; 25 26 import java.lang.reflect.Constructor; 27 import java.lang.reflect.InvocationTargetException; 28 29 30 /** 31 * Common functions for custom Slices. 32 * <p> 33 * A template for all Settings slices which are not represented by a Preference. By 34 * standardizing the methods used by the Slice helpers, we can use generically take actions 35 * rather than maintaining a list of all of the custom slices every time we reference Slices in 36 * Settings. 37 * <p> 38 * By default, all Slices in Settings should be built through Preference Controllers extending 39 * {@link com.android.settings.core.BasePreferenceController}, which are automatically piped 40 * into Settings-Slices infrastructure. Cases where you should implement this interface are: 41 * <ul> 42 * <li>Multi-line slices</li> 43 * <li>Slices that don't exist in the UI</li> 44 * <li>Preferences that use a supported component, like a Switch Bar</li> 45 * </ul> 46 * <p> 47 * Note that if your UI is supported because the Preference is not backed by a 48 * {@link com.android.settings.dashboard.DashboardFragment}, then you should first convert the 49 * existing fragment into a dashboard fragment, and then extend 50 * {@link com.android.settings.core.BasePreferenceController}. 51 * <p> 52 * If you implement this interface, you should add your Slice to {@link CustomSliceManager}. 53 */ 54 public interface CustomSliceable extends Sliceable { 55 56 /** 57 * The color representing not to be tinted for the slice. 58 */ 59 int COLOR_NOT_TINTED = -1; 60 61 /** 62 * @return an complete instance of the {@link Slice}. 63 */ getSlice()64 Slice getSlice(); 65 66 /** 67 * @return a {@link android.content.ContentResolver#SCHEME_CONTENT content} {@link Uri} which 68 * backs the {@link Slice} returned by {@link #getSlice()}. 69 */ getUri()70 Uri getUri(); 71 72 /** 73 * Handles the actions sent by the {@link Intent intents} bound to the {@link Slice} returned by 74 * {@link #getSlice()}. 75 * 76 * @param intent which has the action taken on a {@link Slice}. 77 */ onNotifyChange(Intent intent)78 default void onNotifyChange(Intent intent) {} 79 80 /** 81 * @return an {@link Intent} to the source of the Slice data. 82 */ getIntent()83 Intent getIntent(); 84 85 /** 86 * Standardize the intents returned to indicate actions by the Slice. 87 * <p> 88 * The {@link PendingIntent} is linked to {@link SliceBroadcastReceiver} where the Intent 89 * Action is found by {@code getUri().toString()}. 90 * 91 * @return a {@link PendingIntent} linked to {@link SliceBroadcastReceiver}. 92 */ getBroadcastIntent(Context context)93 default PendingIntent getBroadcastIntent(Context context) { 94 final Intent intent = new Intent(getUri().toString()) 95 .setData(getUri()) 96 .setClass(context, SliceBroadcastReceiver.class); 97 return PendingIntent.getBroadcast(context, 0 /* requestCode */, intent, 98 PendingIntent.FLAG_CANCEL_CURRENT); 99 } 100 101 @Override isSliceable()102 default boolean isSliceable() { 103 return true; 104 } 105 106 /** 107 * Build an instance of a {@link CustomSliceable} which has a {@link Context}-only constructor. 108 */ createInstance(Context context, Class<? extends CustomSliceable> sliceable)109 static CustomSliceable createInstance(Context context, 110 Class<? extends CustomSliceable> sliceable) { 111 try { 112 final Constructor<? extends CustomSliceable> constructor = 113 sliceable.getConstructor(Context.class); 114 final Object[] params = new Object[]{context.getApplicationContext()}; 115 return constructor.newInstance(params); 116 } catch (NoSuchMethodException | InstantiationException | 117 IllegalArgumentException | InvocationTargetException | IllegalAccessException e) { 118 throw new IllegalStateException( 119 "Invalid sliceable class: " + sliceable, e); 120 } 121 } 122 }