diff options
Diffstat (limited to 'packages/SystemUI/src/com/android/systemui/qs')
15 files changed, 572 insertions, 541 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItemsList.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItemsList.java index 578a983..3e0ab8b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItemsList.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItemsList.java @@ -27,6 +27,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListAdapter; @@ -42,7 +43,7 @@ import java.util.List; /** * Quick settings common detail list view with line items. */ -public class QSDetailItemsList extends LinearLayout { +public class QSDetailItemsList extends FrameLayout { private static final String TAG = "QSDetailItemsList"; private ListView mListView; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java index 86fc49e..13f552c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java @@ -19,10 +19,8 @@ package com.android.systemui.qs; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.ActivityManager; -import android.app.AlertDialog; import android.content.ContentResolver; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; @@ -32,12 +30,10 @@ import android.graphics.Color; import android.graphics.Point; import android.graphics.PointF; import android.graphics.PorterDuff; -import android.graphics.drawable.Animatable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.UserHandle; -import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.util.ArrayMap; @@ -49,7 +45,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; -import android.widget.EditText; import android.widget.ExpandableListView; import android.widget.ImageView; import android.widget.LinearLayout; @@ -58,12 +53,12 @@ import com.android.internal.logging.MetricsLogger; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.cm.UserContentObserver; +import com.android.systemui.qs.tiles.CustomQSTile; import com.android.systemui.qs.tiles.EditTile; -import com.android.systemui.qs.tiles.IntentTile; import com.android.systemui.settings.BrightnessController; import com.android.systemui.settings.ToggleSlider; +import com.android.systemui.statusbar.phone.NotificationPanelView; 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; @@ -80,6 +75,8 @@ import java.util.List; import java.util.ListIterator; import java.util.Map; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; + public class QSDragPanel extends QSPanel implements View.OnDragListener, View.OnLongClickListener { private static final String TAG = "QSDragPanel"; @@ -94,6 +91,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On protected final ArrayList<QSPage> mPages = new ArrayList<>(); + private NotificationPanelView mPanelView; protected QSViewPager mViewPager; protected PagerAdapter mPagerAdapter; QSPanelTopView mQsPanelTop; @@ -102,6 +100,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On private TextView mDetailRemoveButton; private DragTileRecord mDraggingRecord, mLastDragRecord; + private ViewGroup mDetailButtons; private boolean mEditing; private boolean mDragging; private float mLastTouchLocationX, mLastTouchLocationY; @@ -119,15 +118,13 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On List<TileRecord> mCurrentlyAnimating = Collections.synchronizedList(new ArrayList<TileRecord>()); - private Point mDisplaySize; - private int[] mTmpLoc; - private Runnable mResetPage = new Runnable() { @Override public void run() { - if (!mListening) { + if (!mExpanded) { // only reset when the user isn't interacting at all mViewPager.setCurrentItem(0); + mPagerAdapter.notifyDataSetChanged(); } } }; @@ -145,6 +142,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On updateResources(); mDetail = LayoutInflater.from(mContext).inflate(R.layout.qs_detail, this, false); + mDetailButtons = (ViewGroup) mDetail.findViewById(R.id.buttons); mDetailContent = (ViewGroup) mDetail.findViewById(android.R.id.content); mDetailRemoveButton = (TextView) mDetail.findViewById(android.R.id.button3); mDetailSettingsButton = (TextView) mDetail.findViewById(android.R.id.button2); @@ -355,7 +353,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On @Override public boolean hasOverlappingRendering() { - return mClipper.isAnimating() || mEditing; + return mClipper.isAnimating() || mEditing || !mCurrentlyAnimating.isEmpty(); } @Override @@ -370,7 +368,14 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On protected void drawTile(TileRecord r, QSTile.State state) { if (mEditing) { - state.visible = true; + if ((r.tile instanceof CustomQSTile) + && (((CustomQSTile) r.tile).isUserRemoved() + || ((CustomQSTile) r.tile).getTile() == null)) { + // don't modify visibility state if removed, or not yet published + } else { + state.visible = true; + state.enabled = true; + } } final int visibility = state.visible ? VISIBLE : GONE; setTileVisibility(r.tileView, visibility); @@ -382,12 +387,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On public void setListening(boolean listening) { if (mListening == listening) return; mListening = listening; - // reset the page when inactive for a while - if (listening) { - removeCallbacks(mResetPage); - } else { - postDelayed(mResetPage, PAGE_RESET_DELAY); - } for (TileRecord r : mRecords) { r.tile.setListening(mListening); } @@ -465,7 +464,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On mLastRightShift = -1; mQsPanelTop.onStopDrag(); - requestLayout(); } protected View getDropTarget() { @@ -501,14 +499,16 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } public void setTiles(final Collection<QSTile<?>> tilesCollection) { - final List<QSTile<?>> tiles = new ArrayList<>(tilesCollection); + // we try to be as efficient as possible here because this can happen while the user + // is in edit mode, or maybe even while tiles are animating + // step 1: stop all animations + // step 2: remove tiles no longer to be used, cache ones that are still valid + // step 3: remove empty viewpager pages + // step 4: generate new tiles, re-add cached ones + if (DEBUG_TILES) { - Log.i(TAG, "setTiles() called with " + "tiles = [" - + tiles + "]"); + Log.i(TAG, "setTiles() called with tiles = [" + tilesCollection + "]"); } - - int currentViewPagerPage = mViewPager.getCurrentItem(); - if (mLastDragRecord != null && mRecords.indexOf(mLastDragRecord) == -1) { // the last removed record might be stored in mLastDragRecord if we just shifted // re-add it to the list so we'll clean it up below @@ -516,25 +516,41 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On mLastDragRecord = null; } - Map<QSTile<?>, DragTileRecord> recordMap = new ArrayMap<>(); + // step kinda-1 + if (mDraggingRecord != null) { + // dragging record might be animating back, force it to finished position + mDraggingRecord.tileView.animate().cancel(); + } + + int currentViewPagerPage = mViewPager.getCurrentItem(); + int removedPages = 0; + + Map<QSTile<?>, DragTileRecord> cachedRecords = new ArrayMap<>(); ListIterator<TileRecord> iterator = mRecords.listIterator(mRecords.size()); int recordsRemoved = 0; // cleanup current records - while (iterator.hasPrevious()) { + while (iterator.hasPrevious()) { // mRecords DragTileRecord dr = (DragTileRecord) iterator.previous(); - if (dr.page >= 0) { - // clean up view - mPages.get(dr.page).removeView(dr.tileView); - } + // step 1 + dr.tileView.animate().cancel(); - if (tiles.contains(dr.tile)) { + // step 2 + if (tilesCollection.contains(dr.tile)) { if (DEBUG_TILES) { Log.i(TAG, "caching tile: " + dr.tile); } - recordMap.put(dr.tile, dr); + cachedRecords.put(dr.tile, dr); } else { + if (dr.page >= 0) { + if (DEBUG_TILES) { + Log.w(TAG, "removed dr.tileView: " + dr.tileView + " from page: " + + dr.page + " (dest page: " + dr.destinationPage + ")"); + } + + removeTileView(dr.tileView); + } if (DEBUG_TILES) { Log.i(TAG, "removing tile: " + dr.tile); } @@ -543,74 +559,99 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On iterator.remove(); recordsRemoved++; - if (dr.page >= getCurrentMaxPageCount() - 1) { - final int childCount = mPages.get(dr.page).getChildCount(); - - if (childCount == 0) { - final int currentIndex = mViewPager.getCurrentItem(); - if (currentIndex > 0 && currentViewPagerPage == currentIndex) { - // if we are about to remove the page we are currently on, move back - currentViewPagerPage--; - } - final int pageIndex = dr.page + (mEditing ? 1 : 0); - mPagerAdapter.startUpdate(mViewPager); - mPagerAdapter.destroyItem(mViewPager, pageIndex, mPages.get(dr.page)); - mPagerAdapter.finishUpdate(mViewPager); - mPagerAdapter.notifyDataSetChanged(); - } - } + dr.page = -1; + dr.destinationPage = -1; } - dr.page = -1; - dr.destinationPage = -1; } - // at this point recordMap should have all retained tiles, no new or old tiles - int delta = tiles.size() - recordMap.size() - recordsRemoved; + // at this point cachedRecords should have all retained tiles, no new or old tiles + int delta = tilesCollection.size() - cachedRecords.size() - recordsRemoved; if (DEBUG_TILES) { Log.i(TAG, "record map delta: " + delta); } - mRecords.ensureCapacity(tiles.size()); - mPagerAdapter.notifyDataSetChanged(); + // step 3 + final Iterator<QSPage> pageIterator = mPages.iterator(); + while (pageIterator.hasNext()) { + final QSPage page = pageIterator.next(); + final int viewpagerIndex = page.getPageIndex() + (mEditing ? 1 : 0); + final int childCount = page.getChildCount(); - // even though we explicitly destroy old pages, without this call, - // the viewpager doesn't seem to want to pick up the fact that we have less pages - // and allows "empty" scrolls to the right where there is no page. - mViewPager.setAdapter(mPagerAdapter); + if (DEBUG_TILES) { + Log.d(TAG, "page " + viewpagerIndex + " has " + childCount); + } + if (page.getPageIndex() >= getCurrentMaxPageCount() - 1) { + if (DEBUG_TILES) { + Log.d(TAG, "page : " + page + " has " + childCount + " children"); + } + if (childCount == 0) { + removedPages++; + + page.removeAllViews(); + mPagerAdapter.startUpdate(mViewPager); + mPagerAdapter.destroyItem(mViewPager, viewpagerIndex, page); + mPagerAdapter.finishUpdate(mViewPager); + mPagerAdapter.notifyDataSetChanged(); + } + } + } + + if (removedPages > 0) { + // even though we explicitly destroy old pages, without this call, + // the viewpager doesn't seem to want to pick up the fact that we have less pages + // and allows "empty" scrolls to the right where there is no page. + if (DEBUG_TILES) { + Log.d(TAG, "re-setting adapter, page: " + currentViewPagerPage); + } + mViewPager.setAdapter(mPagerAdapter); + mViewPager.setCurrentItem(Math.min(currentViewPagerPage, mPagerAdapter.getCount()), + false); + mPagerAdapter.notifyDataSetChanged(); + } + + // step 4 + mRecords.ensureCapacity(tilesCollection.size()); + int runningCount = 0; - // add new tiles - for (int i = 0; i < tiles.size(); i++) { - QSTile<?> tile = tiles.get(i); - final int tileDestPage = getPagesForCount(i + 1) - 1; + final Iterator<QSTile<?>> newTileIterator = tilesCollection.iterator(); + while (newTileIterator.hasNext()) { + QSTile<?> tile = newTileIterator.next(); + if (tile instanceof CustomQSTile) { + if (((CustomQSTile) tile).isUserRemoved() + || ((CustomQSTile) tile).getTile() == null) { + // tile not published yet + continue; + } + } + final int tileDestPage = getPagesForCount(runningCount + 1) - 1; if (DEBUG_TILES) { - Log.d(TAG, "tile at : " + i + ": " + tile + " to dest page: " + tileDestPage); + Log.d(TAG, "tile at : " + runningCount + ": " + tile + + " to dest page: " + tileDestPage); } DragTileRecord record; - if (!recordMap.containsKey(tile)) { + if (!cachedRecords.containsKey(tile)) { if (DEBUG_TILES) { - Log.d(TAG, "tile at: " + i + " not cached, adding it to records"); + Log.d(TAG, "tile at: " + runningCount + " not cached, adding it to records"); } record = makeRecord(tile); record.destinationPage = tileDestPage; - recordMap.put(tile, record); - mRecords.add(i, record); + mRecords.add(runningCount, record); mPagerAdapter.notifyDataSetChanged(); } else { - record = recordMap.get(tile); + record = cachedRecords.get(tile); if (DEBUG_TILES) { - Log.d(TAG, "tile at : " + i + ": cached, restoring: " + record); + Log.d(TAG, "tile at : " + runningCount + ": cached, restoring: " + record); } - int indexOf = mRecords.indexOf(record); - if (indexOf != i) { - if (DEBUG_TILES) { - Log.w(TAG, "moving index of " + record + " from " - + indexOf + " to " + i); - } - Collections.swap(mRecords, indexOf, i); - } + mPages.get(record.page).removeView(record.tileView); + + record.page = -1; record.destinationPage = tileDestPage; + + mRecords.remove(record); + mRecords.add(runningCount, record); + mPagerAdapter.notifyDataSetChanged(); } if (record.page == -1) { // add the view @@ -620,11 +661,9 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On Log.d(TAG, "added view " + record); } } + runningCount++; } - // restore the visible page - mViewPager.setCurrentItem(currentViewPagerPage, false); - if (isShowingDetail()) { mDetail.bringToFront(); } @@ -718,10 +757,18 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On return r; } + private void removeTileView(QSTileView v) { + for (QSPage page : mPages) { + page.removeView(v); + page.removeTransientView(v); + } + + } + private void removeDraggingRecord() { // what spec is this tile? String spec = mHost.getSpec(mDraggingRecord.tile); - if (DEBUG_DRAG) { + if (DEBUG_TILES) { Log.w(TAG, "removing tile: " + mDraggingRecord + " with spec: " + spec); } onStopDrag(); @@ -736,13 +783,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } @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); @@ -756,37 +796,18 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On if (mFooter.hasFooter()) { h += mFooter.getView().getMeasuredHeight(); } + mGridHeight = h; + mDetail.measure(exactly(width), MeasureSpec.UNSPECIFIED); + if (mDetail.getMeasuredHeight() < h) { mDetail.measure(exactly(width), exactly(h)); } - - // 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, h); - mGridHeight = h; + if (isShowingDetail() && !isClosingDetail() && mExpanded) { + h = mDetail.getMeasuredHeight(); } + setMeasuredDimension(width, h); for (TileRecord record : mRecords) { setupRecord(record); } @@ -816,11 +837,13 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } r.tile.setDetailListening(show); int x = (int) ((DragTileRecord) r).destination.x + r.tileView.getWidth() / 2; - int y = mViewPager.getTop() + (int) ((DragTileRecord) r).destination.y + r.tileView.getHeight() / 2; + int y = mViewPager.getTop() + + (int) ((DragTileRecord) r).destination.y + r.tileView.getHeight() / 2; handleShowDetailImpl(r, show, x, y); } else { super.handleShowDetailTile(r, show); } + mPageIndicator.setVisibility(!show ? View.VISIBLE : View.INVISIBLE); } @Override @@ -833,9 +856,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On // view pager laid out from top of brightness view to bottom to page through settings mViewPager.layout(0, 0, w, viewPagerBottom); - // layout page indicator inside viewpager inset - mPageIndicator.layout(0, b - mPageIndicatorHeight, w, b); - mDetail.layout(0, 0, w, mDetail.getMeasuredHeight()); if (mFooter.hasFooter()) { @@ -846,7 +866,10 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On if (!isShowingDetail() && !isClosingDetail()) { mQsPanelTop.bringToFront(); + } + // layout page indicator inside viewpager inset + mPageIndicator.layout(0, b - mPageIndicatorHeight, w, b); } protected int getRowTop(int row) { @@ -1014,7 +1037,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } if (originatingTileEvent && !event.getResult()) { // view pager probably ate the event - restoreDraggingTilePosition(v); + restoreDraggingTilePosition(v, null); } break; @@ -1032,15 +1055,38 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On Log.d(TAG, "dropping on delete target!!"); } if (mDraggingRecord.tile instanceof EditTile) { + final QSTileView editTileView = mDraggingRecord.tileView; + mQsPanelTop.toast(R.string.quick_settings_cannot_delete_edit_tile); - restoreDraggingTilePosition(v); + restoreDraggingTilePosition(v, new Runnable() { + @Override + public void run() { + // move edit tile to the back + final TileRecord editTile = getRecord(editTileView); + if (mRecords.remove(editTile)) { + // we depend on mHost.setTiles() placing it on the end + persistRecords(); + } + } + }); break; + } else if (mDraggingRecord.tile instanceof CustomQSTile) { + ((CustomQSTile) mDraggingRecord.tile).setUserRemoved(true); + final String spec = mHost.getSpec(mDraggingRecord.tile); + restoreDraggingTilePosition(v, new Runnable() { + @Override + public void run() { + // it might get added back later by the app, but that's ok, + // we just want to reset its position after it has been removed. + mHost.remove(spec); + } + }); } else { mRestored = true; removeDraggingRecord(); } } else { - restoreDraggingTilePosition(v); + restoreDraggingTilePosition(v, null); } break; @@ -1171,7 +1217,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On return false; } - private void restoreDraggingTilePosition(View v) { + private void restoreDraggingTilePosition(View v, final Runnable onAnimationFinishedRunnable) { if (mRestored) { return; } @@ -1251,6 +1297,20 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } @Override + public void onAnimationCancel(Animator animation) { + mViewPager.requestDisallowInterceptTouchEvent(false); + removeTransientView(mDraggingRecord.tileView); + mCurrentlyAnimating.remove(mDraggingRecord); + mRestoring = false; + mPagerAdapter.notifyDataSetChanged(); + onStopDrag(); + + if (onAnimationFinishedRunnable != null) { + postOnAnimation(onAnimationFinishedRunnable); + } + } + + @Override public void onAnimationEnd(Animator animation) { mViewPager.requestDisallowInterceptTouchEvent(false); @@ -1265,8 +1325,8 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On Log.i(TAG, "drag record was attached"); } } - mDraggingRecord.page = mDraggingRecord.destinationPage; targetP.addView(mDraggingRecord.tileView); + mDraggingRecord.page = mDraggingRecord.destinationPage; mDraggingRecord.tileView.setX(mDraggingRecord.destination.x); // reset this to be in the coords of the page, not viewpager anymore @@ -1281,6 +1341,12 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On mPagerAdapter.notifyDataSetChanged(); } onStopDrag(); + + if (onAnimationFinishedRunnable != null) { + postOnAnimation(onAnimationFinishedRunnable); + } else { + requestLayout(); + } } }); } @@ -1497,10 +1563,16 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On .y(ti.destination.y) .setListener(new AnimatorListenerAdapter() { @Override + public void onAnimationCancel(Animator animation) { + tilePageSource.removeTransientView(ti.tileView); + mCurrentlyAnimating.remove(ti); + } + + @Override public void onAnimationEnd(Animator animation) { tilePageSource.removeTransientView(ti.tileView); - ti.page = tilePageTarget.getPageIndex(); tilePageTarget.addView(ti.tileView); + ti.page = tilePageTarget.getPageIndex(); ti.tileView.setX(ti.destination.x); ti.tileView.setY(ti.destination.y); @@ -1516,6 +1588,11 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On .y(ti.destination.y) .setListener(new AnimatorListenerAdapter() { @Override + public void onAnimationCancel(Animator animation) { + mCurrentlyAnimating.remove(ti); + } + + @Override public void onAnimationEnd(Animator animation) { mCurrentlyAnimating.remove(ti); final boolean dual = getPage(ti.destinationPage).dualRecord(ti); @@ -1551,10 +1628,16 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On .y(last.destination.y) .setListener(new AnimatorListenerAdapter() { @Override + public void onAnimationCancel(Animator animation) { + tilePageSource.removeTransientView(last.tileView); + mCurrentlyAnimating.remove(last); + } + + @Override public void onAnimationEnd(Animator animation) { tilePageSource.removeTransientView(last.tileView); - last.page = tilePageTarget.getPageIndex(); tilePageTarget.addView(last.tileView); + last.page = tilePageTarget.getPageIndex(); last.tileView.setX(last.destination.x); last.tileView.setY(last.destination.y); @@ -1573,6 +1656,11 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On .y(last.destination.y) .setListener(new AnimatorListenerAdapter() { @Override + public void onAnimationCancel(Animator animation) { + mCurrentlyAnimating.remove(last); + } + + @Override public void onAnimationEnd(Animator animation) { if (DEBUG_DRAG) { Log.i(TAG, "shift finished: " + last); @@ -1654,10 +1742,16 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } @Override + public void onAnimationCancel(Animator animation) { + page.removeTransientView(ti.tileView); + mCurrentlyAnimating.remove(ti); + } + + @Override public void onAnimationEnd(Animator animation) { page.removeTransientView(ti.tileView); - ti.page = page.getPageIndex(); page.addView(ti.tileView); + ti.page = page.getPageIndex(); mCurrentlyAnimating.remove(ti); requestLayout(); @@ -1710,17 +1804,27 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } }); } + mPanelView.setDetailRequestedScrollLock(mExpanded && show + && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE); } @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); FontSizeUtils.updateFontSize(mDetailRemoveButton, R.dimen.qs_detail_button_text_size); + mPanelView.setDetailRequestedScrollLock(mExpanded && isShowingDetail() + && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE); } @Override public void setExpanded(boolean expanded) { super.setExpanded(expanded); + // reset the page when inactive for a while + if (expanded) { + removeCallbacks(mResetPage); + } else { + postDelayed(mResetPage, PAGE_RESET_DELAY); + } if (!expanded) { if (mEditing) { mHost.setEditing(false); @@ -1747,10 +1851,11 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On for (TileRecord r : mRecords) { r.tile.clearState(); } + updateDetailText(); + mQsPanelTop.updateResources(); if (mListening) { refreshAllTiles(); } - updateDetailText(); } } @@ -1764,6 +1869,10 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } } + public void setPanelView(NotificationPanelView panelView) { + this.mPanelView = panelView; + } + public static class TilesListAdapter extends BaseExpandableListAdapter implements QSTile.DetailAdapter { @@ -1790,7 +1899,8 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On final Iterator<String> i = tiles.iterator(); while (i.hasNext()) { final String spec = i.next(); - if (QSUtils.isStaticQsTile(spec) || QSUtils.isDynamicQsTile(spec)) { + if (QSUtils.isStaticQsTile(spec) + || QSUtils.isDynamicQsTile(extractTileTagFromSpec(spec))) { List<String> packageList = mPackageTileMap.get(PACKAGE_ANDROID); packageList.add(spec); } else { @@ -1803,13 +1913,122 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } } + final Map<String, ?> stringMap = CustomQSTile.getCustomQSTilePrefs(mContext).getAll(); + for (Map.Entry<String, ?> entry : stringMap.entrySet()) { + if (entry.getValue() instanceof Boolean) { + if ((Boolean)entry.getValue()) { + final String key = entry.getKey(); + if (QSUtils.isDynamicQsTile(extractTileTagFromSpec(key))) { + mPackageTileMap.get(PACKAGE_ANDROID).add(key); + } else { + final String customTilePackage = getCustomTilePackage(key); + List<String> packageList = mPackageTileMap.get(customTilePackage); + if (packageList == null) { + mPackageTileMap.put(customTilePackage, + packageList = new ArrayList<>()); + } + packageList.add(key); + + } + } + } + }; + final List<String> systemTiles = mPackageTileMap.get(PACKAGE_ANDROID); Collections.sort(systemTiles); } private String getCustomTilePackage(String spec) { - StatusBarPanelCustomTile sbc = mHost.getCustomTileData().get(spec).sbc; - return sbc.getPackage(); + if (mHost.getCustomTileData().get(spec) != null) { + StatusBarPanelCustomTile sbc = mHost.getCustomTileData().get(spec).sbc; + return sbc.getPackage(); + } else { + return extractPackageFromCustomTileSpec(spec); + } + } + + private static String extractPackageFromCustomTileSpec(String spec) { + if (spec != null && !spec.isEmpty()) { + final String[] split = spec.split("\\|"); + if (split != null && split.length > 2) { + return split[1]; + } + return spec; + } + return null; + } + + private static String extractTileTagFromSpec(String spec) { + if (spec != null && !spec.isEmpty()) { + final String[] split = spec.split("\\|"); + if (split != null && split.length == 5) { + /** for {@link cyanogenmod.app.StatusBarPanelCustomTile#key() **/ + return split[3]; + } else if (split != null && split.length == 3) { + /** for {@link cyanogenmod.app.StatusBarPanelCustomTile#persistableKey()} **/ + return split[2]; + } + return spec; + } + return null; + } + + private Drawable getQSTileIcon(String spec) { + if (QSUtils.isDynamicQsTile(extractTileTagFromSpec(spec))) { + return QSTile.ResourceIcon.get(QSUtils.getDynamicQSTileResIconId(mContext, + UserHandle.myUserId(), extractTileTagFromSpec(spec))).getDrawable(mContext); + } else if (QSUtils.isStaticQsTile(spec)) { + final int res = QSTileHost.getIconResource(spec); + if (res != 0) { + return QSTile.ResourceIcon.get(res).getDrawable(mContext); + } else { + return mContext.getPackageManager().getDefaultActivityIcon(); + } + } 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); + } + } + return getPackageDrawable(getCustomTilePackage(spec)); + } + } + + 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; + } + + private String getQSTileLabel(String spec) { + if (QSUtils.isStaticQsTile(spec)) { + int resource = QSTileHost.getLabelResource(spec); + if (resource != 0) { + return mContext.getText(resource).toString(); + } else { + return spec; + } + } else if (QSUtils.isDynamicQsTile(extractTileTagFromSpec(spec))) { + return QSUtils.getDynamicQSTileLabel(mContext, + UserHandle.myUserId(), extractTileTagFromSpec(spec)); + } else { + return getPackageLabel(getCustomTilePackage(spec)); + } } @Override @@ -1872,6 +2091,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On // special icon systemOrAppIcon.setImageResource(R.drawable.ic_qs_tile_category_system); } else { + group = getPackageLabel(group); systemOrAppIcon.setImageResource(R.drawable.ic_qs_tile_category_other); } title.setText(group); @@ -1910,57 +2130,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On return child; } - private String getQSTileLabel(String spec) { - 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); - } - } - return getPackageDrawable(getCustomTilePackage(spec)); - } - } - - 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; @@ -2001,7 +2170,21 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { String spec = getChild(groupPosition, childPosition); - mPanel.add(spec); + + final QSTile<?> tile = mHost.getTile(spec); + if (tile != null && tile instanceof CustomQSTile) { + // already present + ((CustomQSTile) tile).setUserRemoved(false); + mPanel.refreshAllTiles(); + } else { + // reset its state just in case it's not published + CustomQSTile.getCustomQSTilePrefs(mContext) + .edit() + .remove(spec) + .apply(); + mPanel.add(spec); + // TODO notify user the app isn't publishing the tile, but it now can be! + } mPanel.closeDetail(); return true; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 60dc787..77ede93 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -72,7 +72,7 @@ public class QSPanel extends ViewGroup { protected int mDualTileUnderlap; protected int mBrightnessPaddingTop; protected int mGridHeight; - private boolean mExpanded; + protected boolean mExpanded; protected boolean mListening; private boolean mClosingDetail; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelTopView.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelTopView.java index 5f57be1..b00483c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelTopView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelTopView.java @@ -53,6 +53,7 @@ public class QSPanelTopView extends FrameLayout { protected View mBrightnessView; protected TextView mToastView; protected View mAddTarget; + protected TextView mEditInstructionText; private boolean mEditing = false; private boolean mDisplayingInstructions = false; @@ -112,6 +113,14 @@ public class QSPanelTopView extends FrameLayout { mBrightnessView = findViewById(R.id.brightness_container); mToastView = (TextView) findViewById(R.id.qs_toast); mAddTarget = findViewById(R.id.add_target); + mEditInstructionText = (TextView) findViewById(R.id.edit_text_instruction); + updateResources(); + } + + public void updateResources() { + if (mEditInstructionText != null) { + mEditInstructionText.setText(R.string.qs_tile_edit_header_instruction); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index 2d62724..01a170f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -34,6 +34,7 @@ import android.view.ViewGroup; import android.widget.RemoteViews; import com.android.systemui.qs.QSTile.State; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.FlashlightController; @@ -344,6 +345,7 @@ public abstract class QSTile<TState extends State> implements Listenable { CastController getCastController(); FlashlightController getFlashlightController(); KeyguardMonitor getKeyguardMonitor(); + BatteryController getBatteryController(); boolean isEditing(); void setEditing(boolean editing); void resetTiles(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AdbOverNetworkTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AdbOverNetworkTile.java index 283d617..b970a4c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AdbOverNetworkTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AdbOverNetworkTile.java @@ -91,7 +91,7 @@ public class AdbOverNetworkTile extends QSTile<QSTile.BooleanState> { } private boolean isAdbEnabled() { - return Settings.Secure.getInt(mContext.getContentResolver(), + return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.ADB_ENABLED, 0) > 0; } @@ -120,7 +120,7 @@ public class AdbOverNetworkTile extends QSTile<QSTile.BooleanState> { CMSettings.Secure.getUriFor(CMSettings.Secure.ADB_PORT), false, mObserver); mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Global.ADB_ENABLED), + Settings.Global.getUriFor(Settings.Global.ADB_ENABLED), false, mObserver); } else { mContext.getContentResolver().unregisterContentObserver(mObserver); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java index 37d8d73..1a60fa9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java @@ -25,6 +25,8 @@ import android.provider.Settings; import com.android.systemui.qs.QSTile; import com.android.systemui.R; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.BatteryStateRegistar; import cyanogenmod.power.PerformanceManager; @@ -36,13 +38,15 @@ public class BatterySaverTile extends QSTile<QSTile.BooleanState> { private static final Intent BATTERY_SETTINGS = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY); private final PowerManager mPm; - private final PerformanceManager mPerformanceManager; + private final boolean mHasPowerProfiles; + private boolean mListening; + private boolean mPluggedIn; public BatterySaverTile(Host host) { super(host); mPm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mPerformanceManager = PerformanceManager.getInstance(mContext); + mHasPowerProfiles = PerformanceManager.getInstance(mContext).getNumberOfProfiles() > 0; } @Override @@ -53,7 +57,7 @@ public class BatterySaverTile extends QSTile<QSTile.BooleanState> { @Override public void handleClick() { mPm.setPowerSaveMode(!mState.value); - refreshState(); + refreshState(!mState.value); } @Override @@ -63,8 +67,8 @@ public class BatterySaverTile extends QSTile<QSTile.BooleanState> { @Override protected void handleUpdateState(BooleanState state, Object arg) { - state.value = mPm.isPowerSaveMode(); - state.visible = mPerformanceManager.getNumberOfProfiles() == 0; + state.value = arg instanceof Boolean ? (boolean) arg : mPm.isPowerSaveMode(); + state.visible = !mHasPowerProfiles; state.label = mContext.getString(R.string.quick_settings_battery_saver_label); if (state.value) { state.icon = ResourceIcon.get(R.drawable.ic_qs_battery_saver_on); @@ -75,6 +79,11 @@ public class BatterySaverTile extends QSTile<QSTile.BooleanState> { state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_battery_saver_off); } + + state.enabled = !mPluggedIn; + if (mPluggedIn) { + state.label = mContext.getString(R.string.quick_settings_battery_saver_label_charging); + } } @Override @@ -93,11 +102,24 @@ public class BatterySaverTile extends QSTile<QSTile.BooleanState> { return CMMetricsLogger.TILE_BATTERY_SAVER; } - private ContentObserver mObserver = new ContentObserver(mHandler) { + private BatteryStateRegistar.BatteryStateChangeCallback mBatteryState + = new BatteryStateRegistar.BatteryStateChangeCallback() { + @Override + public void onBatteryLevelChanged(boolean present, int level, boolean pluggedIn, + boolean charging) { + mPluggedIn = pluggedIn || charging; + refreshState(); + } + @Override - public void onChange(boolean selfChange, Uri uri) { + public void onPowerSaveChanged() { refreshState(); } + + @Override + public void onBatteryStyleChanged(int style, int percentMode) { + // ignore + } }; @Override @@ -106,11 +128,9 @@ public class BatterySaverTile extends QSTile<QSTile.BooleanState> { mListening = listening; if (listening) { - mContext.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), - false, mObserver); + getHost().getBatteryController().addStateChangedCallback(mBatteryState); } else { - mContext.getContentResolver().unregisterContentObserver(mObserver); + getHost().getBatteryController().removeStateChangedCallback(mBatteryState); } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index a798e4e..cdedc26 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -225,6 +225,7 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState> { listView.setOnItemClickListener(this); listView.setAdapter(mAdapter = new QSDetailItemsList.QSDetailListAdapter(context, mBluetoothItems)); + mAdapter.setCallback(this); mItemsList.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty, R.string.quick_settings_bluetooth_detail_empty_text); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index 04cc5dc..f49d97e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -39,7 +39,6 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { private final AnimationIcon mDisable = new AnimationIcon(R.drawable.ic_invert_colors_disable_animation); private final SecureSetting mSetting; - private final UsageTracker mUsageTracker; private boolean mListening; @@ -50,29 +49,11 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) { @Override protected void handleValueChanged(int value, boolean observedChange) { - if (value != 0 || observedChange) { - mUsageTracker.trackUsage(); - } if (mListening) { handleRefreshState(value); } } }; - mUsageTracker = new UsageTracker(host.getContext(), - Prefs.Key.COLOR_INVERSION_TILE_LAST_USED, ColorInversionTile.class, - R.integer.days_to_show_color_inversion_tile); - if (mSetting.getValue() != 0 && !mUsageTracker.isRecentlyUsed()) { - mUsageTracker.trackUsage(); - } - mUsageTracker.setListening(true); - mSetting.setListening(true); - } - - @Override - protected void handleDestroy() { - super.handleDestroy(); - mUsageTracker.setListening(false); - mSetting.setListening(false); } @Override @@ -82,7 +63,11 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { @Override public void setListening(boolean listening) { + if (mListening == listening) { + return; + } mListening = listening; + mSetting.setListening(mListening); } @Override @@ -101,25 +86,14 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { @Override protected void handleLongClick() { - if (mState.value) { - mHost.startActivityDismissingKeyguard(ACCESSIBILITY_SETTINGS); - } else { - final String title = mContext.getString( - R.string.quick_settings_reset_confirmation_title, mState.label); - mUsageTracker.showResetConfirmation(title, new Runnable() { - @Override - public void run() { - refreshState(); - } - }); - } + mHost.startActivityDismissingKeyguard(ACCESSIBILITY_SETTINGS); } @Override protected void handleUpdateState(BooleanState state, Object arg) { final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue(); final boolean enabled = value != 0; - state.visible = enabled || mUsageTracker.isRecentlyUsed(); + state.visible = true; state.value = enabled; state.label = mContext.getString(R.string.quick_settings_inversion_label); state.icon = enabled ? mEnable : mDisable; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java index f19e466..85790d1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java @@ -32,6 +32,7 @@ public class CompassTile extends QSTile<QSTile.BooleanState> implements SensorEv private final static float ALPHA = 0.97f; private boolean mActive = false; + private boolean mListening = false; private SensorManager mSensorManager; private Sensor mAccelerationSensor; @@ -95,7 +96,7 @@ public class CompassTile extends QSTile<QSTile.BooleanState> implements SensorEv Float degrees = arg == null ? 0 :(float) arg; state.visible = true; - state.value = mActive; + state.value = mActive && mListening; if (state.value) { state.icon = ResourceIcon.get(R.drawable.ic_qs_compass_on); @@ -126,10 +127,10 @@ public class CompassTile extends QSTile<QSTile.BooleanState> implements SensorEv @Override public void setListening(boolean listening) { - if (!listening) { - setListeningSensors(false); - mActive = false; - } + // setListening might get called multiple times with the same value, we check for it + // in setListeningSensors + mListening = listening; + setListeningSensors(mListening && mActive); } private String formatValueWithCardinalDirection(float degree) { 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 d09ca92..40c7184 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java @@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.ThemeConfig; import android.net.Uri; @@ -31,15 +32,12 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.RemoteViews; import android.widget.TextView; -import com.android.internal.logging.MetricsLogger; - import com.android.systemui.qs.QSDetailItemsGrid; import com.android.systemui.qs.QSDetailItemsList; import cyanogenmod.app.CustomTile; @@ -53,6 +51,8 @@ import java.util.Arrays; public class CustomQSTile extends QSTile<QSTile.State> { + private static final String HIDDEN_TILES_PREF_NAME = "user_hidden_qs_tiles"; + private CustomTile.ExpandedStyle mExpandedStyle; private PendingIntent mOnClick; private PendingIntent mOnLongClick; @@ -61,10 +61,51 @@ public class CustomQSTile extends QSTile<QSTile.State> { private StatusBarPanelCustomTile mTile; private CustomQSDetailAdapter mDetailAdapter; private boolean mCollapsePanel; + private boolean mUserRemoved; + private String mPersistedPlaceHolderKey; + + public CustomQSTile(Host host, String persistedSpec) { + super(host); + mTile = null; + mPersistedPlaceHolderKey = persistedSpec; + } public CustomQSTile(Host host, StatusBarPanelCustomTile tile) { super(host); mTile = tile; + mUserRemoved = getIsUserRemovedPersisted(); + } + + private String getPersistableKey() { + if (mPersistedPlaceHolderKey != null) { + return mPersistedPlaceHolderKey; + } else { + return getTile().persistableKey(); + } + } + + private boolean getIsUserRemovedPersisted() { + return getCustomQSTilePrefs(mContext).getBoolean(getPersistableKey(), false); + } + + public boolean isUserRemoved() { + return mUserRemoved; + } + + public void setUserRemoved(boolean removed) { + if (mUserRemoved != removed) { + if (removed) { + getCustomQSTilePrefs(mContext).edit().putBoolean(getPersistableKey(), true).apply(); + } else { + getCustomQSTilePrefs(mContext).edit().remove(getPersistableKey()).apply(); + } + mUserRemoved = removed; + refreshState(); + } + } + + public static SharedPreferences getCustomQSTilePrefs(Context context) { + return context.getSharedPreferences(HIDDEN_TILES_PREF_NAME, Context.MODE_PRIVATE); } @Override @@ -138,11 +179,18 @@ public class CustomQSTile extends QSTile<QSTile.State> { protected void handleUpdateState(State state, Object arg) { if (arg instanceof StatusBarPanelCustomTile) { mTile = (StatusBarPanelCustomTile) arg; + mPersistedPlaceHolderKey = null; + mUserRemoved = getIsUserRemovedPersisted(); + } + if (mTile == null) { + state.visible = false; + // nothing to show, it's a place holder for now + return; } final CustomTile customTile = mTile.getCustomTile(); state.contentDescription = customTile.contentDescription; state.label = customTile.label; - state.visible = true; + state.visible = !mUserRemoved; final int iconId = customTile.icon; if (iconId != 0 && (customTile.remoteIcon == null)) { final String iconPackage = mTile.getResPkg(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index 25a7fb7..6080358 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -48,7 +48,6 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { new AnimationIcon(R.drawable.ic_hotspot_disable_animation); private final HotspotController mController; private final Callback mCallback = new Callback(); - private final UsageTracker mUsageTracker; private final ConnectivityManager mConnectivityManager; private boolean mListening; private int mNumConnectedClients = 0; @@ -56,18 +55,10 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { public HotspotTile(Host host) { super(host); mController = host.getHotspotController(); - mUsageTracker = newUsageTracker(host.getContext()); - mUsageTracker.setListening(true); mConnectivityManager = host.getContext().getSystemService(ConnectivityManager.class); } @Override - protected void handleDestroy() { - super.handleDestroy(); - mUsageTracker.setListening(false); - } - - @Override protected BooleanState newTileState() { return new BooleanState(); } @@ -108,23 +99,12 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { @Override protected void handleLongClick() { - if (mState.value) { - mHost.startActivityDismissingKeyguard(TETHER_SETTINGS); - } else { - final String title = mContext.getString( - R.string.quick_settings_reset_confirmation_title, mState.label); - mUsageTracker.showResetConfirmation(title, new Runnable() { - @Override - public void run() { - refreshState(); - } - }); - } + mHost.startActivityDismissingKeyguard(TETHER_SETTINGS); } @Override protected void handleUpdateState(BooleanState state, Object arg) { - state.visible = mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed(); + state.visible = mController.isHotspotSupported(); if (arg instanceof Boolean) { state.value = (boolean) arg; @@ -155,11 +135,6 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { } } - private static UsageTracker newUsageTracker(Context context) { - return new UsageTracker(context, Prefs.Key.HOTSPOT_TILE_LAST_USED, HotspotTile.class, - R.integer.days_to_show_hotspot_tile); - } - private BroadcastReceiver mTetherConnectStateChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -175,20 +150,4 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { refreshState(enabled); } }; - - /** - * This will catch broadcasts for changes in hotspot state so we can show - * the hotspot tile for a number of days after use. - */ - public static class APChangedReceiver extends BroadcastReceiver { - private UsageTracker mUsageTracker; - - @Override - public void onReceive(Context context, Intent intent) { - if (mUsageTracker == null) { - mUsageTracker = newUsageTracker(context); - } - mUsageTracker.trackUsage(); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LiveDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LiveDisplayTile.java deleted file mode 100644 index f59c876..0000000 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LiveDisplayTile.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * 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. - */ - -package com.android.systemui.qs.tiles; - -import android.content.Intent; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.database.ContentObserver; -import android.os.Handler; -import android.os.UserHandle; - -import com.android.internal.util.ArrayUtils; -import com.android.systemui.R; -import com.android.systemui.qs.QSTile; - -import cyanogenmod.hardware.CMHardwareManager; -import cyanogenmod.providers.CMSettings; -import org.cyanogenmod.internal.logging.CMMetricsLogger; - -/** Quick settings tile: LiveDisplay mode switcher **/ -public class LiveDisplayTile extends QSTile<LiveDisplayTile.LiveDisplayState> { - - private static final Intent LIVEDISPLAY_SETTINGS = - new Intent("android.settings.LIVEDISPLAY_SETTINGS"); - - private final LiveDisplayObserver mObserver; - private String[] mEntries; - private String[] mDescriptionEntries; - private String[] mAnnouncementEntries; - private String[] mValues; - private final int[] mEntryIconRes; - - private boolean mListening; - - private static final int MODE_OUTDOOR = 3; - private static final int MODE_DAY = 4; - - private static final int OFF_TEMPERATURE = 6500; - - private int mDayTemperature; - - private final boolean mOutdoorModeAvailable; - private final int mDefaultDayTemperature; - - public LiveDisplayTile(Host host) { - super(host); - - Resources res = mContext.getResources(); - TypedArray typedArray = res.obtainTypedArray(R.array.live_display_drawables); - mEntryIconRes = new int[typedArray.length()]; - for (int i = 0; i < mEntryIconRes.length; i++) { - mEntryIconRes[i] = typedArray.getResourceId(i, 0); - } - typedArray.recycle(); - - updateEntries(); - - mOutdoorModeAvailable = - CMHardwareManager.getInstance(mContext) - .isSupported(CMHardwareManager.FEATURE_SUNLIGHT_ENHANCEMENT); - - mDefaultDayTemperature = mContext.getResources().getInteger( - com.android.internal.R.integer.config_dayColorTemperature); - loadDayTemperature(); - - mObserver = new LiveDisplayObserver(mHandler); - mObserver.startObserving(); - } - - private void updateEntries() { - Resources res = mContext.getResources(); - mEntries = res.getStringArray(com.android.internal.R.array.live_display_entries); - mDescriptionEntries = res.getStringArray(R.array.live_display_description); - mAnnouncementEntries = res.getStringArray(R.array.live_display_announcement); - mValues = res.getStringArray(com.android.internal.R.array.live_display_values); - } - - @Override - protected LiveDisplayState newTileState() { - return new LiveDisplayState(); - } - - @Override - public void setListening(boolean listening) { - if (mListening == listening) - return; - mListening = listening; - if (listening) { - mObserver.startObserving(); - } else { - mObserver.endObserving(); - } - } - - @Override - protected void handleClick() { - changeToNextMode(); - } - - @Override - protected void handleLongClick() { - mHost.startActivityDismissingKeyguard(LIVEDISPLAY_SETTINGS); - } - - @Override - protected void handleUpdateState(LiveDisplayState state, Object arg) { - updateEntries(); - state.visible = true; - state.mode = arg == null ? getCurrentModeIndex() : (Integer) arg; - state.label = mEntries[state.mode]; - state.icon = ResourceIcon.get(mEntryIconRes[state.mode]); - state.contentDescription = mDescriptionEntries[state.mode]; - } - - @Override - public int getMetricsCategory() { - return CMMetricsLogger.TILE_LIVE_DISPLAY; - } - - @Override - protected String composeChangeAnnouncement() { - return mAnnouncementEntries[getCurrentModeIndex()]; - } - - private int getCurrentModeIndex() { - return ArrayUtils.indexOf(mValues, - String.valueOf(CMSettings.System.getIntForUser(mContext.getContentResolver(), - CMSettings.System.DISPLAY_TEMPERATURE_MODE, - 0, UserHandle.USER_CURRENT))); - } - - private void changeToNextMode() { - int next = getCurrentModeIndex() + 1; - - if (next >= mValues.length) { - next = 0; - } - - while (true) { - // Skip outdoor mode if it's unsupported, and skip the day setting - // if it's the same as the off setting - if ((!mOutdoorModeAvailable && - Integer.valueOf(mValues[next]) == MODE_OUTDOOR) || - (mDayTemperature == OFF_TEMPERATURE && - Integer.valueOf(mValues[next]) == MODE_DAY)) { - next++; - if (next >= mValues.length) { - next = 0; - } - } else { - break; - } - } - - CMSettings.System.putIntForUser(mContext.getContentResolver(), - CMSettings.System.DISPLAY_TEMPERATURE_MODE, - Integer.valueOf(mValues[next]), UserHandle.USER_CURRENT); - } - - private void loadDayTemperature() { - mDayTemperature = CMSettings.System.getIntForUser(mContext.getContentResolver(), - CMSettings.System.DISPLAY_TEMPERATURE_DAY, - mDefaultDayTemperature, - UserHandle.USER_CURRENT); - } - - private class LiveDisplayObserver extends ContentObserver { - public LiveDisplayObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange) { - loadDayTemperature(); - refreshState(getCurrentModeIndex()); - } - - public void startObserving() { - mContext.getContentResolver().registerContentObserver( - CMSettings.System.getUriFor(CMSettings.System.DISPLAY_TEMPERATURE_MODE), - false, this, UserHandle.USER_ALL); - mContext.getContentResolver().registerContentObserver( - CMSettings.System.getUriFor(CMSettings.System.DISPLAY_TEMPERATURE_DAY), - false, this, UserHandle.USER_ALL); - } - - public void endObserving() { - mContext.getContentResolver().unregisterContentObserver(this); - } - } - - public static class LiveDisplayState extends QSTile.State { - public int mode; - - @Override - public boolean copyTo(State other) { - final LiveDisplayState o = (LiveDisplayState) other; - final boolean changed = mode != o.mode; - return super.copyTo(other) || changed; - } - - @Override - protected StringBuilder toStringBuilder() { - final StringBuilder rt = super.toStringBuilder(); - rt.insert(rt.length() - 1, ",mode=" + mode); - return rt; - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java index 1bfbb8f..a5ffd23 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java @@ -22,12 +22,14 @@ import android.content.Intent; import android.content.IntentFilter; import android.nfc.NfcAdapter; +import android.util.Log; import com.android.systemui.R; import com.android.systemui.qs.QSTile; import org.cyanogenmod.internal.logging.CMMetricsLogger; +import org.cyanogenmod.internal.util.QSUtils; public class NfcTile extends QSTile<QSTile.BooleanState> { - private NfcAdapter mNfcAdapter; + private boolean mListening; private BroadcastReceiver mReceiver = new BroadcastReceiver() { @@ -36,10 +38,11 @@ public class NfcTile extends QSTile<QSTile.BooleanState> { refreshState(); } }; + private final boolean mSupportsNfc; public NfcTile(Host host) { super(host); - mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext); + mSupportsNfc = QSUtils.deviceSupportsNfc(mContext); } @Override @@ -49,7 +52,9 @@ public class NfcTile extends QSTile<QSTile.BooleanState> { @Override protected void handleClick() { - toggleState(); + boolean newState = !getState().value; + setState(newState); + refreshState(); } @Override @@ -57,23 +62,45 @@ public class NfcTile extends QSTile<QSTile.BooleanState> { mHost.startActivityDismissingKeyguard(new Intent("android.settings.NFC_SETTINGS")); } - protected void toggleState() { - int state = getNfcState(); - switch (state) { - case NfcAdapter.STATE_TURNING_ON: - case NfcAdapter.STATE_ON: - mNfcAdapter.disable(); - break; - case NfcAdapter.STATE_TURNING_OFF: - case NfcAdapter.STATE_OFF: - mNfcAdapter.enable(); - break; + private void setState(boolean on) { + try { + NfcAdapter nfcAdapter = NfcAdapter.getNfcAdapter(mContext); + if (nfcAdapter == null) { + Log.e(TAG, "tried to set NFC state, but no NFC adapter was found"); + return; + } + if (on) { + nfcAdapter.enable(); + } else { + nfcAdapter.disable(); + } + } catch (UnsupportedOperationException e) { + // ignore + } + } + + private int getNfcAdapterState() { + try { + NfcAdapter nfcAdapter = NfcAdapter.getNfcAdapter(mContext); + if (nfcAdapter == null) { + Log.e(TAG, "tried to get NFC state, but no NFC adapter was found"); + return NfcAdapter.STATE_OFF; + } + return nfcAdapter.getAdapterState(); + } catch (UnsupportedOperationException e) { + // ignore + return NfcAdapter.STATE_OFF; } } - private boolean isEnabled() { - int state = getNfcState(); - switch (state) { + /** + * Helper method to encapsulate intermediate states (turning off/on) to help determine whether + * the adapter will be on or off. + * @param nfcState The current NFC adapter state. + * @return boolean representing what state the adapter is/will be in + */ + private static boolean isEnabled(int nfcState) { + switch (nfcState) { case NfcAdapter.STATE_TURNING_ON: case NfcAdapter.STATE_ON: return true; @@ -84,17 +111,28 @@ public class NfcTile extends QSTile<QSTile.BooleanState> { } } - private int getNfcState() { - return mNfcAdapter.getAdapterState(); + /** + * Helper method to determine intermediate states + * @param nfcState The current NFC adapter state. + * @return boolean representing if the adapter is in an intermediate state + */ + private static boolean isEnablingDisabling(int nfcState) { + switch (nfcState) { + case NfcAdapter.STATE_TURNING_OFF: + case NfcAdapter.STATE_TURNING_ON: + return true; + default: + return false; + } } @Override protected void handleUpdateState(BooleanState state, Object arg) { - if (mNfcAdapter == null) { - mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext); - } - state.visible = mNfcAdapter != null; - state.value = mNfcAdapter != null && isEnabled(); + state.visible = mSupportsNfc; + final int nfcState = getNfcAdapterState(); + state.value = mSupportsNfc && isEnabled(nfcState); + state.enabled = mSupportsNfc && !isEnablingDisabling(nfcState); + state.icon = ResourceIcon.get(state.value ? R.drawable.ic_qs_nfc_on : R.drawable.ic_qs_nfc_off); state.label = mContext.getString(R.string.quick_settings_nfc_label); @@ -110,12 +148,9 @@ public class NfcTile extends QSTile<QSTile.BooleanState> { if (mListening == listening) return; mListening = listening; if (listening) { - if (mNfcAdapter == null) { - mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext); - refreshState(); - } mContext.registerReceiver(mReceiver, new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)); + refreshState(); } else { mContext.unregisterReceiver(mReceiver); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/PerfProfileTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/PerfProfileTile.java index e6e6bb4..4863683 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/PerfProfileTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/PerfProfileTile.java @@ -48,6 +48,7 @@ public class PerfProfileTile extends QSTile<PerfProfileTile.ProfileState> { private final String[] mDescriptionEntries; private final String[] mAnnouncementEntries; private final int[] mPerfProfileValues; + private final int mNumPerfProfiles; private final Icon mIcon; private final PowerManager mPm; @@ -61,16 +62,36 @@ public class PerfProfileTile extends QSTile<PerfProfileTile.ProfileState> { mObserver = new PerformanceProfileObserver(mHandler); mPm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mPerformanceManager = PerformanceManager.getInstance(mContext); + mNumPerfProfiles = mPerformanceManager.getNumberOfProfiles(); - Resources res = mContext.getResources(); - - mPerfProfileValues = res.getIntArray(org.cyanogenmod.platform.internal.R.array.perf_profile_values); - - mEntries = res.getStringArray(org.cyanogenmod.platform.internal.R.array.perf_profile_entries); - mDescriptionEntries = res.getStringArray(R.array.perf_profile_description); - mAnnouncementEntries = res.getStringArray(R.array.perf_profile_announcement); + mPerfProfileValues = new int[mNumPerfProfiles]; + mEntries = new String[mNumPerfProfiles]; + mDescriptionEntries = new String[mNumPerfProfiles]; + mAnnouncementEntries = new String[mNumPerfProfiles]; mIcon = ResourceIcon.get(R.drawable.ic_qs_perf_profile); + + // Filter out unsupported profiles + Resources res = mContext.getResources(); + final int[] perfProfileValues = res.getIntArray( + org.cyanogenmod.platform.internal.R.array.perf_profile_values); + final String[] entries = res.getStringArray( + org.cyanogenmod.platform.internal.R.array.perf_profile_entries); + final String[] descriptionEntries = res.getStringArray( + R.array.perf_profile_description); + final String[] announcementEntries = res.getStringArray( + R.array.perf_profile_announcement); + int i = 0; + + for (int j = 0; j < perfProfileValues.length; j++) { + if (perfProfileValues[j] < mNumPerfProfiles) { + mPerfProfileValues[i] = perfProfileValues[j]; + mEntries[i] = entries[j]; + mDescriptionEntries[i] = descriptionEntries[j]; + mAnnouncementEntries[i] = announcementEntries[j]; + i++; + } + } } @Override @@ -135,7 +156,7 @@ public class PerfProfileTile extends QSTile<PerfProfileTile.ProfileState> { public void startObserving() { mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(CMSettings.Secure.PERFORMANCE_PROFILE), + CMSettings.Secure.getUriFor(CMSettings.Secure.PERFORMANCE_PROFILE), false, this); } |