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.graphics.drawable.Drawable; 20 import android.metrics.LogMaker; 21 import android.service.quicksettings.Tile; 22 import android.view.View; 23 24 import androidx.annotation.Nullable; 25 26 import com.android.internal.logging.InstanceId; 27 import com.android.systemui.plugins.annotations.DependsOn; 28 import com.android.systemui.plugins.annotations.ProvidesInterface; 29 import com.android.systemui.plugins.qs.QSTile.Callback; 30 import com.android.systemui.plugins.qs.QSTile.Icon; 31 import com.android.systemui.plugins.qs.QSTile.State; 32 33 import java.util.Objects; 34 import java.util.function.Supplier; 35 36 @ProvidesInterface(version = QSTile.VERSION) 37 @DependsOn(target = QSIconView.class) 38 @DependsOn(target = DetailAdapter.class) 39 @DependsOn(target = Callback.class) 40 @DependsOn(target = Icon.class) 41 @DependsOn(target = State.class) 42 public interface QSTile { 43 int VERSION = 1; 44 getDetailAdapter()45 DetailAdapter getDetailAdapter(); 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 createTileView(Context context)58 QSIconView createTileView(Context context); 59 60 /** 61 * The tile was clicked. 62 * 63 * @param view The view that was clicked. 64 */ click(@ullable View view)65 void click(@Nullable View view); 66 67 /** 68 * The tile secondary click was triggered. 69 * 70 * @param view The view that was clicked. 71 */ secondaryClick(@ullable View view)72 void secondaryClick(@Nullable View view); 73 74 /** 75 * The tile was long clicked. 76 * 77 * @param view The view that was clicked. 78 */ longClick(@ullable View view)79 void longClick(@Nullable View view); 80 userSwitch(int currentUser)81 void userSwitch(int currentUser); getMetricsCategory()82 int getMetricsCategory(); 83 setListening(Object client, boolean listening)84 void setListening(Object client, boolean listening); setDetailListening(boolean show)85 void setDetailListening(boolean show); 86 destroy()87 void destroy(); 88 getTileLabel()89 CharSequence getTileLabel(); 90 getState()91 State getState(); 92 populate(LogMaker logMaker)93 default LogMaker populate(LogMaker logMaker) { 94 return logMaker; 95 } 96 97 /** 98 * Return a string to be used to identify the tile in UiEvents. 99 */ getMetricsSpec()100 default String getMetricsSpec() { 101 return getClass().getSimpleName(); 102 } 103 104 /** 105 * Return an {@link InstanceId} to be used to identify the tile in UiEvents. 106 */ getInstanceId()107 InstanceId getInstanceId(); 108 isTileReady()109 default boolean isTileReady() { 110 return false; 111 } 112 113 @ProvidesInterface(version = Callback.VERSION) 114 public interface Callback { 115 public static final int VERSION = 1; onStateChanged(State state)116 void onStateChanged(State state); onShowDetail(boolean show)117 void onShowDetail(boolean show); onToggleStateChanged(boolean state)118 void onToggleStateChanged(boolean state); onScanStateChanged(boolean state)119 void onScanStateChanged(boolean state); onAnnouncementRequested(CharSequence announcement)120 void onAnnouncementRequested(CharSequence announcement); 121 } 122 123 @ProvidesInterface(version = Icon.VERSION) 124 public static abstract class Icon { 125 public static final int VERSION = 1; getDrawable(Context context)126 abstract public Drawable getDrawable(Context context); 127 getInvisibleDrawable(Context context)128 public Drawable getInvisibleDrawable(Context context) { 129 return getDrawable(context); 130 } 131 132 @Override hashCode()133 public int hashCode() { 134 return Icon.class.hashCode(); 135 } 136 getPadding()137 public int getPadding() { 138 return 0; 139 } 140 141 @Override 142 @NonNull toString()143 public String toString() { 144 return "Icon"; 145 } 146 } 147 148 @ProvidesInterface(version = State.VERSION) 149 public static class State { 150 public static final int VERSION = 1; 151 public static final int DEFAULT_STATE = Tile.STATE_ACTIVE; 152 153 public Icon icon; 154 public Supplier<Icon> iconSupplier; 155 public int state = DEFAULT_STATE; 156 public CharSequence label; 157 public CharSequence secondaryLabel; 158 public CharSequence contentDescription; 159 public CharSequence stateDescription; 160 public CharSequence dualLabelContentDescription; 161 public boolean disabledByPolicy; 162 public boolean dualTarget = false; 163 public boolean isTransient = false; 164 public String expandedAccessibilityClassName; 165 public SlashState slash; 166 public boolean handlesLongClick = true; 167 public boolean showRippleEffect = true; 168 public Drawable sideViewCustomDrawable; 169 public String spec; 170 copyTo(State other)171 public boolean copyTo(State other) { 172 if (other == null) throw new IllegalArgumentException(); 173 if (!other.getClass().equals(getClass())) throw new IllegalArgumentException(); 174 final boolean changed = !Objects.equals(other.spec, spec) 175 || !Objects.equals(other.icon, icon) 176 || !Objects.equals(other.iconSupplier, iconSupplier) 177 || !Objects.equals(other.label, label) 178 || !Objects.equals(other.secondaryLabel, secondaryLabel) 179 || !Objects.equals(other.contentDescription, contentDescription) 180 || !Objects.equals(other.stateDescription, stateDescription) 181 || !Objects.equals(other.dualLabelContentDescription, 182 dualLabelContentDescription) 183 || !Objects.equals(other.expandedAccessibilityClassName, 184 expandedAccessibilityClassName) 185 || !Objects.equals(other.disabledByPolicy, disabledByPolicy) 186 || !Objects.equals(other.state, state) 187 || !Objects.equals(other.isTransient, isTransient) 188 || !Objects.equals(other.dualTarget, dualTarget) 189 || !Objects.equals(other.slash, slash) 190 || !Objects.equals(other.handlesLongClick, handlesLongClick) 191 || !Objects.equals(other.showRippleEffect, showRippleEffect) 192 || !Objects.equals(other.sideViewCustomDrawable, sideViewCustomDrawable); 193 other.spec = spec; 194 other.icon = icon; 195 other.iconSupplier = iconSupplier; 196 other.label = label; 197 other.secondaryLabel = secondaryLabel; 198 other.contentDescription = contentDescription; 199 other.stateDescription = stateDescription; 200 other.dualLabelContentDescription = dualLabelContentDescription; 201 other.expandedAccessibilityClassName = expandedAccessibilityClassName; 202 other.disabledByPolicy = disabledByPolicy; 203 other.state = state; 204 other.dualTarget = dualTarget; 205 other.isTransient = isTransient; 206 other.slash = slash != null ? slash.copy() : null; 207 other.handlesLongClick = handlesLongClick; 208 other.showRippleEffect = showRippleEffect; 209 other.sideViewCustomDrawable = sideViewCustomDrawable; 210 return changed; 211 } 212 213 @Override toString()214 public String toString() { 215 return toStringBuilder().toString(); 216 } 217 218 // Used in dumps to determine current state of a tile. 219 // This string may be used for CTS testing of tiles, so removing elements is discouraged. toStringBuilder()220 protected StringBuilder toStringBuilder() { 221 final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('['); 222 sb.append("spec=").append(spec); 223 sb.append(",icon=").append(icon); 224 sb.append(",iconSupplier=").append(iconSupplier); 225 sb.append(",label=").append(label); 226 sb.append(",secondaryLabel=").append(secondaryLabel); 227 sb.append(",contentDescription=").append(contentDescription); 228 sb.append(",stateDescription=").append(stateDescription); 229 sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription); 230 sb.append(",expandedAccessibilityClassName=").append(expandedAccessibilityClassName); 231 sb.append(",disabledByPolicy=").append(disabledByPolicy); 232 sb.append(",dualTarget=").append(dualTarget); 233 sb.append(",isTransient=").append(isTransient); 234 sb.append(",state=").append(state); 235 sb.append(",slash=\"").append(slash).append("\""); 236 sb.append(",sideViewCustomDrawable=").append(sideViewCustomDrawable); 237 return sb.append(']'); 238 } 239 copy()240 public State copy() { 241 State state = new State(); 242 copyTo(state); 243 return state; 244 } 245 } 246 247 @ProvidesInterface(version = BooleanState.VERSION) 248 public static class BooleanState extends State { 249 public static final int VERSION = 1; 250 public boolean value; 251 public boolean forceExpandIcon; 252 253 @Override copyTo(State other)254 public boolean copyTo(State other) { 255 final BooleanState o = (BooleanState) other; 256 final boolean changed = super.copyTo(other) 257 || o.value != value 258 || o.forceExpandIcon != forceExpandIcon; 259 o.value = value; 260 o.forceExpandIcon = forceExpandIcon; 261 return changed; 262 } 263 264 @Override toStringBuilder()265 protected StringBuilder toStringBuilder() { 266 final StringBuilder rt = super.toStringBuilder(); 267 rt.insert(rt.length() - 1, ",value=" + value); 268 rt.insert(rt.length() - 1, ",forceExpandIcon=" + forceExpandIcon); 269 return rt; 270 } 271 272 @Override copy()273 public State copy() { 274 BooleanState state = new BooleanState(); 275 copyTo(state); 276 return state; 277 } 278 } 279 280 @ProvidesInterface(version = SignalState.VERSION) 281 public static final class SignalState extends BooleanState { 282 public static final int VERSION = 1; 283 public boolean activityIn; 284 public boolean activityOut; 285 public boolean isOverlayIconWide; 286 public int overlayIconId; 287 288 @Override copyTo(State other)289 public boolean copyTo(State other) { 290 final SignalState o = (SignalState) other; 291 final boolean changed = o.activityIn != activityIn 292 || o.activityOut != activityOut 293 || o.isOverlayIconWide != isOverlayIconWide 294 || o.overlayIconId != overlayIconId; 295 o.activityIn = activityIn; 296 o.activityOut = activityOut; 297 o.isOverlayIconWide = isOverlayIconWide; 298 o.overlayIconId = overlayIconId; 299 return super.copyTo(other) || changed; 300 } 301 302 @Override toStringBuilder()303 protected StringBuilder toStringBuilder() { 304 final StringBuilder rt = super.toStringBuilder(); 305 rt.insert(rt.length() - 1, ",activityIn=" + activityIn); 306 rt.insert(rt.length() - 1, ",activityOut=" + activityOut); 307 return rt; 308 } 309 310 @Override copy()311 public State copy() { 312 SignalState state = new SignalState(); 313 copyTo(state); 314 return state; 315 } 316 } 317 318 @ProvidesInterface(version = SlashState.VERSION) 319 public static class SlashState { 320 public static final int VERSION = 2; 321 322 public boolean isSlashed; 323 public float rotation; 324 325 @Override toString()326 public String toString() { 327 return "isSlashed=" + isSlashed + ",rotation=" + rotation; 328 } 329 330 @Override equals(Object o)331 public boolean equals(Object o) { 332 if (o == null) return false; 333 try { 334 return (((SlashState) o).rotation == rotation) 335 && (((SlashState) o).isSlashed == isSlashed); 336 } catch (ClassCastException e) { 337 return false; 338 } 339 } 340 copy()341 public SlashState copy() { 342 SlashState state = new SlashState(); 343 state.rotation = rotation; 344 state.isSlashed = isSlashed; 345 return state; 346 } 347 } 348 } 349