@Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final int width = MeasureSpec.getSize(widthMeasureSpec);
    mBrightnessView.measure(exactly(width), MeasureSpec.UNSPECIFIED);
    final int brightnessHeight = mBrightnessView.getMeasuredHeight() + mBrightnessPaddingTop;
    mFooter.getView().measure(exactly(width), MeasureSpec.UNSPECIFIED);
    int r = -1;
    int c = -1;
    int rows = 0;
    for (TileRecord record : mRecords) {
      if (record.tileView.getVisibility() == GONE) continue;
      // wrap to next column if we've reached the max # of columns
      if (mUseMainTiles && r == 0 && c == 1) {
        r = 1;
        c = 0;
      } else if (r == -1 || c == (mColumns - 1)) {
        r++;
        c = 0;
      } else {
        c++;
      }
      record.row = r;
      record.col = c;
      rows = r + 1;
    }

    for (TileRecord record : mRecords) {
      if (record.tileView.getVisibility() == GONE) continue;
      final int cw = (mUseMainTiles && record.row == 0) ? mLargeCellWidth : mCellWidth;
      final int ch = (mUseMainTiles && record.row == 0) ? mLargeCellHeight : mCellHeight;
      record.tileView.measure(exactly(cw), exactly(ch));
    }
    int h = rows == 0 ? brightnessHeight : (getRowTop(rows) + mPanelPaddingBottom);
    if (mFooter.hasFooter()) {
      h += mFooter.getView().getMeasuredHeight();
    }

    mDetail.measure(exactly(width), MeasureSpec.UNSPECIFIED);
    if (isShowingDetail()) {
      Point size = new Point();
      getDisplay().getSize(size);
      final int panelBottom = (mContainerTop - mTranslationTop + h);
      final int detailMinHeight = size.y - mContainerTop - mPanelPaddingBottom;

      if (size.y > panelBottom) {
        int delta = size.y - panelBottom;
        // panel is smaller than screen size
        mDetail.measure(exactly(width), exactly(detailMinHeight - delta));
      } else {
        // panel is hanging below the screen
        mDetail.measure(exactly(width), exactly(detailMinHeight));
      }
    }
    mGridHeight = h;
    setMeasuredDimension(width, Math.max(h, mDetail.getMeasuredHeight()));
  }
  private void handleShowDetailTile(TileRecord r, boolean show) {
    if ((mDetailRecord != null) == show) return;

    if (show) {
      r.detailAdapter = r.tile.getDetailAdapter();
      if (r.detailAdapter == null) return;
    }
    int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
    int y = r.tileView.getTop() + r.tileView.getHeight() / 2;
    handleShowDetailImpl(r, show, x, y - mTranslationTop);
  }
  private void addTile(final QSTile<?> tile) {
    final TileRecord r = new TileRecord();
    r.tile = tile;
    r.tileView = tile.createTileView(mContext);
    r.tileView.setVisibility(View.GONE);
    final QSTile.Callback callback =
        new QSTile.Callback() {
          @Override
          public void onStateChanged(QSTile.State state) {
            int visibility = state.visible ? VISIBLE : GONE;
            if (state.visible && !mGridContentVisible) {

              // We don't want to show it if the content is hidden,
              // then we just set it to invisible, to ensure that it gets visible again
              visibility = INVISIBLE;
            }
            setTileVisibility(r.tileView, visibility);
            r.tileView.onStateChanged(state);
          }

          @Override
          public void onShowDetail(boolean show) {
            QSPanel.this.showDetail(show, r);
          }

          @Override
          public void onToggleStateChanged(boolean state) {
            if (mDetailRecord == r) {
              fireToggleStateChanged(state);
            }
          }

          @Override
          public void onScanStateChanged(boolean state) {
            r.scanState = state;
            if (mDetailRecord == r) {
              fireScanStateChanged(r.scanState);
            }
          }

          @Override
          public void onAnnouncementRequested(CharSequence announcement) {
            announceForAccessibility(announcement);
          }
        };
    r.tile.setCallback(callback);
    final View.OnClickListener click =
        new View.OnClickListener() {
          @Override
          public void onClick(View v) {
            r.tile.click();
            vibrateTile(20);
          }
        };
    final View.OnClickListener clickSecondary =
        new View.OnClickListener() {
          @Override
          public void onClick(View v) {
            r.tile.secondaryClick();
            vibrateTile(20);
          }
        };
    final View.OnLongClickListener clickLong =
        new View.OnLongClickListener() {
          @Override
          public boolean onLongClick(View v) {
            r.tile.longClick();
            vibrateTile(20);
            return true;
          }
        };
    r.tileView.init(click, clickSecondary, clickLong);
    r.tile.setListening(mListening);
    callback.onStateChanged(r.tile.getState());
    r.tile.refreshState();
    mRecords.add(r);

    addView(r.tileView);
  }