diff options
Diffstat (limited to 'packages/SystemUI')
15 files changed, 655 insertions, 110 deletions
diff --git a/packages/SystemUI/res/drawable/ic_qs_tile_category_other.xml b/packages/SystemUI/res/drawable/ic_qs_tile_category_other.xml new file mode 100644 index 0000000..f3f9dca --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_tile_category_other.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2015 The CyangenMod 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" android:width="24dp" + android:viewportHeight="24.0" android:viewportWidth="24.0"> + <path android:fillColor="#FFFFFFFF" + android:pathData="M12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm7,-7H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2V5c0,-1.1 -0.89,-2 -2,-2zm-1.75,9c0,0.23 -0.02,0.46 -0.05,0.68l1.48,1.16c0.13,0.11 0.17,0.3 0.08,0.45l-1.4,2.42c-0.09,0.15 -0.27,0.21 -0.43,0.15l-1.74,-0.7c-0.36,0.28 -0.76,0.51 -1.18,0.69l-0.26,1.85c-0.03,0.17 -0.18,0.3 -0.35,0.3h-2.8c-0.17,0 -0.32,-0.13 -0.35,-0.29l-0.26,-1.85c-0.43,-0.18 -0.82,-0.41 -1.18,-0.69l-1.74,0.7c-0.16,0.06 -0.34,0 -0.43,-0.15l-1.4,-2.42c-0.09,-0.15 -0.05,-0.34 0.08,-0.45l1.48,-1.16c-0.03,-0.23 -0.05,-0.46 -0.05,-0.69 0,-0.23 0.02,-0.46 0.05,-0.68l-1.48,-1.16c-0.13,-0.11 -0.17,-0.3 -0.08,-0.45l1.4,-2.42c0.09,-0.15 0.27,-0.21 0.43,-0.15l1.74,0.7c0.36,-0.28 0.76,-0.51 1.18,-0.69l0.26,-1.85c0.03,-0.17 0.18,-0.3 0.35,-0.3h2.8c0.17,0 0.32,0.13 0.35,0.29l0.26,1.85c0.43,0.18 0.82,0.41 1.18,0.69l1.74,-0.7c0.16,-0.06 0.34,0 0.43,0.15l1.4,2.42c0.09,0.15 0.05,0.34 -0.08,0.45l-1.48,1.16c0.03,0.23 0.05,0.46 0.05,0.69z"/> +</vector> + diff --git a/packages/SystemUI/res/drawable/ic_qs_tile_category_system.xml b/packages/SystemUI/res/drawable/ic_qs_tile_category_system.xml new file mode 100644 index 0000000..a54bec7 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_tile_category_system.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2015 The CyangenMod 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="18dp" + android:width="18dp" + android:viewportHeight="24.0" + android:viewportWidth="24.0" + > + <path android:fillColor="#FFFFFFFF" + android:pathData="M12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm7,-7H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2V5c0,-1.1 -0.89,-2 -2,-2zm-1.75,9c0,0.23 -0.02,0.46 -0.05,0.68l1.48,1.16c0.13,0.11 0.17,0.3 0.08,0.45l-1.4,2.42c-0.09,0.15 -0.27,0.21 -0.43,0.15l-1.74,-0.7c-0.36,0.28 -0.76,0.51 -1.18,0.69l-0.26,1.85c-0.03,0.17 -0.18,0.3 -0.35,0.3h-2.8c-0.17,0 -0.32,-0.13 -0.35,-0.29l-0.26,-1.85c-0.43,-0.18 -0.82,-0.41 -1.18,-0.69l-1.74,0.7c-0.16,0.06 -0.34,0 -0.43,-0.15l-1.4,-2.42c-0.09,-0.15 -0.05,-0.34 0.08,-0.45l1.48,-1.16c-0.03,-0.23 -0.05,-0.46 -0.05,-0.69 0,-0.23 0.02,-0.46 0.05,-0.68l-1.48,-1.16c-0.13,-0.11 -0.17,-0.3 -0.08,-0.45l1.4,-2.42c0.09,-0.15 0.27,-0.21 0.43,-0.15l1.74,0.7c0.36,-0.28 0.76,-0.51 1.18,-0.69l0.26,-1.85c0.03,-0.17 0.18,-0.3 0.35,-0.3h2.8c0.17,0 0.32,0.13 0.35,0.29l0.26,1.85c0.43,0.18 0.82,0.41 1.18,0.69l1.74,-0.7c0.16,-0.06 0.34,0 0.43,0.15l1.4,2.42c0.09,0.15 0.05,0.34 -0.08,0.45l-1.48,1.16c0.03,0.23 0.05,0.46 0.05,0.69z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_tile_contract.xml b/packages/SystemUI/res/drawable/ic_qs_tile_contract.xml new file mode 100644 index 0000000..e98f57c --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_tile_contract.xml @@ -0,0 +1,24 @@ +<!-- +Copyright (C) 2015 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="18.0dp" + android:height="18.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M12.000000,8.000000l-6.000000,6.000000 1.400000,1.400000 4.600000,-4.599999 4.600000,4.599999 1.400000,-1.400000z" + android:fillColor="#FFFFFFFF"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_tile_expand.xml b/packages/SystemUI/res/drawable/ic_qs_tile_expand.xml new file mode 100644 index 0000000..a429456 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_tile_expand.xml @@ -0,0 +1,24 @@ +<!-- +Copyright (C) 2015 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="18.0dp" + android:height="18.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M16.600000,8.600000l-4.600000,4.599999 -4.600000,-4.599999 -1.400000,1.400000 6.000000,6.000000 6.000000,-6.000000z" + android:fillColor="#FFFFFFFF"/> +</vector> diff --git a/packages/SystemUI/res/layout/qs_tile_category_row.xml b/packages/SystemUI/res/layout/qs_tile_category_row.xml new file mode 100644 index 0000000..1736221 --- /dev/null +++ b/packages/SystemUI/res/layout/qs_tile_category_row.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2015 The CyanogenMod 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. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="@dimen/qs_detail_item_height" + android:background="@drawable/btn_borderless_rect" + android:gravity="center_vertical" + android:orientation="horizontal" > + + <ImageView + android:id="@android:id/icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="12dp" + android:layout_marginEnd="12dp" /> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="12dp" + android:layout_weight="1" + android:orientation="vertical" > + + <TextView + android:id="@android:id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textStyle="bold" + android:ellipsize="end" + android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" /> + </LinearLayout> + + <ImageView + android:id="@android:id/icon2" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="12dp" + android:layout_marginEnd="12dp" /> + +</LinearLayout> diff --git a/packages/SystemUI/res/layout/qs_tile_child_row.xml b/packages/SystemUI/res/layout/qs_tile_child_row.xml new file mode 100644 index 0000000..bb30729 --- /dev/null +++ b/packages/SystemUI/res/layout/qs_tile_child_row.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2015 The CyanogenMod 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. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="@dimen/qs_detail_item_height" + android:background="@drawable/btn_borderless_rect" + android:gravity="center_vertical" + android:orientation="horizontal" > + + <ImageView + android:id="@android:id/icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="72dp" + android:layout_marginEnd="12dp" /> + + <TextView + android:id="@android:id/title" + android:layout_weight="1" + android:layout_marginStart="12dp" + android:layout_width="0dp" + android:gravity="center_vertical" + android:layout_height="wrap_content" + android:ellipsize="end" + android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" /> + +</LinearLayout> diff --git a/packages/SystemUI/res/values/cm_strings.xml b/packages/SystemUI/res/values/cm_strings.xml index a41fd3c..481bdf5 100644 --- a/packages/SystemUI/res/values/cm_strings.xml +++ b/packages/SystemUI/res/values/cm_strings.xml @@ -211,4 +211,8 @@ <string name="accessibility_quick_settings_location_changed_gps_only">Location reporting changed to sensors only mode.</string> <!-- Announcement made when the location tile changes to high accuracy (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_quick_settings_location_changed_high_accuracy">Location reporting changed to high accuracy mode.</string> + + <string name="quick_settings_tiles_category_system">System tiles</string> + <!-- detail header when adding a tile --> + <string name="quick_settings_tiles_add_tiles">Add a tile</string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java index 9326504..157dc87 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java @@ -23,24 +23,35 @@ import android.app.AlertDialog; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; -import android.content.res.Resources; +import android.content.Intent; +import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Point; import android.graphics.PointF; +import android.graphics.PorterDuff; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.UserHandle; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; +import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; import android.view.DragEvent; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import android.widget.BaseExpandableListAdapter; import android.widget.EditText; +import android.widget.ExpandableListView; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.logging.MetricsLogger; import com.android.systemui.FontSizeUtils; @@ -54,14 +65,10 @@ import com.android.systemui.statusbar.phone.QSTileHost; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.tuner.QsTuner; - import com.viewpagerindicator.CirclePageIndicator; - -import org.cyanogenmod.internal.util.QSUtils; - -import cyanogenmod.providers.CMSettings; - import cyanogenmod.app.StatusBarPanelCustomTile; +import cyanogenmod.providers.CMSettings; +import org.cyanogenmod.internal.util.QSUtils; import java.util.ArrayList; import java.util.Arrays; @@ -106,6 +113,10 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On List<TileRecord> mCurrentlyAnimating = Collections.synchronizedList(new ArrayList<TileRecord>()); private Collection<QSTile<?>> mTempTiles = null; + protected boolean mSettingTiles; + + private Point mDisplaySize; + private int[] mTmpLoc; public QSDragPanel(Context context) { this(context, null); @@ -150,11 +161,13 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On }); // add target click listener - mQsPanelTop.findViewById(R.id.add_target).setOnClickListener( + mQsPanelTop.getAddTarget().setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { - showAddDialog(); + TilesListAdapter adapter = new TilesListAdapter(mContext, QSDragPanel.this); + showDetailAdapter(true, adapter, + v.getLocationOnScreen()); } }); mViewPager = new QSViewPager(getContext()); @@ -390,7 +403,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On mPagerAdapter.notifyDataSetChanged(); ensurePagerState(); - requestLayout(); } protected void onStartDrag() { @@ -408,8 +420,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On mLastRightShift = -1; mQsPanelTop.onStopDrag(); - - requestLayout(); ensurePagerState(); } @@ -426,7 +436,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } protected int getPagesForCount(int tileCount) { - tileCount -= getTilesPerPage(true); + tileCount -= getTilesPerPage(true); // first page + rest of tiles return 1 + (int) Math.ceil(tileCount / (double) getTilesPerPage(false)); } @@ -476,6 +486,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } public void setTiles(Collection<QSTile<?>> tiles) { + mSettingTiles = true; if (DEBUG_DRAG) { Log.i(TAG, "setTiles() called with " + "tiles = [" + tiles + "], mTempTiles: " + mTempTiles); @@ -505,8 +516,17 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On mTempTiles = Collections.synchronizedCollection(new ArrayList<QSTile<?>>(tiles)); } mPagerAdapter.notifyDataSetChanged(); - requestLayout(); ensurePagerState(); + + mSettingTiles = false; + requestLayout(); + } + + @Override + public void requestLayout() { + if (!mSettingTiles) { + super.requestLayout(); + } } protected void addTile(final QSTile<?> tile) { @@ -612,13 +632,20 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } public int getTilesPerPage(boolean firstPage) { - if ((!mFirstRowLarge && firstPage)|| !firstPage) { + if ((!mFirstRowLarge && firstPage) || !firstPage) { return QSTileHost.TILES_PER_PAGE + 1; } return QSTileHost.TILES_PER_PAGE; } @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + mTmpLoc = null; + mDisplaySize = null; + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = MeasureSpec.getSize(widthMeasureSpec); @@ -639,8 +666,32 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On if (mDetail.getMeasuredHeight() < h) { mDetail.measure(exactly(width), exactly(h)); } - mGridHeight = h; - setMeasuredDimension(width, Math.max(h, mDetail.getMeasuredHeight())); + + // Check if the detail view would be overflowing below the physical height of the device + // and cutting the content off. If it is, reduce the detail height to fit. + if (isShowingDetail()) { + if (mDisplaySize == null) { + mDisplaySize = new Point(); + getDisplay().getSize(mDisplaySize); + } + if (mTmpLoc == null) { + mTmpLoc = new int[2]; + mDetail.getLocationOnScreen(mTmpLoc); + } + + final int containerTop = mTmpLoc[1]; + final int detailBottom = containerTop + mDetail.getMeasuredHeight(); + if (detailBottom >= mDisplaySize.y) { + // panel is hanging below the screen + final int detailMinHeight = mDisplaySize.y - containerTop; + mDetail.measure(exactly(width), exactly(detailMinHeight)); + } + setMeasuredDimension(width, mDetail.getMeasuredHeight()); + mGridHeight = mDetail.getMeasuredHeight(); + } else { + setMeasuredDimension(width, Math.max(h, mDetail.getMeasuredHeight())); + mGridHeight = h; + } for (TileRecord record : mRecords) { setupRecord(record); @@ -686,8 +737,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On // layout page indicator below view pager mPageIndicator.layout(0, top, w, top + mPageIndicator.getMeasuredHeight()); - // detail takes up whole height - mDetail.layout(0, 0, mDetail.getMeasuredWidth(), getMeasuredHeight()); + mDetail.layout(0, 0, w, mDetail.getMeasuredHeight()); if (mFooter.hasFooter()) { View footer = mFooter.getView(); @@ -865,15 +915,13 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On return true; } else { mRestored = true; - getPage(mDraggingRecord.page).removeView(mDraggingRecord.tileView); - // what spec is this tile? String spec = mHost.getSpec(mDraggingRecord.tile); if (DEBUG_DRAG) { Log.w(TAG, "removing tile: " + mDraggingRecord + " with spec: " + spec); } - mHost.remove(spec); onStopDrag(); + mHost.remove(spec); } } else { restoreDraggingTilePosition(v); @@ -1076,11 +1124,12 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On final QSPage targetP = getPage(mDraggingRecord.destinationPage); - if (dragRecordDetached) { - Log.i(TAG, "drag record was detached"); - - } else { - Log.i(TAG, "drag record was attached"); + if (DEBUG_DRAG) { + if (dragRecordDetached) { + Log.i(TAG, "drag record was detached"); + } else { + Log.i(TAG, "drag record was attached"); + } } mDraggingRecord.page = mDraggingRecord.destinationPage; targetP.addView(mDraggingRecord.tileView); @@ -1155,8 +1204,10 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On private void setToLastDestination(DragTileRecord record) { DragTileRecord last = (DragTileRecord) mRecords.get(mRecords.size() - 1); - Log.d(TAG, "setToLastDestination() called with record = [" - + record + "], and last record is: " + last); + if (DEBUG_DRAG) { + Log.d(TAG, "setToLastDestination() called with record = [" + + record + "], and last record is: " + last); + } if (record != last && record.destinationPage <= last.destinationPage) { record.destinationPage = last.destinationPage; record.row = last.row; @@ -1398,7 +1449,9 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On if (ti.row != lastRow) { desiredColCount = getColumnCount(ti.destinationPage, ti.row); - Log.e(TAG, "updating desired colum count to: " + desiredColCount); + if (DEBUG_DRAG) { + Log.e(TAG, "updating desired colum count to: " + desiredColCount); + } } // save current tile's loc @@ -1499,14 +1552,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } } - public int getDesiredColumnCount(int page, int row) { - if (page == 0 && row == 0) { - return 2; // TODO change if large tiles are disabled - } else { - return mColumns; - } - } - @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); @@ -1518,7 +1563,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On super.setExpanded(expanded); if (!expanded) { if (mEditing) { - setEditing(false); + mHost.setEditing(false); } } } @@ -1552,74 +1597,315 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On return mCurrentlyAnimating.contains(t); } - // todo implement proper add tile ui - protected void showAddDialog() { - List<String> currentTileSpec = mHost.getTileSpecs(); - final List<String> availableTilesSpec = QSUtils.getAvailableTiles(getContext()); + public static class TilesListAdapter extends BaseExpandableListAdapter + implements QSTile.DetailAdapter { + + public static final String PACKAGE_ANDROID = "android"; + + Context mContext; + QSTileHost mHost; + QSDragPanel mPanel; + + ArrayMap<String, List<String>> mPackageTileMap = new ArrayMap<>(); + + public TilesListAdapter(Context context, QSDragPanel panel) { + mContext = context; + mHost = panel.getHost(); + mPanel = panel; + + List<String> currentTileSpec = mHost.getTileSpecs(); + final Collection<String> tiles = QSUtils.getAvailableTiles(mContext); + tiles.removeAll(currentTileSpec); + + // we'll always have a system tiles category + mPackageTileMap.put(PACKAGE_ANDROID, new ArrayList<String>()); - // Remove tiles already used - availableTilesSpec.removeAll(currentTileSpec); + final Iterator<String> i = tiles.iterator(); + while (i.hasNext()) { + final String spec = i.next(); + if (QSUtils.isStaticQsTile(spec) || QSUtils.isDynamicQsTile(spec)) { + List<String> packageList = mPackageTileMap.get(PACKAGE_ANDROID); + packageList.add(spec); + } else { + String tilePackage = getCustomTilePackage(spec); + List<String> packageList = mPackageTileMap.get(tilePackage); + if (packageList == null) { + mPackageTileMap.put(tilePackage, packageList = new ArrayList<>()); + } + packageList.add(spec); + } + } + + // add broadcast tile + mPackageTileMap.get(PACKAGE_ANDROID).add(BROADCAST_TILE_SPEC_PLACEHOLDER); + } + + private String getCustomTilePackage(String spec) { + StatusBarPanelCustomTile sbc = mHost.getCustomTileData().get(spec).sbc; + return sbc.getPackage(); + } + + @Override + public int getGroupCount() { + return mPackageTileMap.keySet().size(); + } + + @Override + public int getChildrenCount(int groupPosition) { + return mPackageTileMap.valueAt(groupPosition).size(); + } + + @Override + public String getGroup(int groupPosition) { + return mPackageTileMap.keyAt(groupPosition); + } + + @Override + public String getChild(int groupPosition, int childPosition) { + return mPackageTileMap.valueAt(groupPosition).get(childPosition); + } + + @Override + public long getGroupId(int groupPosition) { + return groupPosition; + } + + @Override + public long getChildId(int groupPosition, int childPosition) { + return mPackageTileMap.valueAt(groupPosition).get(childPosition).hashCode(); + } + + @Override + public boolean hasStableIds() { + return true; + } - // Populate labels - List<String> availableTilesLabel = new ArrayList<String>(); - for (String tileSpec : availableTilesSpec) { - int resource = QSTileHost.getLabelResource(tileSpec); - if (resource != 0) { - availableTilesLabel.add(getContext().getString(resource)); + @Override + public View getGroupView(int groupPosition, boolean isExpanded, View convertView, + ViewGroup parent) { + LinearLayout row = (LinearLayout) convertView; + if (row == null) { + row = (LinearLayout) LayoutInflater.from(mContext) + .inflate(R.layout.qs_tile_category_row, parent, false); + } + TextView title = (TextView) row.findViewById(android.R.id.title); + + ImageView systemOrAppIcon = (ImageView) row.findViewById(android.R.id.icon); + ImageView expansionIndicator = (ImageView) row.findViewById(android.R.id.icon2); + + expansionIndicator.setImageResource(isExpanded ? R.drawable.ic_qs_tile_contract + : R.drawable.ic_qs_tile_expand); + // hide indicator when there's only 1 group + expansionIndicator.setVisibility(getGroupCount() == 1 ? View.GONE : View.VISIBLE); + + String group = getGroup(groupPosition); + if (group.equals(PACKAGE_ANDROID)) { + group = mContext.getText(R.string.quick_settings_tiles_category_system).toString(); + // special icon + systemOrAppIcon.setImageResource(R.drawable.ic_qs_tile_category_system); } else { - availableTilesLabel.add(tileSpec); + systemOrAppIcon.setImageResource(R.drawable.ic_qs_tile_category_other); } + title.setText(group); + + if (isExpanded) { + expansionIndicator.setColorFilter(0xFF80cbc4, PorterDuff.Mode.SRC_ATOP); + systemOrAppIcon.setColorFilter(0xFF80cbc4, PorterDuff.Mode.SRC_ATOP); + title.setTextColor(0xFF80cbc4); + } else { + title.setTextColor(Color.WHITE); + systemOrAppIcon.setColorFilter(null); + expansionIndicator.setColorFilter(null); + } + return row; } - // Add broadcast tile - availableTilesLabel.add(getContext().getString(R.string.broadcast_tile)); - availableTilesSpec.add(BROADCAST_TILE_SPEC_PLACEHOLDER); + @Override + public View getChildView(int groupPosition, int childPosition, boolean isLastChild, + View convertView, ViewGroup parent) { + LinearLayout child = (LinearLayout) convertView; + if (child == null) { + child = (LinearLayout) LayoutInflater.from(mContext) + .inflate(R.layout.qs_tile_child_row, parent, false); + } + String spec = getChild(groupPosition, childPosition); - String[] items = new String[availableTilesLabel.size()]; - availableTilesLabel.toArray(items); + TextView title = (TextView) child.findViewById(android.R.id.title); + title.setText(getQSTileLabel(spec)); - final AlertDialog d = new AlertDialog.Builder(getContext(), R.style.Theme_SystemUI_Dialog) - .setTitle(R.string.add_tile) - .setItems(items, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - String tileSpec = availableTilesSpec.get(which); - if (tileSpec.equals(BROADCAST_TILE_SPEC_PLACEHOLDER)) { - showBroadcastTileDialog(); - } else { - add(tileSpec); - } + ImageView icon = (ImageView) child.findViewById(android.R.id.icon); + icon.setImageDrawable(getQSTileIcon(spec)); + + return child; + } + + private String getQSTileLabel(String spec) { + if (spec.equals(BROADCAST_TILE_SPEC_PLACEHOLDER)) { + return mContext.getText(R.string.broadcast_tile).toString(); + } else if (QSUtils.isStaticQsTile(spec)) { + int resource = QSTileHost.getLabelResource(spec); + return mContext.getText(resource).toString(); + } else if (QSUtils.isDynamicQsTile(spec)) { + return QSUtils.getDynamicQSTileLabel(mContext, + UserHandle.myUserId(), spec); + } else { + return getPackageLabel(getCustomTilePackage(spec)); + } + } + + private Drawable getQSTileIcon(String spec) { + if (QSUtils.isDynamicQsTile(spec)) { + return QSTile.ResourceIcon.get( + QSUtils.getDynamicQSTileResIconId(mContext, UserHandle.myUserId(), spec)) + .getDrawable(mContext); + } else if (QSUtils.isStaticQsTile(spec)) { + return QSTile.ResourceIcon.get(QSTileHost.getIconResource(spec)) + .getDrawable(mContext); + } else { + QSTile<?> tile = mHost.getTile(spec); + if (tile != null) { + QSTile.State state = tile.getState(); + if (state != null && state.icon != null) { + return state.icon.getDrawable(mContext); } - }).create(); - SystemUIDialog.makeSystemUIDialog(d); - d.show(); - } + } + if (spec.equals(BROADCAST_TILE_SPEC_PLACEHOLDER)) { + return getPackageDrawable(PACKAGE_ANDROID); + } + return getPackageDrawable(getCustomTilePackage(spec)); + } + } - public void showBroadcastTileDialog() { - final EditText editText = new EditText(getContext()); - final AlertDialog d = new AlertDialog.Builder(getContext()) - .setTitle(R.string.broadcast_tile) - .setView(editText) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - String action = editText.getText().toString(); - if (isValid(action)) { - add(IntentTile.PREFIX + action + ')'); - } + private String getPackageLabel(String packageName) { + try { + return mContext.getPackageManager().getApplicationLabel( + mContext.getPackageManager().getApplicationInfo(packageName, 0)).toString(); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + private Drawable getPackageDrawable(String packageName) { + try { + return mContext.getPackageManager().getApplicationIcon(packageName); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public boolean isChildSelectable(int groupPosition, int childPosition) { + return true; + } + + @Override + public int getTitle() { + return R.string.quick_settings_tiles_add_tiles; + } + + @Override + public Boolean getToggleState() { + return null; + } + + @Override + public View createDetailView(Context context, View convertView, ViewGroup parent) { + ExpandableListView lv = (ExpandableListView) convertView; + if (lv == null) { + lv = new ExpandableListView(parent.getContext()); + lv.setOnTouchListener(new OnTouchListener() { + // Setting on Touch Listener for handling the touch inside ScrollView + @Override + public boolean onTouch(View v, MotionEvent event) { + // Disallow the touch request for parent scroll on touch of child view + v.getParent().requestDisallowInterceptTouchEvent(true); + return false; } - }).create(); - SystemUIDialog.makeSystemUIDialog(d); - d.show(); - } + }); + } + lv.setAdapter(this); + lv.expandGroup(mPackageTileMap.indexOfKey(PACKAGE_ANDROID)); + lv.setGroupIndicator(null); + lv.setChildIndicator(null); + lv.setChildDivider(new ColorDrawable(Color.TRANSPARENT)); + lv.setOnChildClickListener(new ExpandableListView.OnChildClickListener() { + @Override + public boolean onChildClick(ExpandableListView parent, View v, + int groupPosition, int childPosition, long id) { + String spec = getChild(groupPosition, childPosition); + if (spec.equals(BROADCAST_TILE_SPEC_PLACEHOLDER)) { + showBroadcastTileDialog(); + } else { + mPanel.add(spec); + mPanel.closeDetail(); + } + return true; + } + }); + lv.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() { + @Override + public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, + long id) { + if (getGroupCount() == 1) { + // disable contracting/expanding group when there's only 1 + return true; + } + return false; + } + }); + return lv; + } - private boolean isValid(String action) { - for (int i = 0; i < action.length(); i++) { - char c = action.charAt(i); - if (!Character.isAlphabetic(c) && !Character.isDigit(c) && c != '.') { - return false; + @Override + public Intent getSettingsIntent() { + return null; + } + + @Override + public StatusBarPanelCustomTile getCustomTile() { + return null; + } + + @Override + public void setToggleState(boolean state) { + + } + + @Override + public int getMetricsCategory() { + return MetricsLogger.DONT_TRACK_ME_BRO; + } + + public void showBroadcastTileDialog() { + final EditText editText = new EditText(mContext); + final AlertDialog d = new AlertDialog.Builder(mContext) + .setTitle(R.string.broadcast_tile) + .setView(editText) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + String action = editText.getText().toString(); + if (isValid(action)) { + mPanel.add(IntentTile.PREFIX + action + ')'); + mPanel.closeDetail(); + } + } + }).create(); + SystemUIDialog.makeSystemUIDialog(d); + d.show(); + } + + private boolean isValid(String action) { + for (int i = 0; i < action.length(); i++) { + char c = action.charAt(i); + if (!Character.isAlphabetic(c) && !Character.isDigit(c) && c != '.') { + return false; + } } + return true; } - return true; } public void add(String tile) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPage.java b/packages/SystemUI/src/com/android/systemui/qs/QSPage.java index be2f7e5..f5f6663 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPage.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPage.java @@ -52,6 +52,13 @@ public class QSPage extends ViewGroup { } @Override + public void requestLayout() { + if (mPanel == null || !mPanel.mSettingTiles) { + super.requestLayout(); + } + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = MeasureSpec.getSize(widthMeasureSpec); if (mPanel.mCurrentlyAnimating.isEmpty() && !mPanel.isDragging()) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelTopView.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelTopView.java index c2862e6..060d06c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelTopView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelTopView.java @@ -40,6 +40,7 @@ public class QSPanelTopView extends FrameLayout { protected View mDropTarget; protected View mBrightnessView; protected TextView mToastView; + protected View mAddTarget; private boolean mEditing = false; private boolean mDisplayingInstructions = false; @@ -74,6 +75,10 @@ public class QSPanelTopView extends FrameLayout { return mBrightnessView; } + public View getAddTarget() { + return mAddTarget; + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -81,6 +86,7 @@ public class QSPanelTopView extends FrameLayout { mEditTileInstructionView = findViewById(R.id.edit_container); mBrightnessView = findViewById(R.id.brightness_container); mToastView = (TextView) findViewById(R.id.qs_toast); + mAddTarget = findViewById(R.id.add_target); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java index 667bd30..d1a1ad5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java @@ -128,7 +128,6 @@ public class QSTileView extends ViewGroup { } private void recreateLabel() { - Log.d(TAG, "recreateLabel() called with " + ""); CharSequence labelText = null; CharSequence labelDescription = null; if (mLabel != null) { @@ -392,7 +391,9 @@ public class QSTileView extends ViewGroup { if (mLabel != null) { mLabel.setFocusable(!editing); } - mRipple.setVisible(!editing, false); + if (mRipple != null) { + mRipple.setVisible(!editing, false); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSViewPager.java b/packages/SystemUI/src/com/android/systemui/qs/QSViewPager.java index 01c48b1..fa871c4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSViewPager.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSViewPager.java @@ -16,6 +16,7 @@ public class QSViewPager extends ViewPager { protected static final float SCROLL_PERCENT = .10f; private boolean mPagingEnabled; QSDragPanel mDragPanel; + private int mLastHeight = 0; public QSViewPager(Context context) { super(context); @@ -43,14 +44,22 @@ public class QSViewPager extends ViewPager { } @Override + public void requestLayout() { + if (mDragPanel == null || !mDragPanel.mSettingTiles) { + super.requestLayout(); + } + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int height = 0; + int height = mLastHeight; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); int h = child.getMeasuredHeight(); if (h > height) height = h; } + mLastHeight = height; heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java index 96cc51a..adac18c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java @@ -119,6 +119,10 @@ public class CustomQSTile extends QSTile<QSTile.State> { } } + public StatusBarPanelCustomTile getTile() { + return mTile; + } + @Override protected void handleUpdateState(State state, Object arg) { if (arg instanceof StatusBarPanelCustomTile) { @@ -282,4 +286,4 @@ public class CustomQSTile extends QSTile<QSTile.State> { return themeConfig != null ? themeConfig.getOverlayForStatusBar() : null; } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java index 2da937c..c3428bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -30,6 +30,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.Log; import com.android.internal.logging.MetricsLogger; @@ -294,7 +295,7 @@ public class QSTileHost implements QSTile.Host, Tunable { final List<String> tileSpecs = loadTileSpecs(newValue); if (tileSpecs.equals(mTileSpecs)) return; for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) { - if (!tileSpecs.contains(tile.getKey())) { + if (!tileSpecs.contains(tile.getKey()) && mCustomTileData.get(tile.getKey()) == null) { if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey()); tile.getValue().destroy(); } @@ -415,6 +416,10 @@ public class QSTileHost implements QSTile.Host, Tunable { CMSettings.Secure.QS_TILES, "default", ActivityManager.getCurrentUser()); } + public QSTile<?> getTile(String spec) { + return mTiles.get(spec); + } + public static int getLabelResource(String spec) { if (spec.equals("wifi")) return R.string.quick_settings_wifi_label; else if (spec.equals("bt")) return R.string.quick_settings_bluetooth_label; @@ -443,6 +448,34 @@ public class QSTileHost implements QSTile.Host, Tunable { return 0; } + public static int getIconResource(String spec) { + if (spec.equals("wifi")) return R.drawable.ic_qs_wifi_full_4; + else if (spec.equals("bt")) return R.drawable.ic_qs_bluetooth_on; + else if (spec.equals("inversion")) return R.drawable.ic_invert_colors_enable_animation; + else if (spec.equals("cell")) return R.drawable.ic_qs_signal_full_4; + else if (spec.equals("airplane")) return R.drawable.ic_signal_airplane_enable; + else if (spec.equals("dnd")) return R.drawable.ic_dnd; + else if (spec.equals("rotation")) return R.drawable.ic_portrait_from_auto_rotate; + else if (spec.equals("flashlight")) return R.drawable.ic_signal_flashlight_enable; + else if (spec.equals("location")) return R.drawable.ic_signal_location_enable; + else if (spec.equals("cast")) return R.drawable.ic_qs_cast_on; + else if (spec.equals("hotspot")) return R.drawable.ic_hotspot_enable; + else if (spec.equals("edit")) return R.drawable.ic_qs_edit_tiles; + else if (spec.equals("adb_network")) return R.drawable.ic_qs_network_adb_on; + else if (spec.equals("compass")) return R.drawable.ic_qs_compass_on; + else if (spec.equals("nfc")) return R.drawable.ic_qs_nfc_on; + else if (spec.equals("profiles")) return R.drawable.ic_qs_profiles_on; + else if (spec.equals("sync")) return R.drawable.ic_qs_sync_on; + else if (spec.equals("volume_panel")) return R.drawable.ic_qs_volume_panel; + else if (spec.equals("usb_tether")) return R.drawable.ic_qs_usb_tether_on; + else if (spec.equals("screen_timeout")) return R.drawable.ic_qs_screen_timeout_short_avd; + else if (spec.equals("performance")) return R.drawable.ic_qs_perf_profile; + else if (spec.equals("lockscreen")) return R.drawable.ic_qs_lock_screen_on; + else if (spec.equals("ambient_display")) return R.drawable.ic_qs_ambientdisplay_on; + else if (spec.equals("live_display")) return R.drawable.ic_livedisplay_auto; + return 0; + } + void updateCustomTile(StatusBarPanelCustomTile sbc) { synchronized (mTiles) { if (mTiles.containsKey(sbc.getKey())) { @@ -479,7 +512,7 @@ public class QSTileHost implements QSTile.Host, Tunable { } } - CustomTileData getCustomTileData() { + public CustomTileData getCustomTileData() { return mCustomTileData; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java index 6624b30..56cbf68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -157,6 +157,8 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL private SettingsObserver mSettingsObserver; private boolean mShowWeather; private boolean mShowBatteryTextExpanded; + + private QSTile.DetailAdapter mEditingDetailAdapter; private boolean mEditing; public StatusBarHeaderView(Context context, AttributeSet attrs) { @@ -747,13 +749,8 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL public void setEditing(boolean editing) { mEditing = editing; - if (mEditing) { - mQsPanelCallback.onShowingDetail(new QSTile.DetailAdapter() { - @Override - public StatusBarPanelCustomTile getCustomTile() { - return null; - } - + if (mEditingDetailAdapter == null) { + mEditingDetailAdapter = new QSTile.DetailAdapter() { @Override public int getTitle() { return R.string.quick_settings_edit_label; @@ -775,6 +772,11 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL } @Override + public StatusBarPanelCustomTile getCustomTile() { + return null; + } + + @Override public void setToggleState(boolean state) { } @@ -783,10 +785,10 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL public int getMetricsCategory() { return MetricsConstants.DONT_TRACK_ME_BRO; } - }); - } else { - mQsPanelCallback.onShowingDetail(null); + }; } + mQsPanelCallback.onShowingDetail(mEditing ? mEditingDetailAdapter : null); + updateEverything(); } /** @@ -865,7 +867,7 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL post(new Runnable() { @Override public void run() { - handleShowingDetail(detail); + handleShowingDetail(mEditing && detail == null ? mEditingDetailAdapter : detail); } }); } |