New quick settings implementation.
Bug:14133785
Change-Id: I7f57f8e7ebcc3e1a06fa5204f477470f14299e1f
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
new file mode 100644
index 0000000..afb5483
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+
+/** View that represents the quick settings tile panel. **/
+public class QSPanel extends ViewGroup {
+ private static final float TILE_ASPECT = 1.4f;
+ private static final float LARGE_TILE_FACTOR = 1.1f;
+
+ private final Context mContext;
+ private final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
+ private final FrameLayout mDetail;
+ private final CircularClipper mClipper;
+ private final H mHandler = new H();
+
+ private int mColumns;
+ private int mCellWidth;
+ private int mCellHeight;
+ private int mLargeCellWidth;
+ private int mLargeCellHeight;
+
+ private TileRecord mDetailRecord;
+
+ public QSPanel(Context context) {
+ this(context, null);
+ }
+
+ public QSPanel(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+
+ mDetail = new FrameLayout(mContext);
+ mDetail.setVisibility(GONE);
+ mDetail.setClickable(true);
+ addView(mDetail);
+ mClipper = new CircularClipper(mDetail);
+ updateResources();
+ }
+
+ public void updateResources() {
+ final int columns = Math.max(1,
+ mContext.getResources().getInteger(R.integer.quick_settings_num_columns));
+ if (mColumns != columns) {
+ mColumns = columns;
+ postInvalidate();
+ }
+ }
+
+ public void setExpanded(boolean expanded) {
+ if (!expanded) {
+ showDetail(false /*show*/, mDetailRecord);
+ }
+ for (TileRecord r : mRecords) {
+ r.tile.setShown(expanded);
+ }
+ }
+
+ private void showDetail(boolean show, TileRecord r) {
+ mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
+ }
+
+ private void setTileVisibility(View v, boolean visible) {
+ mHandler.obtainMessage(H.SET_TILE_VISIBILITY, visible ? 1 : 0, 0, v).sendToTarget();
+ }
+
+ private void handleSetTileVisibility(View v, boolean visible) {
+ v.setVisibility(visible ? VISIBLE : GONE);
+ }
+
+ public void addTile(final QSTile<?> tile) {
+ final TileRecord r = new TileRecord();
+ r.tile = tile;
+ r.tileView = tile.createTileView(mContext);
+ r.tileView.setVisibility(View.GONE);
+ r.tile.setCallback(new QSTile.Callback() {
+ @Override
+ public void onStateChanged(QSTile.State state) {
+ setTileVisibility(r.tileView, state.visible);
+ r.tileView.onStateChanged(state);
+ }
+ @Override
+ public void onShowDetail(boolean show) {
+ QSPanel.this.showDetail(show, r);
+ }
+ });
+ final View.OnClickListener click = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ r.tile.click();
+ }
+ };
+ final View.OnClickListener clickSecondary = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ r.tile.secondaryClick();
+ }
+ };
+ r.tileView.init(click, clickSecondary);
+ mRecords.add(r);
+
+ addView(r.tileView);
+ }
+
+ private void handleShowDetail(TileRecord r, boolean show) {
+ AnimatorListener listener = null;
+ if (show) {
+ if (mDetailRecord != null) return;
+ final View detail = r.tile.createDetailView(mContext, mDetail);
+ if (detail == null) return;
+ mDetailRecord = r;
+ mDetail.removeAllViews();
+ mDetail.bringToFront();
+ mDetail.addView(detail);
+ } else {
+ if (mDetailRecord == null) return;
+ listener = mTeardownDetailWhenDone;
+ }
+ int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
+ int y = r.tileView.getTop() + r.tileView.getHeight() / 2;
+ mClipper.animateCircularClip(x, y, show, listener);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int width = MeasureSpec.getSize(widthMeasureSpec);
+ mCellWidth = width / mColumns;
+ mCellHeight = (int)(mCellWidth / TILE_ASPECT);
+ mLargeCellWidth = (int)(mCellWidth * LARGE_TILE_FACTOR);
+ mLargeCellHeight = (int)(mCellHeight * LARGE_TILE_FACTOR);
+ int r = 0;
+ int c = 0;
+ int rows = 0;
+ for (TileRecord record : mRecords) {
+ if (record.tileView.getVisibility() == GONE) continue;
+ record.row = r;
+ record.col = c;
+ rows = r + 1;
+ c++;
+ if (c == mColumns /*end of normal column*/ || r == 0 && c == 2 /*end of 1st column*/) {
+ c = 0;
+ r++;
+ }
+ }
+
+ for (TileRecord record : mRecords) {
+ if (record.tileView.getVisibility() == GONE) continue;
+ record.tileView.setDual(record.row == 0);
+ final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
+ final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight;
+ record.tileView.measure(exactly(cw), exactly(ch));
+ }
+ final int actualHeight = rows == 0 ? 0 : getRowTop(rows);
+ mDetail.measure(exactly(width), exactly(actualHeight));
+ setMeasuredDimension(width, actualHeight);
+ }
+
+ private static int exactly(int size) {
+ return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int w = mCellWidth * mColumns;
+ for (TileRecord record : mRecords) {
+ if (record.tileView.getVisibility() == GONE) continue;
+ final int cols = getColumnCount(record.row);
+ final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
+ final int extra = (w - cw * cols) / (cols + 1);
+ final int left = record.col * cw + (record.col + 1) * extra;
+ final int top = getRowTop(record.row);
+ record.tileView.layout(left, top,
+ left + record.tileView.getMeasuredWidth(),
+ top + record.tileView.getMeasuredHeight());
+ }
+ mDetail.layout(0, 0, mDetail.getMeasuredWidth(), mDetail.getMeasuredHeight());
+ }
+
+ private int getRowTop(int row) {
+ if (row <= 0) return 0;
+ return mLargeCellHeight + (row - 1) * mCellHeight;
+ }
+
+ private int getColumnCount(int row) {
+ int cols = 0;
+ for (TileRecord record : mRecords) {
+ if (record.tileView.getVisibility() == GONE) continue;
+ if (record.row == row) cols++;
+ }
+ return cols;
+ }
+
+ private class H extends Handler {
+ private static final int SHOW_DETAIL = 1;
+ private static final int SET_TILE_VISIBILITY = 2;
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == SHOW_DETAIL) {
+ handleShowDetail((TileRecord)msg.obj, msg.arg1 != 0);
+ } else if (msg.what == SET_TILE_VISIBILITY) {
+ handleSetTileVisibility((View)msg.obj, msg.arg1 != 0);
+ }
+ }
+ }
+
+ private static final class TileRecord {
+ QSTile<?> tile;
+ QSTileView tileView;
+ int row;
+ int col;
+ }
+
+ private final AnimatorListenerAdapter mTeardownDetailWhenDone = new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ mDetail.removeAllViews();
+ mDetailRecord = null;
+ };
+ };
+}