diff options
Diffstat (limited to 'packages/SystemUI/src')
56 files changed, 1356 insertions, 432 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FilterCanvas.java b/packages/SystemUI/src/com/android/systemui/qs/FilterCanvas.java deleted file mode 100644 index 05c8ee3..0000000 --- a/packages/SystemUI/src/com/android/systemui/qs/FilterCanvas.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.qs; - -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.Rect; - -/** Canvas that forwards calls to another canvas. Can be subclassed to transform drawing calls. - * Temporary solution to runtime modification of a single drawable shape into two - * enabled & disabled versions. See QSImageView. **/ -public class FilterCanvas extends Canvas { - private final Canvas mCanvas; - - public FilterCanvas(Canvas c) { - mCanvas = c; - } - - @Override - public void drawPath(Path path, Paint paint) { - mCanvas.drawPath(path, paint); - } - - @Override - public int getSaveCount() { - return mCanvas.getSaveCount(); - } - - @Override - public int save() { - return mCanvas.save(); - } - - @Override - public void translate(float dx, float dy) { - mCanvas.translate(dx, dy); - } - - @Override - public boolean clipRect(int left, int top, int right, int bottom) { - return mCanvas.clipRect(left, top, right, bottom); - } - - @Override - public boolean clipRect(Rect rect) { - return mCanvas.clipRect(rect); - } - - @Override - public void concat(Matrix matrix) { - mCanvas.concat(matrix); - } - - @Override - public void restoreToCount(int saveCount) { - mCanvas.restoreToCount(saveCount); - } - - @Override - public void drawRect(Rect r, Paint paint) { - mCanvas.drawRect(r, paint); - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java b/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java index 1e15b9f..c169df0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java +++ b/packages/SystemUI/src/com/android/systemui/qs/GlobalSetting.java @@ -21,10 +21,10 @@ import android.database.ContentObserver; import android.os.Handler; import android.provider.Settings.Global; -import com.android.systemui.statusbar.policy.Disposable; +import com.android.systemui.statusbar.policy.Listenable; /** Helper for managing a global setting. **/ -public abstract class GlobalSetting extends ContentObserver implements Disposable { +public abstract class GlobalSetting extends ContentObserver implements Listenable { private final Context mContext; private final String mSettingName; @@ -34,8 +34,6 @@ public abstract class GlobalSetting extends ContentObserver implements Disposabl super(handler); mContext = context; mSettingName = settingName; - mContext.getContentResolver().registerContentObserver( - Global.getUriFor(mSettingName), false, this); } public int getValue() { @@ -47,8 +45,13 @@ public abstract class GlobalSetting extends ContentObserver implements Disposabl } @Override - public void dispose() { - mContext.getContentResolver().unregisterContentObserver(this); + public void setListening(boolean listening) { + if (listening) { + mContext.getContentResolver().registerContentObserver( + Global.getUriFor(mSettingName), false, this); + } else { + mContext.getContentResolver().unregisterContentObserver(this); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImageView.java b/packages/SystemUI/src/com/android/systemui/qs/QSImageView.java deleted file mode 100644 index ed67560..0000000 --- a/packages/SystemUI/src/com/android/systemui/qs/QSImageView.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.qs; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.Path; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.VectorDrawable; -import android.util.AttributeSet; -import android.widget.ImageView; - -import com.android.systemui.R; - -/** ImageView that performs runtime modification of vector drawables (using FilterCanvas). **/ -public class QSImageView extends ImageView { - - private final int mOutlineWidth; - private final int mColorEnabled; - private final int mColorDisabled; - private FilterCanvas mFilterCanvas; - private Canvas mCanvas; - private boolean mEnabledVersion = true; - private boolean mFilter; - - public QSImageView(Context context) { - this(context, null); - } - - public QSImageView(Context context, AttributeSet attrs) { - super(context, attrs); - final Resources res = context.getResources(); - mOutlineWidth = res.getDimensionPixelSize(R.dimen.quick_settings_tile_icon_outline); - mColorEnabled = res.getColor(R.color.quick_settings_tile_icon_enabled); - mColorDisabled = res.getColor(R.color.quick_settings_tile_icon_disabled); - } - - public void setEnabledVersion(boolean enabledVersion) { - mEnabledVersion = enabledVersion; - invalidate(); - } - - @Override - public void setImageDrawable(Drawable drawable) { - mFilter = drawable instanceof VectorDrawable; - super.setImageDrawable(drawable); - } - - @Override - public void setImageResource(int resId) { - setImageDrawable(mContext.getDrawable(resId)); - } - - @Override - public void draw(Canvas canvas) { - if (mFilter) { - if (canvas != mCanvas) { - mCanvas = canvas; - mFilterCanvas = new QSFilterCanvas(canvas); - } - super.draw(mFilterCanvas); - } else { - super.draw(canvas); - } - } - - private class QSFilterCanvas extends FilterCanvas { - public QSFilterCanvas(Canvas c) { - super(c); - } - - @Override - public void drawPath(Path path, Paint paint) { - if (mEnabledVersion) { - paint.setColor(mColorEnabled); - } else { - paint.setStyle(Style.STROKE); - paint.setStrokeJoin(Paint.Join.ROUND); - paint.setColor(mColorDisabled); - paint.setStrokeWidth(mOutlineWidth); - } - super.drawPath(path, paint); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index afb5483..6176eb6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -80,7 +80,10 @@ public class QSPanel extends ViewGroup { showDetail(false /*show*/, mDetailRecord); } for (TileRecord r : mRecords) { - r.tile.setShown(expanded); + r.tile.setListening(expanded); + if (expanded) { + r.tile.refreshState(); + } } } @@ -125,6 +128,7 @@ public class QSPanel extends ViewGroup { } }; r.tileView.init(click, clickSecondary); + r.tile.refreshState(); mRecords.add(r); addView(r.tileView); @@ -156,24 +160,29 @@ public class QSPanel extends ViewGroup { mCellHeight = (int)(mCellWidth / TILE_ASPECT); mLargeCellWidth = (int)(mCellWidth * LARGE_TILE_FACTOR); mLargeCellHeight = (int)(mCellHeight * LARGE_TILE_FACTOR); - int r = 0; - int c = 0; + int r = -1; + int c = -1; int rows = 0; + boolean rowIsDual = false; for (TileRecord record : mRecords) { if (record.tileView.getVisibility() == GONE) continue; + // wrap to next column if we've reached the max # of columns + // also don't allow dual + single tiles on the same row + if (r == -1 || c == (mColumns - 1) || rowIsDual != record.tile.supportsDualTargets()) { + r++; + c = 0; + rowIsDual = record.tile.supportsDualTargets(); + } else { + c++; + } record.row = r; record.col = c; rows = r + 1; - c++; - if (c == mColumns /*end of normal column*/ || r == 0 && c == 2 /*end of 1st column*/) { - c = 0; - r++; - } } for (TileRecord record : mRecords) { if (record.tileView.getVisibility() == GONE) continue; - record.tileView.setDual(record.row == 0); + record.tileView.setDual(record.tile.supportsDualTargets()); final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth; final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight; record.tileView.measure(exactly(cw), exactly(ch)); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index 05f308d..835a5c4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -19,7 +19,6 @@ package com.android.systemui.qs; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; -import android.graphics.drawable.VectorDrawable; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -30,7 +29,7 @@ import android.view.ViewGroup; import com.android.systemui.qs.QSTile.State; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.CastController; -import com.android.systemui.statusbar.policy.Disposable; +import com.android.systemui.statusbar.policy.Listenable; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.RotationLockController; @@ -47,8 +46,10 @@ import java.util.Objects; * handleUpdateState. Callbacks affecting state should use refreshState to trigger another * state update pass on tile looper. */ -public abstract class QSTile<TState extends State> implements Disposable { - private final String TAG = "QSTile." + getClass().getSimpleName(); +public abstract class QSTile<TState extends State> implements Listenable { + protected final String TAG = "QSTile." + getClass().getSimpleName(); + protected static final boolean DEBUG = false; + public static final int FEEDBACK_START_DELAY = 400; protected final Host mHost; protected final Context mContext; @@ -69,6 +70,10 @@ public abstract class QSTile<TState extends State> implements Disposable { mHandler = new H(host.getLooper()); } + public boolean supportsDualTargets() { + return false; + } + public Host getHost() { return mHost; } @@ -111,10 +116,6 @@ public abstract class QSTile<TState extends State> implements Disposable { mHandler.obtainMessage(H.USER_SWITCH, newUserId).sendToTarget(); } - public void setShown(boolean shown) { - mHandler.obtainMessage(H.SHOWN, shown ? 1 : 0, 0).sendToTarget(); - } - // call only on tile worker looper private void handleSetCallback(Callback callback) { @@ -126,10 +127,6 @@ public abstract class QSTile<TState extends State> implements Disposable { // optional } - protected void handleShown(boolean shown) { - // optional, discouraged - } - protected void handleRefreshState(Object arg) { handleUpdateState(mTmpState, arg); final boolean changed = mTmpState.copyTo(mState); @@ -161,7 +158,6 @@ public abstract class QSTile<TState extends State> implements Disposable { private static final int REFRESH_STATE = 4; private static final int SHOW_DETAIL = 5; private static final int USER_SWITCH = 6; - private static final int SHOWN = 7; private H(Looper looper) { super(looper); @@ -189,9 +185,6 @@ public abstract class QSTile<TState extends State> implements Disposable { } else if (msg.what == USER_SWITCH) { name = "handleUserSwitch"; handleUserSwitch(msg.arg1); - } else if (msg.what == SHOWN) { - name = "handleShown"; - handleShown(msg.arg1 != 0); } } catch (Throwable t) { final String error = "Error in " + name; @@ -212,7 +205,6 @@ public abstract class QSTile<TState extends State> implements Disposable { void collapsePanels(); Looper getLooper(); Context getContext(); - VectorDrawable getVectorDrawable(int resId); BluetoothController getBluetoothController(); LocationController getLocationController(); RotationLockController getRotationLockController(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java index 17a95fb..4cfb636 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java @@ -25,9 +25,11 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.TypedValue; +import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.ImageView.ScaleType; import android.widget.TextView; @@ -43,9 +45,9 @@ public class QSTileView extends ViewGroup { protected final Context mContext; private final View mIcon; private final View mDivider; - private final TextView mLabel; private final H mHandler = new H(); + private TextView mLabel; private boolean mDual; private OnClickListener mClickPrimary; private OnClickListener mClickSecondary; @@ -55,14 +57,7 @@ public class QSTileView extends ViewGroup { mContext = context; final Resources res = context.getResources(); - mLabel = new TextView(mContext); - mLabel.setId(android.R.id.title); - mLabel.setTextColor(res.getColor(R.color.quick_settings_tile_text)); - mLabel.setGravity(Gravity.CENTER); - mLabel.setTypeface(CONDENSED); - mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, - res.getDimensionPixelSize(R.dimen.quick_settings_tile_text_size)); - addView(mLabel); + recreateLabel(); setClipChildren(false); mIcon = createIcon(); @@ -78,8 +73,33 @@ public class QSTileView extends ViewGroup { setBackground(getSelectableBackground()); } + private void recreateLabel() { + CharSequence labelText = null; + if (mLabel != null) { + labelText = mLabel.getText(); + removeView(mLabel); + } + final Resources res = mContext.getResources(); + mLabel = new TextView(mDual ? new ContextThemeWrapper(mContext, R.style.QSBorderless_Tiny) + : mContext); + mLabel.setId(android.R.id.title); + mLabel.setTextColor(res.getColor(R.color.quick_settings_tile_text)); + mLabel.setGravity(Gravity.CENTER); + mLabel.setTypeface(CONDENSED); + mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, + res.getDimensionPixelSize(R.dimen.quick_settings_tile_text_size)); + if (labelText != null) { + mLabel.setText(labelText); + } + addView(mLabel); + } + public void setDual(boolean dual) { + final boolean changed = dual != mDual; mDual = dual; + if (changed) { + recreateLabel(); + } if (mDual) { setOnClickListener(mClickPrimary); mLabel.setClickable(true); @@ -98,7 +118,7 @@ public class QSTileView extends ViewGroup { } protected View createIcon() { - QSImageView icon = new QSImageView(mContext); + final ImageView icon = new ImageView(mContext); icon.setId(android.R.id.icon); icon.setScaleType(ScaleType.CENTER_INSIDE); return icon; @@ -120,9 +140,10 @@ public class QSTileView extends ViewGroup { final int iconSpec = exactly((int)mLabel.getTextSize() * 2); mIcon.measure(iconSpec, iconSpec); mLabel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(h, MeasureSpec.AT_MOST)); - mLabel.measure(widthMeasureSpec, exactly(mLabel.getMeasuredHeight() + p * 2)); if (mDual) { mDivider.measure(widthMeasureSpec, exactly(mDivider.getLayoutParams().height)); + } else { + mLabel.measure(widthMeasureSpec, exactly(mLabel.getMeasuredHeight() + p * 2)); } setMeasuredDimension(w, h); } @@ -156,15 +177,12 @@ public class QSTileView extends ViewGroup { } protected void handleStateChanged(QSTile.State state) { - if (mIcon instanceof QSImageView) { - QSImageView qsiv = (QSImageView) mIcon; + if (mIcon instanceof ImageView) { + ImageView iv = (ImageView) mIcon; if (state.icon != null) { - qsiv.setImageDrawable(state.icon); + iv.setImageDrawable(state.icon); } else if (state.iconId > 0) { - qsiv.setImageResource(state.iconId); - } - if (state.icon != null && state instanceof QSTile.BooleanState) { - qsiv.setEnabledVersion(((QSTile.BooleanState)state).value); + iv.setImageResource(state.iconId); } } mLabel.setText(state.label); diff --git a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java index 4debaa9..3ed3d30 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java +++ b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java @@ -21,10 +21,10 @@ import android.database.ContentObserver; import android.os.Handler; import android.provider.Settings.Secure; -import com.android.systemui.statusbar.policy.Disposable; +import com.android.systemui.statusbar.policy.Listenable; /** Helper for managing a secure setting. **/ -public abstract class SecureSetting extends ContentObserver implements Disposable { +public abstract class SecureSetting extends ContentObserver implements Listenable { private final Context mContext; private final String mSettingName; @@ -38,8 +38,7 @@ public abstract class SecureSetting extends ContentObserver implements Disposabl } public void rebindForCurrentUser() { - mContext.getContentResolver().registerContentObserver( - Secure.getUriFor(mSettingName), false, this); + setListening(true); } public int getValue() { @@ -51,8 +50,13 @@ public abstract class SecureSetting extends ContentObserver implements Disposabl } @Override - public void dispose() { - mContext.getContentResolver().unregisterContentObserver(this); + public void setListening(boolean listening) { + if (listening) { + mContext.getContentResolver().registerContentObserver( + Secure.getUriFor(mSettingName), false, this); + } else { + mContext.getContentResolver().unregisterContentObserver(this); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index 5fe8422..c0f9bf2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -39,11 +39,6 @@ public class AirplaneModeTile extends QSTile<QSTile.BooleanState> { handleRefreshState(value); } }; - - final IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); - mContext.registerReceiver(mReceiver, filter); - refreshState(); } @Override @@ -70,7 +65,6 @@ public class AirplaneModeTile extends QSTile<QSTile.BooleanState> { state.value = airplaneMode; state.visible = true; state.label = mContext.getString(R.string.quick_settings_airplane_mode_label); - state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_airplane); if (airplaneMode) { state.iconId = R.drawable.ic_qs_airplane_on; state.contentDescription = mContext.getString( @@ -84,9 +78,15 @@ public class AirplaneModeTile extends QSTile<QSTile.BooleanState> { } } - public void dispose() { - mSetting.dispose(); - mContext.unregisterReceiver(mReceiver); + public void setListening(boolean listening) { + if (listening) { + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); + mContext.registerReceiver(mReceiver, filter); + } else { + mContext.unregisterReceiver(mReceiver); + } + mSetting.setListening(listening); } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 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 60a6047..7335ab4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -33,7 +33,11 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState> { public BluetoothTile(Host host) { super(host); mController = host.getBluetoothController(); - mController.addStateChangedCallback(mCallback); + } + + @Override + public boolean supportsDualTargets() { + return true; } @Override @@ -42,8 +46,12 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState> { } @Override - public void dispose() { - mController.removeStateChangedCallback(mCallback); + public void setListening(boolean listening) { + if (listening) { + mController.addStateChangedCallback(mCallback); + } else { + mController.removeStateChangedCallback(mCallback); + } } @Override @@ -64,14 +72,13 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState> { final boolean connected = mController.isBluetoothConnected(); state.visible = supported; state.value = enabled; - state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_bluetooth); final String stateContentDescription; if (enabled) { if (connected) { - state.iconId = R.drawable.ic_qs_bluetooth_on; + state.iconId = R.drawable.ic_qs_bluetooth_connected; stateContentDescription = mContext.getString(R.string.accessibility_desc_connected); } else { - state.iconId = R.drawable.ic_qs_bluetooth_not_connected; + state.iconId = R.drawable.ic_qs_bluetooth_on; stateContentDescription = mContext.getString(R.string.accessibility_desc_on); } state.label = mContext.getString(R.string.quick_settings_bluetooth_label); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java index 0e9b9a7..bfd416d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java @@ -51,21 +51,25 @@ public class BugreportTile extends QSTile<QSTile.State> { } @Override - public void dispose() { - mSetting.dispose(); + public void setListening(boolean listening) { + mSetting.setListening(listening); } @Override protected void handleClick() { - mHost.collapsePanels(); - mUiHandler.post(mShowDialog); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + mHost.collapsePanels(); + mUiHandler.post(mShowDialog); + } + }, FEEDBACK_START_DELAY); } @Override protected void handleUpdateState(State state, Object pushArg) { state.visible = mSetting.getValue() != 0; - state.iconId = com.android.internal.R.drawable.stat_sys_adb; - state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_bugreport); + state.iconId = R.drawable.ic_qs_bugreport; state.label = mContext.getString(com.android.internal.R.string.bugreport_title); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index a3eaa2c..907c77e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -35,14 +35,9 @@ public class CastTile extends QSTile<QSTile.BooleanState> { private final CastController mController; - private boolean mShown; - public CastTile(Host host) { super(host); mController = host.getCastController(); - if (mController != null) { - mController.addCallback(mCallback); - } } @Override @@ -51,9 +46,14 @@ public class CastTile extends QSTile<QSTile.BooleanState> { } @Override - public void dispose() { + public void setListening(boolean listening) { if (mController == null) return; - mController.removeCallback(mCallback); + if (listening) { + mController.addCallback(mCallback); + } else { + mController.removeCallback(mCallback); + } + mController.setDiscovering(listening); } @Override @@ -64,17 +64,13 @@ public class CastTile extends QSTile<QSTile.BooleanState> { } @Override - protected void handleShown(boolean shown) { - if (mShown == shown) return; - if (mController == null) return; - mShown = shown; - mController.setDiscovering(mShown); - } - - @Override protected void handleClick() { - mHost.collapsePanels(); - mUiHandler.post(mShowDialog); + mHandler.postDelayed(new Runnable() { + public void run() { + mHost.collapsePanels(); + mUiHandler.post(mShowDialog); + } + }, FEEDBACK_START_DELAY); } @Override @@ -82,13 +78,13 @@ public class CastTile extends QSTile<QSTile.BooleanState> { state.visible = true; state.label = mContext .getString(R.string.quick_settings_remote_display_no_connection_label); - state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_cast); if (arg instanceof CallbackInfo) { final CallbackInfo cb = (CallbackInfo) arg; if (cb.connectedRouteName != null) { state.value = !cb.connecting; } } + state.iconId = state.value ? R.drawable.ic_qs_cast_on : R.drawable.ic_qs_cast_off; } private static class CallbackInfo { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index 86a4e79..182a0ce 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -38,7 +38,6 @@ public class CellularTile extends QSTile<QSTile.SignalState> { public CellularTile(Host host) { super(host); mController = host.getNetworkController(); - mController.addNetworkSignalChangedCallback(mCallback); } @Override @@ -47,8 +46,12 @@ public class CellularTile extends QSTile<QSTile.SignalState> { } @Override - public void dispose() { - mController.removeNetworkSignalChangedCallback(mCallback); + public void setListening(boolean listening) { + if (listening) { + mController.addNetworkSignalChangedCallback(mCallback); + } else { + mController.removeNetworkSignalChangedCallback(mCallback); + } } @Override 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 66740af..5301362 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -39,8 +39,6 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { handleRefreshState(value); } }; - - refreshState(); } @Override @@ -49,8 +47,8 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { } @Override - public void dispose() { - mSetting.dispose(); + public void setListening(boolean listening) { + mSetting.setListening(listening); } @Override @@ -73,6 +71,6 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { state.visible = mVisible; state.value = enabled; state.label = mContext.getString(R.string.quick_settings_inversion_label); - state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_invert_colors); + state.iconId = R.drawable.ic_qs_color_inversion; } } 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 1a67afc..f2ba558 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -42,7 +42,7 @@ public class HotspotTile extends QSTile<QSTile.State> { } @Override - public void dispose() { + public void setListening(boolean listening) { } @@ -55,6 +55,6 @@ public class HotspotTile extends QSTile<QSTile.State> { protected void handleUpdateState(State state, Object arg) { state.visible = mController != null; state.label = mContext.getString(R.string.quick_settings_hotspot_label); - state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_hotspot); + state.iconId = R.drawable.ic_qs_hotspot_off; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index d32f98f..c5ad9e6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -16,6 +16,8 @@ package com.android.systemui.qs.tiles; +import android.graphics.drawable.AnimationDrawable; + import com.android.systemui.R; import com.android.systemui.qs.QSTile; import com.android.systemui.statusbar.policy.LocationController; @@ -29,7 +31,6 @@ public class LocationTile extends QSTile<QSTile.BooleanState> { public LocationTile(Host host) { super(host); mController = host.getLocationController(); - mController.addSettingsChangedCallback(mCallback); } @Override @@ -37,8 +38,13 @@ public class LocationTile extends QSTile<QSTile.BooleanState> { return new BooleanState(); } - public void dispose() { - mController.removeSettingsChangedCallback(mCallback); + @Override + public void setListening(boolean listening) { + if (listening) { + mController.addSettingsChangedCallback(mCallback); + } else { + mController.removeSettingsChangedCallback(mCallback); + } } @Override @@ -56,16 +62,27 @@ public class LocationTile extends QSTile<QSTile.BooleanState> { protected void handleUpdateState(BooleanState state, Object arg) { final boolean locationEnabled = mController.isLocationEnabled(); state.visible = true; - state.value = locationEnabled; - state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_location); + if (state.value != locationEnabled) { + state.value = locationEnabled; + final AnimationDrawable d = (AnimationDrawable) mContext.getDrawable(locationEnabled + ? R.drawable.ic_qs_location_on + : R.drawable.ic_qs_location_off); + state.icon = d; + mUiHandler.post(new Runnable() { + @Override + public void run() { + d.start(); + } + }); + } if (locationEnabled) { - state.iconId = R.drawable.ic_qs_location_on; + if (state.icon == null) state.iconId = R.drawable.ic_qs_location_01; state.label = mContext.getString(R.string.quick_settings_location_label); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_location, mContext.getString(R.string.accessibility_desc_on)); } else { - state.iconId = R.drawable.ic_qs_location_off; + if (state.icon == null) state.iconId = R.drawable.ic_qs_location_11; state.label = mContext.getString(R.string.quick_settings_location_off_label); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_location, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java index 36a579c..c5e9b52 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java @@ -33,8 +33,6 @@ public class RingerModeTile extends QSTile<RingerModeTile.IntState> { public RingerModeTile(Host host) { super(host); mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION); - mContext.registerReceiver(mReceiver, filter); } @Override @@ -43,8 +41,13 @@ public class RingerModeTile extends QSTile<RingerModeTile.IntState> { } @Override - public void dispose() { - mContext.unregisterReceiver(mReceiver); + public void setListening(boolean listening) { + if (listening) { + final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION); + mContext.registerReceiver(mReceiver, filter); + } else { + mContext.unregisterReceiver(mReceiver); + } } @Override @@ -64,13 +67,13 @@ public class RingerModeTile extends QSTile<RingerModeTile.IntState> { state.visible = true; state.value = ringerMode; if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { - state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_ringer_vibrate); + state.iconId = R.drawable.ic_qs_ringer_vibrate; state.label = "Vibrate"; } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) { - state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_ringer_silent); + state.iconId = R.drawable.ic_qs_ringer_silent; state.label = "Silent"; } else { - state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_ringer_audible); + state.iconId = R.drawable.ic_qs_ringer_audible; state.label = "Audible"; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index d075299..1b0967b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -33,8 +33,6 @@ public class RotationLockTile extends QSTile<QSTile.BooleanState> { public RotationLockTile(Host host) { super(host); mController = host.getRotationLockController(); - if (mController == null) return; - mController.addRotationLockControllerCallback(mCallback); } @Override @@ -42,9 +40,13 @@ public class RotationLockTile extends QSTile<QSTile.BooleanState> { return new BooleanState(); } - public void dispose() { + public void setListening(boolean listening) { if (mController == null) return; - mController.removeRotationLockControllerCallback(mCallback); + if (listening) { + mController.addRotationLockControllerCallback(mCallback); + } else { + mController.removeRotationLockControllerCallback(mCallback); + } } @Override @@ -61,8 +63,8 @@ public class RotationLockTile extends QSTile<QSTile.BooleanState> { if (state.value != rotationLocked) { state.value = rotationLocked; final AnimationDrawable d = (AnimationDrawable) mContext.getDrawable(rotationLocked - ? R.drawable.ic_rotate_locked_anim - : R.drawable.ic_rotate_unlocked_anim); + ? R.drawable.ic_qs_rotation_locked + : R.drawable.ic_qs_rotation_unlocked); state.icon = d; mUiHandler.post(new Runnable() { @Override @@ -80,12 +82,12 @@ public class RotationLockTile extends QSTile<QSTile.BooleanState> { : R.string.quick_settings_rotation_locked_label; state.label = mContext.getString(label); if (state.icon == null) { - state.icon = mContext.getDrawable(R.drawable.ic_rotate_24_15); + state.icon = mContext.getDrawable(R.drawable.ic_qs_rotation_15); } } else { state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label); if (state.icon == null) { - state.icon = mContext.getDrawable(R.drawable.ic_rotate_24_01); + state.icon = mContext.getDrawable(R.drawable.ic_qs_rotation_01); } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index e08a6fa..ef7fb89 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.provider.Settings; +import android.util.Log; import com.android.systemui.R; import com.android.systemui.qs.QSTile; @@ -37,7 +38,11 @@ public class WifiTile extends QSTile<QSTile.SignalState> { public WifiTile(Host host) { super(host); mController = host.getNetworkController(); - mController.addNetworkSignalChangedCallback(mCallback); + } + + @Override + public boolean supportsDualTargets() { + return true; } @Override @@ -46,8 +51,12 @@ public class WifiTile extends QSTile<QSTile.SignalState> { } @Override - public void dispose() { - mController.removeNetworkSignalChangedCallback(mCallback); + public void setListening(boolean listening) { + if (listening) { + mController.addNetworkSignalChangedCallback(mCallback); + } else { + mController.removeNetworkSignalChangedCallback(mCallback); + } } @Override @@ -67,8 +76,9 @@ public class WifiTile extends QSTile<QSTile.SignalState> { @Override protected void handleUpdateState(SignalState state, Object arg) { - if (arg == null) return; state.visible = true; + if (DEBUG) Log.d(TAG, "handleUpdateState arg=" + arg); + if (arg == null) return; CallbackInfo cb = (CallbackInfo) arg; boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null); @@ -114,6 +124,18 @@ public class WifiTile extends QSTile<QSTile.SignalState> { boolean activityIn; boolean activityOut; String wifiSignalContentDescription; + + @Override + public String toString() { + return new StringBuilder("CallbackInfo[") + .append("enabled=").append(enabled) + .append(",wifiSignalIconId=").append(wifiSignalIconId) + .append(",enabledDesc=").append(enabledDesc) + .append(",activityIn=").append(activityIn) + .append(",activityOut=").append(activityOut) + .append(",wifiSignalContentDescription=").append(wifiSignalContentDescription) + .append(']').toString(); + } } private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() { @@ -121,6 +143,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> { public void onWifiSignalChanged(boolean enabled, int wifiSignalIconId, boolean activityIn, boolean activityOut, String wifiSignalContentDescriptionId, String description) { + if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + enabled); final CallbackInfo info = new CallbackInfo(); info.enabled = enabled; info.wifiSignalIconId = wifiSignalIconId; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java index dceb856..2edefe7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java @@ -32,6 +32,7 @@ import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.ImageView; import android.widget.ListView; import android.widget.RadioButton; import android.widget.RelativeLayout; @@ -39,7 +40,6 @@ import android.widget.Switch; import android.widget.TextView; import com.android.systemui.R; -import com.android.systemui.qs.QSImageView; import com.android.systemui.qs.QSTile; import com.android.systemui.statusbar.policy.ZenModeController; @@ -72,9 +72,7 @@ public class ZenModeDetail extends RelativeLayout { mContext = getContext(); mController = mHost.getZenModeController(); - final QSImageView close = (QSImageView) findViewById(android.R.id.button1); - close.setImageDrawable(mHost.getVectorDrawable(R.drawable.ic_qs_close)); - close.setEnabledVersion(true); + final ImageView close = (ImageView) findViewById(android.R.id.button1); close.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -239,9 +237,7 @@ public class ZenModeDetail extends RelativeLayout { title.setText(condition.summary); title.setEnabled(enabled); title.setAlpha(enabled ? 1 : .5f); - final QSImageView button1 = (QSImageView) row.findViewById(android.R.id.button1); - button1.setImageDrawable(mHost.getVectorDrawable(R.drawable.ic_qs_minus)); - button1.setEnabledVersion(true); + final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1); button1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { @@ -250,9 +246,7 @@ public class ZenModeDetail extends RelativeLayout { } }); - final QSImageView button2 = (QSImageView) row.findViewById(android.R.id.button2); - button2.setImageDrawable(mHost.getVectorDrawable(R.drawable.ic_qs_plus)); - button2.setEnabledVersion(true); + final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2); button2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java index 83918e8..bfa9c19 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles; import android.content.Context; +import android.util.Log; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; @@ -33,7 +34,6 @@ public class ZenModeTile extends QSTile<QSTile.BooleanState> { public ZenModeTile(Host host) { super(host); mController = host.getZenModeController(); - mController.addCallback(mCallback); } @Override @@ -51,8 +51,12 @@ public class ZenModeTile extends QSTile<QSTile.BooleanState> { } @Override - public void dispose() { - mController.removeCallback(mCallback); + public void setListening(boolean listening) { + if (listening) { + mController.addCallback(mCallback); + } else { + mController.removeCallback(mCallback); + } } @Override @@ -69,14 +73,14 @@ public class ZenModeTile extends QSTile<QSTile.BooleanState> { final boolean zen = arg instanceof Boolean ? (Boolean)arg : mController.isZen(); state.value = zen; state.visible = true; - state.iconId = R.drawable.stat_sys_zen_limited; - state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_zen); + state.iconId = zen ? R.drawable.ic_qs_zen_on : R.drawable.ic_qs_zen_off; state.label = mContext.getString(R.string.zen_mode_title); } private final ZenModeController.Callback mCallback = new ZenModeController.Callback() { @Override public void onZenChanged(boolean zen) { + if (DEBUG) Log.d(TAG, "onZenChanged " + zen); refreshState(zen); } }; diff --git a/packages/SystemUI/src/com/android/systemui/recent/Recents.java b/packages/SystemUI/src/com/android/systemui/recent/Recents.java index d3e949f..00c43e8 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recent/Recents.java @@ -43,14 +43,12 @@ public class Recents extends SystemUI implements RecentsComponent { private static final boolean DEBUG = true; // Which recents to use - boolean mUseAlternateRecents; + boolean mUseAlternateRecents = true; AlternateRecentsComponent mAlternateRecents; boolean mBootCompleted = false; @Override public void start() { - Configuration config = mContext.getResources().getConfiguration(); - mUseAlternateRecents = (config.smallestScreenWidthDp < 600); if (mUseAlternateRecents) { if (mAlternateRecents == null) { mAlternateRecents = new AlternateRecentsComponent(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java index 1ca0476..1c12ac2 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java @@ -400,15 +400,14 @@ public class RecentsTaskLoader { ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId); if (info == null) continue; - ActivityManager.RecentsActivityValues av = t.activityValues; + ActivityManager.TaskDescription av = t.taskDescription; String activityLabel = null; BitmapDrawable activityIcon = null; int activityColor = 0; if (av != null) { - activityLabel = (av.label != null ? av.label.toString() : - ssp.getActivityLabel(info)); - activityIcon = (av.icon != null) ? new BitmapDrawable(res, av.icon) : null; - activityColor = av.colorPrimary; + activityLabel = (av.getLabel() != null ? av.getLabel() : ssp.getActivityLabel(info)); + activityIcon = (av.getIcon() != null) ? new BitmapDrawable(res, av.getIcon()) : null; + activityColor = av.getPrimaryColor(); } else { activityLabel = ssp.getActivityLabel(info); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java index 8d82883..59d0ea6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java @@ -96,18 +96,19 @@ public class SystemServicesProxy { int packageIndex = i % Constants.DebugFlags.App.SystemServicesProxyMockPackageCount; ComponentName cn = new ComponentName("com.android.test" + packageIndex, "com.android.test" + i + ".Activity"); + String description = "" + i + " - " + + Long.toString(Math.abs(new Random().nextLong()), 36); // Create the recent task info ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); rti.id = rti.persistentId = i; rti.baseIntent = new Intent(); rti.baseIntent.setComponent(cn); - rti.activityValues = new ActivityManager.RecentsActivityValues(); - rti.description = "" + i + " - " + - Long.toString(Math.abs(new Random().nextLong()), 36); + rti.description = description; if (i % 2 == 0) { - rti.activityValues.label = rti.description; - rti.activityValues.icon = Bitmap.createBitmap(mDummyIcon); - rti.activityValues.colorPrimary = new Random().nextInt(); + rti.taskDescription = new ActivityManager.TaskDescription(description, + Bitmap.createBitmap(mDummyIcon), new Random().nextInt()); + } else { + rti.taskDescription = new ActivityManager.TaskDescription(); } tasks.add(rti); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index 91df9ef..5e8c769 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; +import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.MotionEvent; @@ -45,6 +46,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private int mBgResId = R.drawable.notification_quantum_bg; private int mDimmedBgResId = R.drawable.notification_quantum_bg_dim; + private int mBgTint = 0; + private int mDimmedBgTint = 0; + /** * Flag to indicate that the notification has been touched once and the second touch will * click it. @@ -209,17 +213,23 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView * @param bgResId The background resource to use in normal state. * @param dimmedBgResId The background resource to use in dimmed state. */ - public void setBackgroundResourceIds(int bgResId, int dimmedBgResId) { + public void setBackgroundResourceIds(int bgResId, int bgTint, int dimmedBgResId, int dimmedTint) { mBgResId = bgResId; + mBgTint = bgTint; mDimmedBgResId = dimmedBgResId; + mDimmedBgTint = dimmedTint; updateBackgroundResource(); } + public void setBackgroundResourceIds(int bgResId, int dimmedBgResId) { + setBackgroundResourceIds(bgResId, 0, dimmedBgResId, 0); + } + private void fadeBackgroundResource() { if (mDimmed) { - setBackgroundDimmed(mDimmedBgResId); + setBackgroundDimmed(mDimmedBgResId, mDimmedBgTint); } else { - setBackgroundNormal(mBgResId); + setBackgroundNormal(mBgResId, mBgTint); } int startAlpha = mDimmed ? 255 : 0; int endAlpha = mDimmed ? 0 : 255; @@ -256,12 +266,12 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private void updateBackgroundResource() { if (mDimmed) { - setBackgroundDimmed(mDimmedBgResId); + setBackgroundDimmed(mDimmedBgResId, mDimmedBgTint); mBackgroundDimmed.setAlpha(255); setBackgroundNormal(null); } else { setBackgroundDimmed(null); - setBackgroundNormal(mBgResId); + setBackgroundNormal(mBgResId, mBgTint); mBackgroundNormal.setAlpha(255); } } @@ -295,12 +305,20 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView invalidate(); } - private void setBackgroundNormal(int drawableResId) { - setBackgroundNormal(getResources().getDrawable(drawableResId)); + private void setBackgroundNormal(int drawableResId, int tintColor) { + final Drawable d = getResources().getDrawable(drawableResId); + if (tintColor != 0) { + d.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP); + } + setBackgroundNormal(d); } - private void setBackgroundDimmed(int drawableResId) { - setBackgroundDimmed(getResources().getDrawable(drawableResId)); + private void setBackgroundDimmed(int drawableResId, int tintColor) { + final Drawable d = getResources().getDrawable(drawableResId); + if (tintColor != 0) { + d.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP); + } + setBackgroundDimmed(d); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index b079265..7918dec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -429,14 +429,16 @@ public abstract class BaseStatusBar extends SystemUI implements protected void applyLegacyRowBackground(StatusBarNotification sbn, NotificationData.Entry entry) { + int version = 0; + try { + ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.getPackageName(), 0); + version = info.targetSdkVersion; + } catch (NameNotFoundException ex) { + Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex); + } + if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) { - int version = 0; - try { - ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.getPackageName(), 0); - version = info.targetSdkVersion; - } catch (NameNotFoundException ex) { - Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex); - } + // Using custom RemoteViews if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) { entry.row.setBackgroundResource(R.drawable.notification_row_legacy_bg); } else if (version < Build.VERSION_CODES.L) { @@ -903,6 +905,7 @@ public abstract class BaseStatusBar extends SystemUI implements entry.row = row; entry.row.setHeightRange(mRowMinHeight, mRowMaxHeight); entry.row.setOnActivatedListener(this); + entry.row.setIsBelowSpeedBump(isBelowSpeedBump(entry.notification)); entry.expanded = contentViewLocal; entry.expandedPublic = publicViewLocal; entry.setBigContentView(bigContentViewLocal); @@ -1065,8 +1068,8 @@ public abstract class BaseStatusBar extends SystemUI implements if (DEBUG) { Log.d(TAG, "addNotificationViews: added at " + pos); } - updateRowStates(); updateNotificationIcons(); + updateRowStates(); } private void addNotificationViews(IBinder key, StatusBarNotification notification) { @@ -1086,6 +1089,7 @@ public abstract class BaseStatusBar extends SystemUI implements mKeyguardIconOverflowContainer.getIconsView().removeAllViews(); int n = mNotificationData.size(); int visibleNotifications = 0; + int speedBumpIndex = -1; boolean onKeyguard = mState == StatusBarState.KEYGUARD; for (int i = n-1; i >= 0; i--) { NotificationData.Entry entry = mNotificationData.get(i); @@ -1113,6 +1117,10 @@ public abstract class BaseStatusBar extends SystemUI implements entry.row.setVisibility(View.VISIBLE); visibleNotifications++; } + if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1 + && entry.row.isBelowSpeedBump() ) { + speedBumpIndex = n - 1 - i; + } } if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) { @@ -1120,6 +1128,8 @@ public abstract class BaseStatusBar extends SystemUI implements } else { mKeyguardIconOverflowContainer.setVisibility(View.GONE); } + + mStackScroller.updateSpeedBumpIndex(speedBumpIndex); } private boolean shouldShowOnKeyguard(StatusBarNotification sbn) { @@ -1335,9 +1345,19 @@ public abstract class BaseStatusBar extends SystemUI implements } else { entry.row.setOnClickListener(null); } + boolean wasBelow = entry.row.isBelowSpeedBump(); + boolean nowBelow = isBelowSpeedBump(notification); + if (wasBelow != nowBelow) { + entry.row.setIsBelowSpeedBump(nowBelow); + } entry.row.notifyContentUpdated(); } + private boolean isBelowSpeedBump(StatusBarNotification notification) { + return notification.getNotification().priority == + Notification.PRIORITY_MIN; + } + protected void notifyHeadsUpScreenOn(boolean screenOn) { if (!screenOn && mInterruptingNotificationEntry != null) { mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 39f2bb9..f6c80fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -52,6 +52,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private NotificationContentView mPublicLayout; private NotificationContentView mPrivateLayout; private int mMaxExpandHeight; + private boolean mIsBelowSpeedBump; public ExpandableNotificationRow(Context context, AttributeSet attrs) { super(context, attrs); @@ -244,6 +245,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { mPrivateLayout.setClipTopAmount(clipTopAmount); } + public boolean isBelowSpeedBump() { + return mIsBelowSpeedBump; + } + + public void setIsBelowSpeedBump(boolean isBelow) { + this.mIsBelowSpeedBump = isBelow; + } + public void notifyContentUpdated() { mPrivateLayout.notifyContentUpdated(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index 4bd0e1c..061396d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -40,11 +40,15 @@ public abstract class ExpandableView extends FrameLayout { protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (!mActualHeightInitialized && mActualHeight == 0) { - mActualHeight = getHeight(); + mActualHeight = getInitialHeight(); } mActualHeightInitialized = true; } + protected int getInitialHeight() { + return getHeight(); + } + @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (filterMotionEvent(ev)) { @@ -146,6 +150,10 @@ public abstract class ExpandableView extends FrameLayout { } } + public boolean isTransparent() { + return false; + } + /** * A listener notifying when {@link #getActualHeight} changes. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java index 6401695..9c39002 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java @@ -89,7 +89,7 @@ public class InterceptedNotifications { return; } final Notification n = new Notification.Builder(mContext) - .setSmallIcon(R.drawable.stat_sys_zen_limited) + .setSmallIcon(R.drawable.ic_qs_zen_on) .setContentTitle(mContext.getResources().getQuantityString( R.plurals.zen_mode_notification_title, mIntercepted.size(), mIntercepted.size())) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotView.java new file mode 100644 index 0000000..3ca021a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotView.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Outline; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.view.View; + +/** + * An single dot of the {@link com.android.systemui.statusbar.SpeedBumpDotsLayout} + */ +public class SpeedBumpDotView extends View { + + private final Paint mPaint = new Paint(); + + public SpeedBumpDotView(Context context, AttributeSet attrs) { + super(context, attrs); + mPaint.setAntiAlias(true); + } + + @Override + protected void onDraw(Canvas canvas) { + float radius = getWidth() / 2.0f; + canvas.drawCircle(radius, radius, radius, mPaint); + } + + public void setColor(int color) { + mPaint.setColor(color); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsAlgorithm.java new file mode 100644 index 0000000..cac6327 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsAlgorithm.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar; + +import android.content.Context; +import android.view.View; +import com.android.systemui.R; + +/** + * The Algorithm of the {@link com.android.systemui.statusbar.SpeedBumpDotsLayout} which can be + * queried for {@link * com.android.systemui.statusbar.SpeedBumpDotsState} + */ +public class SpeedBumpDotsAlgorithm { + + private final float mDotRadius; + + public SpeedBumpDotsAlgorithm(Context context) { + mDotRadius = context.getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height) + / 2.0f; + } + + public void getState(SpeedBumpDotsState resultState) { + + // First reset the current state and ensure that every View has a ViewState + resultState.resetViewStates(); + + SpeedBumpDotsLayout hostView = resultState.getHostView(); + boolean currentlyVisible = hostView.isCurrentlyVisible(); + resultState.setActiveState(currentlyVisible + ? SpeedBumpDotsState.SHOWN + : SpeedBumpDotsState.HIDDEN); + int hostWidth = hostView.getWidth(); + float layoutWidth = hostWidth - 2 * mDotRadius; + int childCount = hostView.getChildCount(); + float paddingBetween = layoutWidth / (childCount - 1); + float centerY = hostView.getHeight() / 2.0f; + for (int i = 0; i < childCount; i++) { + View child = hostView.getChildAt(i); + SpeedBumpDotsState.ViewState viewState = resultState.getViewStateForView(child); + if (currentlyVisible) { + float xTranslation = i * paddingBetween; + viewState.xTranslation = xTranslation; + viewState.yTranslation = calculateYTranslation(hostView, centerY, xTranslation, + layoutWidth); + } else { + viewState.xTranslation = layoutWidth / 2; + viewState.yTranslation = centerY - mDotRadius; + } + viewState.alpha = currentlyVisible ? 1.0f : 0.0f; + viewState.scale = currentlyVisible ? 1.0f : 0.5f; + } + } + + private float calculateYTranslation(SpeedBumpDotsLayout hostView, float centerY, + float xTranslation, float layoutWidth) { + float t = hostView.getAnimationProgress(); + if (t == 0.0f || t == 1.0f) { + return centerY - mDotRadius; + } + float damping = (0.5f -Math.abs(0.5f - t)) * 1.3f; + float partialOffset = xTranslation / layoutWidth; + float indentFactor = (float) (Math.sin((t + partialOffset * 1.5f) * - Math.PI) * damping); + return (1.0f - indentFactor) * centerY - mDotRadius; + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsLayout.java new file mode 100644 index 0000000..ddf5215 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsLayout.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar; + +import android.animation.TimeAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AccelerateDecelerateInterpolator; +import com.android.systemui.R; + +/** + * A layout with a certain number of dots which are integrated in the + * {@link com.android.systemui.statusbar.SpeedBumpView} + */ +public class SpeedBumpDotsLayout extends ViewGroup { + + private static final float DOT_CLICK_ANIMATION_LENGTH = 300; + private final int mDotSize; + private final SpeedBumpDotsAlgorithm mAlgorithm = new SpeedBumpDotsAlgorithm(getContext()); + private final SpeedBumpDotsState mCurrentState = new SpeedBumpDotsState(this); + private boolean mIsCurrentlyVisible = true; + private final ValueAnimator mClickAnimator; + private float mAnimationProgress; + private ValueAnimator.AnimatorUpdateListener mClickUpdateListener + = new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mAnimationProgress = animation.getAnimatedFraction(); + updateChildren(); + } + }; + + public SpeedBumpDotsLayout(Context context, AttributeSet attrs) { + super(context, attrs); + mDotSize = getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height); + createDots(context, attrs); + mClickAnimator = TimeAnimator.ofFloat(0, DOT_CLICK_ANIMATION_LENGTH); + mClickAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); + mClickAnimator.addUpdateListener(mClickUpdateListener); + } + + private void createDots(Context context, AttributeSet attrs) { + SpeedBumpDotView blueDot = new SpeedBumpDotView(context, attrs); + blueDot.setColor(getResources().getColor(R.color.speed_bump_dot_blue)); + addView(blueDot); + + SpeedBumpDotView redDot = new SpeedBumpDotView(context, attrs); + redDot.setColor(getResources().getColor(R.color.speed_bump_dot_red)); + addView(redDot); + + SpeedBumpDotView yellowDot = new SpeedBumpDotView(context, attrs); + yellowDot.setColor(getResources().getColor(R.color.speed_bump_dot_yellow)); + addView(yellowDot); + + SpeedBumpDotView greenDot = new SpeedBumpDotView(context, attrs); + greenDot.setColor(getResources().getColor(R.color.speed_bump_dot_green)); + addView(greenDot); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int childWidthSpec = MeasureSpec.makeMeasureSpec(mDotSize, + MeasureSpec.getMode(widthMeasureSpec)); + int childHeightSpec = MeasureSpec.makeMeasureSpec(mDotSize, + MeasureSpec.getMode(heightMeasureSpec)); + measureChildren(childWidthSpec, childHeightSpec); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + child.layout(0, 0, mDotSize, mDotSize); + } + if (changed) { + updateChildren(); + } + } + + private void updateChildren() { + mAlgorithm.getState(mCurrentState); + mCurrentState.apply(); + } + + public void performVisibilityAnimation(boolean visible) { + if (mClickAnimator.isRunning()) { + mClickAnimator.cancel(); + } + mIsCurrentlyVisible = visible; + mAlgorithm.getState(mCurrentState); + mCurrentState.animateToState(); + } + + public void setInvisible() { + mIsCurrentlyVisible = false; + mAlgorithm.getState(mCurrentState); + mCurrentState.apply(); + } + + public boolean isCurrentlyVisible() { + return mIsCurrentlyVisible; + } + + public void performDotClickAnimation() { + if (mClickAnimator.isRunning()) { + // don't perform an animation if it's running already + return; + } + mClickAnimator.start(); + } + + + public float getAnimationProgress() { + return mAnimationProgress; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsState.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsState.java new file mode 100644 index 0000000..06a7f95 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsState.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar; + +import android.view.View; +import android.view.ViewPropertyAnimator; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; + +import java.util.HashMap; +import java.util.Map; + +/** + * A state of a {@link com.android.systemui.statusbar.SpeedBumpDotsLayout} + */ +public class SpeedBumpDotsState { + + public static final int HIDDEN = 1; + public static final int SHOWN = 2; + private static final int VISIBILITY_ANIMATION_DELAY_PER_ELEMENT = 80; + + private final SpeedBumpDotsLayout mHostView; + private final HashMap<View, ViewState> mStateMap = new HashMap<View, ViewState>(); + private final Interpolator mFastOutSlowInInterpolator; + private int mActiveState = 0; + + public SpeedBumpDotsState(SpeedBumpDotsLayout hostLayout) { + mHostView = hostLayout; + mFastOutSlowInInterpolator = AnimationUtils + .loadInterpolator(hostLayout.getContext(), + android.R.interpolator.fast_out_slow_in); + } + + public SpeedBumpDotsLayout getHostView() { + return mHostView; + } + + public void resetViewStates() { + int numChildren = mHostView.getChildCount(); + for (int i = 0; i < numChildren; i++) { + View child = mHostView.getChildAt(i); + ViewState viewState = mStateMap.get(child); + if (viewState == null) { + viewState = new ViewState(); + mStateMap.put(child, viewState); + } + } + } + + public ViewState getViewStateForView(View requestedView) { + return mStateMap.get(requestedView); + } + + public void apply() { + int childCount = mHostView.getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = mHostView.getChildAt(i); + ViewState viewState = mStateMap.get(child); + float translationX = child.getTranslationX(); + float translationY = child.getTranslationY(); + float scale = child.getScaleX(); + float alpha = child.getAlpha(); + if (translationX != viewState.xTranslation) { + child.setTranslationX(viewState.xTranslation); + } + if (translationY != viewState.yTranslation) { + child.setTranslationY(viewState.yTranslation); + } + if (scale != viewState.scale) { + child.setScaleX(viewState.scale); + child.setScaleY(viewState.scale); + } + if (alpha != viewState.alpha) { + child.setAlpha(viewState.alpha); + } + } + } + + public void animateToState() { + int childCount = mHostView.getChildCount(); + int middleIndex = (childCount - 1) / 2; + long delayPerElement = VISIBILITY_ANIMATION_DELAY_PER_ELEMENT; + boolean isAppearing = getActiveState() == SHOWN; + boolean isDisappearing = getActiveState() == HIDDEN; + for (int i = 0; i < childCount; i++) { + int delayIndex; + if (i <= middleIndex) { + delayIndex = i * 2; + } else { + int distToMiddle = i - middleIndex; + delayIndex = (childCount - 1) - (distToMiddle - 1) * 2; + } + long startDelay = 0; + if (isAppearing || isDisappearing) { + if (isDisappearing) { + delayIndex = childCount - 1 - delayIndex; + } + startDelay = delayIndex * delayPerElement; + } + View child = mHostView.getChildAt(i); + ViewState viewState = mStateMap.get(child); + child.animate().setInterpolator(mFastOutSlowInInterpolator) + .setStartDelay(startDelay) + .alpha(viewState.alpha).withLayer() + .translationX(viewState.xTranslation) + .translationY(viewState.yTranslation) + .scaleX(viewState.scale).scaleY(viewState.scale); + } + } + + public int getActiveState() { + return mActiveState; + } + + public void setActiveState(int mActiveState) { + this.mActiveState = mActiveState; + } + + public static class ViewState { + float xTranslation; + float yTranslation; + float alpha; + float scale; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java new file mode 100644 index 0000000..8ae503a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Outline; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; +import android.widget.TextView; +import com.android.systemui.R; + +/** + * The view representing the separation between important and less important notifications + */ +public class SpeedBumpView extends ExpandableView implements View.OnClickListener { + + private final int mCollapsedHeight; + private final int mDotsHeight; + private final int mTextPaddingInset; + private SpeedBumpDotsLayout mDots; + private View mLineLeft; + private View mLineRight; + private boolean mIsExpanded; + private boolean mDividerVisible = true; + private ValueAnimator mCurrentAnimator; + private final Interpolator mFastOutSlowInInterpolator; + private float mCenterX; + private TextView mExplanationText; + private boolean mExplanationTextVisible = false; + private AnimatorListenerAdapter mHideExplanationListener = new AnimatorListenerAdapter() { + private boolean mCancelled; + + @Override + public void onAnimationEnd(Animator animation) { + if (!mCancelled) { + mExplanationText.setVisibility(View.INVISIBLE); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + + @Override + public void onAnimationStart(Animator animation) { + mCancelled = false; + } + }; + private Animator.AnimatorListener mAnimationFinishedListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mCurrentAnimator = null; + } + }; + + public SpeedBumpView(Context context, AttributeSet attrs) { + super(context, attrs); + mCollapsedHeight = getResources() + .getDimensionPixelSize(R.dimen.speed_bump_height_collapsed); + mTextPaddingInset = getResources().getDimensionPixelSize( + R.dimen.speed_bump_text_padding_inset); + mDotsHeight = getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height); + setOnClickListener(this); + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), + android.R.interpolator.fast_out_slow_in); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mDots = (SpeedBumpDotsLayout) findViewById(R.id.speed_bump_dots_layout); + mLineLeft = findViewById(R.id.speedbump_line_left); + mLineRight = findViewById(R.id.speedbump_line_right); + mExplanationText = (TextView) findViewById(R.id.speed_bump_text); + resetExplanationText(); + + } + + @Override + protected int getInitialHeight() { + return mCollapsedHeight; + } + + @Override + public int getIntrinsicHeight() { + return getActualHeight(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + Outline outline = new Outline(); + mCenterX = getWidth() / 2; + float centerY = getHeight() / 2; + // TODO: hide outline better + // Temporary workaround to hide outline on a transparent view + int outlineLeft = (int) (mCenterX - getResources().getDisplayMetrics().densityDpi * 8); + int outlineTop = (int) (centerY - mDotsHeight / 2); + outline.setOval(outlineLeft, outlineTop, outlineLeft + mDotsHeight, + outlineTop + mDotsHeight); + setOutline(outline); + mLineLeft.setPivotX(mLineLeft.getWidth()); + mLineLeft.setPivotY(mLineLeft.getHeight() / 2); + mLineRight.setPivotX(0); + mLineRight.setPivotY(mLineRight.getHeight() / 2); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + measureChildren(widthMeasureSpec, heightMeasureSpec); + int height = mCollapsedHeight + mExplanationText.getMeasuredHeight() - mTextPaddingInset; + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height); + } + + @Override + public void onClick(View v) { + if (mCurrentAnimator != null) { + return; + } + int startValue = mIsExpanded ? getMaxHeight() : mCollapsedHeight; + int endValue = mIsExpanded ? mCollapsedHeight : getMaxHeight(); + mCurrentAnimator = ValueAnimator.ofInt(startValue, endValue); + mCurrentAnimator.setInterpolator(mFastOutSlowInInterpolator); + mCurrentAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + setActualHeight((int) animation.getAnimatedValue()); + } + }); + mCurrentAnimator.addListener(mAnimationFinishedListener); + mCurrentAnimator.start(); + mIsExpanded = !mIsExpanded; + mDots.performDotClickAnimation(); + animateExplanationTextInternal(mIsExpanded); + } + + private void animateExplanationTextInternal(boolean visible) { + if (mExplanationTextVisible != visible) { + float translationY = 0.0f; + float scale = 0.5f; + float alpha = 0.0f; + boolean needsHideListener = true; + if (visible) { + mExplanationText.setVisibility(VISIBLE); + translationY = mDots.getBottom() - mTextPaddingInset; + scale = 1.0f; + alpha = 1.0f; + needsHideListener = false; + } + mExplanationText.animate().setInterpolator(mFastOutSlowInInterpolator) + .alpha(alpha) + .scaleX(scale) + .scaleY(scale) + .translationY(translationY) + .setListener(needsHideListener ? mHideExplanationListener : null) + .withLayer(); + mExplanationTextVisible = visible; + } + } + + @Override + public boolean isTransparent() { + return true; + } + + public void performVisibilityAnimation(boolean nowVisible) { + animateDivider(nowVisible); + + // Animate explanation Text + if (mIsExpanded) { + animateExplanationTextInternal(nowVisible); + } + } + + public void animateDivider(boolean nowVisible) { + if (nowVisible != mDividerVisible) { + // Animate dividers + float endValue = nowVisible ? 1.0f : 0.0f; + float endTranslationXLeft = nowVisible ? 0.0f : mCenterX - mLineLeft.getRight(); + float endTranslationXRight = nowVisible ? 0.0f : mCenterX - mLineRight.getLeft(); + mLineLeft.animate() + .alpha(endValue) + .withLayer() + .scaleX(endValue) + .scaleY(endValue) + .translationX(endTranslationXLeft) + .setInterpolator(mFastOutSlowInInterpolator); + mLineRight.animate() + .alpha(endValue) + .withLayer() + .scaleX(endValue) + .scaleY(endValue) + .translationX(endTranslationXRight) + .setInterpolator(mFastOutSlowInInterpolator); + + // Animate dots + mDots.performVisibilityAnimation(nowVisible); + mDividerVisible = nowVisible; + } + } + + public void setInvisible() { + float endTranslationXLeft = mCenterX - mLineLeft.getRight(); + float endTranslationXRight = mCenterX - mLineRight.getLeft(); + mLineLeft.setAlpha(0.0f); + mLineLeft.setScaleX(0.0f); + mLineLeft.setScaleY(0.0f); + mLineLeft.setTranslationX(endTranslationXLeft); + mLineRight.setAlpha(0.0f); + mLineRight.setScaleX(0.0f); + mLineRight.setScaleY(0.0f); + mLineRight.setTranslationX(endTranslationXRight); + mDots.setInvisible(); + resetExplanationText(); + + mDividerVisible = false; + } + + public void collapse() { + if (mIsExpanded) { + setActualHeight(mCollapsedHeight); + mIsExpanded = false; + } + resetExplanationText(); + } + + public void animateExplanationText(boolean nowVisible) { + if (mIsExpanded) { + animateExplanationTextInternal(nowVisible); + } + } + + private void resetExplanationText() { + mExplanationText.setTranslationY(0); + mExplanationText.setVisibility(INVISIBLE); + mExplanationText.setAlpha(0.0f); + mExplanationText.setScaleX(0.5f); + mExplanationText.setScaleY(0.5f); + mExplanationTextVisible = false; + } + + public boolean isExpanded() { + return mIsExpanded; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java index eb63a54..a41ec22 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java @@ -43,6 +43,7 @@ public class BarTransitions { public static final int MODE_SEMI_TRANSPARENT = 1; public static final int MODE_TRANSLUCENT = 2; public static final int MODE_LIGHTS_OUT = 3; + public static final int MODE_TRANSPARENT = 4; public static final int LIGHTS_IN_DURATION = 250; public static final int LIGHTS_OUT_DURATION = 750; @@ -69,7 +70,8 @@ public class BarTransitions { public void transitionTo(int mode, boolean animate) { // low-end devices do not support translucent modes, fallback to opaque - if (!HIGH_END && (mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSLUCENT)) { + if (!HIGH_END && (mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSLUCENT + || mode == MODE_TRANSPARENT)) { mode = MODE_OPAQUE; } if (mMode == mode) return; @@ -97,6 +99,7 @@ public class BarTransitions { if (mode == MODE_SEMI_TRANSPARENT) return "MODE_SEMI_TRANSPARENT"; if (mode == MODE_TRANSLUCENT) return "MODE_TRANSLUCENT"; if (mode == MODE_LIGHTS_OUT) return "MODE_LIGHTS_OUT"; + if (mode == MODE_TRANSPARENT) return "MODE_TRANSPARENT"; throw new IllegalArgumentException("Unknown mode " + mode); } @@ -111,6 +114,7 @@ public class BarTransitions { private static class BarBackgroundDrawable extends Drawable { private final int mOpaque; private final int mSemiTransparent; + private final int mTransparent; private final Drawable mGradient; private final TimeInterpolator mInterpolator; @@ -130,9 +134,11 @@ public class BarTransitions { if (DEBUG_COLORS) { mOpaque = 0xff0000ff; mSemiTransparent = 0x7f0000ff; + mTransparent = 0x2f0000ff; } else { mOpaque = res.getColor(R.color.system_bar_background_opaque); mSemiTransparent = res.getColor(R.color.system_bar_background_semi_transparent); + mTransparent = res.getColor(R.color.system_bar_background_transparent); } mGradient = res.getDrawable(gradientResourceId); mInterpolator = new LinearInterpolator(); @@ -184,9 +190,11 @@ public class BarTransitions { public void draw(Canvas canvas) { int targetGradientAlpha = 0, targetColor = 0; if (mMode == MODE_TRANSLUCENT) { - targetGradientAlpha = 0xff; + targetColor = mSemiTransparent; } else if (mMode == MODE_SEMI_TRANSPARENT) { targetColor = mSemiTransparent; + } else if (mMode == MODE_TRANSPARENT) { + targetColor = mTransparent; } else { targetColor = mOpaque; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index 869edff..0a3fdef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -60,7 +60,8 @@ public class DemoStatusIcons extends LinearLayout implements DemoMode { } else if (mDemoMode && command.equals(COMMAND_STATUS)) { String volume = args.getString("volume"); if (volume != null) { - int iconId = volume.equals("silent") ? R.drawable.stat_sys_ringer_silent + int iconId = volume.equals("zen") ? R.drawable.stat_sys_ringer_zen + : volume.equals("silent") ? R.drawable.stat_sys_ringer_silent : volume.equals("vibrate") ? R.drawable.stat_sys_ringer_vibrate : 0; updateSlot("volume", null, iconId); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java index a0582ee..c83b479 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java @@ -61,7 +61,7 @@ public final class NavigationBarTransitions extends BarTransitions { @Override public void transitionTo(int mode, boolean animate) { mRequestedMode = mode; - if (mVertical && mode == MODE_TRANSLUCENT) { + if (mVertical && (mode == MODE_TRANSLUCENT || mode == MODE_TRANSPARENT)) { // translucent mode not allowed when vertical mode = MODE_OPAQUE; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index b9f5ab2..19252c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.content.Context; import android.util.AttributeSet; @@ -25,6 +27,7 @@ import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -36,12 +39,11 @@ import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.stack.StackStateAnimator; public class NotificationPanelView extends PanelView implements ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener, View.OnClickListener { - public static final boolean DEBUG_GESTURES = true; - private static final int EXPANSION_ANIMATION_LENGTH = 375; PhoneStatusBar mStatusBar; private StatusBarHeaderView mHeader; @@ -80,6 +82,20 @@ public class NotificationPanelView extends PanelView implements private FlingAnimationUtils mFlingAnimationUtils; private int mStatusBarMinHeight; + private int mClockNotificationsMarginMin; + private int mClockNotificationsMarginMax; + private float mClockYFractionMin; + private float mClockYFractionMax; + private Interpolator mFastOutSlowInInterpolator; + private ObjectAnimator mClockAnimator; + private int mClockAnimationTarget = -1; + + /** + * The number (fractional) of notifications the "more" card counts when calculating how many + * notifications are currently visible for the y positioning of the clock. + */ + private float mMoreCardNotificationAmount; + public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -113,9 +129,27 @@ public class NotificationPanelView extends PanelView implements mNotificationStackScroller = (NotificationStackScrollLayout) findViewById(R.id.notification_stack_scroller); mNotificationStackScroller.setOnHeightChangedListener(this); + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), + android.R.interpolator.fast_out_slow_in); + } + + @Override + protected void loadDimens() { + super.loadDimens(); mNotificationTopPadding = getResources().getDimensionPixelSize( R.dimen.notifications_top_padding); mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height); + mClockNotificationsMarginMin = getResources().getDimensionPixelSize( + R.dimen.keyguard_clock_notifications_margin_min); + mClockNotificationsMarginMax = getResources().getDimensionPixelSize( + R.dimen.keyguard_clock_notifications_margin_max); + mClockYFractionMin = + getResources().getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1); + mClockYFractionMax = + getResources().getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1); + mMoreCardNotificationAmount = + (float) getResources().getDimensionPixelSize(R.dimen.notification_summary_height) / + getResources().getDimensionPixelSize(R.dimen.notification_min_height); mFlingAnimationUtils = new FlingAnimationUtils(getContext()); mStatusBarMinHeight = getResources().getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height); @@ -124,15 +158,8 @@ public class NotificationPanelView extends PanelView implements @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - int keyguardBottomMargin = - ((MarginLayoutParams) mKeyguardStatusView.getLayoutParams()).bottomMargin; if (!mQsExpanded) { - mStackScrollerIntrinsicPadding = mStatusBar.getBarState() == StatusBarState.KEYGUARD - ? mKeyguardStatusView.getBottom() + keyguardBottomMargin - : mHeader.getBottom() + mNotificationTopPadding; - mNotificationStackScroller.setTopPadding(mStackScrollerIntrinsicPadding, - mAnimateNextTopPaddingChange); - mAnimateNextTopPaddingChange = false; + positionClockAndNotifications(); } // Calculate quick setting heights. @@ -143,8 +170,81 @@ public class NotificationPanelView extends PanelView implements } } - public void animateNextTopPaddingChange() { + /** + * Positions the clock and notifications dynamically depending on how many notifications are + * showing. + */ + private void positionClockAndNotifications() { + boolean animateClock = mNotificationStackScroller.isAddOrRemoveAnimationPending(); + if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) { + mStackScrollerIntrinsicPadding = mHeader.getBottom() + mNotificationTopPadding; + } else { + int notificationCount = mNotificationStackScroller.getNotGoneChildCount(); + int y = getClockY(notificationCount) - mKeyguardStatusView.getHeight()/2; + int padding = getClockNotificationsPadding(notificationCount); + if (animateClock || mClockAnimator != null) { + startClockAnimation(y); + } else { + mKeyguardStatusView.setY(y); + } + mStackScrollerIntrinsicPadding = y + mKeyguardStatusView.getHeight() + padding; + } + mNotificationStackScroller.setTopPadding(mStackScrollerIntrinsicPadding, + mAnimateNextTopPaddingChange || animateClock); + mAnimateNextTopPaddingChange = false; + } + + private void startClockAnimation(int y) { + if (mClockAnimationTarget == y) { + return; + } + mClockAnimationTarget = y; + getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + getViewTreeObserver().removeOnPreDrawListener(this); + if (mClockAnimator != null) { + mClockAnimator.removeAllListeners(); + mClockAnimator.cancel(); + } + mClockAnimator = + ObjectAnimator.ofFloat(mKeyguardStatusView, View.Y, mClockAnimationTarget); + mClockAnimator.setInterpolator(mFastOutSlowInInterpolator); + mClockAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); + mClockAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mClockAnimator = null; + mClockAnimationTarget = -1; + } + }); + StackStateAnimator.startInstantly(mClockAnimator); + return true; + } + }); + } + + private int getClockNotificationsPadding(int notificationCount) { + float t = notificationCount + / (mStatusBar.getMaxKeyguardNotifications() + mMoreCardNotificationAmount); + t = Math.min(t, 1.0f); + return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax); + } + + private float getClockYFraction(int notificationCount) { + float t = notificationCount + / (mStatusBar.getMaxKeyguardNotifications() + mMoreCardNotificationAmount); + t = Math.min(t, 1.0f); + return (1 - t) * mClockYFractionMax + t * mClockYFractionMin; + } + + private int getClockY(int notificationCount) { + return (int) (getClockYFraction(notificationCount) * getHeight()); + } + + public void animateToFullShade() { mAnimateNextTopPaddingChange = true; + mNotificationStackScroller.goToFullShade(); requestLayout(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 8c70517..517f763 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -20,6 +20,7 @@ import android.animation.ObjectAnimator; import android.animation.TimeAnimator; import android.animation.TimeAnimator.TimeListener; import android.content.Context; +import android.content.res.Configuration; import android.content.res.Resources; import android.util.AttributeSet; import android.util.Log; @@ -218,7 +219,7 @@ public class PanelView extends FrameLayout { }; private float mVel, mAccel; - protected int mMaxPanelHeight = 0; + protected int mMaxPanelHeight = -1; private String mViewName; private float mInitialTouchY; private float mInitialTouchX; @@ -321,7 +322,7 @@ public class PanelView extends FrameLayout { setOnHierarchyChangeListener(mHierarchyListener); } - private void loadDimens() { + protected void loadDimens() { final Resources res = getContext().getResources(); mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity); @@ -582,8 +583,14 @@ public class PanelView extends FrameLayout { @Override protected void onFinishInflate() { super.onFinishInflate(); + loadDimens(); + } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); loadDimens(); + mMaxPanelHeight = -1; } public void fling(float vel, boolean always) { @@ -617,7 +624,8 @@ public class PanelView extends FrameLayout { // Did one of our children change size? int newHeight = getMeasuredHeight(); - if (newHeight != mMaxPanelHeight) { + if (newHeight > mMaxPanelHeight) { + // we only adapt the max height if it's bigger mMaxPanelHeight = newHeight; // If the user isn't actively poking us, let's rubberband to the content if (!mTracking && !mTimeAnimator.isStarted() @@ -706,6 +714,7 @@ public class PanelView extends FrameLayout { * @return the default implementation simply returns the maximum height. */ protected int getMaxPanelHeight() { + mMaxPanelHeight = Math.max(mMaxPanelHeight, getHeight()); return mMaxPanelHeight; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 842627c..1072e49 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -26,6 +26,7 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OU import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT; +import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -105,6 +106,7 @@ import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.NotificationOverflowContainer; import com.android.systemui.statusbar.SignalClusterView; +import com.android.systemui.statusbar.SpeedBumpView; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.policy.BatteryController; @@ -221,8 +223,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, IconMerger mNotificationIcons; // [+> View mMoreIcon; - // mode indicator icon - ImageView mModeIcon; // expanded notifications NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window @@ -486,13 +486,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public void setZenMode(int mode) { super.setZenMode(mode); - if (mModeIcon == null) return; if (!isDeviceProvisioned()) return; final boolean zen = mode != Settings.Global.ZEN_MODE_OFF; - mModeIcon.setVisibility(zen ? View.VISIBLE : View.GONE); if (!zen) { mIntercepted.releaseIntercepted(); } + if (mIconPolicy != null) { + mIconPolicy.setZenMode(zen); + } } @Override @@ -616,8 +617,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons); mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon); mNotificationIcons.setOverflowIndicator(mMoreIcon); - mModeIcon = (ImageView)mStatusBarView.findViewById(R.id.modeIcon); - mModeIcon.setImageResource(R.drawable.stat_sys_zen_limited); mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents); mTickerView = mStatusBarView.findViewById(R.id.ticker); @@ -633,6 +632,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener); mStackScroller.addView(mKeyguardIconOverflowContainer); + SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate( + R.layout.status_bar_notification_speed_bump, mStackScroller, false); + mStackScroller.setSpeedBumpView(speedBump); + mExpandedContents = mStackScroller; mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header); @@ -1149,7 +1152,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, ArrayList<View> toRemove = new ArrayList<View>(); for (int i=0; i< mStackScroller.getChildCount(); i++) { View child = mStackScroller.getChildAt(i); - if (!toShow.contains(child) && child != mKeyguardIconOverflowContainer) { + if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) { toRemove.add(child); } } @@ -1870,6 +1873,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private int barMode(int vis, int transientFlag, int translucentFlag) { return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT + : (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT : MODE_OPAQUE; } @@ -2558,13 +2562,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0; } - public void postStartSettingsActivity(final Intent intent) { - mHandler.post(new Runnable() { + public void postStartSettingsActivity(final Intent intent, int delay) { + mHandler.postDelayed(new Runnable() { @Override public void run() { handleStartSettingsActivity(intent, true /*onlyProvisioned*/); } - }); + }, delay); } private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) { @@ -2580,7 +2584,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public void startSettingsActivity(String action) { - postStartSettingsActivity(new Intent(action)); + postStartSettingsActivity(new Intent(action), 0); } private static class FastColorDrawable extends Drawable { @@ -2720,7 +2724,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, setBarState(StatusBarState.SHADE); if (mLeaveOpenOnKeyguardHide) { mLeaveOpenOnKeyguardHide = false; - mNotificationPanel.animateNextTopPaddingChange(); + mNotificationPanel.animateToFullShade(); } else { instantCollapseNotificationPanel(); } @@ -2892,7 +2896,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mLeaveOpenOnKeyguardHide = true; showBouncer(); } else { - mNotificationPanel.animateNextTopPaddingChange(); + mNotificationPanel.animateToFullShade(); setBarState(StatusBarState.SHADE_LOCKED); updateKeyguardState(); } @@ -2915,4 +2919,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void reattachSystemIcons() { mSystemIconArea.addView(mSystemIcons, 0); } + + public void onScreenTurnedOff() { + mStackScroller.setAnimationsEnabled(false); + } + + public void onScreenTurnedOn() { + mStackScroller.setAnimationsEnabled(true); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 194774d..b6f5ae0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -63,6 +63,9 @@ public class PhoneStatusBarPolicy { // ringer volume private boolean mVolumeVisible; + // zen mode + private boolean mZen; + // bluetooth device status private boolean mBluetoothEnabled = false; @@ -152,6 +155,11 @@ public class PhoneStatusBarPolicy { updateVolume(); } + public void setZenMode(boolean zen) { + mZen = zen; + updateVolume(); + } + private final void updateAlarm(Intent intent) { boolean alarmSet = intent.getBooleanExtra("alarmSet", false); mService.setIconVisibility("alarm_clock", alarmSet); @@ -195,11 +203,15 @@ public class PhoneStatusBarPolicy { AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); final int ringerMode = audioManager.getRingerMode(); final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT || - ringerMode == AudioManager.RINGER_MODE_VIBRATE; + ringerMode == AudioManager.RINGER_MODE_VIBRATE || + mZen; final int iconId; String contentDescription = null; - if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { + if (mZen) { + iconId = R.drawable.stat_sys_ringer_zen; + contentDescription = mContext.getString(R.string.zen_mode_title); + } else if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { iconId = R.drawable.stat_sys_ringer_vibrate; contentDescription = mContext.getString(R.string.accessibility_ringer_vibrate); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java index 8406565..8520f40 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java @@ -68,7 +68,8 @@ public final class PhoneStatusBarTransitions extends BarTransitions { } private boolean isOpaque(int mode) { - return !(mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSLUCENT); + return !(mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSLUCENT + || mode == MODE_TRANSPARENT); } @Override 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 1fe3be5..7029898 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.phone; import android.content.Context; import android.content.Intent; -import android.graphics.drawable.VectorDrawable; import android.os.HandlerThread; import android.os.Looper; @@ -113,7 +112,7 @@ public class QSTileHost implements QSTile.Host { @Override public void startSettingsActivity(final Intent intent) { - mStatusBar.postStartSettingsActivity(intent); + mStatusBar.postStartSettingsActivity(intent, QSTile.FEEDBACK_START_DELAY); } @Override @@ -137,11 +136,6 @@ public class QSTileHost implements QSTile.Host { } @Override - public VectorDrawable getVectorDrawable(int resId) { - return (VectorDrawable) mContext.getDrawable(resId); - } - - @Override public BluetoothController getBluetoothController() { return mBluetooth; } 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 2305445..36b063b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -86,6 +86,7 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL (ImageView) findViewById(R.id.brightness_icon), (ToggleSlider) findViewById(R.id.brightness_slider)); loadDimens(); + updateVisibilities(); } private void loadDimens() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 77b760e..1040c15 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -125,11 +125,13 @@ public class StatusBarKeyguardViewManager { public void onScreenTurnedOff() { mScreenOn = false; + mPhoneStatusBar.onScreenTurnedOff(); mBouncer.onScreenTurnedOff(); } public void onScreenTurnedOn(final IKeyguardShowCallback callback) { mScreenOn = true; + mPhoneStatusBar.onScreenTurnedOn(); if (callback != null) { callbackAfterDraw(callback); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java index 46a637b..b7bf6cd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java @@ -75,8 +75,7 @@ public class StatusBarWindowManager { | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, + | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, PixelFormat.TRANSLUCENT); mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; mLp.gravity = Gravity.TOP; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java index 1c7119f..5a19881 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -59,6 +59,7 @@ public class BluetoothControllerImpl extends BroadcastReceiver implements Blueto public void addStateChangedCallback(BluetoothStateChangeCallback cb) { mChangeCallbacks.add(cb); + fireCallback(cb); } @Override @@ -131,7 +132,11 @@ public class BluetoothControllerImpl extends BroadcastReceiver implements Blueto private void fireCallbacks() { for (BluetoothStateChangeCallback cb : mChangeCallbacks) { - cb.onBluetoothStateChange(mEnabled); + fireCallback(cb); } } + + private void fireCallback(BluetoothStateChangeCallback cb) { + cb.onBluetoothStateChange(mEnabled); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java index 33a85b1..bcd865c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java @@ -28,6 +28,10 @@ public class CastControllerImpl implements CastController { private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); private final MediaRouter mMediaRouter; + private boolean mEnabled; + private boolean mConnecting; + private String mConnectedRouteName; + public CastControllerImpl(Context context) { mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); } @@ -35,6 +39,7 @@ public class CastControllerImpl implements CastController { @Override public void addCallback(Callback callback) { mCallbacks.add(callback); + fireStateChanged(callback); } @Override @@ -76,12 +81,23 @@ public class CastControllerImpl implements CastController { if (connectedRoute != null) { connectedRouteName = connectedRoute.getName().toString(); } - fireStateChanged(enabled, connecting, connectedRouteName); + synchronized(mCallbacks) { + mEnabled = enabled; + mConnecting = connecting; + mConnectedRouteName = connectedRouteName; + } + fireStateChanged(); } - private void fireStateChanged(boolean enabled, boolean connecting, String connectedRouteName) { + private void fireStateChanged() { for (Callback callback : mCallbacks) { - callback.onStateChanged(enabled, connecting, connectedRouteName); + fireStateChanged(callback); + } + } + + private void fireStateChanged(Callback callback) { + synchronized(mCallbacks) { + callback.onStateChanged(mEnabled, mConnecting, mConnectedRouteName); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Disposable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Listenable.java index 158e9c1..4fa59fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Disposable.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Listenable.java @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.policy; -/** Common interface for items requiring manual cleanup. **/ -public interface Disposable { - void dispose(); +/** Common interface for components with an active listening state. **/ +public interface Listenable { + void setListening(boolean listening); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java index 9e5ad18..d5b2548 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java @@ -92,6 +92,7 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio */ public void addSettingsChangedCallback(LocationSettingsChangeCallback cb) { mSettingsChangeCallbacks.add(cb); + locationSettingsChanged(cb); } public void removeSettingsChangedCallback(LocationSettingsChangeCallback cb) { @@ -204,6 +205,10 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio } } + private void locationSettingsChanged(LocationSettingsChangeCallback cb) { + cb.onLocationSettingsChanged(isLocationEnabled()); + } + @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java index 1eb678d..93c4691 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.policy; -public interface RotationLockController extends Disposable { +public interface RotationLockController extends Listenable { int getRotationLockOrientation(); boolean isRotationLockAffordanceVisible(); boolean isRotationLocked(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java index caa07ef..c3bcd94 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java @@ -39,12 +39,12 @@ public final class RotationLockControllerImpl implements RotationLockController public RotationLockControllerImpl(Context context) { mContext = context; - RotationPolicy.registerRotationPolicyListener(mContext, - mRotationPolicyListener, UserHandle.USER_ALL); + setListening(true); } public void addRotationLockControllerCallback(RotationLockControllerCallback callback) { mCallbacks.add(callback); + notifyChanged(callback); } public void removeRotationLockControllerCallback(RotationLockControllerCallback callback) { @@ -68,14 +68,23 @@ public final class RotationLockControllerImpl implements RotationLockController } @Override - public void dispose() { - RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener); + public void setListening(boolean listening) { + if (listening) { + RotationPolicy.registerRotationPolicyListener(mContext, mRotationPolicyListener, + UserHandle.USER_ALL); + } else { + RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener); + } } private void notifyChanged() { for (RotationLockControllerCallback callback : mCallbacks) { - callback.onRotationLockStateChanged(RotationPolicy.isRotationLocked(mContext), - RotationPolicy.isRotationLockToggleVisible(mContext)); + notifyChanged(callback); } } + + private void notifyChanged(RotationLockControllerCallback callback) { + callback.onRotationLockStateChanged(RotationPolicy.isRotationLocked(mContext), + RotationPolicy.isRotationLockToggleVisible(mContext)); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java index d760f78..adf2935 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -52,6 +52,7 @@ public class ZenModeControllerImpl implements ZenModeController { fireZenChanged(value != 0); } }; + mSetting.setListening(true); mNoMan = INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index deab757..b21e12c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -30,6 +30,7 @@ public class AmbientState { private View mActivatedChild; private float mOverScrollTopAmount; private float mOverScrollBottomAmount; + private int mSpeedBumpIndex = -1; public int getScrollY() { return mScrollY; @@ -86,4 +87,12 @@ public class AmbientState { public float getOverScrollAmount(boolean top) { return top ? mOverScrollTopAmount : mOverScrollBottomAmount; } + + public int getSpeedBumpIndex() { + return mSpeedBumpIndex; + } + + public void setSpeedBumpIndex(int speedBumpIndex) { + mSpeedBumpIndex = speedBumpIndex; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index fbb6695..58ada75 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -39,6 +39,7 @@ import com.android.systemui.R; import com.android.systemui.SwipeHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; +import com.android.systemui.statusbar.SpeedBumpView; import com.android.systemui.statusbar.stack.StackScrollState.ViewState; import com.android.systemui.statusbar.policy.ScrollAdapter; @@ -107,6 +108,7 @@ public class NotificationStackScrollLayout extends ViewGroup = new ArrayList<AnimationEvent>(); private ArrayList<View> mSwipedOutViews = new ArrayList<View>(); private final StackStateAnimator mStateAnimator = new StackStateAnimator(this); + private boolean mAnimationsEnabled; /** * The raw amount of the overScroll on the top, which is not rubber-banded. @@ -126,6 +128,8 @@ public class NotificationStackScrollLayout extends ViewGroup private boolean mActivateNeedsAnimation; private boolean mIsExpanded = true; private boolean mChildrenUpdateRequested; + private SpeedBumpView mSpeedBumpView; + private boolean mIsExpansionChanging; private ViewTreeObserver.OnPreDrawListener mChildrenUpdater = new ViewTreeObserver.OnPreDrawListener() { @Override @@ -244,6 +248,22 @@ public class NotificationStackScrollLayout extends ViewGroup requestChildrenUpdate(); } + public void updateSpeedBumpIndex(int newIndex) { + int currentIndex = indexOfChild(mSpeedBumpView); + + // If we are currently layouted before the new speed bump index, we have to decrease it. + boolean validIndex = newIndex > 0; + if (newIndex > getChildCount() - 1) { + validIndex = false; + newIndex = -1; + } + if (validIndex && currentIndex != newIndex) { + changeViewPosition(mSpeedBumpView, newIndex); + } + updateSpeedBump(validIndex); + mAmbientState.setSpeedBumpIndex(newIndex); + } + public void setChildLocationsChangedListener(OnChildLocationsChangedListener listener) { mListener = listener; } @@ -333,7 +353,7 @@ public class NotificationStackScrollLayout extends ViewGroup mTopPadding = topPadding; updateAlgorithmHeightAndPadding(); updateContentHeight(); - if (animate) { + if (animate && mAnimationsEnabled && mIsExpanded) { mTopPaddingNeedsAnimation = true; mNeedsAnimation = true; } @@ -421,9 +441,11 @@ public class NotificationStackScrollLayout extends ViewGroup public void onChildSnappedBack(View animView) { mAmbientState.onDragFinished(animView); if (!mDragAnimPendingChildren.contains(animView)) { - mSnappedBackChildren.add(animView); + if (mAnimationsEnabled) { + mSnappedBackChildren.add(animView); + mNeedsAnimation = true; + } requestChildrenUpdate(); - mNeedsAnimation = true; } else { // We start the swipe and snap back in the same frame, we don't want any animation mDragAnimPendingChildren.remove(animView); @@ -432,10 +454,12 @@ public class NotificationStackScrollLayout extends ViewGroup public void onBeginDrag(View v) { setSwipingInProgress(true); - mDragAnimPendingChildren.add(v); mAmbientState.onBeginDrag(v); + if (mAnimationsEnabled) { + mDragAnimPendingChildren.add(v); + mNeedsAnimation = true; + } requestChildrenUpdate(); - mNeedsAnimation = true; } public void onDragCancelled(View v) { @@ -942,6 +966,21 @@ public class NotificationStackScrollLayout extends ViewGroup return null; } + /** + * @return the number of children which have visibility unequal to GONE + */ + public int getNotGoneChildCount() { + int childCount = getChildCount(); + int count = 0; + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() != View.GONE) { + count++; + } + } + return count; + } + private int getMaxExpandHeight(View view) { if (view instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) view; @@ -1044,8 +1083,11 @@ public class NotificationStackScrollLayout extends ViewGroup mCurrentStackScrollState.removeViewStateForView(child); mStackScrollAlgorithm.notifyChildrenChanged(this); updateScrollStateForRemovedChild(child); - if (mIsExpanded) { + generateRemoveAnimation(child); + } + private void generateRemoveAnimation(View child) { + if (mIsExpanded && mAnimationsEnabled) { if (!mChildrenToAddAnimated.contains(child)) { // Generate Animations mChildrenToRemoveAnimated.add(child); @@ -1103,8 +1145,17 @@ public class NotificationStackScrollLayout extends ViewGroup } } + public void setAnimationsEnabled(boolean animationsEnabled) { + mAnimationsEnabled = animationsEnabled; + } + + public boolean isAddOrRemoveAnimationPending() { + return mNeedsAnimation + && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty()); + } + public void generateAddAnimation(View child) { - if (mIsExpanded) { + if (mIsExpanded && mAnimationsEnabled) { // Generate Animations mChildrenToAddAnimated.add(child); @@ -1120,7 +1171,9 @@ public class NotificationStackScrollLayout extends ViewGroup */ public void changeViewPosition(View child, int newIndex) { if (child != null && child.getParent() == this) { - // TODO: handle this + removeView(child); + addView(child, newIndex); + // TODO: handle events } } @@ -1362,10 +1415,12 @@ public class NotificationStackScrollLayout extends ViewGroup } public void onExpansionStarted() { + mIsExpansionChanging = true; mStackScrollAlgorithm.onExpansionStarted(mCurrentStackScrollState); } public void onExpansionStopped() { + mIsExpansionChanging = false; mStackScrollAlgorithm.onExpansionStopped(); } @@ -1374,6 +1429,7 @@ public class NotificationStackScrollLayout extends ViewGroup mStackScrollAlgorithm.setIsExpanded(isExpanded); if (!isExpanded) { mOwnScrollY = 0; + mSpeedBumpView.collapse(); } } @@ -1404,7 +1460,7 @@ public class NotificationStackScrollLayout extends ViewGroup mStackScrollAlgorithm.setDimmed(dimmed); mAmbientState.setDimmed(dimmed); updatePadding(dimmed); - if (animate) { + if (animate && mAnimationsEnabled) { mDimmedNeedsAnimation = true; mNeedsAnimation = true; } @@ -1416,8 +1472,10 @@ public class NotificationStackScrollLayout extends ViewGroup */ public void setActivatedChild(View activatedChild) { mAmbientState.setActivatedChild(activatedChild); - mActivateNeedsAnimation = true; - mNeedsAnimation = true; + if (mAnimationsEnabled) { + mActivateNeedsAnimation = true; + mNeedsAnimation = true; + } requestChildrenUpdate(); } @@ -1432,6 +1490,34 @@ public class NotificationStackScrollLayout extends ViewGroup } } + public void setSpeedBumpView(SpeedBumpView speedBumpView) { + mSpeedBumpView = speedBumpView; + addView(speedBumpView); + } + + private void updateSpeedBump(boolean visible) { + int newVisibility = visible ? VISIBLE : GONE; + int oldVisibility = mSpeedBumpView.getVisibility(); + if (newVisibility != oldVisibility) { + mSpeedBumpView.setVisibility(newVisibility); + if (visible) { + mSpeedBumpView.collapse(); + // Make invisible to ensure that the appear animation is played. + mSpeedBumpView.setInvisible(); + if (!mIsExpansionChanging) { + generateAddAnimation(mSpeedBumpView); + } + } else { + mSpeedBumpView.performVisibilityAnimation(false); + generateRemoveAnimation(mSpeedBumpView); + } + } + } + + public void goToFullShade() { + updateSpeedBump(true); + } + /** * A listener that is notified when some child locations might have changed. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java index 8fc26d8..011411c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java @@ -23,6 +23,7 @@ import android.view.View; import android.view.ViewGroup; import com.android.systemui.statusbar.ExpandableView; +import com.android.systemui.statusbar.SpeedBumpView; import java.util.HashMap; import java.util.Map; @@ -167,11 +168,48 @@ public class StackScrollState { clipHeight, (int) (newHeight - (previousNotificationStart - newYTranslation))); - previousNotificationStart = newYTranslation + child.getClipTopAmount(); - previousNotificationEnd = newNotificationEnd; - previousNotificationIsSwiped = child.getTranslationX() != 0; + if (!child.isTransparent()) { + // Only update the previous values if we are not transparent, + // otherwise we would clip to a transparent view. + previousNotificationStart = newYTranslation + child.getClipTopAmount(); + previousNotificationEnd = newNotificationEnd; + previousNotificationIsSwiped = child.getTranslationX() != 0; + } + + if(child instanceof SpeedBumpView) { + performSpeedBumpAnimation(i, (SpeedBumpView) child, newNotificationEnd, + newYTranslation); + } + } + } + } + + private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd, + float speedBumpStart) { + View nextChild = getNextChildNotGone(i); + if (nextChild != null) { + ViewState nextState = getViewStateForView(nextChild); + boolean startIsAboveNext = nextState.yTranslation > speedBumpStart; + speedBump.animateDivider(startIsAboveNext); + + // handle expanded case + if (speedBump.isExpanded()) { + boolean endIsAboveNext = nextState.yTranslation > speedBumpEnd; + speedBump.animateExplanationText(endIsAboveNext); + } + + } + } + + private View getNextChildNotGone(int childIndex) { + int childCount = mHostView.getChildCount(); + for (int i = childIndex + 1; i < childCount; i++) { + View child = mHostView.getChildAt(i); + if (child.getVisibility() != View.GONE) { + return child; } } + return null; } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index 5ac51f8..a9dcdd6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -434,7 +434,7 @@ public class StackStateAnimator { /** * Start an animator instantly instead of waiting on the next synchronization frame */ - private void startInstantly(ValueAnimator animator) { + public static void startInstantly(ValueAnimator animator) { animator.start(); animator.setCurrentPlayTime(0); } |
