1 /* 2 * Copyright (C) 2014 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.systemui.qs; 18 19 import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState; 20 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; 21 22 import android.annotation.Nullable; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.res.Configuration; 26 import android.content.res.Resources; 27 import android.metrics.LogMaker; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.os.Message; 31 import android.service.quicksettings.Tile; 32 import android.util.AttributeSet; 33 import android.view.LayoutInflater; 34 import android.view.View; 35 import android.widget.LinearLayout; 36 37 import com.android.internal.logging.MetricsLogger; 38 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 39 import com.android.settingslib.Utils; 40 import com.android.systemui.Dependency; 41 import com.android.systemui.DumpController; 42 import com.android.systemui.Dumpable; 43 import com.android.systemui.R; 44 import com.android.systemui.plugins.qs.DetailAdapter; 45 import com.android.systemui.plugins.qs.QSTile; 46 import com.android.systemui.plugins.qs.QSTileView; 47 import com.android.systemui.qs.QSHost.Callback; 48 import com.android.systemui.qs.customize.QSCustomizer; 49 import com.android.systemui.qs.external.CustomTile; 50 import com.android.systemui.settings.BrightnessController; 51 import com.android.systemui.settings.ToggleSliderView; 52 import com.android.systemui.statusbar.policy.BrightnessMirrorController; 53 import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener; 54 import com.android.systemui.tuner.TunerService; 55 import com.android.systemui.tuner.TunerService.Tunable; 56 57 import java.io.FileDescriptor; 58 import java.io.PrintWriter; 59 import java.util.ArrayList; 60 import java.util.Collection; 61 62 import javax.inject.Inject; 63 import javax.inject.Named; 64 65 /** View that represents the quick settings tile panel (when expanded/pulled down). **/ 66 public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener, 67 Dumpable { 68 69 public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness"; 70 public static final String QS_SHOW_HEADER = "qs_show_header"; 71 72 private static final String TAG = "QSPanel"; 73 74 protected final Context mContext; 75 protected final ArrayList<TileRecord> mRecords = new ArrayList<>(); 76 protected final View mBrightnessView; 77 private final H mHandler = new H(); 78 private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); 79 private final QSTileRevealController mQsTileRevealController; 80 81 protected boolean mExpanded; 82 protected boolean mListening; 83 84 private QSDetail.Callback mCallback; 85 private BrightnessController mBrightnessController; 86 private DumpController mDumpController; 87 protected QSTileHost mHost; 88 89 protected QSSecurityFooter mFooter; 90 private PageIndicator mFooterPageIndicator; 91 private boolean mGridContentVisible = true; 92 93 protected QSTileLayout mTileLayout; 94 95 private QSCustomizer mCustomizePanel; 96 private Record mDetailRecord; 97 98 private BrightnessMirrorController mBrightnessMirrorController; 99 private View mDivider; 100 QSPanel(Context context)101 public QSPanel(Context context) { 102 this(context, null); 103 } 104 QSPanel(Context context, AttributeSet attrs)105 public QSPanel(Context context, AttributeSet attrs) { 106 this(context, attrs, null); 107 } 108 109 @Inject QSPanel(@amedVIEW_CONTEXT) Context context, AttributeSet attrs, DumpController dumpController)110 public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, 111 DumpController dumpController) { 112 super(context, attrs); 113 mContext = context; 114 115 setOrientation(VERTICAL); 116 117 mBrightnessView = LayoutInflater.from(mContext).inflate( 118 R.layout.quick_settings_brightness_dialog, this, false); 119 addView(mBrightnessView); 120 121 mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate( 122 R.layout.qs_paged_tile_layout, this, false); 123 mTileLayout.setListening(mListening); 124 addView((View) mTileLayout); 125 126 mQsTileRevealController = new QSTileRevealController(mContext, this, 127 (PagedTileLayout) mTileLayout); 128 129 addDivider(); 130 131 mFooter = new QSSecurityFooter(this, context); 132 addView(mFooter.getView()); 133 134 updateResources(); 135 136 mBrightnessController = new BrightnessController(getContext(), 137 findViewById(R.id.brightness_slider)); 138 mDumpController = dumpController; 139 } 140 addDivider()141 protected void addDivider() { 142 mDivider = LayoutInflater.from(mContext).inflate(R.layout.qs_divider, this, false); 143 mDivider.setBackgroundColor(Utils.applyAlpha(mDivider.getAlpha(), 144 getColorForState(mContext, Tile.STATE_ACTIVE))); 145 addView(mDivider); 146 } 147 148 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)149 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 150 // We want all the logic of LinearLayout#onMeasure, and for it to assign the excess space 151 // not used by the other children to PagedTileLayout. However, in this case, LinearLayout 152 // assumes that PagedTileLayout would use all the excess space. This is not the case as 153 // PagedTileLayout height is quantized (because it shows a certain number of rows). 154 // Therefore, after everything is measured, we need to make sure that we add up the correct 155 // total height 156 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 157 int height = getPaddingBottom() + getPaddingTop(); 158 int numChildren = getChildCount(); 159 for (int i = 0; i < numChildren; i++) { 160 View child = getChildAt(i); 161 if (child.getVisibility() != View.GONE) height += child.getMeasuredHeight(); 162 } 163 setMeasuredDimension(getMeasuredWidth(), height); 164 } 165 getDivider()166 public View getDivider() { 167 return mDivider; 168 } 169 getQsTileRevealController()170 public QSTileRevealController getQsTileRevealController() { 171 return mQsTileRevealController; 172 } 173 isShowingCustomize()174 public boolean isShowingCustomize() { 175 return mCustomizePanel != null && mCustomizePanel.isCustomizing(); 176 } 177 178 @Override onAttachedToWindow()179 protected void onAttachedToWindow() { 180 super.onAttachedToWindow(); 181 final TunerService tunerService = Dependency.get(TunerService.class); 182 tunerService.addTunable(this, QS_SHOW_BRIGHTNESS); 183 184 if (mHost != null) { 185 setTiles(mHost.getTiles()); 186 } 187 if (mBrightnessMirrorController != null) { 188 mBrightnessMirrorController.addCallback(this); 189 } 190 if (mDumpController != null) mDumpController.addListener(this); 191 } 192 193 @Override onDetachedFromWindow()194 protected void onDetachedFromWindow() { 195 Dependency.get(TunerService.class).removeTunable(this); 196 if (mHost != null) { 197 mHost.removeCallback(this); 198 } 199 for (TileRecord record : mRecords) { 200 record.tile.removeCallbacks(); 201 } 202 if (mBrightnessMirrorController != null) { 203 mBrightnessMirrorController.removeCallback(this); 204 } 205 if (mDumpController != null) mDumpController.removeListener(this); 206 super.onDetachedFromWindow(); 207 } 208 209 @Override onTilesChanged()210 public void onTilesChanged() { 211 setTiles(mHost.getTiles()); 212 } 213 214 @Override onTuningChanged(String key, String newValue)215 public void onTuningChanged(String key, String newValue) { 216 if (QS_SHOW_BRIGHTNESS.equals(key)) { 217 updateViewVisibilityForTuningValue(mBrightnessView, newValue); 218 } 219 } 220 updateViewVisibilityForTuningValue(View view, @Nullable String newValue)221 private void updateViewVisibilityForTuningValue(View view, @Nullable String newValue) { 222 view.setVisibility(TunerService.parseIntegerSwitch(newValue, true) ? VISIBLE : GONE); 223 } 224 openDetails(String subPanel)225 public void openDetails(String subPanel) { 226 QSTile tile = getTile(subPanel); 227 // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory), 228 // QSFactory will not be able to create a tile and getTile will return null 229 if (tile != null) { 230 showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0}); 231 } 232 } 233 getTile(String subPanel)234 private QSTile getTile(String subPanel) { 235 for (int i = 0; i < mRecords.size(); i++) { 236 if (subPanel.equals(mRecords.get(i).tile.getTileSpec())) { 237 return mRecords.get(i).tile; 238 } 239 } 240 return mHost.createTile(subPanel); 241 } 242 setBrightnessMirror(BrightnessMirrorController c)243 public void setBrightnessMirror(BrightnessMirrorController c) { 244 if (mBrightnessMirrorController != null) { 245 mBrightnessMirrorController.removeCallback(this); 246 } 247 mBrightnessMirrorController = c; 248 if (mBrightnessMirrorController != null) { 249 mBrightnessMirrorController.addCallback(this); 250 } 251 updateBrightnessMirror(); 252 } 253 254 @Override onBrightnessMirrorReinflated(View brightnessMirror)255 public void onBrightnessMirrorReinflated(View brightnessMirror) { 256 updateBrightnessMirror(); 257 } 258 getBrightnessView()259 View getBrightnessView() { 260 return mBrightnessView; 261 } 262 setCallback(QSDetail.Callback callback)263 public void setCallback(QSDetail.Callback callback) { 264 mCallback = callback; 265 } 266 setHost(QSTileHost host, QSCustomizer customizer)267 public void setHost(QSTileHost host, QSCustomizer customizer) { 268 mHost = host; 269 mHost.addCallback(this); 270 setTiles(mHost.getTiles()); 271 mFooter.setHostEnvironment(host); 272 mCustomizePanel = customizer; 273 if (mCustomizePanel != null) { 274 mCustomizePanel.setHost(mHost); 275 } 276 } 277 278 /** 279 * Links the footer's page indicator, which is used in landscape orientation to save space. 280 * 281 * @param pageIndicator indicator to use for page scrolling 282 */ setFooterPageIndicator(PageIndicator pageIndicator)283 public void setFooterPageIndicator(PageIndicator pageIndicator) { 284 if (mTileLayout instanceof PagedTileLayout) { 285 mFooterPageIndicator = pageIndicator; 286 updatePageIndicator(); 287 } 288 } 289 updatePageIndicator()290 private void updatePageIndicator() { 291 if (mTileLayout instanceof PagedTileLayout) { 292 if (mFooterPageIndicator != null) { 293 mFooterPageIndicator.setVisibility(View.GONE); 294 295 ((PagedTileLayout) mTileLayout).setPageIndicator(mFooterPageIndicator); 296 } 297 } 298 } 299 getHost()300 public QSTileHost getHost() { 301 return mHost; 302 } 303 updateResources()304 public void updateResources() { 305 final Resources res = mContext.getResources(); 306 setPadding(0, res.getDimensionPixelSize(R.dimen.qs_panel_padding_top), 0, res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom)); 307 308 updatePageIndicator(); 309 310 if (mListening) { 311 refreshAllTiles(); 312 } 313 if (mTileLayout != null) { 314 mTileLayout.updateResources(); 315 } 316 } 317 318 @Override onConfigurationChanged(Configuration newConfig)319 protected void onConfigurationChanged(Configuration newConfig) { 320 super.onConfigurationChanged(newConfig); 321 mFooter.onConfigurationChanged(); 322 updateResources(); 323 324 updateBrightnessMirror(); 325 } 326 updateBrightnessMirror()327 public void updateBrightnessMirror() { 328 if (mBrightnessMirrorController != null) { 329 ToggleSliderView brightnessSlider = findViewById(R.id.brightness_slider); 330 ToggleSliderView mirrorSlider = mBrightnessMirrorController.getMirror() 331 .findViewById(R.id.brightness_slider); 332 brightnessSlider.setMirror(mirrorSlider); 333 brightnessSlider.setMirrorController(mBrightnessMirrorController); 334 } 335 } 336 onCollapse()337 public void onCollapse() { 338 if (mCustomizePanel != null && mCustomizePanel.isShown()) { 339 mCustomizePanel.hide(); 340 } 341 } 342 setExpanded(boolean expanded)343 public void setExpanded(boolean expanded) { 344 if (mExpanded == expanded) return; 345 mExpanded = expanded; 346 if (!mExpanded && mTileLayout instanceof PagedTileLayout) { 347 ((PagedTileLayout) mTileLayout).setCurrentItem(0, false); 348 } 349 mMetricsLogger.visibility(MetricsEvent.QS_PANEL, mExpanded); 350 if (!mExpanded) { 351 closeDetail(); 352 } else { 353 logTiles(); 354 } 355 } 356 setPageListener(final PagedTileLayout.PageListener pageListener)357 public void setPageListener(final PagedTileLayout.PageListener pageListener) { 358 if (mTileLayout instanceof PagedTileLayout) { 359 ((PagedTileLayout) mTileLayout).setPageListener(pageListener); 360 } 361 } 362 isExpanded()363 public boolean isExpanded() { 364 return mExpanded; 365 } 366 setListening(boolean listening)367 public void setListening(boolean listening) { 368 if (mListening == listening) return; 369 mListening = listening; 370 if (mTileLayout != null) { 371 mTileLayout.setListening(listening); 372 } 373 if (mListening) { 374 refreshAllTiles(); 375 } 376 } 377 setListening(boolean listening, boolean expanded)378 public void setListening(boolean listening, boolean expanded) { 379 setListening(listening && expanded); 380 getFooter().setListening(listening); 381 // Set the listening as soon as the QS fragment starts listening regardless of the expansion, 382 // so it will update the current brightness before the slider is visible. 383 setBrightnessListening(listening); 384 } 385 setBrightnessListening(boolean listening)386 public void setBrightnessListening(boolean listening) { 387 if (listening) { 388 mBrightnessController.registerCallbacks(); 389 } else { 390 mBrightnessController.unregisterCallbacks(); 391 } 392 } 393 refreshAllTiles()394 public void refreshAllTiles() { 395 mBrightnessController.checkRestrictionAndSetEnabled(); 396 for (TileRecord r : mRecords) { 397 r.tile.refreshState(); 398 } 399 mFooter.refreshState(); 400 } 401 showDetailAdapter(boolean show, DetailAdapter adapter, int[] locationInWindow)402 public void showDetailAdapter(boolean show, DetailAdapter adapter, int[] locationInWindow) { 403 int xInWindow = locationInWindow[0]; 404 int yInWindow = locationInWindow[1]; 405 ((View) getParent()).getLocationInWindow(locationInWindow); 406 407 Record r = new Record(); 408 r.detailAdapter = adapter; 409 r.x = xInWindow - locationInWindow[0]; 410 r.y = yInWindow - locationInWindow[1]; 411 412 locationInWindow[0] = xInWindow; 413 locationInWindow[1] = yInWindow; 414 415 showDetail(show, r); 416 } 417 showDetail(boolean show, Record r)418 protected void showDetail(boolean show, Record r) { 419 mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget(); 420 } 421 setTiles(Collection<QSTile> tiles)422 public void setTiles(Collection<QSTile> tiles) { 423 setTiles(tiles, false); 424 } 425 setTiles(Collection<QSTile> tiles, boolean collapsedView)426 public void setTiles(Collection<QSTile> tiles, boolean collapsedView) { 427 if (!collapsedView) { 428 mQsTileRevealController.updateRevealedTiles(tiles); 429 } 430 for (TileRecord record : mRecords) { 431 mTileLayout.removeTile(record); 432 record.tile.removeCallback(record.callback); 433 } 434 mRecords.clear(); 435 for (QSTile tile : tiles) { 436 addTile(tile, collapsedView); 437 } 438 } 439 drawTile(TileRecord r, QSTile.State state)440 protected void drawTile(TileRecord r, QSTile.State state) { 441 r.tileView.onStateChanged(state); 442 } 443 createTileView(QSTile tile, boolean collapsedView)444 protected QSTileView createTileView(QSTile tile, boolean collapsedView) { 445 return mHost.createTileView(tile, collapsedView); 446 } 447 shouldShowDetail()448 protected boolean shouldShowDetail() { 449 return mExpanded; 450 } 451 addTile(final QSTile tile, boolean collapsedView)452 protected TileRecord addTile(final QSTile tile, boolean collapsedView) { 453 final TileRecord r = new TileRecord(); 454 r.tile = tile; 455 r.tileView = createTileView(tile, collapsedView); 456 final QSTile.Callback callback = new QSTile.Callback() { 457 @Override 458 public void onStateChanged(QSTile.State state) { 459 drawTile(r, state); 460 } 461 462 @Override 463 public void onShowDetail(boolean show) { 464 // Both the collapsed and full QS panels get this callback, this check determines 465 // which one should handle showing the detail. 466 if (shouldShowDetail()) { 467 QSPanel.this.showDetail(show, r); 468 } 469 } 470 471 @Override 472 public void onToggleStateChanged(boolean state) { 473 if (mDetailRecord == r) { 474 fireToggleStateChanged(state); 475 } 476 } 477 478 @Override 479 public void onScanStateChanged(boolean state) { 480 r.scanState = state; 481 if (mDetailRecord == r) { 482 fireScanStateChanged(r.scanState); 483 } 484 } 485 486 @Override 487 public void onAnnouncementRequested(CharSequence announcement) { 488 if (announcement != null) { 489 mHandler.obtainMessage(H.ANNOUNCE_FOR_ACCESSIBILITY, announcement) 490 .sendToTarget(); 491 } 492 } 493 }; 494 r.tile.addCallback(callback); 495 r.callback = callback; 496 r.tileView.init(r.tile); 497 r.tile.refreshState(); 498 mRecords.add(r); 499 500 if (mTileLayout != null) { 501 mTileLayout.addTile(r); 502 } 503 504 return r; 505 } 506 507 showEdit(final View v)508 public void showEdit(final View v) { 509 v.post(new Runnable() { 510 @Override 511 public void run() { 512 if (mCustomizePanel != null) { 513 if (!mCustomizePanel.isCustomizing()) { 514 int[] loc = v.getLocationOnScreen(); 515 int x = loc[0] + v.getWidth() / 2; 516 int y = loc[1] + v.getHeight() / 2; 517 mCustomizePanel.show(x, y); 518 } 519 } 520 521 } 522 }); 523 } 524 closeDetail()525 public void closeDetail() { 526 if (mCustomizePanel != null && mCustomizePanel.isShown()) { 527 // Treat this as a detail panel for now, to make things easy. 528 mCustomizePanel.hide(); 529 return; 530 } 531 showDetail(false, mDetailRecord); 532 } 533 getGridHeight()534 public int getGridHeight() { 535 return getMeasuredHeight(); 536 } 537 handleShowDetail(Record r, boolean show)538 protected void handleShowDetail(Record r, boolean show) { 539 if (r instanceof TileRecord) { 540 handleShowDetailTile((TileRecord) r, show); 541 } else { 542 int x = 0; 543 int y = 0; 544 if (r != null) { 545 x = r.x; 546 y = r.y; 547 } 548 handleShowDetailImpl(r, show, x, y); 549 } 550 } 551 handleShowDetailTile(TileRecord r, boolean show)552 private void handleShowDetailTile(TileRecord r, boolean show) { 553 if ((mDetailRecord != null) == show && mDetailRecord == r) return; 554 555 if (show) { 556 r.detailAdapter = r.tile.getDetailAdapter(); 557 if (r.detailAdapter == null) return; 558 } 559 r.tile.setDetailListening(show); 560 int x = r.tileView.getLeft() + r.tileView.getWidth() / 2; 561 int y = r.tileView.getDetailY() + mTileLayout.getOffsetTop(r) + getTop(); 562 handleShowDetailImpl(r, show, x, y); 563 } 564 handleShowDetailImpl(Record r, boolean show, int x, int y)565 private void handleShowDetailImpl(Record r, boolean show, int x, int y) { 566 setDetailRecord(show ? r : null); 567 fireShowingDetail(show ? r.detailAdapter : null, x, y); 568 } 569 setDetailRecord(Record r)570 protected void setDetailRecord(Record r) { 571 if (r == mDetailRecord) return; 572 mDetailRecord = r; 573 final boolean scanState = mDetailRecord instanceof TileRecord 574 && ((TileRecord) mDetailRecord).scanState; 575 fireScanStateChanged(scanState); 576 } 577 setGridContentVisibility(boolean visible)578 void setGridContentVisibility(boolean visible) { 579 int newVis = visible ? VISIBLE : INVISIBLE; 580 setVisibility(newVis); 581 if (mGridContentVisible != visible) { 582 mMetricsLogger.visibility(MetricsEvent.QS_PANEL, newVis); 583 } 584 mGridContentVisible = visible; 585 } 586 logTiles()587 private void logTiles() { 588 for (int i = 0; i < mRecords.size(); i++) { 589 QSTile tile = mRecords.get(i).tile; 590 mMetricsLogger.write(tile.populate(new LogMaker(tile.getMetricsCategory()) 591 .setType(MetricsEvent.TYPE_OPEN))); 592 } 593 } 594 fireShowingDetail(DetailAdapter detail, int x, int y)595 private void fireShowingDetail(DetailAdapter detail, int x, int y) { 596 if (mCallback != null) { 597 mCallback.onShowingDetail(detail, x, y); 598 } 599 } 600 fireToggleStateChanged(boolean state)601 private void fireToggleStateChanged(boolean state) { 602 if (mCallback != null) { 603 mCallback.onToggleStateChanged(state); 604 } 605 } 606 fireScanStateChanged(boolean state)607 private void fireScanStateChanged(boolean state) { 608 if (mCallback != null) { 609 mCallback.onScanStateChanged(state); 610 } 611 } 612 clickTile(ComponentName tile)613 public void clickTile(ComponentName tile) { 614 final String spec = CustomTile.toSpec(tile); 615 final int N = mRecords.size(); 616 for (int i = 0; i < N; i++) { 617 if (mRecords.get(i).tile.getTileSpec().equals(spec)) { 618 mRecords.get(i).tile.click(); 619 break; 620 } 621 } 622 } 623 getTileLayout()624 QSTileLayout getTileLayout() { 625 return mTileLayout; 626 } 627 getTileView(QSTile tile)628 QSTileView getTileView(QSTile tile) { 629 for (TileRecord r : mRecords) { 630 if (r.tile == tile) { 631 return r.tileView; 632 } 633 } 634 return null; 635 } 636 getFooter()637 public QSSecurityFooter getFooter() { 638 return mFooter; 639 } 640 showDeviceMonitoringDialog()641 public void showDeviceMonitoringDialog() { 642 mFooter.showDeviceMonitoringDialog(); 643 } 644 setMargins(int sideMargins)645 public void setMargins(int sideMargins) { 646 for (int i = 0; i < getChildCount(); i++) { 647 View view = getChildAt(i); 648 if (view != mTileLayout) { 649 LayoutParams lp = (LayoutParams) view.getLayoutParams(); 650 lp.leftMargin = sideMargins; 651 lp.rightMargin = sideMargins; 652 } 653 } 654 } 655 656 private class H extends Handler { 657 private static final int SHOW_DETAIL = 1; 658 private static final int SET_TILE_VISIBILITY = 2; 659 private static final int ANNOUNCE_FOR_ACCESSIBILITY = 3; 660 661 @Override handleMessage(Message msg)662 public void handleMessage(Message msg) { 663 if (msg.what == SHOW_DETAIL) { 664 handleShowDetail((Record) msg.obj, msg.arg1 != 0); 665 } else if (msg.what == ANNOUNCE_FOR_ACCESSIBILITY) { 666 announceForAccessibility((CharSequence) msg.obj); 667 } 668 } 669 } 670 671 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)672 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 673 pw.println(getClass().getSimpleName() + ":"); 674 pw.println(" Tile records:"); 675 for (TileRecord record : mRecords) { 676 if (record.tile instanceof Dumpable) { 677 pw.print(" "); ((Dumpable) record.tile).dump(fd, pw, args); 678 pw.print(" "); pw.println(record.tileView.toString()); 679 } 680 } 681 } 682 683 protected static class Record { 684 DetailAdapter detailAdapter; 685 int x; 686 int y; 687 } 688 689 public static final class TileRecord extends Record { 690 public QSTile tile; 691 public com.android.systemui.plugins.qs.QSTileView tileView; 692 public boolean scanState; 693 public QSTile.Callback callback; 694 } 695 696 public interface QSTileLayout { 697 saveInstanceState(Bundle outState)698 default void saveInstanceState(Bundle outState) {} 699 restoreInstanceState(Bundle savedInstanceState)700 default void restoreInstanceState(Bundle savedInstanceState) {} 701 addTile(TileRecord tile)702 void addTile(TileRecord tile); 703 removeTile(TileRecord tile)704 void removeTile(TileRecord tile); 705 getOffsetTop(TileRecord tile)706 int getOffsetTop(TileRecord tile); 707 updateResources()708 boolean updateResources(); 709 setListening(boolean listening)710 void setListening(boolean listening); 711 setExpansion(float expansion)712 default void setExpansion(float expansion) {} 713 getNumVisibleTiles()714 int getNumVisibleTiles(); 715 } 716 } 717