1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.plugins.qs; 16 17 import android.annotation.NonNull; 18 import android.content.Context; 19 import android.content.res.Resources; 20 import android.graphics.drawable.Drawable; 21 import android.metrics.LogMaker; 22 import android.service.quicksettings.Tile; 23 import android.text.TextUtils; 24 25 import androidx.annotation.Nullable; 26 27 import com.android.internal.logging.InstanceId; 28 import com.android.systemui.animation.Expandable; 29 import com.android.systemui.plugins.annotations.DependsOn; 30 import com.android.systemui.plugins.annotations.ProvidesInterface; 31 import com.android.systemui.plugins.qs.QSTile.Callback; 32 import com.android.systemui.plugins.qs.QSTile.Icon; 33 import com.android.systemui.plugins.qs.QSTile.State; 34 35 import java.util.Objects; 36 import java.util.function.Supplier; 37 38 @ProvidesInterface(version = QSTile.VERSION) 39 @DependsOn(target = QSIconView.class) 40 @DependsOn(target = Callback.class) 41 @DependsOn(target = Icon.class) 42 @DependsOn(target = State.class) 43 public interface QSTile { 44 int VERSION = 4; 45 getTileSpec()46 String getTileSpec(); 47 isAvailable()48 boolean isAvailable(); setTileSpec(String tileSpec)49 void setTileSpec(String tileSpec); 50 clearState()51 @Deprecated default void clearState() {} refreshState()52 void refreshState(); 53 addCallback(Callback callback)54 void addCallback(Callback callback); removeCallback(Callback callback)55 void removeCallback(Callback callback); removeCallbacks()56 void removeCallbacks(); 57 58 /** 59 * The tile was clicked. 60 * 61 * @param expandable {@link Expandable} that was clicked. 62 */ click(@ullable Expandable expandable)63 void click(@Nullable Expandable expandable); 64 65 /** 66 * The tile secondary click was triggered. 67 * 68 * @param expandable {@link Expandable} that was clicked. 69 */ secondaryClick(@ullable Expandable expandable)70 void secondaryClick(@Nullable Expandable expandable); 71 72 /** 73 * The tile was long clicked. 74 * 75 * @param expandable {@link Expandable} that was clicked. 76 */ longClick(@ullable Expandable expandable)77 void longClick(@Nullable Expandable expandable); 78 userSwitch(int currentUser)79 void userSwitch(int currentUser); 80 81 /** 82 * @deprecated not needed as {@link com.android.internal.logging.UiEvent} will use 83 * {@link #getMetricsSpec} 84 */ 85 @Deprecated getMetricsCategory()86 int getMetricsCategory(); 87 setListening(Object client, boolean listening)88 void setListening(Object client, boolean listening); setDetailListening(boolean show)89 void setDetailListening(boolean show); 90 destroy()91 void destroy(); 92 getTileLabel()93 CharSequence getTileLabel(); 94 getState()95 State getState(); 96 populate(LogMaker logMaker)97 default LogMaker populate(LogMaker logMaker) { 98 return logMaker; 99 } 100 101 /** 102 * Return a string to be used to identify the tile in UiEvents. 103 */ getMetricsSpec()104 default String getMetricsSpec() { 105 return getClass().getSimpleName(); 106 } 107 108 /** 109 * Return an {@link InstanceId} to be used to identify the tile in UiEvents. 110 */ getInstanceId()111 InstanceId getInstanceId(); 112 isTileReady()113 default boolean isTileReady() { 114 return false; 115 } 116 117 /** 118 * Return whether the tile is set to its listening state and therefore receiving updates and 119 * refreshes from controllers 120 */ isListening()121 boolean isListening(); 122 123 @ProvidesInterface(version = Callback.VERSION) 124 interface Callback { 125 static final int VERSION = 2; onStateChanged(State state)126 void onStateChanged(State state); 127 } 128 129 @ProvidesInterface(version = Icon.VERSION) 130 public static abstract class Icon { 131 public static final int VERSION = 1; getDrawable(Context context)132 abstract public Drawable getDrawable(Context context); 133 getInvisibleDrawable(Context context)134 public Drawable getInvisibleDrawable(Context context) { 135 return getDrawable(context); 136 } 137 138 @Override hashCode()139 public int hashCode() { 140 return Icon.class.hashCode(); 141 } 142 getPadding()143 public int getPadding() { 144 return 0; 145 } 146 147 @Override 148 @NonNull toString()149 public String toString() { 150 return "Icon"; 151 } 152 } 153 154 @ProvidesInterface(version = State.VERSION) 155 public static class State { 156 public static final int VERSION = 1; 157 public static final int DEFAULT_STATE = Tile.STATE_ACTIVE; 158 159 public Icon icon; 160 public Supplier<Icon> iconSupplier; 161 public int state = DEFAULT_STATE; 162 public CharSequence label; 163 @Nullable public CharSequence secondaryLabel; 164 public CharSequence contentDescription; 165 @Nullable public CharSequence stateDescription; 166 public CharSequence dualLabelContentDescription; 167 public boolean disabledByPolicy; 168 public boolean dualTarget = false; 169 public boolean isTransient = false; 170 public String expandedAccessibilityClassName; 171 public boolean handlesLongClick = true; 172 @Nullable 173 public Drawable sideViewCustomDrawable; 174 public String spec; 175 176 /** Get the state text. */ getStateText(int arrayResId, Resources resources)177 public CharSequence getStateText(int arrayResId, Resources resources) { 178 if (state == Tile.STATE_UNAVAILABLE || this instanceof QSTile.BooleanState) { 179 String[] array = resources.getStringArray(arrayResId); 180 return array[state]; 181 } else { 182 return ""; 183 } 184 } 185 186 /** Get the text for secondaryLabel. */ getSecondaryLabel(CharSequence stateText)187 public CharSequence getSecondaryLabel(CharSequence stateText) { 188 // Use a local reference as the value might change from other threads 189 CharSequence localSecondaryLabel = secondaryLabel; 190 if (TextUtils.isEmpty(localSecondaryLabel)) { 191 return stateText; 192 } 193 return localSecondaryLabel; 194 } 195 copyTo(State other)196 public boolean copyTo(State other) { 197 if (other == null) throw new IllegalArgumentException(); 198 if (!other.getClass().equals(getClass())) throw new IllegalArgumentException(); 199 final boolean changed = !Objects.equals(other.spec, spec) 200 || !Objects.equals(other.icon, icon) 201 || !Objects.equals(other.iconSupplier, iconSupplier) 202 || !Objects.equals(other.label, label) 203 || !Objects.equals(other.secondaryLabel, secondaryLabel) 204 || !Objects.equals(other.contentDescription, contentDescription) 205 || !Objects.equals(other.stateDescription, stateDescription) 206 || !Objects.equals(other.dualLabelContentDescription, 207 dualLabelContentDescription) 208 || !Objects.equals(other.expandedAccessibilityClassName, 209 expandedAccessibilityClassName) 210 || !Objects.equals(other.disabledByPolicy, disabledByPolicy) 211 || !Objects.equals(other.state, state) 212 || !Objects.equals(other.isTransient, isTransient) 213 || !Objects.equals(other.dualTarget, dualTarget) 214 || !Objects.equals(other.handlesLongClick, handlesLongClick) 215 || !Objects.equals(other.sideViewCustomDrawable, sideViewCustomDrawable); 216 other.spec = spec; 217 other.icon = icon; 218 other.iconSupplier = iconSupplier; 219 other.label = label; 220 other.secondaryLabel = secondaryLabel; 221 other.contentDescription = contentDescription; 222 other.stateDescription = stateDescription; 223 other.dualLabelContentDescription = dualLabelContentDescription; 224 other.expandedAccessibilityClassName = expandedAccessibilityClassName; 225 other.disabledByPolicy = disabledByPolicy; 226 other.state = state; 227 other.dualTarget = dualTarget; 228 other.isTransient = isTransient; 229 other.handlesLongClick = handlesLongClick; 230 other.sideViewCustomDrawable = sideViewCustomDrawable; 231 return changed; 232 } 233 234 @Override toString()235 public String toString() { 236 return toStringBuilder().toString(); 237 } 238 239 // Used in dumps to determine current state of a tile. 240 // This string may be used for CTS testing of tiles, so removing elements is discouraged. toStringBuilder()241 protected StringBuilder toStringBuilder() { 242 final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('['); 243 sb.append("spec=").append(spec); 244 sb.append(",icon=").append(icon); 245 sb.append(",iconSupplier=").append(iconSupplier); 246 sb.append(",label=").append(label); 247 sb.append(",secondaryLabel=").append(secondaryLabel); 248 sb.append(",contentDescription=").append(contentDescription); 249 sb.append(",stateDescription=").append(stateDescription); 250 sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription); 251 sb.append(",expandedAccessibilityClassName=").append(expandedAccessibilityClassName); 252 sb.append(",disabledByPolicy=").append(disabledByPolicy); 253 sb.append(",dualTarget=").append(dualTarget); 254 sb.append(",isTransient=").append(isTransient); 255 sb.append(",state=").append(state); 256 sb.append(",sideViewCustomDrawable=").append(sideViewCustomDrawable); 257 return sb.append(']'); 258 } 259 copy()260 public State copy() { 261 State state = new State(); 262 copyTo(state); 263 return state; 264 } 265 } 266 267 /** 268 * Distinguished from [BooleanState] for use-case purposes such as allowing null secondary label 269 */ 270 @ProvidesInterface(version = AdapterState.VERSION) 271 class AdapterState extends State { 272 public static final int VERSION = 1; 273 public boolean value; 274 public boolean forceExpandIcon; 275 276 @Override copyTo(State other)277 public boolean copyTo(State other) { 278 final AdapterState o = (AdapterState) other; 279 final boolean changed = super.copyTo(other) 280 || o.value != value 281 || o.forceExpandIcon != forceExpandIcon; 282 o.value = value; 283 o.forceExpandIcon = forceExpandIcon; 284 return changed; 285 } 286 287 @Override toStringBuilder()288 protected StringBuilder toStringBuilder() { 289 final StringBuilder rt = super.toStringBuilder(); 290 rt.insert(rt.length() - 1, ",value=" + value); 291 rt.insert(rt.length() - 1, ",forceExpandIcon=" + forceExpandIcon); 292 return rt; 293 } 294 295 @Override copy()296 public State copy() { 297 AdapterState state = new AdapterState(); 298 copyTo(state); 299 return state; 300 } 301 } 302 303 @ProvidesInterface(version = BooleanState.VERSION) 304 class BooleanState extends AdapterState { 305 public static final int VERSION = 1; 306 307 @Override copy()308 public State copy() { 309 BooleanState state = new BooleanState(); 310 copyTo(state); 311 return state; 312 } 313 } 314 } 315