diff options
Diffstat (limited to 'packages/SystemUI/src/com')
66 files changed, 3840 insertions, 1611 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index e606156..06c2957 100755 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2013-14 The Android Open Source Project + * Copyright (C) 2016 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,43 +17,47 @@ package com.android.systemui; -import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.statusbar.policy.BatteryStateRegistar; - import android.animation.ArgbEvaluator; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.content.res.Resources; +import android.content.res.ThemeConfig; import android.content.res.TypedArray; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; -import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; -import android.graphics.RectF; import android.graphics.Typeface; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; import android.os.BatteryManager; import android.os.Bundle; import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; import android.view.View; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.BatteryStateRegistar; + +import org.cyanogenmod.graphics.drawable.StopMotionVectorDrawable; + public class BatteryMeterView extends View implements DemoMode, BatteryController.BatteryStateChangeCallback { public static final String TAG = BatteryMeterView.class.getSimpleName(); public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST"; - private static final int FULL = 96; - - private static final float BOLT_LEVEL_THRESHOLD = 0.3f; // opaque bolt below this fraction - private final int[] mColors; protected boolean mShowPercent = true; - private float mButtonHeightFraction; - private float mSubpixelSmoothingLeft; - private float mSubpixelSmoothingRight; public enum BatteryMeterMode { BATTERY_METER_GONE, @@ -66,14 +71,9 @@ public class BatteryMeterView extends View implements DemoMode, private int mWidth; private String mWarningString; private final int mCriticalLevel; - private final int mFrameColor; private boolean mAnimationsEnabled; - private final Path mShapePath = new Path(); - private final Path mClipPath = new Path(); - private final Path mTextPath = new Path(); - private BatteryStateRegistar mBatteryStateRegistar; private BatteryController mBatteryController; private boolean mPowerSaveEnabled; @@ -94,6 +94,9 @@ public class BatteryMeterView extends View implements DemoMode, private BatteryMeterDrawable mBatteryMeterDrawable; private int mIconTint = Color.WHITE; + private int mCurrentBackgroundColor = 0; + private int mCurrentFillColor = 0; + protected class BatteryTracker extends BroadcastReceiver { public static final int UNKNOWN_LEVEL = -1; @@ -225,8 +228,6 @@ public class BatteryMeterView extends View implements DemoMode, final Resources res = context.getResources(); TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView, defStyle, 0); - mFrameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor, - res.getColor(R.color.batterymeter_frame_color)); TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels); TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values); @@ -242,12 +243,6 @@ public class BatteryMeterView extends View implements DemoMode, mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol); mCriticalLevel = getContext().getResources().getInteger( com.android.internal.R.integer.config_criticalBatteryWarningLevel); - mButtonHeightFraction = context.getResources().getFraction( - R.fraction.battery_button_height_fraction, 1, 1); - mSubpixelSmoothingLeft = context.getResources().getFraction( - R.fraction.battery_subpixel_smoothing_left, 1, 1); - mSubpixelSmoothingRight = context.getResources().getFraction( - R.fraction.battery_subpixel_smoothing_right, 1, 1); mDarkModeBackgroundColor = context.getColor(R.color.dark_mode_icon_color_dual_tone_background); @@ -260,17 +255,13 @@ public class BatteryMeterView extends View implements DemoMode, } protected BatteryMeterDrawable createBatteryMeterDrawable(BatteryMeterMode mode) { - Resources res = mContext.getResources(); + Resources res = getResources(); switch (mode) { - case BATTERY_METER_CIRCLE: - return new CircleBatteryMeterDrawable(res); - case BATTERY_METER_ICON_LANDSCAPE: - return new NormalBatteryMeterDrawable(res, true); case BATTERY_METER_TEXT: case BATTERY_METER_GONE: return null; default: - return new NormalBatteryMeterDrawable(res, false); + return new AllInOneBatteryMeterDrawable(res, mode); } } @@ -279,13 +270,8 @@ public class BatteryMeterView extends View implements DemoMode, int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); - if (mMeterMode == BatteryMeterMode.BATTERY_METER_CIRCLE) { - height += (CircleBatteryMeterDrawable.STROKE_WITH / 3); - width = height; - } else if (mMeterMode == BatteryMeterMode.BATTERY_METER_TEXT) { + if (mMeterMode == BatteryMeterMode.BATTERY_METER_TEXT) { onSizeChanged(width, height, 0, 0); // Force a size changed event - } else if (mMeterMode == BatteryMeterMode.BATTERY_METER_ICON_LANDSCAPE) { - width = (int)(height * 1.2f); } setMeasuredDimension(width, height); @@ -378,11 +364,6 @@ public class BatteryMeterView extends View implements DemoMode, mBatteryMeterDrawable.onDispose(); } mBatteryMeterDrawable = createBatteryMeterDrawable(mode); - if (mMeterMode == BatteryMeterMode.BATTERY_METER_ICON_PORTRAIT || - mMeterMode == BatteryMeterMode.BATTERY_METER_ICON_LANDSCAPE) { - ((NormalBatteryMeterDrawable)mBatteryMeterDrawable).loadBoltPoints( - mContext.getResources()); - } if (tracker.present) { setVisibility(View.VISIBLE); requestLayout(); @@ -394,7 +375,6 @@ public class BatteryMeterView extends View implements DemoMode, } public int getColorForLevel(int percent) { - // If we are in power save mode, always use the normal color. if (mPowerSaveEnabled) { return mColors[mColors.length-1]; @@ -418,9 +398,9 @@ public class BatteryMeterView extends View implements DemoMode, public void setDarkIntensity(float darkIntensity) { if (mBatteryMeterDrawable != null) { - int backgroundColor = getBackgroundColor(darkIntensity); - int fillColor = getFillColor(darkIntensity); - mBatteryMeterDrawable.setDarkIntensity(backgroundColor, fillColor); + mCurrentBackgroundColor = getBackgroundColor(darkIntensity); + mCurrentFillColor = getFillColor(darkIntensity); + mBatteryMeterDrawable.setDarkIntensity(mCurrentBackgroundColor, mCurrentFillColor); } } @@ -482,253 +462,83 @@ public class BatteryMeterView extends View implements DemoMode, void setDarkIntensity(int backgroundColor, int fillColor); } - protected class NormalBatteryMeterDrawable implements BatteryMeterDrawable { + protected class AllInOneBatteryMeterDrawable implements BatteryMeterDrawable { private static final boolean SINGLE_DIGIT_PERCENT = false; private static final boolean SHOW_100_PERCENT = false; private boolean mDisposed; - protected final boolean mHorizontal; + private boolean mIsAnimating; // stores charge-animation status to remove callbacks + + private float mTextX, mTextY; // precalculated position for drawText() to appear centered - private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint; - private float mTextHeight, mWarningTextHeight; + private boolean mInitialized; - private int mChargeColor; - private final float[] mBoltPoints; - private final Path mBoltPath = new Path(); + private Paint mTextAndBoltPaint; + private Paint mWarningTextPaint; + private Paint mClearPaint; - private final RectF mFrame = new RectF(); - private final RectF mButtonFrame = new RectF(); - private final RectF mBoltFrame = new RectF(); + private LayerDrawable mBatteryDrawable; + private Drawable mFrameDrawable; + private StopMotionVectorDrawable mLevelDrawable; + private Drawable mBoltDrawable; - public NormalBatteryMeterDrawable(Resources res, boolean horizontal) { + private BatteryMeterMode mMode; + private int mTextGravity; + + public AllInOneBatteryMeterDrawable(Resources res, BatteryMeterMode mode) { super(); - mHorizontal = horizontal; - mDisposed = false; - mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mFramePaint.setColor(mFrameColor); - mFramePaint.setDither(true); - mFramePaint.setStrokeWidth(0); - mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE); + loadBatteryDrawables(res, mode); - mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mBatteryPaint.setDither(true); - mBatteryPaint.setStrokeWidth(0); - mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE); + mMode = mode; + mDisposed = false; - mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + // load text gravity and blend mode + int[] attrs = new int[] {android.R.attr.gravity, R.attr.blendMode}; + int resId = getBatteryDrawableStyleResourceForMode(mode); + PorterDuff.Mode xferMode = PorterDuff.Mode.XOR; + if (resId != 0) { + TypedArray a = getContext().obtainStyledAttributes( + getBatteryDrawableStyleResourceForMode(mode), attrs); + mTextGravity = a.getInt(0, Gravity.CENTER); + xferMode = PorterDuff.intToMode(a.getInt(1, + PorterDuff.modeToInt(PorterDuff.Mode.XOR))); + } else { + mTextGravity = Gravity.CENTER; + } + Log.d(TAG, "mTextGravity=" + mTextGravity); + + mTextAndBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG); Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD); - mTextPaint.setTypeface(font); - mTextPaint.setTextAlign(Paint.Align.CENTER); + mTextAndBoltPaint.setTypeface(font); + mTextAndBoltPaint.setTextAlign(getPaintAlignmentFromGravity(mTextGravity)); + mTextAndBoltPaint.setXfermode(new PorterDuffXfermode(xferMode)); + mTextAndBoltPaint.setColor(mCurrentFillColor != 0 + ? mCurrentFillColor + : res.getColor(R.color.batterymeter_bolt_color)); mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mWarningTextPaint.setColor(mColors[1]); font = Typeface.create("sans-serif", Typeface.BOLD); mWarningTextPaint.setTypeface(font); - mWarningTextPaint.setTextAlign(Paint.Align.CENTER); - - mChargeColor = getResources().getColor(R.color.batterymeter_charge_color); + mWarningTextPaint.setTextAlign(getPaintAlignmentFromGravity(mTextGravity)); - mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color)); - mBoltPoints = loadBoltPoints(res); + mClearPaint = new Paint(); + mClearPaint.setColor(0); } @Override public void onDraw(Canvas c, BatteryTracker tracker) { if (mDisposed) return; - final int level = tracker.level; - - if (level == BatteryTracker.UNKNOWN_LEVEL) return; - - float drawFrac = (float) level / 100f; - final int pt = getPaddingTop() + (mHorizontal ? (int)(mHeight * 0.12f) : 0); - final int pl = getPaddingLeft(); - final int pr = getPaddingRight(); - final int pb = getPaddingBottom() + (mHorizontal ? (int)(mHeight * 0.08f) : 0); - final int height = mHeight - pt - pb; - final int width = mWidth - pl - pr; - - final int buttonHeight = (int) ((mHorizontal ? width : height) * mButtonHeightFraction); - - mFrame.set(0, 0, width, height); - mFrame.offset(pl, pt); - - if (mHorizontal) { - mButtonFrame.set( - /*cover frame border of intersecting area*/ - width - buttonHeight - mFrame.left, - mFrame.top + Math.round(height * 0.25f), - mFrame.right, - mFrame.bottom - Math.round(height * 0.25f)); - - mButtonFrame.top += mSubpixelSmoothingLeft; - mButtonFrame.bottom -= mSubpixelSmoothingRight; - mButtonFrame.right -= mSubpixelSmoothingRight; - } else { - // button-frame: area above the battery body - mButtonFrame.set( - mFrame.left + Math.round(width * 0.25f), - mFrame.top, - mFrame.right - Math.round(width * 0.25f), - mFrame.top + buttonHeight); - - mButtonFrame.top += mSubpixelSmoothingLeft; - mButtonFrame.left += mSubpixelSmoothingLeft; - mButtonFrame.right -= mSubpixelSmoothingRight; - } - - // frame: battery body area - - if (mHorizontal) { - mFrame.right -= buttonHeight; - } else { - mFrame.top += buttonHeight; - } - mFrame.left += mSubpixelSmoothingLeft; - mFrame.top += mSubpixelSmoothingLeft; - mFrame.right -= mSubpixelSmoothingRight; - mFrame.bottom -= mSubpixelSmoothingRight; - - // set the battery charging color - mBatteryPaint.setColor(tracker.plugged ? mChargeColor : getColorForLevel(level)); - - if (level >= FULL) { - drawFrac = 1f; - } else if (level <= mCriticalLevel) { - drawFrac = 0f; - } - - final float levelTop; - - if (drawFrac == 1f) { - if (mHorizontal) { - levelTop = mButtonFrame.right; - } else { - levelTop = mButtonFrame.top; - } - } else { - if (mHorizontal) { - levelTop = (mFrame.right - (mFrame.width() * (1f - drawFrac))); - } else { - levelTop = (mFrame.top + (mFrame.height() * (1f - drawFrac))); - } - } - - // define the battery shape - mShapePath.reset(); - mShapePath.moveTo(mButtonFrame.left, mButtonFrame.top); - if (mHorizontal) { - mShapePath.lineTo(mButtonFrame.right, mButtonFrame.top); - mShapePath.lineTo(mButtonFrame.right, mButtonFrame.bottom); - mShapePath.lineTo(mButtonFrame.left, mButtonFrame.bottom); - mShapePath.lineTo(mFrame.right, mFrame.bottom); - mShapePath.lineTo(mFrame.left, mFrame.bottom); - mShapePath.lineTo(mFrame.left, mFrame.top); - mShapePath.lineTo(mButtonFrame.left, mFrame.top); - mShapePath.lineTo(mButtonFrame.left, mButtonFrame.top); - } else { - mShapePath.lineTo(mButtonFrame.right, mButtonFrame.top); - mShapePath.lineTo(mButtonFrame.right, mFrame.top); - mShapePath.lineTo(mFrame.right, mFrame.top); - mShapePath.lineTo(mFrame.right, mFrame.bottom); - mShapePath.lineTo(mFrame.left, mFrame.bottom); - mShapePath.lineTo(mFrame.left, mFrame.top); - mShapePath.lineTo(mButtonFrame.left, mFrame.top); - mShapePath.lineTo(mButtonFrame.left, mButtonFrame.top); + if (!mInitialized) { + init(); } - if (tracker.plugged) { - // define the bolt shape - final float bl = mFrame.left + mFrame.width() / (mHorizontal ? 9f : 4.5f); - final float bt = mFrame.top + mFrame.height() / (mHorizontal ? 4.5f : 6f); - final float br = mFrame.right - mFrame.width() / (mHorizontal ? 6f : 7f); - final float bb = mFrame.bottom - mFrame.height() / (mHorizontal ? 7f : 10f); - if (mBoltFrame.left != bl || mBoltFrame.top != bt - || mBoltFrame.right != br || mBoltFrame.bottom != bb) { - mBoltFrame.set(bl, bt, br, bb); - mBoltPath.reset(); - mBoltPath.moveTo( - mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height()); - for (int i = 2; i < mBoltPoints.length; i += 2) { - mBoltPath.lineTo( - mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height()); - } - mBoltPath.lineTo( - mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height()); - } - - float boltPct = mHorizontal ? - (mBoltFrame.left - levelTop) / (mBoltFrame.left - mBoltFrame.right) : - (mBoltFrame.bottom - levelTop) / (mBoltFrame.bottom - mBoltFrame.top); - boltPct = Math.min(Math.max(boltPct, 0), 1); - if (boltPct <= BOLT_LEVEL_THRESHOLD) { - // draw the bolt if opaque - c.drawPath(mBoltPath, mBoltPaint); - } else { - // otherwise cut the bolt out of the overall shape - mShapePath.op(mBoltPath, Path.Op.DIFFERENCE); - } - } - - // compute percentage text - boolean pctOpaque = false; - float pctX = 0, pctY = 0; - String pctText = null; - if (!tracker.plugged && level > mCriticalLevel && mShowPercent) { - mTextPaint.setColor(getColorForLevel(level)); - final float full = mHorizontal ? 0.60f : 0.45f; - final float nofull = mHorizontal ? 0.75f : 0.6f; - final float single = mHorizontal ? 0.86f : 0.75f; - mTextPaint.setTextSize(height * - (SINGLE_DIGIT_PERCENT ? single - : (tracker.level == 100 ? full : nofull))); - mTextHeight = -mTextPaint.getFontMetrics().ascent; - pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level); - pctX = mWidth * 0.5f; - pctY = (mHeight + mTextHeight) * 0.47f; - if (mHorizontal) { - pctOpaque = pctX > levelTop; - } else { - pctOpaque = levelTop > pctY; - } - if (!pctOpaque) { - mTextPath.reset(); - mTextPaint.getTextPath(pctText, 0, pctText.length(), pctX, pctY, mTextPath); - // cut the percentage text out of the overall shape - mShapePath.op(mTextPath, Path.Op.DIFFERENCE); - } - } - - // draw the battery shape background - c.drawPath(mShapePath, mFramePaint); - - // draw the battery shape, clipped to charging level - if (mHorizontal) { - mFrame.right = levelTop; - } else { - mFrame.top = levelTop; - } - mClipPath.reset(); - mClipPath.addRect(mFrame, Path.Direction.CCW); - mShapePath.op(mClipPath, Path.Op.INTERSECT); - c.drawPath(mShapePath, mBatteryPaint); - - if (!tracker.plugged) { - if (level <= mCriticalLevel) { - // draw the warning text - final float x = mWidth * 0.5f; - final float y = (mHeight + mWarningTextHeight) * 0.48f; - c.drawText(mWarningString, x, y, mWarningTextPaint); - } else if (pctOpaque) { - // draw the percentage text - c.drawText(pctText, pctX, pctY, mTextPaint); - } + drawBattery(c, tracker); + if (mAnimationsEnabled) { + // TODO: Allow custom animations to be used } } @@ -740,282 +550,293 @@ public class BatteryMeterView extends View implements DemoMode, @Override public void setDarkIntensity(int backgroundColor, int fillColor) { mIconTint = fillColor; - mFramePaint.setColor(backgroundColor); - mBoltPaint.setColor(fillColor); - mChargeColor = fillColor; + // Make bolt fully opaque for increased visibility + mBoltDrawable.setTint(0xff000000 | fillColor); + mFrameDrawable.setTint(backgroundColor); + updateBoltDrawableLayer(mBatteryDrawable, mBoltDrawable); invalidate(); } @Override public void onSizeChanged(int w, int h, int oldw, int oldh) { - mHeight = h; - mWidth = w; - mWarningTextPaint.setTextSize(h * 0.75f); - mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent; - } - - private float[] loadBoltPoints(Resources res) { - final int[] pts = res.getIntArray(getBoltPointsArrayResource()); - int maxX = 0, maxY = 0; - for (int i = 0; i < pts.length; i += 2) { - maxX = Math.max(maxX, pts[i]); - maxY = Math.max(maxY, pts[i + 1]); - } - final float[] ptsF = new float[pts.length]; - for (int i = 0; i < pts.length; i += 2) { - ptsF[i] = (float)pts[i] / maxX; - ptsF[i + 1] = (float)pts[i + 1] / maxY; - } - return ptsF; + init(); } - protected int getBoltPointsArrayResource() { - return mHorizontal - ? R.array.batterymeter_inverted_bolt_points - : R.array.batterymeter_bolt_points; + private boolean isThemeApplied() { + ThemeConfig themeConfig = ThemeConfig.getBootTheme(getContext().getContentResolver()); + return themeConfig != null && + !ThemeConfig.SYSTEM_DEFAULT.equals(themeConfig.getOverlayForStatusBar()); } - } - protected class CircleBatteryMeterDrawable implements BatteryMeterDrawable { - private static final boolean SINGLE_DIGIT_PERCENT = false; - private static final boolean SHOW_100_PERCENT = false; - - private static final int FULL = 96; + private void checkBatteryMeterDrawableValid(Resources res, BatteryMeterMode mode) { + final int resId = getBatteryDrawableResourceForMode(mode); + final Drawable batteryDrawable; + try { + batteryDrawable = res.getDrawable(resId); + } catch (Resources.NotFoundException e) { + throw new BatteryMeterDrawableException(res.getResourceName(resId) + " is an " + + "invalid drawable", e); + } - public static final float STROKE_WITH = 6.5f; + // check that the drawable is a LayerDrawable + if (!(batteryDrawable instanceof LayerDrawable)) { + throw new BatteryMeterDrawableException("Expected a LayerDrawable but received a " + + batteryDrawable.getClass().getSimpleName()); + } - private boolean mDisposed; + final LayerDrawable layerDrawable = (LayerDrawable) batteryDrawable; + final Drawable frame = layerDrawable.findDrawableByLayerId(R.id.battery_frame); + final Drawable level = layerDrawable.findDrawableByLayerId(R.id.battery_fill); + final Drawable bolt = layerDrawable.findDrawableByLayerId( + R.id.battery_charge_indicator); + // now check that the required layers exist and are of the correct type + if (frame == null) { + throw new BatteryMeterDrawableException("Missing battery_frame drawble"); + } + if (bolt == null) { + throw new BatteryMeterDrawableException( + "Missing battery_charge_indicator drawable"); + } + if (level != null) { + // check that the level drawable is an AnimatedVectorDrawable + if (!(level instanceof AnimatedVectorDrawable)) { + throw new BatteryMeterDrawableException("Expected a AnimatedVectorDrawable " + + "but received a " + level.getClass().getSimpleName()); + } + // make sure we can stop motion animate the level drawable + try { + StopMotionVectorDrawable smvd = new StopMotionVectorDrawable(level); + smvd.setCurrentFraction(0.5f); + } catch (Exception e) { + throw new BatteryMeterDrawableException("Unable to perform stop motion on " + + "battery_fill drawable", e); + } + } else { + throw new BatteryMeterDrawableException("Missing battery_fill drawable"); + } + } - private int mAnimOffset; - private boolean mIsAnimating; // stores charge-animation status to reliably - //remove callbacks + private void loadBatteryDrawables(Resources res, BatteryMeterMode mode) { + if (isThemeApplied()) { + try { + checkBatteryMeterDrawableValid(res, mode); + } catch (BatteryMeterDrawableException e) { + Log.w(TAG, "Invalid themed battery meter drawable, falling back to system", e); + final Context context = getContext(); + PackageManager pm = getContext().getPackageManager(); + try { + res = pm.getThemedResourcesForApplication(context.getPackageName(), + ThemeConfig.SYSTEM_DEFAULT); + } catch (PackageManager.NameNotFoundException nnfe) { + /* ignore, this should not happen */ + } + } + } - private int mCircleSize; // draw size of circle - private RectF mRectLeft; // contains the precalculated rect used in drawArc(), - // derived from mCircleSize - private float mTextX, mTextY; // precalculated position for drawText() to appear centered + int drawableResId = getBatteryDrawableResourceForMode(mode); + mBatteryDrawable = (LayerDrawable) res.getDrawable(drawableResId); + mFrameDrawable = mBatteryDrawable.findDrawableByLayerId(R.id.battery_frame); + mFrameDrawable.setTint(mCurrentBackgroundColor != 0 + ? mCurrentBackgroundColor + : res.getColor(R.color.batterymeter_frame_color)); + // set the animated vector drawable we will be stop animating + Drawable levelDrawable = mBatteryDrawable.findDrawableByLayerId(R.id.battery_fill); + mLevelDrawable = new StopMotionVectorDrawable(levelDrawable); + mBoltDrawable = mBatteryDrawable.findDrawableByLayerId(R.id.battery_charge_indicator); + } - private Paint mTextPaint; - private Paint mFrontPaint; - private Paint mBackPaint; - private Paint mBoltPaint; - private Paint mWarningTextPaint; + private void drawBattery(Canvas canvas, BatteryTracker tracker) { + boolean unknownStatus = tracker.status == BatteryManager.BATTERY_STATUS_UNKNOWN; + int level = tracker.level; - private final RectF mBoltFrame = new RectF(); + if (unknownStatus || tracker.status == BatteryManager.BATTERY_STATUS_FULL) { + level = 100; + } - private int mChargeColor; - private final float[] mBoltPoints; - private final Path mBoltPath = new Path(); + mTextAndBoltPaint.setColor(getColorForLevel(level)); - public CircleBatteryMeterDrawable(Resources res) { - super(); - mDisposed = false; + // Make sure we don't draw the charge indicator if not plugged in + Drawable d = mBatteryDrawable.findDrawableByLayerId(R.id.battery_charge_indicator); + if (d instanceof BitmapDrawable) { + // In case we are using a BitmapDrawable, which we should be unless something bad + // happened, we need to change the paint rather than the alpha in case the blendMode + // has been set to clear. Clear always clears regardless of alpha level ;) + BitmapDrawable bd = (BitmapDrawable) d; + bd.getPaint().set(tracker.plugged ? mTextAndBoltPaint : mClearPaint); + } else { + d.setAlpha(tracker.plugged ? 255 : 0); + } - mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD); - mTextPaint.setTypeface(font); - mTextPaint.setTextAlign(Paint.Align.CENTER); - - mFrontPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mFrontPaint.setStrokeCap(Paint.Cap.BUTT); - mFrontPaint.setDither(true); - mFrontPaint.setStrokeWidth(0); - mFrontPaint.setStyle(Paint.Style.STROKE); - - mBackPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mBackPaint.setColor(res.getColor(R.color.batterymeter_frame_color)); - mBackPaint.setStrokeCap(Paint.Cap.BUTT); - mBackPaint.setDither(true); - mBackPaint.setStrokeWidth(0); - mBackPaint.setStyle(Paint.Style.STROKE); + // Now draw the level indicator + // set the level and tint color of the fill drawable + mLevelDrawable.setCurrentFraction(level / 100f); + mLevelDrawable.setTint(getColorForLevel(level)); + mBatteryDrawable.draw(canvas); - mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mWarningTextPaint.setColor(mColors[1]); - font = Typeface.create("sans-serif", Typeface.BOLD); - mWarningTextPaint.setTypeface(font); - mWarningTextPaint.setTextAlign(Paint.Align.CENTER); + // if chosen by options, draw percentage text in the middle + // always skip percentage when 100, so layout doesnt break + if (unknownStatus) { + mTextAndBoltPaint.setColor(getContext().getColor(R.color.batterymeter_frame_color)); + canvas.drawText("?", mTextX, mTextY, mTextAndBoltPaint); - mChargeColor = getResources().getColor(R.color.batterymeter_charge_color); + } else if (!tracker.plugged) { + drawPercentageText(canvas, tracker); + } + } - mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mBoltPaint.setColor(res.getColor(R.color.batterymeter_bolt_color)); - mBoltPoints = loadBoltPoints(res); + private void drawPercentageText(Canvas canvas, BatteryTracker tracker) { + final int level = tracker.level; + if (level > mCriticalLevel + && (mShowPercent && !(level == 100 && !SHOW_100_PERCENT))) { + // draw the percentage text + String pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level); + mTextAndBoltPaint.setColor(getColorForLevel(level)); + canvas.drawText(pctText, mTextX, mTextY, mTextAndBoltPaint); + } else if (level <= mCriticalLevel) { + // draw the warning text + canvas.drawText(mWarningString, mTextX, mTextY, mWarningTextPaint); + } } - @Override - public void onDraw(Canvas c, BatteryTracker tracker) { - if (mDisposed) return; + /** + * initializes all size dependent variables + */ + private void init() { + // not much we can do with zero width or height, we'll get another pass later + if (mWidth <= 0 || mHeight <=0) return; + + final float widthDiv2 = mWidth / 2f; + // text size is width / 2 - 2dp for wiggle room + final float textSize = widthDiv2 - getResources().getDisplayMetrics().density * 2; + mTextAndBoltPaint.setTextSize(textSize); + mWarningTextPaint.setTextSize(textSize); + + int pLeft = getPaddingLeft(); + Rect iconBounds = new Rect(pLeft, 0, pLeft + mWidth, mHeight); + mBatteryDrawable.setBounds(iconBounds); - if (mRectLeft == null) { - initSizeBasedStuff(); + // calculate text position + Rect bounds = new Rect(); + mTextAndBoltPaint.getTextBounds("99", 0, "99".length(), bounds); + boolean isRtl = isLayoutRtl(); + + // compute mTextX based on text gravity + if ((mTextGravity & Gravity.START) == Gravity.START) { + mTextX = isRtl ? mWidth : 0; + } else if ((mTextGravity & Gravity.END) == Gravity.END) { + mTextX = isRtl ? 0 : mWidth; + } else if ((mTextGravity & Gravity.LEFT) == Gravity.LEFT) { + mTextX = 0; + }else if ((mTextGravity & Gravity.RIGHT) == Gravity.RIGHT) { + mTextX = mWidth; + } else { + mTextX = widthDiv2 + pLeft; } - drawCircle(c, tracker, mTextX, mRectLeft); - if (mAnimationsEnabled) { - updateChargeAnim(tracker); + // compute mTextY based on text gravity + if ((mTextGravity & Gravity.TOP) == Gravity.TOP) { + mTextY = bounds.height(); + } else if ((mTextGravity & Gravity.BOTTOM) == Gravity.BOTTOM) { + mTextY = mHeight; + } else { + mTextY = widthDiv2 + bounds.height() / 2.0f; } - } - @Override - public void onDispose() { - mDisposed = true; + updateBoltDrawableLayer(mBatteryDrawable, mBoltDrawable); + + mInitialized = true; } - @Override - public void setDarkIntensity(int backgroundColor, int fillColor) { - mIconTint = fillColor; - mBoltPaint.setColor(fillColor); - mChargeColor = fillColor; - invalidate(); + private int getBatteryDrawableResourceForMode(BatteryMeterMode mode) { + switch (mode) { + case BATTERY_METER_ICON_LANDSCAPE: + return R.drawable.ic_battery_landscape; + case BATTERY_METER_CIRCLE: + return R.drawable.ic_battery_circle; + case BATTERY_METER_ICON_PORTRAIT: + return R.drawable.ic_battery_portrait; + default: + return 0; + } } - @Override - public void onSizeChanged(int w, int h, int oldw, int oldh) { - initSizeBasedStuff(); + private int getBatteryDrawableStyleResourceForMode(BatteryMeterMode mode) { + switch (mode) { + case BATTERY_METER_ICON_LANDSCAPE: + return R.style.BatteryMeterViewDrawable_Landscape; + case BATTERY_METER_CIRCLE: + return R.style.BatteryMeterViewDrawable_Circle; + case BATTERY_METER_ICON_PORTRAIT: + return R.style.BatteryMeterViewDrawable_Portrait; + default: + return R.style.BatteryMeterViewDrawable; + } } - private float[] loadBoltPoints(Resources res) { - final int[] pts = res.getIntArray(getBoltPointsArrayResource()); - int maxX = 0, maxY = 0; - for (int i = 0; i < pts.length; i += 2) { - maxX = Math.max(maxX, pts[i]); - maxY = Math.max(maxY, pts[i + 1]); + private Paint.Align getPaintAlignmentFromGravity(int gravity) { + boolean isRtl = isLayoutRtl(); + if ((gravity & Gravity.START) == Gravity.START) { + return isRtl ? Paint.Align.RIGHT : Paint.Align.LEFT; } - final float[] ptsF = new float[pts.length]; - for (int i = 0; i < pts.length; i += 2) { - ptsF[i] = (float)pts[i] / maxX; - ptsF[i + 1] = (float)pts[i + 1] / maxY; + if ((gravity & Gravity.END) == Gravity.END) { + return isRtl ? Paint.Align.LEFT : Paint.Align.RIGHT; } - return ptsF; - } + if ((gravity & Gravity.LEFT) == Gravity.LEFT) return Paint.Align.LEFT; + if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) return Paint.Align.RIGHT; - protected int getBoltPointsArrayResource() { - return R.array.batterymeter_bolt_points; + // default to center + return Paint.Align.CENTER; } - private void drawCircle(Canvas canvas, BatteryTracker tracker, - float textX, RectF drawRect) { - boolean unknownStatus = tracker.status == BatteryManager.BATTERY_STATUS_UNKNOWN; - int level = tracker.level; - Paint paint; - - if (unknownStatus) { - paint = mBackPaint; - level = 100; // Draw all the circle; + // Creates a BitmapDrawable of the bolt so we can make use of the XOR xfer mode with vector + // based drawables + private void updateBoltDrawableLayer(LayerDrawable batteryDrawable, Drawable boltDrawable) { + BitmapDrawable newBoltDrawable; + if (boltDrawable instanceof BitmapDrawable) { + newBoltDrawable = (BitmapDrawable) boltDrawable.mutate(); } else { - paint = mFrontPaint; - paint.setColor(getColorForLevel(level)); - if (tracker.status == BatteryManager.BATTERY_STATUS_FULL) { - level = 100; + Bitmap boltBitmap = createBoltBitmap(boltDrawable); + if (boltBitmap == null) { + // not much to do with a null bitmap so keep original bolt for now + return; } + Rect bounds = boltDrawable.getBounds(); + newBoltDrawable = new BitmapDrawable(getResources(), boltBitmap); + newBoltDrawable.setBounds(bounds); } - - // draw thin gray ring first - canvas.drawArc(drawRect, 270, 360, false, mBackPaint); - if (level != 0) { - // draw colored arc representing charge level - canvas.drawArc(drawRect, 270 + mAnimOffset, 3.6f * level, false, paint); - } - // if chosen by options, draw percentage text in the middle - // always skip percentage when 100, so layout doesnt break - if (unknownStatus) { - mTextPaint.setColor(paint.getColor()); - canvas.drawText("?", textX, mTextY, mTextPaint); - - } else if (tracker.plugged) { - canvas.drawPath(mBoltPath, mBoltPaint); - } else { - if (level > mCriticalLevel - && (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT))) { - // draw the percentage text - String pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level); - mTextPaint.setColor(paint.getColor()); - canvas.drawText(pctText, textX, mTextY, mTextPaint); - } else if (level <= mCriticalLevel) { - // draw the warning text - canvas.drawText(mWarningString, textX, mTextY, mWarningTextPaint); + newBoltDrawable.getPaint().set(mTextAndBoltPaint); + batteryDrawable.setDrawableByLayerId(R.id.battery_charge_indicator, newBoltDrawable); + } + + private Bitmap createBoltBitmap(Drawable boltDrawable) { + // not much we can do with zero width or height, we'll get another pass later + if (mWidth <= 0 || mHeight <= 0) return null; + + Bitmap bolt; + if (!(boltDrawable instanceof BitmapDrawable)) { + int pLeft = getPaddingLeft(); + Rect iconBounds = new Rect(pLeft, 0, pLeft + mWidth, mHeight); + bolt = Bitmap.createBitmap(iconBounds.width(), iconBounds.height(), + Bitmap.Config.ARGB_8888); + if (bolt != null) { + Canvas c = new Canvas(bolt); + c.drawColor(-1, PorterDuff.Mode.CLEAR); + boltDrawable.draw(c); } - } - } - - /** - * updates the animation counter - * cares for timed callbacks to continue animation cycles - * uses mInvalidate for delayed invalidate() callbacks - */ - private void updateChargeAnim(BatteryTracker tracker) { - // Stop animation when battery is full or after the meter - // rotated back to 0 after unplugging. - if (!tracker.shouldIndicateCharging() - || tracker.status == BatteryManager.BATTERY_STATUS_FULL - || tracker.level == 0) { - mIsAnimating = false; } else { - mIsAnimating = true; - } - - if (mAnimOffset > 360) { - mAnimOffset = 0; - } - - boolean continueAnimation = mIsAnimating || mAnimOffset != 0; - - if (continueAnimation) { - mAnimOffset += 3; + bolt = ((BitmapDrawable) boltDrawable).getBitmap(); } - if (continueAnimation) { - postInvalidateDelayed(50); - } + return bolt; } - /** - * initializes all size dependent variables - * sets stroke width and text size of all involved paints - * YES! i think the method name is appropriate - */ - private void initSizeBasedStuff() { - mCircleSize = Math.min(getMeasuredWidth(), getMeasuredHeight()); - mTextPaint.setTextSize(mCircleSize / 2f); - mWarningTextPaint.setTextSize(mCircleSize / 2f); - - float strokeWidth = mCircleSize / STROKE_WITH; - mFrontPaint.setStrokeWidth(strokeWidth); - mBackPaint.setStrokeWidth(strokeWidth); - - // calculate rectangle for drawArc calls - int pLeft = getPaddingLeft(); - mRectLeft = new RectF(pLeft + strokeWidth / 2.0f, 0 + strokeWidth / 2.0f, mCircleSize - - strokeWidth / 2.0f + pLeft, mCircleSize - strokeWidth / 2.0f); + private class BatteryMeterDrawableException extends RuntimeException { + public BatteryMeterDrawableException(String detailMessage) { + super(detailMessage); + } - // calculate Y position for text - Rect bounds = new Rect(); - mTextPaint.getTextBounds("99", 0, "99".length(), bounds); - mTextX = mCircleSize / 2.0f + getPaddingLeft(); - // the +1dp at end of formula balances out rounding issues.works out on all resolutions - mTextY = mCircleSize / 2.0f + (bounds.bottom - bounds.top) / 2.0f - - strokeWidth / 2.0f + getResources().getDisplayMetrics().density; - - // draw the bolt - final float bl = (int) (mRectLeft.left + mRectLeft.width() / 3.2f); - final float bt = (int) (mRectLeft.top + mRectLeft.height() / 4f); - final float br = (int) (mRectLeft.right - mRectLeft.width() / 5.2f); - final float bb = (int) (mRectLeft.bottom - mRectLeft.height() / 8f); - if (mBoltFrame.left != bl || mBoltFrame.top != bt - || mBoltFrame.right != br || mBoltFrame.bottom != bb) { - mBoltFrame.set(bl, bt, br, bb); - mBoltPath.reset(); - mBoltPath.moveTo( - mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height()); - for (int i = 2; i < mBoltPoints.length; i += 2) { - mBoltPath.lineTo( - mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height()); - } - mBoltPath.lineTo( - mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(), - mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height()); + public BatteryMeterDrawableException(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); } } } diff --git a/packages/SystemUI/src/com/android/systemui/DockBatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/DockBatteryMeterView.java index b80e6d0..a29b16c 100755 --- a/packages/SystemUI/src/com/android/systemui/DockBatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/DockBatteryMeterView.java @@ -18,7 +18,6 @@ package com.android.systemui; import android.content.Context; import android.content.Intent; -import android.content.res.Resources; import android.os.BatteryManager; import android.util.AttributeSet; import android.view.View; @@ -148,45 +147,4 @@ public class DockBatteryMeterView extends BatteryMeterView { setVisibility(View.GONE); } } - - @Override - protected BatteryMeterDrawable createBatteryMeterDrawable(BatteryMeterMode mode) { - Resources res = mContext.getResources(); - switch (mode) { - case BATTERY_METER_CIRCLE: - return new DockCircleBatteryMeterDrawable(res); - case BATTERY_METER_ICON_LANDSCAPE: - return new DockNormalBatteryMeterDrawable(res, true); - case BATTERY_METER_TEXT: - case BATTERY_METER_GONE: - return null; - default: - return new DockNormalBatteryMeterDrawable(res, false); - } - } - - protected class DockNormalBatteryMeterDrawable extends NormalBatteryMeterDrawable { - - public DockNormalBatteryMeterDrawable(Resources res, boolean horizontal) { - super(res, horizontal); - } - - @Override - protected int getBoltPointsArrayResource() { - return mHorizontal - ? R.array.dockbatterymeter_inverted_bolt_points - : R.array.dockbatterymeter_bolt_points; - } - } - - protected class DockCircleBatteryMeterDrawable extends CircleBatteryMeterDrawable { - public DockCircleBatteryMeterDrawable(Resources res) { - super(res); - } - - @Override - protected int getBoltPointsArrayResource() { - return R.array.dockbatterymeter_bolt_points; - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags b/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags index a584cf6..a08d4b7 100644 --- a/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags +++ b/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags @@ -51,3 +51,28 @@ option java_package com.android.systemui; # SearchPanelView.java # --------------------------- 36050 sysui_searchpanel_touch (type|1),(x|1),(y|1) + +# --------------------------- +# LiveLockScreenController.java +# --------------------------- +# sysui_lls_keyguard_showing +## screenOn: 0:screen turned off +## 1:screen turned on +36060 sysui_lls_keyguard_showing (screenOn|1) +# sysui_lls_keyguard_dismissed: Logged when user unlocks the device +## onLls: 0:dismissed while showing notifications +## 1:dismissed while user interacting with LLS +36061 sysui_lls_keyguard_dismissed (onLls|1) +# sysui_lls_notification_panel_shown: Logged when the notification panel is swiped in and out +## shown: 0:panel is hidden +## 1:panel is visible +36062 sysui_lls_notification_panel_shown (shown|1) + +# --------------------------- +# RecentsView.java +# --------------------------- +36070 sysui_recents_event (what|1) +## what: 1: OPEN +## 2: CLOSE +## 3: CHOSE_TASK +## 4: CLOSE_ALL_TASKS diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java index 1dca149..5911916 100644 --- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java @@ -36,6 +36,7 @@ import android.view.ViewConfiguration; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.FlingAnimationUtils; +import com.android.systemui.statusbar.MediaExpandableNotificationRow; import com.android.systemui.statusbar.policy.ScrollAdapter; public class ExpandHelper implements Gefingerpoken { @@ -96,7 +97,7 @@ public class ExpandHelper implements Gefingerpoken { private float mCurrentHeight; private int mSmallSize; - private int mLargeSize; + private int mLargeSize, mInitialLargeSize; private float mMaximumStretch; private boolean mOnlyMovements; @@ -161,6 +162,7 @@ public class ExpandHelper implements Gefingerpoken { mSmallSize = small; mMaximumStretch = mSmallSize * STRETCH_INTERVAL; mLargeSize = large; + mInitialLargeSize = large; mContext = context; mCallback = callback; mScaler = new ViewScaler(); @@ -511,7 +513,14 @@ public class ExpandHelper implements Gefingerpoken { mCurrentHeight = mOldHeight; if (mCallback.canChildBeExpanded(v)) { if (DEBUG) Log.d(TAG, "working on an expandable child"); - mNaturalHeight = mScaler.getNaturalHeight(mLargeSize); + if (v instanceof MediaExpandableNotificationRow) { + final int maxHeight = ((MediaExpandableNotificationRow) v).getMaxContentHeight(); + mLargeSize = maxHeight; + mNaturalHeight = maxHeight; + } else { + mLargeSize = mInitialLargeSize; + mNaturalHeight = mScaler.getNaturalHeight(mLargeSize); + } } else { if (DEBUG) Log.d(TAG, "working on a non-expandable child"); mNaturalHeight = mOldHeight; diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 33f6564..c3e5043 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -45,9 +45,15 @@ public class SwipeHelper implements Gefingerpoken { public static final int X = 0; public static final int Y = 1; + public static final int SWIPE_ZONE_LEFT = 0x1; + public static final int SWIPE_ZONE_RIGHT = 0x2; + public static final int SWIPE_ZONE_TOP = 0x4; + public static final int SWIPE_ZONE_BOTTOM = 0x8; private static LinearInterpolator sLinearInterpolator = new LinearInterpolator(); private final Interpolator mFastOutLinearInInterpolator; + private final int mTouchSlop; + private int mSwipeZone; private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms @@ -69,6 +75,7 @@ public class SwipeHelper implements Gefingerpoken { private VelocityTracker mVelocityTracker; private float mInitialTouchPos; + private float mPerpendicularInitialTouchPos; private boolean mDragging; private View mCurrView; private View mCurrAnimView; @@ -84,6 +91,8 @@ public class SwipeHelper implements Gefingerpoken { private int mFalsingThreshold; private boolean mTouchAboveFalsingThreshold; + private float mSwipeProgressFadeEnd; + public SwipeHelper(int swipeDirection, Callback callback, Context context) { mCallback = callback; mHandler = new Handler(); @@ -91,12 +100,28 @@ public class SwipeHelper implements Gefingerpoken { mVelocityTracker = VelocityTracker.obtain(); mDensityScale = context.getResources().getDisplayMetrics().density; mPagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop(); + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f); // extra long-press! mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_linear_in); mFalsingThreshold = context.getResources().getDimensionPixelSize( R.dimen.swipe_helper_falsing_threshold); + if (swipeDirection == X) { + mSwipeZone = SWIPE_ZONE_LEFT | SWIPE_ZONE_RIGHT; + } else { + mSwipeZone = SWIPE_ZONE_TOP | SWIPE_ZONE_BOTTOM; + } + mSwipeProgressFadeEnd = SWIPE_PROGRESS_FADE_END; + } + + public SwipeHelper(int swipeDirection, int swipeZone, Callback callback, Context context) { + this(swipeDirection, callback, context); + mSwipeZone = swipeZone; + } + + public boolean isDragging() { + return mDragging; } public void setLongPressListener(LongPressListener listener) { @@ -115,6 +140,10 @@ public class SwipeHelper implements Gefingerpoken { return mSwipeDirection == X ? ev.getX() : ev.getY(); } + private float getPerpendicularPos(MotionEvent ev) { + return mSwipeDirection == X ? ev.getY() : ev.getX(); + } + private float getTranslation(View v) { return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY(); } @@ -158,7 +187,7 @@ public class SwipeHelper implements Gefingerpoken { private float getSwipeProgressForOffset(View view) { float viewSize = getSize(view); - final float fadeSize = SWIPE_PROGRESS_FADE_END * viewSize; + final float fadeSize = mSwipeProgressFadeEnd * viewSize; float result = 1.0f; float pos = getTranslation(view); if (pos >= viewSize * SWIPE_PROGRESS_FADE_START) { @@ -236,6 +265,7 @@ public class SwipeHelper implements Gefingerpoken { mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView); mVelocityTracker.addMovement(ev); mInitialTouchPos = getPos(ev); + mPerpendicularInitialTouchPos = getPerpendicularPos(ev); if (mLongPressListener != null) { if (mWatchLongPress == null) { @@ -413,11 +443,30 @@ public class SwipeHelper implements Gefingerpoken { case MotionEvent.ACTION_OUTSIDE: case MotionEvent.ACTION_MOVE: if (mCurrView != null) { + float pos = getPos(ev); + float altPos = getPerpendicularPos(ev); float delta = getPos(ev) - mInitialTouchPos; float absDelta = Math.abs(delta); if (absDelta >= getFalsingThreshold()) { mTouchAboveFalsingThreshold = true; } + + boolean touchBeyondZoneLimit = true; + if (mSwipeDirection == X) { + if ((mSwipeZone & SWIPE_ZONE_RIGHT) == 0 && pos > mInitialTouchPos) { + touchBeyondZoneLimit = false; + } else if ((mSwipeZone & SWIPE_ZONE_LEFT) == 0 && pos < mInitialTouchPos) { + touchBeyondZoneLimit = false; + } + } else { + if ((mSwipeZone & SWIPE_ZONE_TOP) == 0 && altPos < mPerpendicularInitialTouchPos) { + touchBeyondZoneLimit = false; + } else if ((mSwipeZone & SWIPE_ZONE_BOTTOM) == 0 && pos > mInitialTouchPos) { + touchBeyondZoneLimit = false; + } + } + if (!touchBeyondZoneLimit) return false; + // don't let items that can't be dismissed be dragged more than // maxScrollDistance if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) { @@ -470,6 +519,10 @@ public class SwipeHelper implements Gefingerpoken { return true; } + public void setSwipeProgressFadeEnd(float end) { + mSwipeProgressFadeEnd = end; + } + private int getFalsingThreshold() { float factor = mCallback.getFalsingThresholdFactor(); return (int) (mFalsingThreshold * factor); @@ -505,6 +558,49 @@ public class SwipeHelper implements Gefingerpoken { float getFalsingThresholdFactor(); } + public static abstract class SimpleCallback implements Callback { + public abstract View getChildAtPosition(MotionEvent ev); + public abstract View getChildContentView(View v); + + @Override + public boolean canChildBeDismissed(View v) { + return false; + } + + @Override + public boolean isAntiFalsingNeeded() { + return false; + } + + @Override + public void onBeginDrag(View v) { + } + + @Override + public void onChildDismissed(View v) { + } + + @Override + public void onDragCancelled(View v) { + } + + @Override + public void onChildSnappedBack(View animView) { + } + + @Override + public boolean updateSwipeProgress(View animView, + boolean dismissable, + float swipeProgress) { + return false; + } + + @Override + public float getFalsingThresholdFactor() { + return 0; + } + } + /** * Equivalent to View.OnLongClickListener with coordinates */ diff --git a/packages/SystemUI/src/com/android/systemui/cm/LockscreenShortcutsActivity.java b/packages/SystemUI/src/com/android/systemui/cm/LockscreenShortcutsActivity.java index d8cfa5e..3cd86fe 100644 --- a/packages/SystemUI/src/com/android/systemui/cm/LockscreenShortcutsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/cm/LockscreenShortcutsActivity.java @@ -1,5 +1,6 @@ package com.android.systemui.cm; +import com.android.internal.app.AssistUtils; import com.android.settingslib.cm.ShortcutPickHelper; import com.android.systemui.R; import com.android.systemui.cm.LockscreenShortcutsHelper.Shortcuts; @@ -34,6 +35,7 @@ public class LockscreenShortcutsActivity extends Activity implements View.OnClic private View mSelectedView; private ColorMatrixColorFilter mFilter; private ColorStateList mDefaultTintList; + private AssistUtils mAssistUtils; @Override public void shortcutPicked(String uri, String friendlyName, boolean isApplication) { @@ -107,6 +109,7 @@ public class LockscreenShortcutsActivity extends Activity implements View.OnClic mFilter = new ColorMatrixColorFilter(cm); ImageView unlockButton = (ImageView) findViewById(R.id.middle_button); mDefaultTintList = unlockButton.getImageTintList(); + mAssistUtils = new AssistUtils(this); createActionList(); initiateViews(); updateDrawables(); @@ -146,19 +149,40 @@ public class LockscreenShortcutsActivity extends Activity implements View.OnClic if (mShortcutHelper.isTargetEmpty(shortcut)) { drawable = getResources().getDrawable(R.drawable.ic_lockscreen_shortcuts_blank); } else { - drawable = mShortcutHelper.getDrawableForTarget(shortcut); - if (drawable == null) { - drawable = getResources().getDrawable(shortcut == Shortcuts.LEFT_SHORTCUT - ? R.drawable.ic_phone_24dp : R.drawable.ic_camera_alt_24dp); + if (shortcut == Shortcuts.LEFT_SHORTCUT && + !mShortcutHelper.isTargetCustom(shortcut)) { + drawable = getLeftAffordanceDrawable(); v.setImageTintList(mDefaultTintList); } else { - v.setColorFilter(mFilter); + drawable = mShortcutHelper.getDrawableForTarget(shortcut); + if (drawable == null) { + drawable = (shortcut == Shortcuts.LEFT_SHORTCUT) ? + getLeftAffordanceDrawable() + : getResources().getDrawable(R.drawable.ic_camera_alt_24dp); + v.setImageTintList(mDefaultTintList); + } else { + v.setColorFilter(mFilter); + } } } v.setImageDrawable(drawable); } } + private Drawable getLeftAffordanceDrawable() { + Drawable drawable; + if (canLaunchVoiceAssist()) { + drawable = getResources().getDrawable(R.drawable.ic_mic_26dp); + } else { + drawable = getResources().getDrawable(R.drawable.ic_phone_24dp); + } + return drawable; + } + + private boolean canLaunchVoiceAssist() { + return mAssistUtils.activeServiceSupportsLaunchFromKeyguard(); + } + private void createActionList() { mActions = new ActionHolder(); mActions.addAction(LockscreenShortcutsHelper.NONE, R.string.lockscreen_none_target); @@ -211,13 +235,15 @@ public class LockscreenShortcutsActivity extends Activity implements View.OnClic private void onTargetChange(String uri) { if (uri == null) { - final GlowBackground background = (GlowBackground) mSelectedView.getBackground(); - background.hideGlow(); + if (mSelectedView != null) { + final GlowBackground background = (GlowBackground) mSelectedView.getBackground(); + background.hideGlow(); + } return; } if (uri.equals(ACTION_APP)) { - mPicker.pickShortcut(null, null, 0); + mPicker.pickShortcut(null, null, 0, false); } else { mSelectedView.setTag(uri); saveCustomActions(); diff --git a/packages/SystemUI/src/com/android/systemui/cm/LockscreenShortcutsHelper.java b/packages/SystemUI/src/com/android/systemui/cm/LockscreenShortcutsHelper.java index 12b9810..b47b69f 100644 --- a/packages/SystemUI/src/com/android/systemui/cm/LockscreenShortcutsHelper.java +++ b/packages/SystemUI/src/com/android/systemui/cm/LockscreenShortcutsHelper.java @@ -53,9 +53,15 @@ public class LockscreenShortcutsHelper { if (listener != null) { mListener = listener; mHandler = new Handler(Looper.getMainLooper()); - mContext.getContentResolver().registerContentObserver( - CMSettings.Secure.getUriFor(CMSettings.Secure.LOCKSCREEN_TARGETS), false, mObserver); + registerAndFetchTargets(); + } else { + fetchTargets(); } + } + + public void registerAndFetchTargets() { + mContext.getContentResolver().registerContentObserver( + CMSettings.Secure.getUriFor(CMSettings.Secure.LOCKSCREEN_TARGETS), false, mObserver); fetchTargets(); } @@ -76,7 +82,6 @@ public class LockscreenShortcutsHelper { public void cleanup() { mContext.getContentResolver().unregisterContentObserver(mObserver); - mListener = null; } public static class TargetInfo { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index d2c60ef..a6ca6a0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -90,6 +90,12 @@ public class KeyguardService extends Service { mKeyguardViewMediator.setOccluded(isOccluded); } + @Override + public void showKeyguard() { + checkPermission(); + mKeyguardViewMediator.showKeyguard(); + } + @Override // Binder interface public void dismiss() { checkPermission(); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 06ce8cc..2833759 100755 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -53,6 +53,7 @@ import android.util.EventLog; import android.util.Log; import android.util.Slog; import android.view.IWindowManager; +import android.view.View; import android.view.ViewGroup; import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicy; @@ -61,6 +62,7 @@ import android.view.animation.AnimationUtils; import com.android.systemui.cm.UserContentObserver; import com.android.systemui.qs.tiles.LockscreenToggleTile; +import com.android.systemui.statusbar.StatusBarState; import cyanogenmod.app.Profile; import cyanogenmod.app.ProfileManager; @@ -173,6 +175,7 @@ public class KeyguardViewMediator extends SystemUI { private static final int NOTIFY_SCREEN_TURNED_ON = 22; private static final int NOTIFY_SCREEN_TURNED_OFF = 23; private static final int NOTIFY_STARTED_GOING_TO_SLEEP = 24; + private static final int NOTIFY_KEYGUARD_PANEL_FOCUS_CHANGED = 25; /** * The default amount of time we stay awake (used for all key input) @@ -254,6 +257,8 @@ public class KeyguardViewMediator extends SystemUI { // true if the keyguard is hidden by another window private boolean mOccluded = false; + private boolean mKeyguardPanelFocused = false; + /** * Helps remember whether the screen has turned on since the last time * it turned off due to timeout. see {@link #onScreenTurnedOff(int)} @@ -351,6 +356,8 @@ public class KeyguardViewMediator extends SystemUI { private IKeyguardDrawnCallback mDrawnCallback; private LockscreenEnabledSettingsObserver mSettingsObserver; + private PhoneStatusBar mStatusBar; + public static class LockscreenEnabledSettingsObserver extends UserContentObserver { private static final String KEY_ENABLED = "lockscreen_enabled"; @@ -480,21 +487,20 @@ public class KeyguardViewMediator extends SystemUI { // only force lock screen in case of missing sim if user hasn't // gone through setup wizard synchronized (this) { - if (shouldWaitForProvisioning()) { - if (!mShowing) { - if (DEBUG_SIM_STATES) Log.d(TAG, "ICC_ABSENT isn't showing," - + " we need to show the keyguard since the " - + "device isn't provisioned yet."); - doKeyguardLocked(null); - } else { - resetStateLocked(); - } + if (shouldWaitForProvisioning() && !mShowing) { + if (DEBUG_SIM_STATES) Log.d(TAG, "ICC_ABSENT isn't showing," + + " we need to show the keyguard since the " + + "device isn't provisioned yet."); + doKeyguardLocked(null); + } else { + resetStateLocked(); } } break; case PIN_REQUIRED: case PUK_REQUIRED: synchronized (this) { + mStatusBar.hideHeadsUp(); if (!mShowing) { if (DEBUG_SIM_STATES) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't " @@ -1291,6 +1297,36 @@ public class KeyguardViewMediator extends SystemUI { mHandler.sendEmptyMessage(DISMISS); } + public void showKeyguard() { + // This is to prevent left edge from interfering + // with affordances. + if (mStatusBar.isAffordanceSwipeInProgress() + || mStatusBar.getBarState() == StatusBarState.KEYGUARD) { + return; + } + + // Disable edge detector once we're back on lockscreen + try { + WindowManagerGlobal.getWindowManagerService() + .setLiveLockscreenEdgeDetector(false); + } catch (RemoteException e){ + Log.e(TAG, e.getMessage()); + } + + mHandler.post(new Runnable() { + @Override + public void run() { + // Hide status bar window to avoid flicker, + // slideNotificationPanelIn will make it visible later. + mStatusBar.getStatusBarWindow().setVisibility(View.INVISIBLE); + // Get the keyguard into the correct state by calling mStatusBar.showKeyguard() + mStatusBar.showKeyguard(); + // Now have the notification panel slid back into view + mStatusBar.slideNotificationPanelIn(); + } + }); + } + /** * Send message to keyguard telling it to reset its state. * @see #handleReset @@ -1485,6 +1521,9 @@ public class KeyguardViewMediator extends SystemUI { case ON_ACTIVITY_DRAWN: handleOnActivityDrawn(); break; + case NOTIFY_KEYGUARD_PANEL_FOCUS_CHANGED: + notifyKeyguardPanelFocusChanged(msg.arg1 != 0); + break; } } }; @@ -1865,6 +1904,7 @@ public class KeyguardViewMediator extends SystemUI { FingerprintUnlockController fingerprintUnlockController) { mStatusBarKeyguardViewManager.registerStatusBar(phoneStatusBar, container, statusBarWindowManager, scrimController, fingerprintUnlockController); + mStatusBar = phoneStatusBar; return mStatusBarKeyguardViewManager; } @@ -1935,6 +1975,31 @@ public class KeyguardViewMediator extends SystemUI { } } + public void setKeyguardPanelFocused(boolean focused) { + if (DEBUG) Log.d(TAG, "setSlideOffset " + focused); + mHandler.removeMessages(NOTIFY_KEYGUARD_PANEL_FOCUS_CHANGED); + Message msg = mHandler.obtainMessage(NOTIFY_KEYGUARD_PANEL_FOCUS_CHANGED, + focused ? 1 : 0, 0); + mHandler.sendMessage(msg); + } + + public void notifyKeyguardPanelFocusChanged(boolean focused) { + if (focused != mKeyguardPanelFocused) { + mKeyguardPanelFocused = focused; + int size = mKeyguardStateCallbacks.size(); + for (int i = size - 1; i >= 0; i--) { + try { + mKeyguardStateCallbacks.get(i).onKeyguardPanelFocusChanged(focused); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call onShowingStateChanged", e); + if (e instanceof DeadObjectException) { + mKeyguardStateCallbacks.remove(i); + } + } + } + } + } + public void addStateMonitorCallback(IKeyguardStateCallback callback) { synchronized (this) { mKeyguardStateCallbacks.add(callback); @@ -1942,6 +2007,7 @@ public class KeyguardViewMediator extends SystemUI { callback.onSimSecureStateChanged(mUpdateMonitor.isSimPinSecure()); callback.onShowingStateChanged(mShowing); callback.onInputRestrictedStateChanged(mInputRestricted); + callback.onKeyguardPanelFocusChanged(mKeyguardPanelFocused); } catch (RemoteException e) { Slog.w(TAG, "Failed to call onShowingStateChanged or onSimSecureStateChanged or onInputRestrictedStateChanged", e); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItemsList.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItemsList.java index 578a983..3e0ab8b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItemsList.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItemsList.java @@ -27,6 +27,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListAdapter; @@ -42,7 +43,7 @@ import java.util.List; /** * Quick settings common detail list view with line items. */ -public class QSDetailItemsList extends LinearLayout { +public class QSDetailItemsList extends FrameLayout { private static final String TAG = "QSDetailItemsList"; private ListView mListView; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java index 86fc49e..13f552c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDragPanel.java @@ -19,10 +19,8 @@ package com.android.systemui.qs; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.ActivityManager; -import android.app.AlertDialog; import android.content.ContentResolver; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; @@ -32,12 +30,10 @@ import android.graphics.Color; import android.graphics.Point; import android.graphics.PointF; import android.graphics.PorterDuff; -import android.graphics.drawable.Animatable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.UserHandle; -import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.util.ArrayMap; @@ -49,7 +45,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; -import android.widget.EditText; import android.widget.ExpandableListView; import android.widget.ImageView; import android.widget.LinearLayout; @@ -58,12 +53,12 @@ import com.android.internal.logging.MetricsLogger; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.cm.UserContentObserver; +import com.android.systemui.qs.tiles.CustomQSTile; import com.android.systemui.qs.tiles.EditTile; -import com.android.systemui.qs.tiles.IntentTile; import com.android.systemui.settings.BrightnessController; import com.android.systemui.settings.ToggleSlider; +import com.android.systemui.statusbar.phone.NotificationPanelView; import com.android.systemui.statusbar.phone.QSTileHost; -import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.tuner.QsTuner; import com.viewpagerindicator.CirclePageIndicator; @@ -80,6 +75,8 @@ import java.util.List; import java.util.ListIterator; import java.util.Map; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; + public class QSDragPanel extends QSPanel implements View.OnDragListener, View.OnLongClickListener { private static final String TAG = "QSDragPanel"; @@ -94,6 +91,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On protected final ArrayList<QSPage> mPages = new ArrayList<>(); + private NotificationPanelView mPanelView; protected QSViewPager mViewPager; protected PagerAdapter mPagerAdapter; QSPanelTopView mQsPanelTop; @@ -102,6 +100,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On private TextView mDetailRemoveButton; private DragTileRecord mDraggingRecord, mLastDragRecord; + private ViewGroup mDetailButtons; private boolean mEditing; private boolean mDragging; private float mLastTouchLocationX, mLastTouchLocationY; @@ -119,15 +118,13 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On List<TileRecord> mCurrentlyAnimating = Collections.synchronizedList(new ArrayList<TileRecord>()); - private Point mDisplaySize; - private int[] mTmpLoc; - private Runnable mResetPage = new Runnable() { @Override public void run() { - if (!mListening) { + if (!mExpanded) { // only reset when the user isn't interacting at all mViewPager.setCurrentItem(0); + mPagerAdapter.notifyDataSetChanged(); } } }; @@ -145,6 +142,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On updateResources(); mDetail = LayoutInflater.from(mContext).inflate(R.layout.qs_detail, this, false); + mDetailButtons = (ViewGroup) mDetail.findViewById(R.id.buttons); mDetailContent = (ViewGroup) mDetail.findViewById(android.R.id.content); mDetailRemoveButton = (TextView) mDetail.findViewById(android.R.id.button3); mDetailSettingsButton = (TextView) mDetail.findViewById(android.R.id.button2); @@ -355,7 +353,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On @Override public boolean hasOverlappingRendering() { - return mClipper.isAnimating() || mEditing; + return mClipper.isAnimating() || mEditing || !mCurrentlyAnimating.isEmpty(); } @Override @@ -370,7 +368,14 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On protected void drawTile(TileRecord r, QSTile.State state) { if (mEditing) { - state.visible = true; + if ((r.tile instanceof CustomQSTile) + && (((CustomQSTile) r.tile).isUserRemoved() + || ((CustomQSTile) r.tile).getTile() == null)) { + // don't modify visibility state if removed, or not yet published + } else { + state.visible = true; + state.enabled = true; + } } final int visibility = state.visible ? VISIBLE : GONE; setTileVisibility(r.tileView, visibility); @@ -382,12 +387,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On public void setListening(boolean listening) { if (mListening == listening) return; mListening = listening; - // reset the page when inactive for a while - if (listening) { - removeCallbacks(mResetPage); - } else { - postDelayed(mResetPage, PAGE_RESET_DELAY); - } for (TileRecord r : mRecords) { r.tile.setListening(mListening); } @@ -465,7 +464,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On mLastRightShift = -1; mQsPanelTop.onStopDrag(); - requestLayout(); } protected View getDropTarget() { @@ -501,14 +499,16 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } public void setTiles(final Collection<QSTile<?>> tilesCollection) { - final List<QSTile<?>> tiles = new ArrayList<>(tilesCollection); + // we try to be as efficient as possible here because this can happen while the user + // is in edit mode, or maybe even while tiles are animating + // step 1: stop all animations + // step 2: remove tiles no longer to be used, cache ones that are still valid + // step 3: remove empty viewpager pages + // step 4: generate new tiles, re-add cached ones + if (DEBUG_TILES) { - Log.i(TAG, "setTiles() called with " + "tiles = [" - + tiles + "]"); + Log.i(TAG, "setTiles() called with tiles = [" + tilesCollection + "]"); } - - int currentViewPagerPage = mViewPager.getCurrentItem(); - if (mLastDragRecord != null && mRecords.indexOf(mLastDragRecord) == -1) { // the last removed record might be stored in mLastDragRecord if we just shifted // re-add it to the list so we'll clean it up below @@ -516,25 +516,41 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On mLastDragRecord = null; } - Map<QSTile<?>, DragTileRecord> recordMap = new ArrayMap<>(); + // step kinda-1 + if (mDraggingRecord != null) { + // dragging record might be animating back, force it to finished position + mDraggingRecord.tileView.animate().cancel(); + } + + int currentViewPagerPage = mViewPager.getCurrentItem(); + int removedPages = 0; + + Map<QSTile<?>, DragTileRecord> cachedRecords = new ArrayMap<>(); ListIterator<TileRecord> iterator = mRecords.listIterator(mRecords.size()); int recordsRemoved = 0; // cleanup current records - while (iterator.hasPrevious()) { + while (iterator.hasPrevious()) { // mRecords DragTileRecord dr = (DragTileRecord) iterator.previous(); - if (dr.page >= 0) { - // clean up view - mPages.get(dr.page).removeView(dr.tileView); - } + // step 1 + dr.tileView.animate().cancel(); - if (tiles.contains(dr.tile)) { + // step 2 + if (tilesCollection.contains(dr.tile)) { if (DEBUG_TILES) { Log.i(TAG, "caching tile: " + dr.tile); } - recordMap.put(dr.tile, dr); + cachedRecords.put(dr.tile, dr); } else { + if (dr.page >= 0) { + if (DEBUG_TILES) { + Log.w(TAG, "removed dr.tileView: " + dr.tileView + " from page: " + + dr.page + " (dest page: " + dr.destinationPage + ")"); + } + + removeTileView(dr.tileView); + } if (DEBUG_TILES) { Log.i(TAG, "removing tile: " + dr.tile); } @@ -543,74 +559,99 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On iterator.remove(); recordsRemoved++; - if (dr.page >= getCurrentMaxPageCount() - 1) { - final int childCount = mPages.get(dr.page).getChildCount(); - - if (childCount == 0) { - final int currentIndex = mViewPager.getCurrentItem(); - if (currentIndex > 0 && currentViewPagerPage == currentIndex) { - // if we are about to remove the page we are currently on, move back - currentViewPagerPage--; - } - final int pageIndex = dr.page + (mEditing ? 1 : 0); - mPagerAdapter.startUpdate(mViewPager); - mPagerAdapter.destroyItem(mViewPager, pageIndex, mPages.get(dr.page)); - mPagerAdapter.finishUpdate(mViewPager); - mPagerAdapter.notifyDataSetChanged(); - } - } + dr.page = -1; + dr.destinationPage = -1; } - dr.page = -1; - dr.destinationPage = -1; } - // at this point recordMap should have all retained tiles, no new or old tiles - int delta = tiles.size() - recordMap.size() - recordsRemoved; + // at this point cachedRecords should have all retained tiles, no new or old tiles + int delta = tilesCollection.size() - cachedRecords.size() - recordsRemoved; if (DEBUG_TILES) { Log.i(TAG, "record map delta: " + delta); } - mRecords.ensureCapacity(tiles.size()); - mPagerAdapter.notifyDataSetChanged(); + // step 3 + final Iterator<QSPage> pageIterator = mPages.iterator(); + while (pageIterator.hasNext()) { + final QSPage page = pageIterator.next(); + final int viewpagerIndex = page.getPageIndex() + (mEditing ? 1 : 0); + final int childCount = page.getChildCount(); - // even though we explicitly destroy old pages, without this call, - // the viewpager doesn't seem to want to pick up the fact that we have less pages - // and allows "empty" scrolls to the right where there is no page. - mViewPager.setAdapter(mPagerAdapter); + if (DEBUG_TILES) { + Log.d(TAG, "page " + viewpagerIndex + " has " + childCount); + } + if (page.getPageIndex() >= getCurrentMaxPageCount() - 1) { + if (DEBUG_TILES) { + Log.d(TAG, "page : " + page + " has " + childCount + " children"); + } + if (childCount == 0) { + removedPages++; + + page.removeAllViews(); + mPagerAdapter.startUpdate(mViewPager); + mPagerAdapter.destroyItem(mViewPager, viewpagerIndex, page); + mPagerAdapter.finishUpdate(mViewPager); + mPagerAdapter.notifyDataSetChanged(); + } + } + } + + if (removedPages > 0) { + // even though we explicitly destroy old pages, without this call, + // the viewpager doesn't seem to want to pick up the fact that we have less pages + // and allows "empty" scrolls to the right where there is no page. + if (DEBUG_TILES) { + Log.d(TAG, "re-setting adapter, page: " + currentViewPagerPage); + } + mViewPager.setAdapter(mPagerAdapter); + mViewPager.setCurrentItem(Math.min(currentViewPagerPage, mPagerAdapter.getCount()), + false); + mPagerAdapter.notifyDataSetChanged(); + } + + // step 4 + mRecords.ensureCapacity(tilesCollection.size()); + int runningCount = 0; - // add new tiles - for (int i = 0; i < tiles.size(); i++) { - QSTile<?> tile = tiles.get(i); - final int tileDestPage = getPagesForCount(i + 1) - 1; + final Iterator<QSTile<?>> newTileIterator = tilesCollection.iterator(); + while (newTileIterator.hasNext()) { + QSTile<?> tile = newTileIterator.next(); + if (tile instanceof CustomQSTile) { + if (((CustomQSTile) tile).isUserRemoved() + || ((CustomQSTile) tile).getTile() == null) { + // tile not published yet + continue; + } + } + final int tileDestPage = getPagesForCount(runningCount + 1) - 1; if (DEBUG_TILES) { - Log.d(TAG, "tile at : " + i + ": " + tile + " to dest page: " + tileDestPage); + Log.d(TAG, "tile at : " + runningCount + ": " + tile + + " to dest page: " + tileDestPage); } DragTileRecord record; - if (!recordMap.containsKey(tile)) { + if (!cachedRecords.containsKey(tile)) { if (DEBUG_TILES) { - Log.d(TAG, "tile at: " + i + " not cached, adding it to records"); + Log.d(TAG, "tile at: " + runningCount + " not cached, adding it to records"); } record = makeRecord(tile); record.destinationPage = tileDestPage; - recordMap.put(tile, record); - mRecords.add(i, record); + mRecords.add(runningCount, record); mPagerAdapter.notifyDataSetChanged(); } else { - record = recordMap.get(tile); + record = cachedRecords.get(tile); if (DEBUG_TILES) { - Log.d(TAG, "tile at : " + i + ": cached, restoring: " + record); + Log.d(TAG, "tile at : " + runningCount + ": cached, restoring: " + record); } - int indexOf = mRecords.indexOf(record); - if (indexOf != i) { - if (DEBUG_TILES) { - Log.w(TAG, "moving index of " + record + " from " - + indexOf + " to " + i); - } - Collections.swap(mRecords, indexOf, i); - } + mPages.get(record.page).removeView(record.tileView); + + record.page = -1; record.destinationPage = tileDestPage; + + mRecords.remove(record); + mRecords.add(runningCount, record); + mPagerAdapter.notifyDataSetChanged(); } if (record.page == -1) { // add the view @@ -620,11 +661,9 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On Log.d(TAG, "added view " + record); } } + runningCount++; } - // restore the visible page - mViewPager.setCurrentItem(currentViewPagerPage, false); - if (isShowingDetail()) { mDetail.bringToFront(); } @@ -718,10 +757,18 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On return r; } + private void removeTileView(QSTileView v) { + for (QSPage page : mPages) { + page.removeView(v); + page.removeTransientView(v); + } + + } + private void removeDraggingRecord() { // what spec is this tile? String spec = mHost.getSpec(mDraggingRecord.tile); - if (DEBUG_DRAG) { + if (DEBUG_TILES) { Log.w(TAG, "removing tile: " + mDraggingRecord + " with spec: " + spec); } onStopDrag(); @@ -736,13 +783,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - mTmpLoc = null; - mDisplaySize = null; - } - - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = MeasureSpec.getSize(widthMeasureSpec); @@ -756,37 +796,18 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On if (mFooter.hasFooter()) { h += mFooter.getView().getMeasuredHeight(); } + mGridHeight = h; + mDetail.measure(exactly(width), MeasureSpec.UNSPECIFIED); + if (mDetail.getMeasuredHeight() < h) { mDetail.measure(exactly(width), exactly(h)); } - - // Check if the detail view would be overflowing below the physical height of the device - // and cutting the content off. If it is, reduce the detail height to fit. - if (isShowingDetail()) { - if (mDisplaySize == null) { - mDisplaySize = new Point(); - getDisplay().getSize(mDisplaySize); - } - if (mTmpLoc == null) { - mTmpLoc = new int[2]; - mDetail.getLocationOnScreen(mTmpLoc); - } - - final int containerTop = mTmpLoc[1]; - final int detailBottom = containerTop + mDetail.getMeasuredHeight(); - if (detailBottom >= mDisplaySize.y) { - // panel is hanging below the screen - final int detailMinHeight = mDisplaySize.y - containerTop; - mDetail.measure(exactly(width), exactly(detailMinHeight)); - } - setMeasuredDimension(width, mDetail.getMeasuredHeight()); - mGridHeight = mDetail.getMeasuredHeight(); - } else { - setMeasuredDimension(width, h); - mGridHeight = h; + if (isShowingDetail() && !isClosingDetail() && mExpanded) { + h = mDetail.getMeasuredHeight(); } + setMeasuredDimension(width, h); for (TileRecord record : mRecords) { setupRecord(record); } @@ -816,11 +837,13 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } r.tile.setDetailListening(show); int x = (int) ((DragTileRecord) r).destination.x + r.tileView.getWidth() / 2; - int y = mViewPager.getTop() + (int) ((DragTileRecord) r).destination.y + r.tileView.getHeight() / 2; + int y = mViewPager.getTop() + + (int) ((DragTileRecord) r).destination.y + r.tileView.getHeight() / 2; handleShowDetailImpl(r, show, x, y); } else { super.handleShowDetailTile(r, show); } + mPageIndicator.setVisibility(!show ? View.VISIBLE : View.INVISIBLE); } @Override @@ -833,9 +856,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On // view pager laid out from top of brightness view to bottom to page through settings mViewPager.layout(0, 0, w, viewPagerBottom); - // layout page indicator inside viewpager inset - mPageIndicator.layout(0, b - mPageIndicatorHeight, w, b); - mDetail.layout(0, 0, w, mDetail.getMeasuredHeight()); if (mFooter.hasFooter()) { @@ -846,7 +866,10 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On if (!isShowingDetail() && !isClosingDetail()) { mQsPanelTop.bringToFront(); + } + // layout page indicator inside viewpager inset + mPageIndicator.layout(0, b - mPageIndicatorHeight, w, b); } protected int getRowTop(int row) { @@ -1014,7 +1037,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } if (originatingTileEvent && !event.getResult()) { // view pager probably ate the event - restoreDraggingTilePosition(v); + restoreDraggingTilePosition(v, null); } break; @@ -1032,15 +1055,38 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On Log.d(TAG, "dropping on delete target!!"); } if (mDraggingRecord.tile instanceof EditTile) { + final QSTileView editTileView = mDraggingRecord.tileView; + mQsPanelTop.toast(R.string.quick_settings_cannot_delete_edit_tile); - restoreDraggingTilePosition(v); + restoreDraggingTilePosition(v, new Runnable() { + @Override + public void run() { + // move edit tile to the back + final TileRecord editTile = getRecord(editTileView); + if (mRecords.remove(editTile)) { + // we depend on mHost.setTiles() placing it on the end + persistRecords(); + } + } + }); break; + } else if (mDraggingRecord.tile instanceof CustomQSTile) { + ((CustomQSTile) mDraggingRecord.tile).setUserRemoved(true); + final String spec = mHost.getSpec(mDraggingRecord.tile); + restoreDraggingTilePosition(v, new Runnable() { + @Override + public void run() { + // it might get added back later by the app, but that's ok, + // we just want to reset its position after it has been removed. + mHost.remove(spec); + } + }); } else { mRestored = true; removeDraggingRecord(); } } else { - restoreDraggingTilePosition(v); + restoreDraggingTilePosition(v, null); } break; @@ -1171,7 +1217,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On return false; } - private void restoreDraggingTilePosition(View v) { + private void restoreDraggingTilePosition(View v, final Runnable onAnimationFinishedRunnable) { if (mRestored) { return; } @@ -1251,6 +1297,20 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } @Override + public void onAnimationCancel(Animator animation) { + mViewPager.requestDisallowInterceptTouchEvent(false); + removeTransientView(mDraggingRecord.tileView); + mCurrentlyAnimating.remove(mDraggingRecord); + mRestoring = false; + mPagerAdapter.notifyDataSetChanged(); + onStopDrag(); + + if (onAnimationFinishedRunnable != null) { + postOnAnimation(onAnimationFinishedRunnable); + } + } + + @Override public void onAnimationEnd(Animator animation) { mViewPager.requestDisallowInterceptTouchEvent(false); @@ -1265,8 +1325,8 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On Log.i(TAG, "drag record was attached"); } } - mDraggingRecord.page = mDraggingRecord.destinationPage; targetP.addView(mDraggingRecord.tileView); + mDraggingRecord.page = mDraggingRecord.destinationPage; mDraggingRecord.tileView.setX(mDraggingRecord.destination.x); // reset this to be in the coords of the page, not viewpager anymore @@ -1281,6 +1341,12 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On mPagerAdapter.notifyDataSetChanged(); } onStopDrag(); + + if (onAnimationFinishedRunnable != null) { + postOnAnimation(onAnimationFinishedRunnable); + } else { + requestLayout(); + } } }); } @@ -1497,10 +1563,16 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On .y(ti.destination.y) .setListener(new AnimatorListenerAdapter() { @Override + public void onAnimationCancel(Animator animation) { + tilePageSource.removeTransientView(ti.tileView); + mCurrentlyAnimating.remove(ti); + } + + @Override public void onAnimationEnd(Animator animation) { tilePageSource.removeTransientView(ti.tileView); - ti.page = tilePageTarget.getPageIndex(); tilePageTarget.addView(ti.tileView); + ti.page = tilePageTarget.getPageIndex(); ti.tileView.setX(ti.destination.x); ti.tileView.setY(ti.destination.y); @@ -1516,6 +1588,11 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On .y(ti.destination.y) .setListener(new AnimatorListenerAdapter() { @Override + public void onAnimationCancel(Animator animation) { + mCurrentlyAnimating.remove(ti); + } + + @Override public void onAnimationEnd(Animator animation) { mCurrentlyAnimating.remove(ti); final boolean dual = getPage(ti.destinationPage).dualRecord(ti); @@ -1551,10 +1628,16 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On .y(last.destination.y) .setListener(new AnimatorListenerAdapter() { @Override + public void onAnimationCancel(Animator animation) { + tilePageSource.removeTransientView(last.tileView); + mCurrentlyAnimating.remove(last); + } + + @Override public void onAnimationEnd(Animator animation) { tilePageSource.removeTransientView(last.tileView); - last.page = tilePageTarget.getPageIndex(); tilePageTarget.addView(last.tileView); + last.page = tilePageTarget.getPageIndex(); last.tileView.setX(last.destination.x); last.tileView.setY(last.destination.y); @@ -1573,6 +1656,11 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On .y(last.destination.y) .setListener(new AnimatorListenerAdapter() { @Override + public void onAnimationCancel(Animator animation) { + mCurrentlyAnimating.remove(last); + } + + @Override public void onAnimationEnd(Animator animation) { if (DEBUG_DRAG) { Log.i(TAG, "shift finished: " + last); @@ -1654,10 +1742,16 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } @Override + public void onAnimationCancel(Animator animation) { + page.removeTransientView(ti.tileView); + mCurrentlyAnimating.remove(ti); + } + + @Override public void onAnimationEnd(Animator animation) { page.removeTransientView(ti.tileView); - ti.page = page.getPageIndex(); page.addView(ti.tileView); + ti.page = page.getPageIndex(); mCurrentlyAnimating.remove(ti); requestLayout(); @@ -1710,17 +1804,27 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } }); } + mPanelView.setDetailRequestedScrollLock(mExpanded && show + && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE); } @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); FontSizeUtils.updateFontSize(mDetailRemoveButton, R.dimen.qs_detail_button_text_size); + mPanelView.setDetailRequestedScrollLock(mExpanded && isShowingDetail() + && getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE); } @Override public void setExpanded(boolean expanded) { super.setExpanded(expanded); + // reset the page when inactive for a while + if (expanded) { + removeCallbacks(mResetPage); + } else { + postDelayed(mResetPage, PAGE_RESET_DELAY); + } if (!expanded) { if (mEditing) { mHost.setEditing(false); @@ -1747,10 +1851,11 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On for (TileRecord r : mRecords) { r.tile.clearState(); } + updateDetailText(); + mQsPanelTop.updateResources(); if (mListening) { refreshAllTiles(); } - updateDetailText(); } } @@ -1764,6 +1869,10 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } } + public void setPanelView(NotificationPanelView panelView) { + this.mPanelView = panelView; + } + public static class TilesListAdapter extends BaseExpandableListAdapter implements QSTile.DetailAdapter { @@ -1790,7 +1899,8 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On final Iterator<String> i = tiles.iterator(); while (i.hasNext()) { final String spec = i.next(); - if (QSUtils.isStaticQsTile(spec) || QSUtils.isDynamicQsTile(spec)) { + if (QSUtils.isStaticQsTile(spec) + || QSUtils.isDynamicQsTile(extractTileTagFromSpec(spec))) { List<String> packageList = mPackageTileMap.get(PACKAGE_ANDROID); packageList.add(spec); } else { @@ -1803,13 +1913,122 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On } } + final Map<String, ?> stringMap = CustomQSTile.getCustomQSTilePrefs(mContext).getAll(); + for (Map.Entry<String, ?> entry : stringMap.entrySet()) { + if (entry.getValue() instanceof Boolean) { + if ((Boolean)entry.getValue()) { + final String key = entry.getKey(); + if (QSUtils.isDynamicQsTile(extractTileTagFromSpec(key))) { + mPackageTileMap.get(PACKAGE_ANDROID).add(key); + } else { + final String customTilePackage = getCustomTilePackage(key); + List<String> packageList = mPackageTileMap.get(customTilePackage); + if (packageList == null) { + mPackageTileMap.put(customTilePackage, + packageList = new ArrayList<>()); + } + packageList.add(key); + + } + } + } + }; + final List<String> systemTiles = mPackageTileMap.get(PACKAGE_ANDROID); Collections.sort(systemTiles); } private String getCustomTilePackage(String spec) { - StatusBarPanelCustomTile sbc = mHost.getCustomTileData().get(spec).sbc; - return sbc.getPackage(); + if (mHost.getCustomTileData().get(spec) != null) { + StatusBarPanelCustomTile sbc = mHost.getCustomTileData().get(spec).sbc; + return sbc.getPackage(); + } else { + return extractPackageFromCustomTileSpec(spec); + } + } + + private static String extractPackageFromCustomTileSpec(String spec) { + if (spec != null && !spec.isEmpty()) { + final String[] split = spec.split("\\|"); + if (split != null && split.length > 2) { + return split[1]; + } + return spec; + } + return null; + } + + private static String extractTileTagFromSpec(String spec) { + if (spec != null && !spec.isEmpty()) { + final String[] split = spec.split("\\|"); + if (split != null && split.length == 5) { + /** for {@link cyanogenmod.app.StatusBarPanelCustomTile#key() **/ + return split[3]; + } else if (split != null && split.length == 3) { + /** for {@link cyanogenmod.app.StatusBarPanelCustomTile#persistableKey()} **/ + return split[2]; + } + return spec; + } + return null; + } + + private Drawable getQSTileIcon(String spec) { + if (QSUtils.isDynamicQsTile(extractTileTagFromSpec(spec))) { + return QSTile.ResourceIcon.get(QSUtils.getDynamicQSTileResIconId(mContext, + UserHandle.myUserId(), extractTileTagFromSpec(spec))).getDrawable(mContext); + } else if (QSUtils.isStaticQsTile(spec)) { + final int res = QSTileHost.getIconResource(spec); + if (res != 0) { + return QSTile.ResourceIcon.get(res).getDrawable(mContext); + } else { + return mContext.getPackageManager().getDefaultActivityIcon(); + } + } else { + QSTile<?> tile = mHost.getTile(spec); + if (tile != null) { + QSTile.State state = tile.getState(); + if (state != null && state.icon != null) { + return state.icon.getDrawable(mContext); + } + } + return getPackageDrawable(getCustomTilePackage(spec)); + } + } + + private String getPackageLabel(String packageName) { + try { + return mContext.getPackageManager().getApplicationLabel( + mContext.getPackageManager().getApplicationInfo(packageName, 0)).toString(); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + private Drawable getPackageDrawable(String packageName) { + try { + return mContext.getPackageManager().getApplicationIcon(packageName); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + private String getQSTileLabel(String spec) { + if (QSUtils.isStaticQsTile(spec)) { + int resource = QSTileHost.getLabelResource(spec); + if (resource != 0) { + return mContext.getText(resource).toString(); + } else { + return spec; + } + } else if (QSUtils.isDynamicQsTile(extractTileTagFromSpec(spec))) { + return QSUtils.getDynamicQSTileLabel(mContext, + UserHandle.myUserId(), extractTileTagFromSpec(spec)); + } else { + return getPackageLabel(getCustomTilePackage(spec)); + } } @Override @@ -1872,6 +2091,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On // special icon systemOrAppIcon.setImageResource(R.drawable.ic_qs_tile_category_system); } else { + group = getPackageLabel(group); systemOrAppIcon.setImageResource(R.drawable.ic_qs_tile_category_other); } title.setText(group); @@ -1910,57 +2130,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On return child; } - private String getQSTileLabel(String spec) { - if (QSUtils.isStaticQsTile(spec)) { - int resource = QSTileHost.getLabelResource(spec); - return mContext.getText(resource).toString(); - } else if (QSUtils.isDynamicQsTile(spec)) { - return QSUtils.getDynamicQSTileLabel(mContext, - UserHandle.myUserId(), spec); - } else { - return getPackageLabel(getCustomTilePackage(spec)); - } - } - - private Drawable getQSTileIcon(String spec) { - if (QSUtils.isDynamicQsTile(spec)) { - return QSTile.ResourceIcon.get( - QSUtils.getDynamicQSTileResIconId(mContext, UserHandle.myUserId(), spec)) - .getDrawable(mContext); - } else if (QSUtils.isStaticQsTile(spec)) { - return QSTile.ResourceIcon.get(QSTileHost.getIconResource(spec)) - .getDrawable(mContext); - } else { - QSTile<?> tile = mHost.getTile(spec); - if (tile != null) { - QSTile.State state = tile.getState(); - if (state != null && state.icon != null) { - return state.icon.getDrawable(mContext); - } - } - return getPackageDrawable(getCustomTilePackage(spec)); - } - } - - private String getPackageLabel(String packageName) { - try { - return mContext.getPackageManager().getApplicationLabel( - mContext.getPackageManager().getApplicationInfo(packageName, 0)).toString(); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - return null; - } - - private Drawable getPackageDrawable(String packageName) { - try { - return mContext.getPackageManager().getApplicationIcon(packageName); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - return null; - } - @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return true; @@ -2001,7 +2170,21 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { String spec = getChild(groupPosition, childPosition); - mPanel.add(spec); + + final QSTile<?> tile = mHost.getTile(spec); + if (tile != null && tile instanceof CustomQSTile) { + // already present + ((CustomQSTile) tile).setUserRemoved(false); + mPanel.refreshAllTiles(); + } else { + // reset its state just in case it's not published + CustomQSTile.getCustomQSTilePrefs(mContext) + .edit() + .remove(spec) + .apply(); + mPanel.add(spec); + // TODO notify user the app isn't publishing the tile, but it now can be! + } mPanel.closeDetail(); return true; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 60dc787..77ede93 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -72,7 +72,7 @@ public class QSPanel extends ViewGroup { protected int mDualTileUnderlap; protected int mBrightnessPaddingTop; protected int mGridHeight; - private boolean mExpanded; + protected boolean mExpanded; protected boolean mListening; private boolean mClosingDetail; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelTopView.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelTopView.java index 5f57be1..b00483c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelTopView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelTopView.java @@ -53,6 +53,7 @@ public class QSPanelTopView extends FrameLayout { protected View mBrightnessView; protected TextView mToastView; protected View mAddTarget; + protected TextView mEditInstructionText; private boolean mEditing = false; private boolean mDisplayingInstructions = false; @@ -112,6 +113,14 @@ public class QSPanelTopView extends FrameLayout { mBrightnessView = findViewById(R.id.brightness_container); mToastView = (TextView) findViewById(R.id.qs_toast); mAddTarget = findViewById(R.id.add_target); + mEditInstructionText = (TextView) findViewById(R.id.edit_text_instruction); + updateResources(); + } + + public void updateResources() { + if (mEditInstructionText != null) { + mEditInstructionText.setText(R.string.qs_tile_edit_header_instruction); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index 2d62724..01a170f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -34,6 +34,7 @@ import android.view.ViewGroup; import android.widget.RemoteViews; import com.android.systemui.qs.QSTile.State; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.FlashlightController; @@ -344,6 +345,7 @@ public abstract class QSTile<TState extends State> implements Listenable { CastController getCastController(); FlashlightController getFlashlightController(); KeyguardMonitor getKeyguardMonitor(); + BatteryController getBatteryController(); boolean isEditing(); void setEditing(boolean editing); void resetTiles(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AdbOverNetworkTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AdbOverNetworkTile.java index 283d617..b970a4c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AdbOverNetworkTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AdbOverNetworkTile.java @@ -91,7 +91,7 @@ public class AdbOverNetworkTile extends QSTile<QSTile.BooleanState> { } private boolean isAdbEnabled() { - return Settings.Secure.getInt(mContext.getContentResolver(), + return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.ADB_ENABLED, 0) > 0; } @@ -120,7 +120,7 @@ public class AdbOverNetworkTile extends QSTile<QSTile.BooleanState> { CMSettings.Secure.getUriFor(CMSettings.Secure.ADB_PORT), false, mObserver); mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Global.ADB_ENABLED), + Settings.Global.getUriFor(Settings.Global.ADB_ENABLED), false, mObserver); } else { mContext.getContentResolver().unregisterContentObserver(mObserver); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java index 37d8d73..1a60fa9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java @@ -25,6 +25,8 @@ import android.provider.Settings; import com.android.systemui.qs.QSTile; import com.android.systemui.R; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.BatteryStateRegistar; import cyanogenmod.power.PerformanceManager; @@ -36,13 +38,15 @@ public class BatterySaverTile extends QSTile<QSTile.BooleanState> { private static final Intent BATTERY_SETTINGS = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY); private final PowerManager mPm; - private final PerformanceManager mPerformanceManager; + private final boolean mHasPowerProfiles; + private boolean mListening; + private boolean mPluggedIn; public BatterySaverTile(Host host) { super(host); mPm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mPerformanceManager = PerformanceManager.getInstance(mContext); + mHasPowerProfiles = PerformanceManager.getInstance(mContext).getNumberOfProfiles() > 0; } @Override @@ -53,7 +57,7 @@ public class BatterySaverTile extends QSTile<QSTile.BooleanState> { @Override public void handleClick() { mPm.setPowerSaveMode(!mState.value); - refreshState(); + refreshState(!mState.value); } @Override @@ -63,8 +67,8 @@ public class BatterySaverTile extends QSTile<QSTile.BooleanState> { @Override protected void handleUpdateState(BooleanState state, Object arg) { - state.value = mPm.isPowerSaveMode(); - state.visible = mPerformanceManager.getNumberOfProfiles() == 0; + state.value = arg instanceof Boolean ? (boolean) arg : mPm.isPowerSaveMode(); + state.visible = !mHasPowerProfiles; state.label = mContext.getString(R.string.quick_settings_battery_saver_label); if (state.value) { state.icon = ResourceIcon.get(R.drawable.ic_qs_battery_saver_on); @@ -75,6 +79,11 @@ public class BatterySaverTile extends QSTile<QSTile.BooleanState> { state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_battery_saver_off); } + + state.enabled = !mPluggedIn; + if (mPluggedIn) { + state.label = mContext.getString(R.string.quick_settings_battery_saver_label_charging); + } } @Override @@ -93,11 +102,24 @@ public class BatterySaverTile extends QSTile<QSTile.BooleanState> { return CMMetricsLogger.TILE_BATTERY_SAVER; } - private ContentObserver mObserver = new ContentObserver(mHandler) { + private BatteryStateRegistar.BatteryStateChangeCallback mBatteryState + = new BatteryStateRegistar.BatteryStateChangeCallback() { + @Override + public void onBatteryLevelChanged(boolean present, int level, boolean pluggedIn, + boolean charging) { + mPluggedIn = pluggedIn || charging; + refreshState(); + } + @Override - public void onChange(boolean selfChange, Uri uri) { + public void onPowerSaveChanged() { refreshState(); } + + @Override + public void onBatteryStyleChanged(int style, int percentMode) { + // ignore + } }; @Override @@ -106,11 +128,9 @@ public class BatterySaverTile extends QSTile<QSTile.BooleanState> { mListening = listening; if (listening) { - mContext.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), - false, mObserver); + getHost().getBatteryController().addStateChangedCallback(mBatteryState); } else { - mContext.getContentResolver().unregisterContentObserver(mObserver); + getHost().getBatteryController().removeStateChangedCallback(mBatteryState); } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index a798e4e..cdedc26 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -225,6 +225,7 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState> { listView.setOnItemClickListener(this); listView.setAdapter(mAdapter = new QSDetailItemsList.QSDetailListAdapter(context, mBluetoothItems)); + mAdapter.setCallback(this); mItemsList.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty, R.string.quick_settings_bluetooth_detail_empty_text); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index 04cc5dc..f49d97e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -39,7 +39,6 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { private final AnimationIcon mDisable = new AnimationIcon(R.drawable.ic_invert_colors_disable_animation); private final SecureSetting mSetting; - private final UsageTracker mUsageTracker; private boolean mListening; @@ -50,29 +49,11 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) { @Override protected void handleValueChanged(int value, boolean observedChange) { - if (value != 0 || observedChange) { - mUsageTracker.trackUsage(); - } if (mListening) { handleRefreshState(value); } } }; - mUsageTracker = new UsageTracker(host.getContext(), - Prefs.Key.COLOR_INVERSION_TILE_LAST_USED, ColorInversionTile.class, - R.integer.days_to_show_color_inversion_tile); - if (mSetting.getValue() != 0 && !mUsageTracker.isRecentlyUsed()) { - mUsageTracker.trackUsage(); - } - mUsageTracker.setListening(true); - mSetting.setListening(true); - } - - @Override - protected void handleDestroy() { - super.handleDestroy(); - mUsageTracker.setListening(false); - mSetting.setListening(false); } @Override @@ -82,7 +63,11 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { @Override public void setListening(boolean listening) { + if (mListening == listening) { + return; + } mListening = listening; + mSetting.setListening(mListening); } @Override @@ -101,25 +86,14 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { @Override protected void handleLongClick() { - if (mState.value) { - mHost.startActivityDismissingKeyguard(ACCESSIBILITY_SETTINGS); - } else { - final String title = mContext.getString( - R.string.quick_settings_reset_confirmation_title, mState.label); - mUsageTracker.showResetConfirmation(title, new Runnable() { - @Override - public void run() { - refreshState(); - } - }); - } + mHost.startActivityDismissingKeyguard(ACCESSIBILITY_SETTINGS); } @Override protected void handleUpdateState(BooleanState state, Object arg) { final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue(); final boolean enabled = value != 0; - state.visible = enabled || mUsageTracker.isRecentlyUsed(); + state.visible = true; state.value = enabled; state.label = mContext.getString(R.string.quick_settings_inversion_label); state.icon = enabled ? mEnable : mDisable; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java index f19e466..85790d1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java @@ -32,6 +32,7 @@ public class CompassTile extends QSTile<QSTile.BooleanState> implements SensorEv private final static float ALPHA = 0.97f; private boolean mActive = false; + private boolean mListening = false; private SensorManager mSensorManager; private Sensor mAccelerationSensor; @@ -95,7 +96,7 @@ public class CompassTile extends QSTile<QSTile.BooleanState> implements SensorEv Float degrees = arg == null ? 0 :(float) arg; state.visible = true; - state.value = mActive; + state.value = mActive && mListening; if (state.value) { state.icon = ResourceIcon.get(R.drawable.ic_qs_compass_on); @@ -126,10 +127,10 @@ public class CompassTile extends QSTile<QSTile.BooleanState> implements SensorEv @Override public void setListening(boolean listening) { - if (!listening) { - setListeningSensors(false); - mActive = false; - } + // setListening might get called multiple times with the same value, we check for it + // in setListeningSensors + mListening = listening; + setListeningSensors(mListening && mActive); } private String formatValueWithCardinalDirection(float degree) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java index d09ca92..40c7184 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomQSTile.java @@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.ThemeConfig; import android.net.Uri; @@ -31,15 +32,12 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.RemoteViews; import android.widget.TextView; -import com.android.internal.logging.MetricsLogger; - import com.android.systemui.qs.QSDetailItemsGrid; import com.android.systemui.qs.QSDetailItemsList; import cyanogenmod.app.CustomTile; @@ -53,6 +51,8 @@ import java.util.Arrays; public class CustomQSTile extends QSTile<QSTile.State> { + private static final String HIDDEN_TILES_PREF_NAME = "user_hidden_qs_tiles"; + private CustomTile.ExpandedStyle mExpandedStyle; private PendingIntent mOnClick; private PendingIntent mOnLongClick; @@ -61,10 +61,51 @@ public class CustomQSTile extends QSTile<QSTile.State> { private StatusBarPanelCustomTile mTile; private CustomQSDetailAdapter mDetailAdapter; private boolean mCollapsePanel; + private boolean mUserRemoved; + private String mPersistedPlaceHolderKey; + + public CustomQSTile(Host host, String persistedSpec) { + super(host); + mTile = null; + mPersistedPlaceHolderKey = persistedSpec; + } public CustomQSTile(Host host, StatusBarPanelCustomTile tile) { super(host); mTile = tile; + mUserRemoved = getIsUserRemovedPersisted(); + } + + private String getPersistableKey() { + if (mPersistedPlaceHolderKey != null) { + return mPersistedPlaceHolderKey; + } else { + return getTile().persistableKey(); + } + } + + private boolean getIsUserRemovedPersisted() { + return getCustomQSTilePrefs(mContext).getBoolean(getPersistableKey(), false); + } + + public boolean isUserRemoved() { + return mUserRemoved; + } + + public void setUserRemoved(boolean removed) { + if (mUserRemoved != removed) { + if (removed) { + getCustomQSTilePrefs(mContext).edit().putBoolean(getPersistableKey(), true).apply(); + } else { + getCustomQSTilePrefs(mContext).edit().remove(getPersistableKey()).apply(); + } + mUserRemoved = removed; + refreshState(); + } + } + + public static SharedPreferences getCustomQSTilePrefs(Context context) { + return context.getSharedPreferences(HIDDEN_TILES_PREF_NAME, Context.MODE_PRIVATE); } @Override @@ -138,11 +179,18 @@ public class CustomQSTile extends QSTile<QSTile.State> { protected void handleUpdateState(State state, Object arg) { if (arg instanceof StatusBarPanelCustomTile) { mTile = (StatusBarPanelCustomTile) arg; + mPersistedPlaceHolderKey = null; + mUserRemoved = getIsUserRemovedPersisted(); + } + if (mTile == null) { + state.visible = false; + // nothing to show, it's a place holder for now + return; } final CustomTile customTile = mTile.getCustomTile(); state.contentDescription = customTile.contentDescription; state.label = customTile.label; - state.visible = true; + state.visible = !mUserRemoved; final int iconId = customTile.icon; if (iconId != 0 && (customTile.remoteIcon == null)) { final String iconPackage = mTile.getResPkg(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index 25a7fb7..6080358 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -48,7 +48,6 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { new AnimationIcon(R.drawable.ic_hotspot_disable_animation); private final HotspotController mController; private final Callback mCallback = new Callback(); - private final UsageTracker mUsageTracker; private final ConnectivityManager mConnectivityManager; private boolean mListening; private int mNumConnectedClients = 0; @@ -56,18 +55,10 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { public HotspotTile(Host host) { super(host); mController = host.getHotspotController(); - mUsageTracker = newUsageTracker(host.getContext()); - mUsageTracker.setListening(true); mConnectivityManager = host.getContext().getSystemService(ConnectivityManager.class); } @Override - protected void handleDestroy() { - super.handleDestroy(); - mUsageTracker.setListening(false); - } - - @Override protected BooleanState newTileState() { return new BooleanState(); } @@ -108,23 +99,12 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { @Override protected void handleLongClick() { - if (mState.value) { - mHost.startActivityDismissingKeyguard(TETHER_SETTINGS); - } else { - final String title = mContext.getString( - R.string.quick_settings_reset_confirmation_title, mState.label); - mUsageTracker.showResetConfirmation(title, new Runnable() { - @Override - public void run() { - refreshState(); - } - }); - } + mHost.startActivityDismissingKeyguard(TETHER_SETTINGS); } @Override protected void handleUpdateState(BooleanState state, Object arg) { - state.visible = mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed(); + state.visible = mController.isHotspotSupported(); if (arg instanceof Boolean) { state.value = (boolean) arg; @@ -155,11 +135,6 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { } } - private static UsageTracker newUsageTracker(Context context) { - return new UsageTracker(context, Prefs.Key.HOTSPOT_TILE_LAST_USED, HotspotTile.class, - R.integer.days_to_show_hotspot_tile); - } - private BroadcastReceiver mTetherConnectStateChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -175,20 +150,4 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { refreshState(enabled); } }; - - /** - * This will catch broadcasts for changes in hotspot state so we can show - * the hotspot tile for a number of days after use. - */ - public static class APChangedReceiver extends BroadcastReceiver { - private UsageTracker mUsageTracker; - - @Override - public void onReceive(Context context, Intent intent) { - if (mUsageTracker == null) { - mUsageTracker = newUsageTracker(context); - } - mUsageTracker.trackUsage(); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LiveDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LiveDisplayTile.java deleted file mode 100644 index f59c876..0000000 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LiveDisplayTile.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2015 The CyanogenMod Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.qs.tiles; - -import android.content.Intent; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.database.ContentObserver; -import android.os.Handler; -import android.os.UserHandle; - -import com.android.internal.util.ArrayUtils; -import com.android.systemui.R; -import com.android.systemui.qs.QSTile; - -import cyanogenmod.hardware.CMHardwareManager; -import cyanogenmod.providers.CMSettings; -import org.cyanogenmod.internal.logging.CMMetricsLogger; - -/** Quick settings tile: LiveDisplay mode switcher **/ -public class LiveDisplayTile extends QSTile<LiveDisplayTile.LiveDisplayState> { - - private static final Intent LIVEDISPLAY_SETTINGS = - new Intent("android.settings.LIVEDISPLAY_SETTINGS"); - - private final LiveDisplayObserver mObserver; - private String[] mEntries; - private String[] mDescriptionEntries; - private String[] mAnnouncementEntries; - private String[] mValues; - private final int[] mEntryIconRes; - - private boolean mListening; - - private static final int MODE_OUTDOOR = 3; - private static final int MODE_DAY = 4; - - private static final int OFF_TEMPERATURE = 6500; - - private int mDayTemperature; - - private final boolean mOutdoorModeAvailable; - private final int mDefaultDayTemperature; - - public LiveDisplayTile(Host host) { - super(host); - - Resources res = mContext.getResources(); - TypedArray typedArray = res.obtainTypedArray(R.array.live_display_drawables); - mEntryIconRes = new int[typedArray.length()]; - for (int i = 0; i < mEntryIconRes.length; i++) { - mEntryIconRes[i] = typedArray.getResourceId(i, 0); - } - typedArray.recycle(); - - updateEntries(); - - mOutdoorModeAvailable = - CMHardwareManager.getInstance(mContext) - .isSupported(CMHardwareManager.FEATURE_SUNLIGHT_ENHANCEMENT); - - mDefaultDayTemperature = mContext.getResources().getInteger( - com.android.internal.R.integer.config_dayColorTemperature); - loadDayTemperature(); - - mObserver = new LiveDisplayObserver(mHandler); - mObserver.startObserving(); - } - - private void updateEntries() { - Resources res = mContext.getResources(); - mEntries = res.getStringArray(com.android.internal.R.array.live_display_entries); - mDescriptionEntries = res.getStringArray(R.array.live_display_description); - mAnnouncementEntries = res.getStringArray(R.array.live_display_announcement); - mValues = res.getStringArray(com.android.internal.R.array.live_display_values); - } - - @Override - protected LiveDisplayState newTileState() { - return new LiveDisplayState(); - } - - @Override - public void setListening(boolean listening) { - if (mListening == listening) - return; - mListening = listening; - if (listening) { - mObserver.startObserving(); - } else { - mObserver.endObserving(); - } - } - - @Override - protected void handleClick() { - changeToNextMode(); - } - - @Override - protected void handleLongClick() { - mHost.startActivityDismissingKeyguard(LIVEDISPLAY_SETTINGS); - } - - @Override - protected void handleUpdateState(LiveDisplayState state, Object arg) { - updateEntries(); - state.visible = true; - state.mode = arg == null ? getCurrentModeIndex() : (Integer) arg; - state.label = mEntries[state.mode]; - state.icon = ResourceIcon.get(mEntryIconRes[state.mode]); - state.contentDescription = mDescriptionEntries[state.mode]; - } - - @Override - public int getMetricsCategory() { - return CMMetricsLogger.TILE_LIVE_DISPLAY; - } - - @Override - protected String composeChangeAnnouncement() { - return mAnnouncementEntries[getCurrentModeIndex()]; - } - - private int getCurrentModeIndex() { - return ArrayUtils.indexOf(mValues, - String.valueOf(CMSettings.System.getIntForUser(mContext.getContentResolver(), - CMSettings.System.DISPLAY_TEMPERATURE_MODE, - 0, UserHandle.USER_CURRENT))); - } - - private void changeToNextMode() { - int next = getCurrentModeIndex() + 1; - - if (next >= mValues.length) { - next = 0; - } - - while (true) { - // Skip outdoor mode if it's unsupported, and skip the day setting - // if it's the same as the off setting - if ((!mOutdoorModeAvailable && - Integer.valueOf(mValues[next]) == MODE_OUTDOOR) || - (mDayTemperature == OFF_TEMPERATURE && - Integer.valueOf(mValues[next]) == MODE_DAY)) { - next++; - if (next >= mValues.length) { - next = 0; - } - } else { - break; - } - } - - CMSettings.System.putIntForUser(mContext.getContentResolver(), - CMSettings.System.DISPLAY_TEMPERATURE_MODE, - Integer.valueOf(mValues[next]), UserHandle.USER_CURRENT); - } - - private void loadDayTemperature() { - mDayTemperature = CMSettings.System.getIntForUser(mContext.getContentResolver(), - CMSettings.System.DISPLAY_TEMPERATURE_DAY, - mDefaultDayTemperature, - UserHandle.USER_CURRENT); - } - - private class LiveDisplayObserver extends ContentObserver { - public LiveDisplayObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange) { - loadDayTemperature(); - refreshState(getCurrentModeIndex()); - } - - public void startObserving() { - mContext.getContentResolver().registerContentObserver( - CMSettings.System.getUriFor(CMSettings.System.DISPLAY_TEMPERATURE_MODE), - false, this, UserHandle.USER_ALL); - mContext.getContentResolver().registerContentObserver( - CMSettings.System.getUriFor(CMSettings.System.DISPLAY_TEMPERATURE_DAY), - false, this, UserHandle.USER_ALL); - } - - public void endObserving() { - mContext.getContentResolver().unregisterContentObserver(this); - } - } - - public static class LiveDisplayState extends QSTile.State { - public int mode; - - @Override - public boolean copyTo(State other) { - final LiveDisplayState o = (LiveDisplayState) other; - final boolean changed = mode != o.mode; - return super.copyTo(other) || changed; - } - - @Override - protected StringBuilder toStringBuilder() { - final StringBuilder rt = super.toStringBuilder(); - rt.insert(rt.length() - 1, ",mode=" + mode); - return rt; - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java index 1bfbb8f..a5ffd23 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java @@ -22,12 +22,14 @@ import android.content.Intent; import android.content.IntentFilter; import android.nfc.NfcAdapter; +import android.util.Log; import com.android.systemui.R; import com.android.systemui.qs.QSTile; import org.cyanogenmod.internal.logging.CMMetricsLogger; +import org.cyanogenmod.internal.util.QSUtils; public class NfcTile extends QSTile<QSTile.BooleanState> { - private NfcAdapter mNfcAdapter; + private boolean mListening; private BroadcastReceiver mReceiver = new BroadcastReceiver() { @@ -36,10 +38,11 @@ public class NfcTile extends QSTile<QSTile.BooleanState> { refreshState(); } }; + private final boolean mSupportsNfc; public NfcTile(Host host) { super(host); - mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext); + mSupportsNfc = QSUtils.deviceSupportsNfc(mContext); } @Override @@ -49,7 +52,9 @@ public class NfcTile extends QSTile<QSTile.BooleanState> { @Override protected void handleClick() { - toggleState(); + boolean newState = !getState().value; + setState(newState); + refreshState(); } @Override @@ -57,23 +62,45 @@ public class NfcTile extends QSTile<QSTile.BooleanState> { mHost.startActivityDismissingKeyguard(new Intent("android.settings.NFC_SETTINGS")); } - protected void toggleState() { - int state = getNfcState(); - switch (state) { - case NfcAdapter.STATE_TURNING_ON: - case NfcAdapter.STATE_ON: - mNfcAdapter.disable(); - break; - case NfcAdapter.STATE_TURNING_OFF: - case NfcAdapter.STATE_OFF: - mNfcAdapter.enable(); - break; + private void setState(boolean on) { + try { + NfcAdapter nfcAdapter = NfcAdapter.getNfcAdapter(mContext); + if (nfcAdapter == null) { + Log.e(TAG, "tried to set NFC state, but no NFC adapter was found"); + return; + } + if (on) { + nfcAdapter.enable(); + } else { + nfcAdapter.disable(); + } + } catch (UnsupportedOperationException e) { + // ignore + } + } + + private int getNfcAdapterState() { + try { + NfcAdapter nfcAdapter = NfcAdapter.getNfcAdapter(mContext); + if (nfcAdapter == null) { + Log.e(TAG, "tried to get NFC state, but no NFC adapter was found"); + return NfcAdapter.STATE_OFF; + } + return nfcAdapter.getAdapterState(); + } catch (UnsupportedOperationException e) { + // ignore + return NfcAdapter.STATE_OFF; } } - private boolean isEnabled() { - int state = getNfcState(); - switch (state) { + /** + * Helper method to encapsulate intermediate states (turning off/on) to help determine whether + * the adapter will be on or off. + * @param nfcState The current NFC adapter state. + * @return boolean representing what state the adapter is/will be in + */ + private static boolean isEnabled(int nfcState) { + switch (nfcState) { case NfcAdapter.STATE_TURNING_ON: case NfcAdapter.STATE_ON: return true; @@ -84,17 +111,28 @@ public class NfcTile extends QSTile<QSTile.BooleanState> { } } - private int getNfcState() { - return mNfcAdapter.getAdapterState(); + /** + * Helper method to determine intermediate states + * @param nfcState The current NFC adapter state. + * @return boolean representing if the adapter is in an intermediate state + */ + private static boolean isEnablingDisabling(int nfcState) { + switch (nfcState) { + case NfcAdapter.STATE_TURNING_OFF: + case NfcAdapter.STATE_TURNING_ON: + return true; + default: + return false; + } } @Override protected void handleUpdateState(BooleanState state, Object arg) { - if (mNfcAdapter == null) { - mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext); - } - state.visible = mNfcAdapter != null; - state.value = mNfcAdapter != null && isEnabled(); + state.visible = mSupportsNfc; + final int nfcState = getNfcAdapterState(); + state.value = mSupportsNfc && isEnabled(nfcState); + state.enabled = mSupportsNfc && !isEnablingDisabling(nfcState); + state.icon = ResourceIcon.get(state.value ? R.drawable.ic_qs_nfc_on : R.drawable.ic_qs_nfc_off); state.label = mContext.getString(R.string.quick_settings_nfc_label); @@ -110,12 +148,9 @@ public class NfcTile extends QSTile<QSTile.BooleanState> { if (mListening == listening) return; mListening = listening; if (listening) { - if (mNfcAdapter == null) { - mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext); - refreshState(); - } mContext.registerReceiver(mReceiver, new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)); + refreshState(); } else { mContext.unregisterReceiver(mReceiver); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/PerfProfileTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/PerfProfileTile.java index e6e6bb4..4863683 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/PerfProfileTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/PerfProfileTile.java @@ -48,6 +48,7 @@ public class PerfProfileTile extends QSTile<PerfProfileTile.ProfileState> { private final String[] mDescriptionEntries; private final String[] mAnnouncementEntries; private final int[] mPerfProfileValues; + private final int mNumPerfProfiles; private final Icon mIcon; private final PowerManager mPm; @@ -61,16 +62,36 @@ public class PerfProfileTile extends QSTile<PerfProfileTile.ProfileState> { mObserver = new PerformanceProfileObserver(mHandler); mPm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mPerformanceManager = PerformanceManager.getInstance(mContext); + mNumPerfProfiles = mPerformanceManager.getNumberOfProfiles(); - Resources res = mContext.getResources(); - - mPerfProfileValues = res.getIntArray(org.cyanogenmod.platform.internal.R.array.perf_profile_values); - - mEntries = res.getStringArray(org.cyanogenmod.platform.internal.R.array.perf_profile_entries); - mDescriptionEntries = res.getStringArray(R.array.perf_profile_description); - mAnnouncementEntries = res.getStringArray(R.array.perf_profile_announcement); + mPerfProfileValues = new int[mNumPerfProfiles]; + mEntries = new String[mNumPerfProfiles]; + mDescriptionEntries = new String[mNumPerfProfiles]; + mAnnouncementEntries = new String[mNumPerfProfiles]; mIcon = ResourceIcon.get(R.drawable.ic_qs_perf_profile); + + // Filter out unsupported profiles + Resources res = mContext.getResources(); + final int[] perfProfileValues = res.getIntArray( + org.cyanogenmod.platform.internal.R.array.perf_profile_values); + final String[] entries = res.getStringArray( + org.cyanogenmod.platform.internal.R.array.perf_profile_entries); + final String[] descriptionEntries = res.getStringArray( + R.array.perf_profile_description); + final String[] announcementEntries = res.getStringArray( + R.array.perf_profile_announcement); + int i = 0; + + for (int j = 0; j < perfProfileValues.length; j++) { + if (perfProfileValues[j] < mNumPerfProfiles) { + mPerfProfileValues[i] = perfProfileValues[j]; + mEntries[i] = entries[j]; + mDescriptionEntries[i] = descriptionEntries[j]; + mAnnouncementEntries[i] = announcementEntries[j]; + i++; + } + } } @Override @@ -135,7 +156,7 @@ public class PerfProfileTile extends QSTile<PerfProfileTile.ProfileState> { public void startObserving() { mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(CMSettings.Secure.PERFORMANCE_PROFILE), + CMSettings.Secure.getUriFor(CMSettings.Secure.PERFORMANCE_PROFILE), false, this); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index f4fe677..9e08599 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -85,6 +85,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Runnable to be executed after we paused ourselves Runnable mAfterPauseRunnable; + private ReferenceCountedTrigger mExitTrigger; + /** * A common Runnable to finish Recents either by calling finish() (with a custom animation) or * launching Home with some ActivityOptions. Generally we always launch home when we exit @@ -95,6 +97,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView class FinishRecentsRunnable implements Runnable { Intent mLaunchIntent; ActivityOptions mLaunchOpts; + boolean mAbort = false; /** * Creates a finish runnable that starts the specified intent, using the given @@ -105,8 +108,15 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView mLaunchOpts = opts; } + public void setAbort(boolean run) { + this.mAbort = run; + } + @Override public void run() { + if (mAbort) { + return; + } // Finish Recents if (mLaunchIntent != null) { try { @@ -317,13 +327,26 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView return false; } + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (!hasFocus && mExitTrigger != null && mExitTrigger.getCount() > 0) { + // we are animating recents out and the window has lost focus during the + // animation. we need to stop everything we're doing now and get out + // without any animations (since we were already animating) + mFinishLaunchHomeRunnable.setAbort(true); + finish(); + overridePendingTransition(0, 0); + } + } + /** Dismisses Recents directly to Home. */ void dismissRecentsToHomeRaw(boolean animated) { if (animated) { - ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, + mExitTrigger = new ReferenceCountedTrigger(this, null, mFinishLaunchHomeRunnable, null); mRecentsView.startExitToHomeAnimation( - new ViewAnimation.TaskViewExitContext(exitTrigger)); + new ViewAnimation.TaskViewExitContext(mExitTrigger)); } else { mFinishLaunchHomeRunnable.run(); } @@ -458,6 +481,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override protected void onStop() { super.onStop(); + + mExitTrigger = null; + MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY); RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); SystemServicesProxy ssp = loader.getSystemServicesProxy(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 64622620..2f11c56 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -31,6 +31,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.AttributeSet; import android.util.Log; +import android.util.EventLog; import android.view.LayoutInflater; import android.view.View; import android.view.WindowInsets; @@ -48,6 +49,8 @@ import com.android.systemui.recents.model.RecentsTaskLoader; import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.EventLogTags; + import java.util.ArrayList; import java.util.List; @@ -267,6 +270,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV stackView.startEnterRecentsAnimation(ctx); } ctx.postAnimationTrigger.decrement(); + + EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_EVENT, 1 /* opened */); } /** Requests all task stacks to start their exit-recents animation */ @@ -620,6 +625,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV launchRunnable.run(); } } + + EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_EVENT, 3 /* chose task */); } @Override @@ -659,6 +666,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV // Keep track of all-deletions MetricsLogger.count(getContext(), "overview_task_all_dismissed", 1); + EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_EVENT, 4 /* closed all */); } /** Final callback after Recents is finally hidden. */ @@ -670,6 +678,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV TaskStackView stackView = stackViews.get(i); stackView.onRecentsHidden(); } + EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_EVENT, 2 /* closed */); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 43b9a3e..454222c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -1018,8 +1018,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal t.setClipViewInStack(false); t.startLaunchTaskAnimation(r, true, true, lockToTask); } else { - boolean occludesLaunchTarget = launchTargetTask.group.isTaskAboveTask(t.getTask(), - launchTargetTask); + boolean occludesLaunchTarget = launchTargetTask != null && + launchTargetTask.group != null && + launchTargetTask.group.isTaskAboveTask(t.getTask(), launchTargetTask); t.startLaunchTaskAnimation(null, false, occludesLaunchTarget, lockToTask); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 7b608bb..e958ee1 100755 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -45,6 +45,7 @@ import android.database.ContentObserver; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.media.session.MediaController; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; @@ -117,6 +118,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +import cyanogenmod.providers.CMSettings; + import static com.android.keyguard.KeyguardHostView.OnDismissAction; public abstract class BaseStatusBar extends SystemUI implements @@ -154,6 +157,8 @@ public abstract class BaseStatusBar extends SystemUI implements private static final String BANNER_ACTION_SETUP = "com.android.systemui.statusbar.banner_action_setup"; + protected static final int SYSTEM_UI_VISIBILITY_MASK = 0xffffffff; + private static final Uri SPAM_MESSAGE_URI = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(SpamMessageProvider.AUTHORITY) @@ -221,6 +226,8 @@ public abstract class BaseStatusBar extends SystemUI implements protected WindowManager mWindowManager; protected IWindowManager mWindowManagerService; + private NotificationManager mNoMan; + protected abstract void refreshLayout(int layoutDirection); protected Display mDisplay; @@ -263,8 +270,8 @@ public abstract class BaseStatusBar extends SystemUI implements protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { - final boolean provisioned = 0 != Settings.Global.getInt( - mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0); + final boolean provisioned = 0 != CMSettings.Secure.getInt( + mContext.getContentResolver(), CMSettings.Secure.CM_SETUP_WIZARD_COMPLETED, 0); if (provisioned != mDeviceProvisioned) { mDeviceProvisioned = provisioned; updateNotifications(); @@ -427,9 +434,7 @@ public abstract class BaseStatusBar extends SystemUI implements } } } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) { - NotificationManager noMan = (NotificationManager) - mContext.getSystemService(Context.NOTIFICATION_SERVICE); - noMan.cancel(R.id.notification_hidden); + mNoMan.cancel(R.id.notification_hidden); Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0); @@ -558,6 +563,9 @@ public abstract class BaseStatusBar extends SystemUI implements public void start() { mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); mWindowManagerService = WindowManagerGlobal.getWindowManagerService(); + + mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + mDisplay = mWindowManager.getDefaultDisplay(); mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService( Context.DEVICE_POLICY_SERVICE); @@ -574,8 +582,8 @@ public abstract class BaseStatusBar extends SystemUI implements mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mContext.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true, - mSettingsObserver); + CMSettings.Secure.getUriFor(CMSettings.Secure.CM_SETUP_WIZARD_COMPLETED), false, + mSettingsObserver, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false, mSettingsObserver); @@ -624,7 +632,7 @@ public abstract class BaseStatusBar extends SystemUI implements mSettingsObserver.onChange(false); // set up disable(switches[0], switches[6], false /* animate */); - setSystemUiVisibility(switches[1], 0xffffffff); + setSystemUiVisibility(switches[1], SYSTEM_UI_VISIBILITY_MASK); topAppWindowChanged(switches[2] != 0); // StatusBarManagerService has a back up of IME token and it's restored here. setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0); @@ -723,9 +731,7 @@ public abstract class BaseStatusBar extends SystemUI implements mContext.getString(R.string.hidden_notifications_setup), setupIntent); - NotificationManager noMan = - (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); - noMan.notify(R.id.notification_hidden, note.build()); + mNoMan.notify(R.id.notification_hidden, note.build()); } } @@ -769,6 +775,10 @@ public abstract class BaseStatusBar extends SystemUI implements return null; } + protected MediaController getCurrentMediaController() { + return null; + } + @Override public NotificationGroupManager getGroupManager() { return mGroupManager; @@ -947,7 +957,9 @@ public abstract class BaseStatusBar extends SystemUI implements } }); - filterButton.setVisibility(View.VISIBLE); + Notification notification = sbn.getNotification(); + filterButton.setVisibility(SpamFilter.hasFilterableContent(notification) + ? View.VISIBLE : View.GONE); filterButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { AsyncTask.execute(new Runnable() { @@ -1014,6 +1026,10 @@ public abstract class BaseStatusBar extends SystemUI implements } ExpandableNotificationRow row = (ExpandableNotificationRow) v; + if (v instanceof MediaExpandableNotificationRow + && !((MediaExpandableNotificationRow) v).inflateGuts()) { + return false; + } bindGuts(row); // Assume we are a status_bar_notification_row @@ -1280,6 +1296,20 @@ public abstract class BaseStatusBar extends SystemUI implements } protected boolean inflateViews(Entry entry, ViewGroup parent) { + final StatusBarNotification sbn = entry.notification; + String themePackageName = mCurrentTheme != null + ? mCurrentTheme.getOverlayPkgNameForApp(sbn.getPackageName()) : null; + boolean inflated = inflateViews(entry, parent, true); + if (!inflated && themePackageName != null + && !ThemeConfig.SYSTEM_DEFAULT.equals(themePackageName)) { + Log.w(TAG, "Couldn't expand themed RemoteViews, trying unthemed for: " + sbn); + inflated = inflateViews(entry, mStackScroller, false); + } + + return inflated; + } + + protected boolean inflateViews(Entry entry, ViewGroup parent, boolean isThemeable) { PackageManager pmUser = getPackageManagerForUser( entry.notification.getUser().getIdentifier()); @@ -1320,8 +1350,19 @@ public abstract class BaseStatusBar extends SystemUI implements // create the row view LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( Context.LAYOUT_INFLATER_SERVICE); - row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row, - parent, false); + + // cannot use isMediaNotification() + if (sbn.getNotification().category != null + && sbn.getNotification().category.equals(Notification.CATEGORY_TRANSPORT)) { + row = (MediaExpandableNotificationRow) inflater.inflate( + R.layout.status_bar_notification_row_media, parent, false); + ((MediaExpandableNotificationRow)row).setMediaController( + getCurrentMediaController()); + } else { + row = (ExpandableNotificationRow) inflater.inflate( + R.layout.status_bar_notification_row, + parent, false); + } row.setExpansionLogger(this, entry.notification.getKey()); row.setGroupManager(mGroupManager); } @@ -1345,10 +1386,12 @@ public abstract class BaseStatusBar extends SystemUI implements View contentViewLocal = null; View bigContentViewLocal = null; View headsUpContentViewLocal = null; - String themePackageName = mCurrentTheme != null - ? mCurrentTheme.getOverlayPkgNameForApp(sbn.getPackageName()) : null; - String statusBarThemePackageName = mCurrentTheme != null - ? mCurrentTheme.getOverlayForStatusBar() : null; + String themePackageName = (isThemeable && mCurrentTheme != null) + ? mCurrentTheme.getOverlayPkgNameForApp(sbn.getPackageName()) + : ThemeConfig.SYSTEM_DEFAULT; + String statusBarThemePackageName = (isThemeable && mCurrentTheme != null) + ? mCurrentTheme.getOverlayForStatusBar() + : ThemeConfig.SYSTEM_DEFAULT; try { contentViewLocal = contentView.apply( @@ -1439,8 +1482,10 @@ public abstract class BaseStatusBar extends SystemUI implements } if (publicViewLocal == null) { + final Context layoutContext = isThemeable ? mContext + : maybeGetThemedContext(mContext, ThemeConfig.SYSTEM_DEFAULT); // Add a basic notification template - publicViewLocal = LayoutInflater.from(mContext).inflate( + publicViewLocal = LayoutInflater.from(layoutContext).inflate( R.layout.notification_public_default, contentContainerPublic, false); publicViewLocal.setIsRootNamespace(true); @@ -1875,7 +1920,18 @@ public abstract class BaseStatusBar extends SystemUI implements } private boolean shouldShowOnKeyguard(StatusBarNotification sbn) { - return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey()); + if (!mShowLockscreenNotifications || mNotificationData.isAmbient(sbn.getKey())) { + return false; + } + final int showOnKeyguard = mNoMan.getShowNotificationForPackageOnKeyguard( + sbn.getPackageName(), sbn.getUid()); + boolean isKeyguardAllowedForApp = + (showOnKeyguard & Notification.SHOW_ALL_NOTI_ON_KEYGUARD) != 0; + if (isKeyguardAllowedForApp && sbn.isOngoing()) { + isKeyguardAllowedForApp = + (showOnKeyguard & Notification.SHOW_NO_ONGOING_NOTI_ON_KEYGUARD) == 0; + } + return isKeyguardAllowedForApp; } protected void setZenMode(int mode) { @@ -2286,4 +2342,24 @@ public abstract class BaseStatusBar extends SystemUI implements mAssistManager.startAssist(args); } } + + /** + * Returns a context with the given theme applied or the original context if we fail to get a + * themed context. + */ + private Context maybeGetThemedContext(Context context, String themePkg) { + Context themedContext; + try { + ApplicationInfo ai = context.getPackageManager().getApplicationInfo( + context.getPackageName(), 0); + themedContext = context.createApplicationContext(ai, themePkg, + 0); + } catch (PackageManager.NameNotFoundException e) { + themedContext = null; + } + if (themedContext == null) { + themedContext = context; + } + return themedContext; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CustomTileData.java b/packages/SystemUI/src/com/android/systemui/statusbar/CustomTileData.java index 4be7292..42974ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CustomTileData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CustomTileData.java @@ -29,7 +29,7 @@ public class CustomTileData { public final StatusBarPanelCustomTile sbc; public Entry(StatusBarPanelCustomTile sbc) { - this.key = sbc.getKey(); + this.key = sbc.persistableKey(); this.sbc = sbc; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index c964ca7..be51e57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -64,7 +64,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private boolean mShowingPublic; private boolean mSensitive; private boolean mShowingPublicInitialized; - private boolean mHideSensitiveForIntrinsicHeight; + protected boolean mHideSensitiveForIntrinsicHeight; /** * Is this notification expanded by the system. The expansion state can be overridden by the @@ -77,10 +77,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { */ private boolean mExpansionDisabled; - private NotificationContentView mPublicLayout; - private NotificationContentView mPrivateLayout; - private int mMaxExpandHeight; - private int mHeadsUpHeight; + protected NotificationContentView mPublicLayout; + protected NotificationContentView mPrivateLayout; + protected int mMaxExpandHeight; + protected int mHeadsUpHeight; private View mVetoButton; private boolean mClearable; private ExpansionLogger mLogger; @@ -101,7 +101,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private ValueAnimator mChildExpandAnimator; private float mChildrenExpandProgress; private float mExpandButtonStart; - private ViewStub mGutsStub; + protected ViewStub mGutsStub; private boolean mHasExpandAction; private boolean mIsSystemChildExpanded; private boolean mIsPinned; @@ -443,10 +443,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { mVetoButton = findViewById(R.id.veto); } - public void inflateGuts() { + public boolean inflateGuts() { if (mGuts == null) { mGutsStub.inflate(); } + return false; } private void updateChildrenVisibility(boolean animated) { @@ -645,7 +646,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } else if (mChildrenExpanded) { maxContentHeight = mChildrenContainer.getIntrinsicHeight(); } else { - maxContentHeight = getMaxExpandHeight(); + maxContentHeight = mMaxExpandHeight; } return maxContentHeight + getBottomDecorHeight(); } @@ -669,7 +670,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { * * @return whether the view state is currently expanded. */ - private boolean isExpanded() { + protected boolean isExpanded() { return !mExpansionDisabled && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded()) || isUserExpanded()); @@ -703,7 +704,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { return super.isChildInvisible(child) || isInvisibleChildContainer; } - private void updateMaxHeights() { + protected void updateMaxHeights() { int intrinsicBefore = getIntrinsicHeight(); View expandedChild = mPrivateLayout.getExpandedChild(); if (expandedChild == null) { @@ -902,10 +903,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } } - public int getMaxExpandHeight() { - return mMaxExpandHeight; - } - @Override public boolean isContentExpandable() { NotificationContentView showingLayout = getShowingLayout(); @@ -961,7 +958,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { return mMaxExpandHeight != 0; } - private NotificationContentView getShowingLayout() { + protected NotificationContentView getShowingLayout() { return mShowingPublic ? mPublicLayout : mPrivateLayout; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index 71baf57..76858ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -42,7 +42,7 @@ public abstract class ExpandableView extends FrameLayout { private boolean mDark; private ArrayList<View> mMatchParentViews = new ArrayList<View>(); private int mClipTopOptimization; - private static Rect mClipRect = new Rect(); + private Rect mClipRect = new Rect(); private boolean mWillBeGone; private int mMinClipTopAmount = 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java index 26b9c8e..1f7e687 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java @@ -187,7 +187,7 @@ public class KeyguardAffordanceView extends ImageView implements Palette.Palette if (mPreviewView != null) { mPreviewView.setVisibility(mLaunchingAffordance ? oldPreviewView.getVisibility() : INVISIBLE); - mPreviewView.setVisibility(INVISIBLE); + mPreviewView.setVisibility(GONE); addOverlay(); } } @@ -283,7 +283,7 @@ public class KeyguardAffordanceView extends ImageView implements Palette.Palette }); animatorToRadius.start(); setImageAlpha(0, true); - if (mPreviewView != null) { + if (mPreviewView != null && mPreviewView.getVisibility() == View.VISIBLE) { mPreviewView.setVisibility(View.VISIBLE); mPreviewClipper = ViewAnimationUtils.createCircularReveal( mPreviewView, getLeft() + mCenterX, getTop() + mCenterY, mCircleRadius, @@ -386,7 +386,7 @@ public class KeyguardAffordanceView extends ImageView implements Palette.Palette invalidate(); if (nowHidden) { if (mPreviewView != null) { - mPreviewView.setVisibility(View.INVISIBLE); + mPreviewView.setVisibility(View.GONE); } } } else if (!mCircleWillBeHidden) { @@ -425,7 +425,7 @@ public class KeyguardAffordanceView extends ImageView implements Palette.Palette mPreviewClipper.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - mPreviewView.setVisibility(View.INVISIBLE); + mPreviewView.setVisibility(View.GONE); } }); mPreviewClipper.start(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 318ef5e..e4a0196 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.Color; import android.hardware.fingerprint.FingerprintManager; @@ -74,16 +75,20 @@ public class KeyguardIndicationController { private int mChargingSpeed; private int mChargingCurrent; private String mMessageToShowOnScreenOn; + private IndicationDirection mIndicationDirection; + private boolean mScreenOnHintsEnabled; public KeyguardIndicationController(Context context, KeyguardIndicationTextView textView, LockIcon lockIcon) { mContext = context; mTextView = textView; mLockIcon = lockIcon; + mIndicationDirection = IndicationDirection.NONE; Resources res = context.getResources(); mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold); mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold); + mScreenOnHintsEnabled = res.getBoolean(R.bool.config_showScreenOnLockScreenHints); mBatteryInfo = IBatteryStats.Stub.asInterface( @@ -121,6 +126,20 @@ public class KeyguardIndicationController { /** * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. */ + public void showTransientIndication(int transientIndication, IndicationDirection direction) { + showTransientIndication(mContext.getResources().getString(transientIndication), direction); + } + + /** + * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. + */ + public void showTransientIndication(String transientIndication, IndicationDirection direction) { + showTransientIndication(transientIndication, Color.WHITE, direction); + } + + /** + * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. + */ public void showTransientIndication(int transientIndication) { showTransientIndication(mContext.getResources().getString(transientIndication)); } @@ -129,15 +148,24 @@ public class KeyguardIndicationController { * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. */ public void showTransientIndication(String transientIndication) { - showTransientIndication(transientIndication, Color.WHITE); + showTransientIndication(transientIndication, Color.WHITE, IndicationDirection.NONE); } /** * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. */ public void showTransientIndication(String transientIndication, int textColor) { + showTransientIndication(transientIndication, textColor, IndicationDirection.NONE); + } + + /** + * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. + */ + public void showTransientIndication(String transientIndication, int textColor, + IndicationDirection direction) { mTransientIndication = transientIndication; mTransientTextColor = textColor; + mIndicationDirection = direction; mHandler.removeMessages(MSG_HIDE_TRANSIENT); updateIndication(); } @@ -160,8 +188,31 @@ public class KeyguardIndicationController { private void updateIndication() { if (mVisible) { + final int color = computeColor(); mTextView.switchIndication(computeIndication()); - mTextView.setTextColor(computeColor()); + mTextView.setTextColor(color); + int top = 0, left = 0, right = 0; + // pad the bottom using ic_empty_space to keep text vertically aligned if needed + int bottom = mScreenOnHintsEnabled ? R.drawable.ic_empty_space : 0; + switch (mIndicationDirection) { + case UP: + top = R.drawable.ic_keyboard_arrow_up; + break; + case DOWN: + bottom = R.drawable.ic_keyboard_arrow_down; + break; + case LEFT: + left = R.drawable.ic_keyboard_arrow_left; + break; + case RIGHT: + right = R.drawable.ic_keyboard_arrow_right; + break; + case NONE: + default: + break; + } + mTextView.setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom); + mTextView.setCompoundDrawableTintList(ColorStateList.valueOf(color)); } } @@ -176,6 +227,7 @@ public class KeyguardIndicationController { if (!TextUtils.isEmpty(mTransientIndication)) { return mTransientIndication; } + mIndicationDirection = IndicationDirection.NONE; if (mPowerPluggedIn) { String indication = computePowerIndication(); if (DEBUG_CHARGING_CURRENT) { @@ -326,4 +378,12 @@ public class KeyguardIndicationController { StatusBarKeyguardViewManager statusBarKeyguardViewManager) { mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; } + + public enum IndicationDirection { + NONE, + UP, + DOWN, + LEFT, + RIGHT + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/MediaExpandableNotificationRow.java new file mode 100644 index 0000000..c25f146 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaExpandableNotificationRow.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.statusbar; + +import android.content.Context; +import android.content.res.Resources; +import android.media.session.MediaController; +import android.os.Build; +import android.os.Handler; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +import com.android.systemui.R; +import com.android.systemui.cm.UserContentObserver; + +import cyanogenmod.providers.CMSettings; + +public class MediaExpandableNotificationRow extends ExpandableNotificationRow { + + private static final String TAG = MediaExpandableNotificationRow.class.getSimpleName(); + public static final boolean DEBUG = false; + + public static final int MAX_QUEUE_ENTRIES = 3; + + private QueueView mQueue; + + private int mMaxQueueHeight; + private int mRowHeight; + private int mShadowHeight; + private int mDisplayedRows; + + private SettingsObserver mSettingsObserver; + private boolean mQueueEnabled; + + public MediaExpandableNotificationRow(Context context, AttributeSet attrs) { + super(context, attrs); + mSettingsObserver = new SettingsObserver(new Handler()); + mQueueEnabled = isQueueEnabled(context); + + Resources res = mContext.getResources(); + + // 3 * queue_row_height + shadow height + mRowHeight = res.getDimensionPixelSize(R.dimen.queue_row_height); + mShadowHeight = res.getDimensionPixelSize(R.dimen.queue_top_shadow); + } + + public boolean inflateGuts() { + if (getGuts() == null) { + mGutsStub.inflate(); + } + if (!mQueueEnabled) { + return true; + } + return !mQueue.isUserSelectingRow(); + } + + @Override + protected void updateMaxHeights() { + // update queue height based on number of rows + int rows = mQueue != null ? mQueue.getCurrentQueueRowCount() : 0; + if (rows != mDisplayedRows) { + mMaxQueueHeight = rows * mRowHeight; + if (mMaxQueueHeight > 0) { + mMaxQueueHeight += mShadowHeight; + } + mDisplayedRows = rows; + } + + int intrinsicBefore = getIntrinsicHeight(); + View expandedChild = mPrivateLayout.getExpandedChild(); + if (expandedChild == null) { + expandedChild = mPrivateLayout.getContractedChild(); + } + mMaxExpandHeight = expandedChild.getHeight() + mMaxQueueHeight; + + View headsUpChild = mPrivateLayout.getHeadsUpChild(); + if (headsUpChild == null) { + headsUpChild = mPrivateLayout.getContractedChild(); + } + mHeadsUpHeight = headsUpChild.getHeight(); + if (intrinsicBefore != getIntrinsicHeight()) { + notifyHeightChanged(false /* needsAnimation */); + } + invalidateOutline(); + } + + @Override + public int getIntrinsicHeight() { + if (getGuts() != null && getGuts().isShown()) { + return getGuts().getActualHeight(); + } + if (!mQueueEnabled) { + return super.getIntrinsicHeight(); + } + if (mHideSensitiveForIntrinsicHeight) { + return getMinHeight(); + } + if (isUserLocked()) { + return getActualHeight(); + } + boolean inExpansionState = isExpanded(); + if (!inExpansionState) { + // not expanded, so we return the collapsed size + return getMinHeight(); + } + return getMaxContentHeight(); + } + + @Override + public int getMaxContentHeight() { + /** + * calls into NotificationContentView.getMaxHeight() + */ + return getShowingLayout().getMaxHeight() + mMaxQueueHeight; + } + + @Override + public void setHeightRange(int rowMinHeight, int rowMaxHeight) { + super.setHeightRange(rowMinHeight, rowMaxHeight); + mMaxViewHeight = Math.max(rowMaxHeight, getMaxContentHeight()); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mQueue = (QueueView) findViewById(R.id.queue_view); + mQueue.setQueueEnabled(mQueueEnabled); + mQueue.setVisibility(mQueueEnabled ? View.VISIBLE : View.GONE); + } + + public void setMediaController(MediaController mediaController) { + if (DEBUG) Log.d(TAG, "setMediaController() called with " + + "mediaController = [" + mediaController + "]"); + if (mQueue.setController(mediaController) && mQueueEnabled) { + notifyHeightChanged(true); + } + } + + @Override + public boolean dispatchGenericMotionEvent(MotionEvent ev) { + if (filterMotionEvent(ev)) { + return super.dispatchGenericMotionEvent(ev); + } + return false; + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (filterMotionEvent(ev)) { + return super.dispatchTouchEvent(ev); + } + return false; + } + + @Override + protected boolean filterMotionEvent(MotionEvent event) { + if (!mQueueEnabled) { + return super.filterMotionEvent(event); + } + if (isExpanded() && mQueue.isUserSelectingRow() + && event.getActionMasked() != MotionEvent.ACTION_DOWN + && event.getActionMasked() != MotionEvent.ACTION_UP + && event.getActionMasked() != MotionEvent.ACTION_CANCEL) { + // this is for hotspot propogation? + return false; + } + return super.filterMotionEvent(event); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mSettingsObserver.observe(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mSettingsObserver.unobserve(); + } + + private class SettingsObserver extends UserContentObserver { + + public SettingsObserver(Handler handler) { + super(handler); + } + + @Override + protected void observe() { + super.observe(); + mContext.getContentResolver().registerContentObserver( + CMSettings.System.getUriFor(CMSettings.System.NOTIFICATION_PLAY_QUEUE), + true, this); + } + + @Override + protected void unobserve() { + super.unobserve(); + mContext.getContentResolver().unregisterContentObserver(this); + } + + @Override + protected void update() { + mQueueEnabled = isQueueEnabled(mContext); + mQueue.setQueueEnabled(mQueueEnabled); + mQueue.setVisibility(mQueueEnabled ? View.VISIBLE : View.GONE); + requestLayout(); + } + } + + public static boolean isQueueEnabled(Context context) { + return CMSettings.System.getInt(context.getContentResolver(), + CMSettings.System.NOTIFICATION_PLAY_QUEUE, 1) == 1; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaNotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/MediaNotificationGuts.java new file mode 100644 index 0000000..14ce8e1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaNotificationGuts.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar; + +import android.content.Context; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.Switch; +import android.widget.TextView; +import com.android.systemui.R; +import cyanogenmod.providers.CMSettings; + +/** + * The guts of a media notification revealed when performing a long press. + */ +public class MediaNotificationGuts extends NotificationGuts { + + private static final String TAG = MediaNotificationGuts.class.getSimpleName(); + + private ViewGroup mQueueGroup; + private TextView mText; + private Switch mSwitch; + + public MediaNotificationGuts(Context context, AttributeSet attrs) { + super(context, attrs); + + setWillNotDraw(true); + } + + @Override + protected void onDraw(Canvas canvas) { + // do nothing! + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mQueueGroup = (ViewGroup) findViewById(R.id.queue_group); + mSwitch = (Switch) findViewById(R.id.queue_switch); + mSwitch.setChecked(MediaExpandableNotificationRow.isQueueEnabled(getContext())); + mText = (TextView) findViewById(R.id.switch_label); + mText.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mSwitch.toggle(); + } + }); + mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + buttonView.setChecked(isChecked); + CMSettings.System.putInt(getContext().getContentResolver(), + CMSettings.System.NOTIFICATION_PLAY_QUEUE, + isChecked ? 1 : 0); + } + }); + } + + + @Override + public void setActualHeight(int actualHeight) { + super.setActualHeight(actualHeight); + } + + @Override + public int getActualHeight() { + return getHeight(); + } + + @Override + public boolean hasOverlappingRendering() { + + // Prevents this view from creating a layer when alpha is animating. + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java index 46e0bf8..23912c4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java @@ -28,9 +28,9 @@ import com.android.systemui.R; */ public class NotificationGuts extends FrameLayout { - private Drawable mBackground; - private int mClipTopAmount; - private int mActualHeight; + protected Drawable mBackground; + protected int mClipTopAmount; + protected int mActualHeight; public NotificationGuts(Context context, AttributeSet attrs) { super(context, attrs); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/QueueView.java b/packages/SystemUI/src/com/android/systemui/statusbar/QueueView.java new file mode 100644 index 0000000..1da2e5e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/QueueView.java @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.statusbar; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.media.MediaDescription; +import android.media.MediaMetadata; +import android.media.session.MediaController; +import android.media.session.MediaSession; +import android.media.session.PlaybackState; +import android.os.Handler; +import android.provider.Settings; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.ListView; +import com.android.systemui.R; +import com.android.systemui.cm.UserContentObserver; + +import java.util.ArrayList; +import java.util.List; + +public class QueueView extends LinearLayout implements + QueueViewRow.UserRowInteractionListener, AdapterView.OnItemClickListener, + AdapterView.OnItemLongClickListener { + + private static final String TAG = QueueView.class.getSimpleName(); + private static final boolean DEBUG = MediaExpandableNotificationRow.DEBUG; + + private MediaController mController; + + private List<MediaSession.QueueItem> mQueue = new ArrayList<>(getMaxQueueRowCount()); + + private QueueItemAdapter mAdapter; + private ListView mList; + private boolean mQueueEnabled; + + long mLastUserInteraction = -1; + + private MediaController.Callback mCallback = new MediaController.Callback() { + @Override + public void onPlaybackStateChanged(@NonNull PlaybackState state) { + super.onPlaybackStateChanged(state); + + if (getParent() != null && updateQueue(mController.getQueue())) { + getParent().requestLayout(); + } + } + + @Override + public void onSessionDestroyed() { + if (DEBUG) Log.d(TAG, "onSessionDestroyed() called with " + ""); + super.onSessionDestroyed(); + setController(null); + } + }; + + public QueueView(Context context, AttributeSet attrs) { + super(context, attrs); + mAdapter = new QueueItemAdapter(context); + setClipToOutline(false); + setClipToPadding(false); + } + + public void setQueueEnabled(boolean enabled) { + mQueueEnabled = enabled; + mAdapter.notifyDataSetChanged(); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mList = (ListView) findViewById(R.id.queue_list); + mList.setItemsCanFocus(true); + mList.setDrawSelectorOnTop(true); + mList.setChoiceMode(ListView.CHOICE_MODE_SINGLE); + mList.setAdapter(mAdapter); + mList.setOnItemLongClickListener(this); + mList.setOnItemClickListener(this); + mList.setVerticalScrollBarEnabled(false); + } + + private class QueueItemAdapter extends ArrayAdapter<MediaSession.QueueItem> { + + public QueueItemAdapter(Context context) { + super(context, R.layout.queue_adapter_row, mQueue); + } + + @Override + public boolean hasStableIds() { + return true; + } + + @Override + public long getItemId(int position) { + if (position > getCount() - 1) { + return -1; + } + return getItem(position).getQueueId(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final MediaSession.QueueItem queueItem = getItem(position); + + if (convertView == null) { + convertView = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.queue_adapter_row, parent, false); + } + + QueueViewRow row = (QueueViewRow) convertView; + row.setHotSpotChangeListener(QueueView.this); + + row.setQueueItem(queueItem); + + return convertView; + } + + @Override + public int getCount() { + if (!mQueueEnabled) { + return 0; + } + return super.getCount(); + } + } + + public boolean isUserSelectingRow() { + final long delta = System.currentTimeMillis() - mLastUserInteraction; + if (DEBUG) Log.i(TAG, "isUserSelectingRow() delta=" + delta); + + if (mLastUserInteraction > 0 && delta < 500) { + if (DEBUG) Log.w(TAG, "user selecting row bc of hotspot change."); + return true; + } + + return false; + } + + public int getMaxQueueRowCount() { + return MediaExpandableNotificationRow.MAX_QUEUE_ENTRIES; + } + + public int getCurrentQueueRowCount() { + if (mAdapter == null) { + return 0; + } + return mAdapter.getCount(); + } + + @Override + public void onHotSpotChanged(float x, float y) { + mLastUserInteraction = System.currentTimeMillis(); + } + + @Override + public void onDrawableStateChanged() { + mLastUserInteraction = System.currentTimeMillis(); + } + + /** + * @param queue + * @return whether the queue size has changed + */ + public boolean updateQueue(List<MediaSession.QueueItem> queue) { + int queueSizeBefore = mAdapter.getCount(); + + mQueue.clear(); + + if (queue != null) { + // add everything *after* the currently playing item + boolean foundNowPlaying = false; + + final PlaybackState playbackState = mController.getPlaybackState(); + + long activeQueueId = -1; + if (playbackState != null) { + activeQueueId = playbackState.getActiveQueueItemId(); + } + + for (int i = 0; i < queue.size() && mQueue.size() < getMaxQueueRowCount(); i++) { + final MediaSession.QueueItem item = queue.get(i); + if (!foundNowPlaying + && activeQueueId != -1 + && activeQueueId == item.getQueueId()) { + foundNowPlaying = true; + continue; + } + if (foundNowPlaying) { + mQueue.add(item); + } + } + + // add everything + if (!foundNowPlaying) { + for(int i = 0; i < getMaxQueueRowCount() && i < queue.size(); i++) { + mQueue.add(queue.get(i)); + } + } + } + mAdapter.notifyDataSetChanged(); + + return mAdapter.getCount() != queueSizeBefore; + } + + public boolean setController(MediaController controller) { + if (mController != null) { + mController.unregisterCallback(mCallback); + } + mController = controller; + if (mController != null) { + mController.registerCallback(mCallback); + } + + return updateQueue(mController != null + ? mController.getQueue() : null); + } + + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + final MediaSession.QueueItem itemAtPosition = (MediaSession.QueueItem) + parent.getItemAtPosition(position); + if (itemAtPosition != null && mController != null) { + mController.getTransportControls().skipToQueueItem(itemAtPosition.getQueueId()); + } + mAdapter.notifyDataSetChanged(); + } + + @Override + public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { + return true; + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/QueueViewRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/QueueViewRow.java new file mode 100644 index 0000000..dab89ff --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/QueueViewRow.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.statusbar; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.media.MediaDescription; +import android.media.session.MediaSession; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; +import com.android.systemui.R; + +public class QueueViewRow extends RelativeLayout { + + private static final String TAG = QueueViewRow.class.getSimpleName(); + + private UserRowInteractionListener mHotSpotChangeListener; + + private ImageView mArt; + private TextView mTitle; + private TextView mSummary; + + public QueueViewRow(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mArt = (ImageView) findViewById(R.id.art); + mTitle = (TextView) findViewById(R.id.title); + mSummary = (TextView) findViewById(R.id.summary); + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + if (mHotSpotChangeListener != null) { + mHotSpotChangeListener.onDrawableStateChanged(); + } + } + + @Override + public void dispatchDrawableHotspotChanged(float x, float y) { + super.dispatchDrawableHotspotChanged(x, y); + if (mHotSpotChangeListener != null) { + mHotSpotChangeListener.onHotSpotChanged(x, y); + } + } + + public void setHotSpotChangeListener(UserRowInteractionListener listener) { + mHotSpotChangeListener = listener; + } + + public TextView getTitle() { + return mTitle; + } + + public TextView getSummary() { + return mSummary; + } + + public void setQueueItem(MediaSession.QueueItem queueItem) { + setTag(queueItem); + + MediaDescription metadata = queueItem.getDescription(); + + final Bitmap bitmap = metadata.getIconBitmap(); + mArt.setImageBitmap(bitmap); + mArt.setVisibility(bitmap != null ? View.VISIBLE : View.GONE); + + mTitle.setText(metadata.getTitle()); + mSummary.setText(metadata.getSubtitle()); + } + + /* package */ interface UserRowInteractionListener { + public void onHotSpotChanged(float x, float y); + public void onDrawableStateChanged(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VisualizerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/VisualizerView.java index 40e4330..538140c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/VisualizerView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/VisualizerView.java @@ -32,11 +32,9 @@ import android.util.Log; import android.view.View; import com.android.systemui.cm.UserContentObserver; -import com.android.systemui.statusbar.policy.KeyguardMonitor; import cyanogenmod.providers.CMSettings; -public class VisualizerView extends View implements Palette.PaletteAsyncListener, - KeyguardMonitor.Callback { +public class VisualizerView extends View implements Palette.PaletteAsyncListener { private static final String TAG = VisualizerView.class.getSimpleName(); private static final boolean DEBUG = false; @@ -48,6 +46,7 @@ public class VisualizerView extends View implements Palette.PaletteAsyncListener private ValueAnimator[] mValueAnimators; private float[] mFFTPoints; + private int mStatusBarState; private boolean mVisualizerEnabled = false; private boolean mVisible = false; private boolean mPlaying = false; @@ -59,7 +58,6 @@ public class VisualizerView extends View implements Palette.PaletteAsyncListener private int mColor; private Bitmap mCurrentBitmap; - private KeyguardMonitor mKeyguardMonitor; private SettingsObserver mObserver; private Visualizer.OnDataCaptureListener mVisualizerListener = @@ -114,6 +112,13 @@ public class VisualizerView extends View implements Palette.PaletteAsyncListener } }; + private final Runnable mAsyncUnlinkVisualizer = new Runnable() { + @Override + public void run() { + AsyncTask.execute(mUnlinkVisualizer); + } + }; + private final Runnable mUnlinkVisualizer = new Runnable() { @Override public void run() { @@ -164,23 +169,19 @@ public class VisualizerView extends View implements Palette.PaletteAsyncListener this(context, null, 0); } - @Override - public void onKeyguardChanged() { - updateViewVisibility(); - } - private void updateViewVisibility() { - setVisibility(mKeyguardMonitor != null && mKeyguardMonitor.isShowing() - && mVisualizerEnabled ? View.VISIBLE : View.GONE); - checkStateChanged(); + final int curVis = getVisibility(); + final int newVis = mStatusBarState != StatusBarState.SHADE + && mVisualizerEnabled ? View.VISIBLE : View.GONE; + if (curVis != newVis) { + setVisibility(newVis); + checkStateChanged(); + } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (mKeyguardMonitor != null) { - mKeyguardMonitor.addCallback(this); - } mObserver = new SettingsObserver(new Handler()); mObserver.observe(); mObserver.update(); @@ -189,23 +190,12 @@ public class VisualizerView extends View implements Palette.PaletteAsyncListener @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (mKeyguardMonitor != null) { - mKeyguardMonitor.removeCallback(this); - } mObserver.unobserve(); mObserver = null; mCurrentBitmap = null; } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - final int size = Math.min(getMeasuredWidth(), getMeasuredHeight()); - setMeasuredDimension(size, size); - } - - @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); @@ -223,7 +213,7 @@ public class VisualizerView extends View implements Palette.PaletteAsyncListener @Override public boolean hasOverlappingRendering() { - return mVisualizerEnabled && mDisplaying; + return false; } @Override @@ -235,16 +225,6 @@ public class VisualizerView extends View implements Palette.PaletteAsyncListener } } - public void setKeyguardMonitor(KeyguardMonitor kgm) { - mKeyguardMonitor = kgm; - if (isAttachedToWindow()) { - // otherwise we might never register ourselves - mKeyguardMonitor.removeCallback(this); - mKeyguardMonitor.addCallback(this); - updateViewVisibility(); - } - } - public void setVisible(boolean visible) { if (mVisible != visible) { if (DEBUG) { @@ -295,6 +275,13 @@ public class VisualizerView extends View implements Palette.PaletteAsyncListener } } + public void setStatusBarState(int statusBarState) { + if (mStatusBarState != statusBarState) { + mStatusBarState = statusBarState; + updateViewVisibility(); + } + } + public void setBitmap(Bitmap bitmap) { if (mCurrentBitmap == bitmap) { return; @@ -365,13 +352,12 @@ public class VisualizerView extends View implements Palette.PaletteAsyncListener if (mVisible) { animate() .alpha(0f) - .withEndAction(mUnlinkVisualizer) + .withEndAction(mAsyncUnlinkVisualizer) .setDuration(600); } else { - AsyncTask.execute(mUnlinkVisualizer); animate(). alpha(0f) - .withEndAction(null) + .withEndAction(mAsyncUnlinkVisualizer) .setDuration(0); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java index 60ebfdf..e1a345f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java @@ -49,7 +49,7 @@ public class KeyguardAffordanceHelper { private VelocityTracker mVelocityTracker; private boolean mSwipingInProgress; private float mInitialTouchX; - private float mInitialTouchY; + private float mInitialTouchYRaw; private float mTranslation; private float mTranslationOnDown; private int mTouchSlop; @@ -128,7 +128,7 @@ public class KeyguardAffordanceHelper { if (mMotionCancelled && action != MotionEvent.ACTION_DOWN) { return false; } - final float y = event.getY(); + final float y = event.getRawY(); final float x = event.getX(); boolean isUp = false; @@ -146,7 +146,7 @@ public class KeyguardAffordanceHelper { } startSwiping(targetView); mInitialTouchX = x; - mInitialTouchY = y; + mInitialTouchYRaw = y; mTranslationOnDown = mTranslation; initVelocityTracker(); trackMovement(event); @@ -159,7 +159,7 @@ public class KeyguardAffordanceHelper { case MotionEvent.ACTION_MOVE: trackMovement(event); float xDist = x - mInitialTouchX; - float yDist = y - mInitialTouchY; + float yDist = y - mInitialTouchYRaw; float distance = (float) Math.hypot(xDist, yDist); if (!mTouchSlopExeeded && distance > mTouchSlop) { mTouchSlopExeeded = true; @@ -211,8 +211,9 @@ public class KeyguardAffordanceHelper { } private boolean isOnIcon(View icon, float x, float y) { + int[] location = icon.getLocationOnScreen(); float iconX = icon.getX() + icon.getWidth() / 2.0f; - float iconY = icon.getY() + icon.getHeight() / 2.0f; + float iconY = location[1] + icon.getHeight() / 2.0f; double distance = Math.hypot(x - iconX, y - iconY); return distance <= mTouchTargetSize / 2; } @@ -241,6 +242,13 @@ public class KeyguardAffordanceHelper { return false; } + public boolean isOnLockIcon(MotionEvent event) { + final float x = event.getX(); + final float y = event.getRawY(); + + return isOnIcon(mCenterIcon, x, y); + } + public void startHintAnimation(boolean right, Runnable onFinishedListener) { cancelAnimation(); @@ -488,7 +496,7 @@ public class KeyguardAffordanceHelper { float aX = mVelocityTracker.getXVelocity(); float aY = mVelocityTracker.getYVelocity(); float bX = lastX - mInitialTouchX; - float bY = lastY - mInitialTouchY; + float bY = lastY - mInitialTouchYRaw; float bLen = (float) Math.hypot(bX, bY); // Project the velocity onto the distance vector: a * b / |b| float projectedVelocity = (aX * bX + aY * bY) / bLen; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index d95a46a..b244e26 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -29,14 +29,10 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; -import android.hardware.fingerprint.FingerprintManager; -import android.content.res.Resources; -import android.graphics.Bitmap; +import android.graphics.PixelFormat; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.hardware.fingerprint.FingerprintManager; import android.os.AsyncTask; import android.os.Bundle; import android.os.IBinder; @@ -50,8 +46,11 @@ import android.telecom.TelecomManager; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; +import android.view.Gravity; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -73,6 +72,8 @@ import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.FlashlightController; import com.android.systemui.statusbar.policy.PreviewInflater; +import java.util.Objects; + import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; @@ -89,6 +90,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL public static final String CAMERA_LAUNCH_SOURCE_AFFORDANCE = "lockscreen_affordance"; public static final String CAMERA_LAUNCH_SOURCE_WIGGLE = "wiggle_gesture"; public static final String CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = "power_double_tap"; + public static final String CAMERA_LAUNCH_SOURCE_SCREEN_GESTURE = "screen_gesture"; public static final String EXTRA_CAMERA_LAUNCH_SOURCE = "com.android.systemui.camera_launch_source"; @@ -126,6 +128,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private boolean mUserSetupComplete; private boolean mPrewarmBound; private Messenger mPrewarmMessenger; + private final WindowManager mWindowManager; + private boolean mBottomAreaAttached; + private final WindowManager.LayoutParams mWindowLayoutParams; + private OnInterceptTouchEventListener mInterceptTouchListener; + private BroadcastReceiver mDevicePolicyReceiver; + private Intent mLastCameraIntent; + private final ServiceConnection mPrewarmConnection = new ServiceConnection() { @Override @@ -139,6 +148,48 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } }; + @Override + public void setVisibility(int visibility) { + if (visibility == View.VISIBLE) { + if (!mBottomAreaAttached) { + addKeyguardBottomArea(false); + } + } else if (mBottomAreaAttached) { + removeKeyguardBottomArea(); + } + super.setVisibility(visibility); + } + + public void expand(boolean expand) { + addKeyguardBottomArea(expand); + } + + private void addKeyguardBottomArea(boolean fullyExpand) { + mWindowLayoutParams.height = fullyExpand ? WindowManager.LayoutParams.MATCH_PARENT : + WindowManager.LayoutParams.WRAP_CONTENT; + if (!mBottomAreaAttached) { + try { + mWindowManager.addView(this, mWindowLayoutParams); + } catch (IllegalStateException e) { + Log.e(TAG, e.getMessage()); + } + mBottomAreaAttached = true; + } else { + mWindowManager.updateViewLayout(this, mWindowLayoutParams); + } + } + + private void removeKeyguardBottomArea() { + if (mBottomAreaAttached) { + try { + mWindowManager.removeView(this); + } catch (IllegalArgumentException e) { + Log.e(TAG, e.getMessage()); + } + mBottomAreaAttached = false; + } + } + private AssistManager mAssistManager; public KeyguardBottomAreaView(Context context) { @@ -161,6 +212,20 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL ColorMatrix cm = new ColorMatrix(); cm.setSaturation(0); mGrayScaleFilter = new ColorMatrixColorFilter(cm); + mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + + mWindowLayoutParams = new WindowManager.LayoutParams(); + mWindowLayoutParams.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; + mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mWindowLayoutParams.privateFlags = + WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; + mWindowLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; + mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; + mWindowLayoutParams.format = PixelFormat.TRANSPARENT; + mWindowLayoutParams.setTitle("KeyguardBottomArea"); + mWindowLayoutParams.gravity = Gravity.BOTTOM; } private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() { @@ -417,6 +482,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private void watchForCameraPolicyChanges() { final IntentFilter filter = new IntentFilter(); filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); + mDevicePolicyReceiver = new DevicePolicyBroadcastReceiver(); getContext().registerReceiverAsUser(mDevicePolicyReceiver, UserHandle.ALL, filter, null, null); KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); @@ -504,7 +570,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL public void launchCamera(String source) { final Intent intent; - if (!mShortcutHelper.isTargetCustom(LockscreenShortcutsHelper.Shortcuts.RIGHT_SHORTCUT)) { + if (source.equals(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) || + source.equals(CAMERA_LAUNCH_SOURCE_SCREEN_GESTURE) || + !mShortcutHelper.isTargetCustom(LockscreenShortcutsHelper.Shortcuts.RIGHT_SHORTCUT)) { intent = getCameraIntent(); } else { intent = mShortcutHelper.getIntent(LockscreenShortcutsHelper.Shortcuts.RIGHT_SHORTCUT); @@ -656,10 +724,19 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL if (isTargetCustom(Shortcuts.RIGHT_SHORTCUT)) { mPreviewContainer.removeView(mCameraPreview); } else { - mCameraPreview = mPreviewInflater.inflatePreview(getCameraIntent()); + Intent cameraIntent = getCameraIntent(); + if (!Objects.equals(cameraIntent, mLastCameraIntent)) { + if (mCameraPreview != null) { + mPreviewContainer.removeView(mCameraPreview); + } + mCameraPreview = mPreviewInflater.inflatePreview(cameraIntent); + if (mCameraPreview != null) { + mPreviewContainer.addView(mCameraPreview); + } + } + mLastCameraIntent = cameraIntent; if (mCameraPreview != null) { - mPreviewContainer.addView(mCameraPreview); - mCameraPreview.setVisibility(View.INVISIBLE); + mCameraPreview.setVisibility(View.GONE); } } } @@ -681,7 +758,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } if (mLeftPreview != null) { mPreviewContainer.addView(mLeftPreview); - mLeftPreview.setVisibility(View.INVISIBLE); + mLeftPreview.setVisibility(View.GONE); } } @@ -714,7 +791,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL .setDuration(DOZE_ANIMATION_ELEMENT_DURATION); } - private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() { + public void cleanup() { + removeKeyguardBottomArea(); + } + + private final class DevicePolicyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { post(new Runnable() { @@ -835,11 +916,42 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mAccessibilityController != null) { + mAccessibilityController.addStateChangedCallback(this); + } + mShortcutHelper.registerAndFetchTargets(); + updateCustomShortcuts(); + mUnlockMethodCache.addListener(this); + watchForCameraPolicyChanges(); + } + + @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mAccessibilityController.removeStateChangedCallback(this); - mContext.unregisterReceiver(mDevicePolicyReceiver); + if (mDevicePolicyReceiver != null) { + mContext.unregisterReceiver(mDevicePolicyReceiver); + mDevicePolicyReceiver = null; + } mShortcutHelper.cleanup(); mUnlockMethodCache.removeListener(this); } + + public interface OnInterceptTouchEventListener { + boolean onInterceptTouchEvent(MotionEvent e); + } + + public void setOnInterceptTouchListener(OnInterceptTouchEventListener listener) { + mInterceptTouchListener = listener; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (mInterceptTouchListener != null) { + return mInterceptTouchListener.onInterceptTouchEvent(ev); + } + return super.onInterceptTouchEvent(ev); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 65e2096..d992b17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -56,6 +56,7 @@ public class KeyguardBouncer { private ViewGroup mRoot; private boolean mShowingSoon; private int mBouncerPromptReason; + private PhoneStatusBar mPhoneStatusBar; private KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @Override @@ -66,13 +67,14 @@ public class KeyguardBouncer { public KeyguardBouncer(Context context, ViewMediatorCallback callback, LockPatternUtils lockPatternUtils, StatusBarWindowManager windowManager, - ViewGroup container) { + ViewGroup container, PhoneStatusBar phoneStatusBar) { mContext = context; mCallback = callback; mLockPatternUtils = lockPatternUtils; mContainer = container; mWindowManager = windowManager; mCmLockPatternUtils = new CmLockPatternUtils(mContext); + mPhoneStatusBar = phoneStatusBar; KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); } @@ -86,7 +88,15 @@ public class KeyguardBouncer { if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) { return; } - + // ensure external keyguard view does not have focus + mPhoneStatusBar.unfocusKeyguardExternalView(); + mPhoneStatusBar.getScrimController().forceHideScrims(false); + // Don't hide bottom area if we are in the middle of a affordance + // launch transition, since once the animation is finished, NPV + // will take care of setting it invisible. + if (!mPhoneStatusBar.mNotificationPanel.isLaunchTransitionRunning()) { + mPhoneStatusBar.mKeyguardBottomArea.setVisibility(View.GONE); + } // Try to dismiss the Keyguard. If no security pattern is set, this will dismiss the whole // Keyguard. If we need to authenticate, show the bouncer. if (!mKeyguardView.dismiss()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index 8e58d14..cf39655 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -61,7 +61,8 @@ public class LockIcon extends KeyguardAffordanceView { @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); - if (isShown()) { + if (isShown() && + KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive()) { mTrustDrawable.start(); } else { mTrustDrawable.stop(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarInsetLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarInsetLayout.java index e767ca5..2028132 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarInsetLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarInsetLayout.java @@ -63,6 +63,7 @@ public class NavBarInsetLayout extends FrameLayout { public NavBarInsetLayout(Context context, AttributeSet attrs) { super(context, attrs); + setMotionEventSplittingEnabled(false); mTransparentSrcPaint.setColor(0); mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); 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 aaa7019..e4e02a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -18,37 +18,41 @@ package com.android.systemui.statusbar.phone; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; -import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.ContentResolver; import android.app.ActivityManager; import android.app.StatusBarManager; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; +import android.content.SharedPreferences; import android.content.pm.ResolveInfo; import android.content.res.Configuration; +import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Point; import android.graphics.Rect; import android.net.Uri; import android.os.Handler; import android.os.PowerManager; +import android.os.RemoteException; +import android.preference.PreferenceManager; import android.util.AttributeSet; import android.util.MathUtils; +import android.view.Display; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; -import android.view.ViewGroup; +import android.view.ViewConfiguration; import android.view.ViewTreeObserver; import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -62,6 +66,7 @@ import com.android.systemui.DejankUtils; import com.android.systemui.EventLogConstants; import com.android.systemui.EventLogTags; import com.android.systemui.R; +import com.android.systemui.SwipeHelper; import com.android.systemui.qs.QSContainer; import com.android.systemui.qs.QSDragPanel; import com.android.systemui.statusbar.ExpandableNotificationRow; @@ -73,22 +78,22 @@ import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; +import com.android.systemui.statusbar.policy.LiveLockScreenController; +import com.android.systemui.statusbar.policy.WeatherController; +import com.android.systemui.statusbar.policy.WeatherControllerImpl; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.StackStateAnimator; -import cyanogenmod.externalviews.KeyguardExternalView; import cyanogenmod.providers.CMSettings; +import cyanogenmod.weather.util.WeatherUtils; import java.util.List; -import java.util.Objects; - -import org.cyanogenmod.internal.util.CmLockPatternUtils; public class NotificationPanelView extends PanelView implements ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener, View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener, KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener, - HeadsUpManager.OnHeadsUpChangedListener { + HeadsUpManager.OnHeadsUpChangedListener, WeatherController.Callback { private static final boolean DEBUG = false; @@ -106,13 +111,19 @@ public class NotificationPanelView extends PanelView implements private static final Rect mDummyDirtyRect = new Rect(0, 0, 1, 1); - public static final long DOZE_ANIMATION_DURATION = 700; + private static final long SLIDE_PANEL_IN_ANIMATION_DURATION = 300; + private static final String KEY_USER_EXPANDED_NOTIFICATIONS_IN_KEYGUARD = + "user_expanded_notifications_in_keyguard"; + private static final String KEY_USER_INTERACTED_WITH_LLS = + "user_interacted_with_lls"; + private static final String KEY_USER_UNLOCKED = + "user_unlocked"; + private static final String KEY_USER_RETURNED_FROM_LLS = + "user_returned_from_lls"; + + public static final long DOZE_ANIMATION_DURATION = 700; - // Layout params for external keyguard view - private static final FrameLayout.LayoutParams EXTERNAL_KEYGUARD_VIEW_PARAMS = - new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); private KeyguardAffordanceHelper mAfforanceHelper; private StatusBarHeaderView mHeader; @@ -229,11 +240,6 @@ public class NotificationPanelView extends PanelView implements private boolean mLaunchingAffordance; private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; - private ComponentName mThirdPartyKeyguardViewComponent; - private KeyguardExternalView mKeyguardExternalView; - private CmLockPatternUtils mLockPatternUtils; - private boolean mLiveLockScreenEnabled; - private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() { @Override public void run() { @@ -254,6 +260,107 @@ public class NotificationPanelView extends PanelView implements private int mStatusBarHeaderHeight; private GestureDetector mDoubleTapGesture; + // Used to identify whether showUnlock() can dismiss the keyguard + // or not. + // TODO - add a new state to make it easier to identify keyguard vs + // LiveLockscreen + public boolean mCanDismissKeyguard; + + // Used to track which direction the user is currently + // interacting with and ensure they don't alternate back + // and forth. Reset every MOTION_UP/MOTION_CANCEL + private SwipeLockedDirection mLockedDirection; + + private SwipeHelper mSwipeHelper; + private final int mMinimumFlingVelocity; + private final int mScreenHeight; + private LiveLockScreenController mLiveLockscreenController; + private final GestureDetector mGestureDetector; + private ViewLinker mViewLinker; + private final UnlockMethodCache mUnlockMethodCache; + private boolean mDetailScrollLock; + + private boolean mKeyguardWeatherEnabled; + private TextView mKeyguardWeatherInfo; + private WeatherControllerImpl mWeatherController; + + // Keep track of common user interactions on the lock screen + private boolean mUserUnlocked; + private boolean mUserExpandedNotifications; + private boolean mUserInteractedWithLiveLockScreen; + private boolean mUserReturnedFromLiveLockScreen; + + private boolean mScreenOnHintsEnabled; + + private enum SwipeLockedDirection { + UNKNOWN, + HORIZONTAL, + VERTICAL + } + + // Handles swiping to the LiveLockscreen from keyguard + SwipeHelper.SimpleCallback mSwipeCallback = new SwipeHelper.SimpleCallback() { + @Override + public View getChildAtPosition(MotionEvent ev) { + return mViewLinker.getParent(); + } + + @Override + public View getChildContentView(View v) { + return mViewLinker.getParent(); + } + + @Override + public boolean canChildBeDismissed(View v) { + return true; + } + + @Override + public void onChildDismissed(View v) { + mCanDismissKeyguard = false; + mStatusBar.focusKeyguardExternalView(); + mLiveLockscreenController.onLiveLockScreenFocusChanged(true /* hasFocus */); + if (!mUserInteractedWithLiveLockScreen) { + mUserInteractedWithLiveLockScreen = true; + saveUserInteractedWithLls(true); + } + if (!mUserReturnedFromLiveLockScreen) { + startShowNotificationsHintAnimation(); + } + resetAlphaTranslation(); + // Enables the left edge gesture to allow user + // to return to keyguard + try { + WindowManagerGlobal.getWindowManagerService() + .setLiveLockscreenEdgeDetector(true); + } catch (RemoteException e){ + e.printStackTrace(); + } + } + + @Override + public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) { + // Let live lockscreen know of swipe progress to allow + // them to translate content in. + mLiveLockscreenController.getLiveLockScreenView() + .onLockscreenSlideOffsetChanged(swipeProgress); + + // Fade out scrim background + float alpha = ScrimController.SCRIM_BEHIND_ALPHA_KEYGUARD - (1f - swipeProgress); + alpha = Math.max(0, alpha); + mStatusBar.getScrimController().setScrimBehindColor(alpha); + return false; + } + + private void resetAlphaTranslation() { + mNotificationStackScroller.setTranslationX(0); + mNotificationStackScroller.setAlpha(1f); + + mKeyguardStatusView.setTranslationX(0); + mKeyguardStatusView.setAlpha(1f); + } + }; + public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); setWillNotDraw(!DEBUG); @@ -268,12 +375,78 @@ public class NotificationPanelView extends PanelView implements return true; } }); + + Resources res = getContext().getResources(); + final int gradientStart = res.getColor(R.color.live_lockscreen_gradient_start); + final int gradientEnd = res.getColor(R.color.live_lockscreen_gradient_end); + mGestureDetector = new GestureDetector(getContext(), + new GestureDetector.SimpleOnGestureListener() { + private float mDown; + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + // Ensure we only capture swipes in the up direction + if (velocityY > 0 || Math.abs(velocityY) <= mMinimumFlingVelocity) { + return false; + } + mCanDismissKeyguard = true; + mStatusBar.showBouncer(); + return true; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + float delta = mDown - e2.getRawY(); + delta = Math.max(0, delta); + float screenHeightHalf = (float) mScreenHeight / 2f; + int color = (Integer) ArgbEvaluator.getInstance() + .evaluate(delta / screenHeightHalf, gradientStart, gradientEnd); + mKeyguardBottomArea.setBackgroundColor(color); + return super.onScroll(e1, e2, distanceX, distanceY); + } + + @Override + public boolean onDown(MotionEvent e) { + mDown = e.getRawY(); + mKeyguardBottomArea.expand(true); + return true; + } + }); + + mSwipeHelper = new SwipeHelper(SwipeHelper.X, + SwipeHelper.SWIPE_ZONE_LEFT, mSwipeCallback, mContext); + mSwipeHelper.setSwipeProgressFadeEnd(1.0f); + mMinimumFlingVelocity = ViewConfiguration.get(getContext()) + .getScaledMinimumFlingVelocity(); + + WindowManager windowManager = (WindowManager) mContext + .getSystemService(Context.WINDOW_SERVICE); + Display display = windowManager.getDefaultDisplay(); + Point point = new Point(); + display.getSize(point); + mScreenHeight = point.y; + mUnlockMethodCache = UnlockMethodCache.getInstance(context); + + mScreenOnHintsEnabled = res.getBoolean(R.bool.config_showScreenOnLockScreenHints); + mUserUnlocked = getUserUnlocked(); + mUserExpandedNotifications = getUserExpandedNotificationsInKeyguard(); + mUserInteractedWithLiveLockScreen = getUserInteractedWithLls(); + mUserReturnedFromLiveLockScreen = getUserReturnedFromLls(); } public void setStatusBar(PhoneStatusBar bar) { mStatusBar = bar; } + public void setLiveController(LiveLockScreenController liveController) { + mLiveLockscreenController = liveController; + } + + public void setWeatherController(WeatherControllerImpl weatherController) { + mWeatherController = weatherController; + mWeatherController.addCallback(this); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -283,6 +456,7 @@ public class NotificationPanelView extends PanelView implements mKeyguardStatusView = (KeyguardStatusView) findViewById(R.id.keyguard_status_view); mQsContainer = (QSContainer) findViewById(R.id.quick_settings_container); mQsPanel = (QSDragPanel) findViewById(R.id.quick_settings_panel); + mQsPanel.setPanelView(this); mClockView = (TextView) findViewById(R.id.clock_view); mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view); mScrollView.setFocusable(false); @@ -301,7 +475,64 @@ public class NotificationPanelView extends PanelView implements android.R.interpolator.fast_out_linear_in); mDozeAnimationInterpolator = AnimationUtils.loadInterpolator(getContext(), android.R.interpolator.linear_out_slow_in); - mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area); + + mViewLinker = new ViewLinker<NotificationStackScrollLayout>(mNotificationStackScroller, + new ViewLinker.LinkInfo(mKeyguardStatusBar, ViewLinker.LINK_ALPHA), + new ViewLinker.LinkInfo(mKeyguardStatusView, ViewLinker.LINK_ALPHA + | ViewLinker.LINK_TRANSLATION)); + + mKeyguardBottomArea = (KeyguardBottomAreaView) View.inflate(getContext(), + R.layout.keyguard_bottom_area, null); + /** Keyguard bottom area lives in a separate window, and as such, + * we must redirect its touch events through the proper flow + */ + mKeyguardBottomArea.setOnInterceptTouchListener(new KeyguardBottomAreaView.OnInterceptTouchEventListener() { + @Override + public boolean onInterceptTouchEvent(MotionEvent e) { + boolean intercept = false; + if (mLiveLockscreenController.getLiveLockScreenHasFocus()) { + // Handles swipe up to fade/dismiss when showing + // live lock screen + intercept = mAfforanceHelper.onInterceptTouchEvent(e); + if (!intercept) { + intercept = mGestureDetector.onTouchEvent(e); + } + } else { + intercept = NotificationPanelView.this.onInterceptTouchEvent(e); + } + return intercept; + } + }); + mKeyguardBottomArea.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent e) { + int action = e.getAction(); + + boolean isCancelOrUp = action == MotionEvent.ACTION_UP || + action == MotionEvent.ACTION_CANCEL; + if (isCancelOrUp) { + mKeyguardBottomArea.setBackground(null); + } + + boolean intercept = false; + if (mLiveLockscreenController.getLiveLockScreenHasFocus()) { + intercept = mAfforanceHelper.onTouchEvent(e); + // If the touch did not originate on the affordance helper, + // we must collapse the panel here since we can't rely on + // the swipe callbacks from being invoked. + if (isCancelOrUp && !isAffordanceSwipeInProgress()) { + mKeyguardBottomArea.expand(false); + } + if (!intercept) { + intercept = mGestureDetector.onTouchEvent(e); + } + } else { + intercept = NotificationPanelView.this.onTouchEvent(e); + } + return intercept; + } + }); + mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim); mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext()); mLastOrientation = getResources().getConfiguration().orientation; @@ -319,19 +550,17 @@ public class NotificationPanelView extends PanelView implements } }); - mLockPatternUtils = new CmLockPatternUtils(getContext()); - if (mLockPatternUtils.isThirdPartyKeyguardEnabled() && mLiveLockScreenEnabled) { - mThirdPartyKeyguardViewComponent = mLockPatternUtils.getThirdPartyKeyguardComponent(); - } + mKeyguardWeatherInfo = (TextView) mKeyguardStatusView.findViewById(R.id.weather_info); + } + + public boolean isAffordanceSwipeInProgress() { + return mAfforanceHelper.isSwipingInProgress(); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mSettingsObserver.observe(); - mContext.registerReceiver(mExternalKeyguardViewChangedReceiver, - new IntentFilter(CmLockPatternUtils.ACTION_THIRD_PARTY_KEYGUARD_COMPONENT_CHANGED)); - mScrollView.setListener(this); } @@ -339,7 +568,7 @@ public class NotificationPanelView extends PanelView implements protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mSettingsObserver.unobserve(); - mContext.unregisterReceiver(mExternalKeyguardViewChangedReceiver); + mWeatherController.removeCallback(this); } @Override @@ -619,6 +848,10 @@ public class NotificationPanelView extends PanelView implements @Override public boolean onInterceptTouchEvent(MotionEvent event) { + // Reset locked direction + mLockedDirection = SwipeLockedDirection.UNKNOWN; + mCanDismissKeyguard = true; + if (mBlockTouches) { return false; } @@ -640,7 +873,15 @@ public class NotificationPanelView extends PanelView implements if (!isFullyCollapsed() && onQsIntercept(event)) { return true; } - return super.onInterceptTouchEvent(event); + + if (isKeyguardInteractiveAndShowing() || mStatusBar.isKeyguardShowingMedia() || + (mUnlockMethodCache.isTrustManaged() && mAfforanceHelper.isOnLockIcon(event))) { + return super.onInterceptTouchEvent(event); + } + + // We want both, we really do + return mSwipeHelper.onInterceptTouchEvent(event) + & super.onInterceptTouchEvent(event); } private boolean onQsIntercept(MotionEvent event) { @@ -795,6 +1036,12 @@ public class NotificationPanelView extends PanelView implements if (mBlockTouches) { return false; } + + int action = event.getActionMasked(); + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + mKeyguardBottomArea.setBackground(null); + } + if (mDoubleTapToSleepEnabled && mStatusBarState == StatusBarState.KEYGUARD && event.getY() < mStatusBarHeaderHeight) { @@ -808,7 +1055,8 @@ public class NotificationPanelView extends PanelView implements } if ((!mIsExpanding || mHintAnimationRunning) && !mQsExpanded - && mStatusBar.getBarState() != StatusBarState.SHADE) { + && (mStatusBar.getBarState() != StatusBarState.SHADE + || mLiveLockscreenController.getLiveLockScreenHasFocus())) { mAfforanceHelper.onTouchEvent(event); } if (mOnlyAffordanceInThisMotion) { @@ -822,10 +1070,39 @@ public class NotificationPanelView extends PanelView implements MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1); updateVerticalPanelPosition(event.getX()); } - super.onTouchEvent(event); + + if (isKeyguardInteractiveAndShowing() || mStatusBar.isKeyguardShowingMedia() || + (mUnlockMethodCache.isTrustManaged() && mAfforanceHelper.isOnLockIcon(event))) { + super.onTouchEvent(event); + return true; + } + + if ((!mIsExpanding || mHintAnimationRunning) + && !mQsExpanded + && mLockedDirection != SwipeLockedDirection.VERTICAL + && mStatusBar.getBarState() != StatusBarState.SHADE) { + mSwipeHelper.onTouchEvent(event); + if (mSwipeHelper.isDragging()) { + mLockedDirection = SwipeLockedDirection.HORIZONTAL; + } + if (mLockedDirection == SwipeLockedDirection.HORIZONTAL) { + requestDisallowInterceptTouchEvent(true); + return true; + } + } + + if (super.onTouchEvent(event)) { + mLockedDirection = SwipeLockedDirection.VERTICAL; + } return true; } + private boolean isKeyguardInteractiveAndShowing() { + return mLiveLockscreenController.getLiveLockScreenHasFocus() || + mStatusBar.getBarState() != StatusBarState.KEYGUARD || + !mLiveLockscreenController.isLiveLockScreenInteractive(); + } + private boolean handleQsTouch(MotionEvent event) { final int action = event.getActionMasked(); if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f @@ -857,7 +1134,8 @@ public class NotificationPanelView extends PanelView implements mTwoFingerQsExpandPossible = true; } if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) - && event.getY(event.getActionIndex()) < mStatusBarMinHeight) { + && event.getY(event.getActionIndex()) < mStatusBarMinHeight + && mExpandedHeight <= mQsPeekHeight) { MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_QS, 1); mQsExpandImmediate = true; requestPanelHeightUpdate(); @@ -915,7 +1193,7 @@ public class NotificationPanelView extends PanelView implements private void handleQsDown(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN - && shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) { + && shouldQuickSettingsIntercept(event.getX(), event.getRawY(), -1)) { mQsTracking = true; onQsExpansionStarted(); mInitialHeightOnTouch = mQsExpansionHeight; @@ -997,8 +1275,13 @@ public class NotificationPanelView extends PanelView implements mTrackingPointer = -1; trackMovement(event); float fraction = getQsExpansionFraction(); - if ((fraction != 0f || y >= mInitialTouchY) - && (fraction != 1f || y <= mInitialTouchY)) { + final boolean fling = (fraction != 0f || y >= mInitialTouchY) + && (fraction != 1f || y <= mInitialTouchY); + final boolean flingExpand = Math.abs(getCurrentVelocity()) + > mFlingAnimationUtils.getMinVelocityPxPerSecond(); + final boolean detailFling = mDetailScrollLock && mQsExpanded + && flingExpand; + if ((fling && !mDetailScrollLock) || detailFling) { flingQsWithCurrentVelocity(y, event.getActionMasked() == MotionEvent.ACTION_CANCEL); } else { @@ -1099,9 +1382,17 @@ public class NotificationPanelView extends PanelView implements mStatusBarState = statusBarState; mKeyguardShowing = keyguardShowing; + if (oldState != statusBarState && statusBarState == StatusBarState.KEYGUARD) { + mCanDismissKeyguard = true; + } - if (goingToFullShade || (oldState == StatusBarState.KEYGUARD - && statusBarState == StatusBarState.SHADE_LOCKED)) { + boolean keyguardToShadeLocked = oldState == StatusBarState.KEYGUARD + && statusBarState == StatusBarState.SHADE_LOCKED; + if (goingToFullShade || keyguardToShadeLocked) { + if (keyguardToShadeLocked && !mUserExpandedNotifications) { + mUserExpandedNotifications = true; + saveUserExpandedNotificationsInKeyguard(true); + } animateKeyguardStatusBarOut(); animateHeaderSlidingIn(); } else if (oldState == StatusBarState.SHADE_LOCKED @@ -1116,26 +1407,14 @@ public class NotificationPanelView extends PanelView implements mAfforanceHelper.updatePreviews(); } } + if (oldState != StatusBarState.SHADE && statusBarState == StatusBarState.SHADE && + !mUserUnlocked) { + mUserUnlocked = true; + saveUserUnlocked(true); + } if (statusBarState == StatusBarState.KEYGUARD || statusBarState == StatusBarState.SHADE_LOCKED) { updateDozingVisibilities(false /* animate */); - if (mThirdPartyKeyguardViewComponent != null) { - if (mKeyguardExternalView == null) { - mKeyguardExternalView = - getExternalKeyguardView(mThirdPartyKeyguardViewComponent); - if (mKeyguardExternalView != null) { - mKeyguardExternalView.registerKeyguardExternalViewCallback( - mExternalKeyguardViewCallbacks); - } - } - if (mKeyguardExternalView != null && !mKeyguardExternalView.isAttachedToWindow()) { - addView(mKeyguardExternalView, 0, EXTERNAL_KEYGUARD_VIEW_PARAMS); - } - } - } else { - if (mKeyguardExternalView != null && mKeyguardExternalView.isAttachedToWindow()) { - removeView(mKeyguardExternalView); - } } resetVerticalPanelPosition(); updateQsState(); @@ -1213,82 +1492,6 @@ public class NotificationPanelView extends PanelView implements } }; - private KeyguardExternalView.KeyguardExternalViewCallbacks mExternalKeyguardViewCallbacks = - new KeyguardExternalView.KeyguardExternalViewCallbacks() { - @Override - public boolean requestDismiss() { - if (hasExternalKeyguardView()) { - post(new Runnable() { - @Override - public void run() { - mStatusBar.showKeyguard(); - mStatusBar.showBouncer(); - } - }); - return true; - } - return false; - } - - @Override - public boolean requestDismissAndStartActivity(final Intent intent) { - if (hasExternalKeyguardView()) { - if (hasExternalKeyguardView()) { - post(new Runnable() { - @Override - public void run() { - mStatusBar.showKeyguard(); - mStatusBar.startActivityDismissingKeyguard(intent, false, true, true, - null); - } - }); - } - return true; - } - return false; - } - - @Override - public void collapseNotificationPanel() { - if (mStatusBar.getBarState() == StatusBarState.KEYGUARD && hasExternalKeyguardView() && - mKeyguardExternalView.isInteractive()) { - post(new Runnable() { - @Override - public void run() { - mStatusBar.focusKeyguardExternalView(); - } - }); - } - } - - @Override - public void providerDied() { - mKeyguardExternalView.unregisterKeyguardExternalViewCallback( - mExternalKeyguardViewCallbacks); - mKeyguardExternalView = null; - } - }; - - private BroadcastReceiver mExternalKeyguardViewChangedReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String pkgName = getSendingPackage(intent); - if (pkgName != null) { - PackageManager pm = context.getPackageManager(); - if (pm.checkPermission(android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, - pkgName) != PackageManager.PERMISSION_GRANTED) { - // we should not be here if the sending app does not have the proper permission, - // so do nothing and return. - return; - } - } else { - // null package name? something is not right so just return and skip doing anything - return; - } - updateExternalKeyguardView(); - } - }; - private void animateHeaderSlidingIn() { // If the QS is already expanded we don't need to slide in the header as it's already // visible. @@ -1487,8 +1690,7 @@ public class NotificationPanelView extends PanelView implements if (mKeyguardShowing) { updateHeaderKeyguard(); } - if (mStatusBarState == StatusBarState.SHADE_LOCKED - || mStatusBarState == StatusBarState.KEYGUARD) { + if (mStatusBarState == StatusBarState.KEYGUARD) { updateKeyguardBottomAreaAlpha(); } if (mStatusBarState == StatusBarState.SHADE && mQsExpanded @@ -1669,7 +1871,8 @@ public class NotificationPanelView extends PanelView implements && y >= header.getTop() && y <= header.getBottom(); if (mQsExpanded) { - return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0) && isInQsArea(x, y); + return onHeader || mDetailScrollLock + || (mScrollView.isScrolledToBottom() && yDiff < 0) && isInQsArea(x, y); } else { return onHeader; } @@ -1759,7 +1962,7 @@ public class NotificationPanelView extends PanelView implements */ private int getTempQsMaxExpansion() { int qsTempMaxExpansion = mQsMaxExpansionHeight; - if (mScrollYOverride != -1) { + if (mScrollYOverride != -1 && !mDetailScrollLock) { qsTempMaxExpansion -= mScrollYOverride; } return qsTempMaxExpansion; @@ -1926,12 +2129,14 @@ public class NotificationPanelView extends PanelView implements } private void updateHeaderKeyguardAlpha() { + if (mSwipeHelper.isDragging()) { + return; + } float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2); mKeyguardStatusBar.setAlpha(Math.min(getKeyguardContentsAlpha(), alphaQsExpansion) * mKeyguardStatusBarAnimateAlpha); mKeyguardStatusBar.setVisibility(mKeyguardStatusBar.getAlpha() != 0f && !mDozing ? VISIBLE : INVISIBLE); - mStatusBar.getVisualizer().setAlpha(mKeyguardStatusBar.getAlpha()); } private void updateHeaderKeyguard() { @@ -1941,6 +2146,9 @@ public class NotificationPanelView extends PanelView implements private void updateKeyguardBottomAreaAlpha() { float alpha = Math.min(getKeyguardContentsAlpha(), 1 - getQsExpansionFraction()); + if (mLiveLockscreenController.getLiveLockScreenHasFocus()) { + alpha = 1f; + } mKeyguardBottomArea.setAlpha(alpha); mKeyguardBottomArea.setImportantForAccessibility(alpha == 0f ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS @@ -2164,10 +2372,12 @@ public class NotificationPanelView extends PanelView implements mLaunchAnimationEndRunnable.run(); mLaunchAnimationEndRunnable = null; } + mKeyguardBottomArea.setVisibility(View.GONE); } @Override protected void startUnlockHintAnimation() { + mKeyguardBottomArea.expand(true); super.startUnlockHintAnimation(); startHighlightIconAnimation(getCenterIcon()); } @@ -2202,11 +2412,13 @@ public class NotificationPanelView extends PanelView implements requestDisallowInterceptTouchEvent(true); mOnlyAffordanceInThisMotion = true; mQsTracking = false; + mKeyguardBottomArea.expand(true); } @Override public void onSwipingAborted() { mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */); + mKeyguardBottomArea.expand(false); } @Override @@ -2215,6 +2427,8 @@ public class NotificationPanelView extends PanelView implements return; } mHintAnimationRunning = true; + mKeyguardBottomArea.expand(true); + mKeyguardBottomArea.getIndicationView().animate().cancel(); mAfforanceHelper.startHintAnimation(rightIcon, new Runnable() { @Override public void run() { @@ -2395,11 +2609,6 @@ public class NotificationPanelView extends PanelView implements // Hide "No notifications" in QS. mNotificationStackScroller.updateEmptyShadeView(mShadeEmpty && !mQsExpanded); - if (mStatusBarState == StatusBarState.KEYGUARD - && (!mQsExpanded || mQsExpandImmediate || mIsExpanding - && mQsExpandedWhenExpandingStarted)) { - positionClockAndNotifications(); - } } public void setQsScrimEnabled(boolean qsScrimEnabled) { @@ -2423,6 +2632,11 @@ public class NotificationPanelView extends PanelView implements public void onScreenTurningOn() { mKeyguardStatusView.refreshTime(); + if (shouldShowScreenOnHints()) { + startScreenOnHintAnimation(mLiveLockscreenController.isLiveLockScreenInteractive() && + !mUserInteractedWithLiveLockScreen, + !mUserUnlocked, !mUserExpandedNotifications); + } } @Override @@ -2584,6 +2798,10 @@ public class NotificationPanelView extends PanelView implements return mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway; } + public KeyguardBottomAreaView getKeyguardBottomArea() { + return mKeyguardBottomArea; + } + class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { super(handler); @@ -2596,7 +2814,7 @@ public class NotificationPanelView extends PanelView implements resolver.registerContentObserver(CMSettings.System.getUriFor( CMSettings.System.DOUBLE_TAP_SLEEP_GESTURE), false, this); resolver.registerContentObserver(CMSettings.Secure.getUriFor( - CMSettings.Secure.LIVE_LOCK_SCREEN_ENABLED), false, this); + CMSettings.Secure.LOCK_SCREEN_WEATHER_ENABLED), false, this); update(); } @@ -2622,11 +2840,12 @@ public class NotificationPanelView extends PanelView implements mDoubleTapToSleepEnabled = CMSettings.System.getInt( resolver, CMSettings.System.DOUBLE_TAP_SLEEP_GESTURE, 1) == 1; - boolean liveLockScreenEnabled = CMSettings.Secure.getInt( - resolver, CMSettings.Secure.LIVE_LOCK_SCREEN_ENABLED, 0) == 1; - if (liveLockScreenEnabled != mLiveLockScreenEnabled) { - mLiveLockScreenEnabled = liveLockScreenEnabled; - updateExternalKeyguardView(); + boolean wasKeyguardWeatherEnabled = mKeyguardWeatherEnabled; + mKeyguardWeatherEnabled = CMSettings.Secure.getInt( + resolver, CMSettings.Secure.LOCK_SCREEN_WEATHER_ENABLED, 0) == 1; + if (mWeatherController != null + && wasKeyguardWeatherEnabled != mKeyguardWeatherEnabled) { + onWeatherChanged(mWeatherController.getWeatherInfo()); } } } @@ -2641,6 +2860,8 @@ public class NotificationPanelView extends PanelView implements mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP; } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) { mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE; + } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_SCREEN_GESTURE) { + mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_SCREEN_GESTURE; } else { // Default. @@ -2650,7 +2871,8 @@ public class NotificationPanelView extends PanelView implements // If we are launching it when we are occluded already we don't want it to animate, // nor setting these flags, since the occluded state doesn't change anymore, hence it's // never reset. - if (!isFullyCollapsed()) { + if (!isFullyCollapsed() && mLastCameraLaunchSource == + KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE) { mLaunchingAffordance = true; setLaunchingAffordance(true); } else { @@ -2699,48 +2921,158 @@ public class NotificationPanelView extends PanelView implements return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); } - public boolean hasExternalKeyguardView() { - return mKeyguardExternalView != null && mKeyguardExternalView.isAttachedToWindow(); + public void setDetailRequestedScrollLock(boolean detailScrollFlag) { + if (mDetailScrollLock != detailScrollFlag) { + if (mStatusBarState != StatusBarState.SHADE) { + mDetailScrollLock = false; + } else { + mDetailScrollLock = detailScrollFlag; + } + if (!detailScrollFlag && getQsExpansionFraction() > 0.3f) { + flingSettings(getCurrentVelocity(), true, new Runnable() { + @Override + public void run() { + mStackScrollerOverscrolling = false; + mQsExpansionFromOverscroll = false; + updateQsState(); + updateHeader(); + updateMaxHeadsUpTranslation(); + updatePanelExpanded(); + requestLayout(); + } + }, false); + } else { + requestLayout(); + } + } } - public boolean isExternalKeyguardViewInteractive() { - return mKeyguardExternalView != null && mKeyguardExternalView.isInteractive(); + @Override + public void onWeatherChanged(WeatherController.WeatherInfo info) { + if (!mKeyguardWeatherEnabled || Double.isNaN(info.temp) || info.condition == null) { + mKeyguardWeatherInfo.setVisibility(GONE); + } else { + mKeyguardWeatherInfo.setText(mContext.getString( + R.string.keyguard_status_view_weather_format, + WeatherUtils.formatTemperature(info.temp, info.tempUnit), + info.condition)); + mKeyguardWeatherInfo.setVisibility(VISIBLE); + } } - public KeyguardExternalView getExternalKeyguardView() { - return mKeyguardExternalView; - } + private class SlideInAnimationListener implements ValueAnimator.AnimatorUpdateListener, + ValueAnimator.AnimatorListener { + @Override + public void onAnimationStart(Animator animator) {} - private KeyguardExternalView getExternalKeyguardView(ComponentName componentName) { - try { - return new KeyguardExternalView(getContext(), null, componentName); - } catch (Exception e) { - // just return null below and move on + @Override + public void onAnimationEnd(Animator animator) { + animationFinished(animator); } - return null; - } - private void updateExternalKeyguardView() { - ComponentName cn = mLiveLockScreenEnabled ? - mLockPatternUtils.getThirdPartyKeyguardComponent() : null; - // If mThirdPartyKeyguardViewComponent differs from cn, go ahead and update - if (!Objects.equals(mThirdPartyKeyguardViewComponent, cn)) { - mThirdPartyKeyguardViewComponent = cn; - if (mKeyguardExternalView != null) { - if (indexOfChild(mKeyguardExternalView) >= 0) { - removeView(mKeyguardExternalView); - } - mKeyguardExternalView.unregisterKeyguardExternalViewCallback( - mExternalKeyguardViewCallbacks); - if (mThirdPartyKeyguardViewComponent != null) { - mKeyguardExternalView = - getExternalKeyguardView(mThirdPartyKeyguardViewComponent); - mKeyguardExternalView.registerKeyguardExternalViewCallback( - mExternalKeyguardViewCallbacks); - } else { - mKeyguardExternalView = null; - } + @Override + public void onAnimationCancel(Animator animator) { + animationFinished(animator); + } + + @Override + public void onAnimationRepeat(Animator animator) {} + + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + View statusBarView = mStatusBar.getStatusBarWindow(); + if (valueAnimator.getAnimatedFraction() > 0 && + statusBarView.getVisibility() != View.VISIBLE) { + statusBarView.setVisibility(View.VISIBLE); } + float translationX = (Float) valueAnimator.getAnimatedValue(); + float alpha = valueAnimator.getAnimatedFraction(); + + mViewLinker.getParent().setTranslationX(translationX); + mViewLinker.getParent().setAlpha(alpha); + + float alpha1 = ScrimController.SCRIM_BEHIND_ALPHA_KEYGUARD * alpha; + alpha1 = Math.max(0, alpha1); + mStatusBar.getScrimController().setScrimBehindColor(alpha1); + mLiveLockscreenController.getLiveLockScreenView() + .onLockscreenSlideOffsetChanged(alpha); } + + private void animationFinished(Animator animator) { + mLiveLockscreenController.onLiveLockScreenFocusChanged(false); + } + } + + private SlideInAnimationListener mSlideInAnimationListener = new SlideInAnimationListener(); + + public void slideLockScreenIn() { + mNotificationStackScroller.setVisibility(View.VISIBLE); + mNotificationStackScroller.setAlpha(0f); + mNotificationStackScroller.setTranslationX(-mNotificationStackScroller.getWidth()); + mKeyguardStatusView.setVisibility(View.VISIBLE); + mKeyguardStatusView.setAlpha(0f); + mKeyguardStatusView.setTranslationX(mNotificationStackScroller.getTranslationX()); + mKeyguardStatusBar.setAlpha(0f); + + mStatusBar.getScrimController().setScrimBehindColor(0f); + ValueAnimator animator = ValueAnimator.ofFloat( + mNotificationStackScroller.getTranslationX(), + 0f); + animator.setDuration(SLIDE_PANEL_IN_ANIMATION_DURATION); + animator.addUpdateListener(mSlideInAnimationListener); + animator.addListener(mSlideInAnimationListener); + animator.start(); + + if (!mUserReturnedFromLiveLockScreen) { + mUserReturnedFromLiveLockScreen = true; + saveUserReturnedFromLls(true); + } + } + + private void saveBooleanSharedPreference(String key, boolean value) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); + prefs.edit().putBoolean(key, value).apply(); + } + + private boolean getSharedPreferenceBoolean(String key, boolean defValue) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); + return prefs.getBoolean(key, defValue); + } + + private void saveUserExpandedNotificationsInKeyguard(boolean expanded) { + saveBooleanSharedPreference(KEY_USER_EXPANDED_NOTIFICATIONS_IN_KEYGUARD, expanded); + } + + private boolean getUserExpandedNotificationsInKeyguard() { + return getSharedPreferenceBoolean(KEY_USER_EXPANDED_NOTIFICATIONS_IN_KEYGUARD, false); + } + + private void saveUserInteractedWithLls(boolean interacted) { + saveBooleanSharedPreference(KEY_USER_INTERACTED_WITH_LLS, interacted); + } + + private boolean getUserInteractedWithLls() { + return getSharedPreferenceBoolean(KEY_USER_INTERACTED_WITH_LLS, false); + } + + private void saveUserUnlocked(boolean unlocked) { + saveBooleanSharedPreference(KEY_USER_UNLOCKED, unlocked); + } + + private boolean getUserUnlocked() { + return getSharedPreferenceBoolean(KEY_USER_UNLOCKED, false); + } + + private void saveUserReturnedFromLls(boolean revealed) { + saveBooleanSharedPreference(KEY_USER_RETURNED_FROM_LLS, revealed); + } + + private boolean getUserReturnedFromLls() { + return getSharedPreferenceBoolean(KEY_USER_RETURNED_FROM_LLS, false); + } + + public boolean shouldShowScreenOnHints() { + return mScreenOnHintsEnabled && mStatusBar.isDeviceProvisioned() && + mStatusBarState == StatusBarState.KEYGUARD; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java index 82b1e1d..e1a400d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java @@ -146,16 +146,6 @@ public abstract class PanelBar extends FrameLayout { return result; } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - for (PanelView pv : mPanels) { - pv.setBar(null); - } - mPanels.clear(); - mPanelHolder.setBar(null); - } - // called from PanelView when self-expanding, too public void startOpeningPanel(PanelView panel) { if (DEBUG) LOG("startOpeningPanel: " + panel); 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 e5711b8..3a2c84c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -49,6 +49,9 @@ public abstract class PanelView extends FrameLayout { public static final boolean DEBUG = PanelBar.DEBUG; public static final String TAG = PanelView.class.getSimpleName(); + private static final long ANIMATION_FADE_DURATION = 1000L; + private static final long HINT_DELAY_DURATION = 1500L; + private final void logf(String fmt, Object... args) { Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args)); } @@ -94,7 +97,7 @@ public abstract class PanelView extends FrameLayout { public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { // update expand height - if (mHeightAnimator != null && mExpanding && mUpdateExpandOnLayout) { + if (mHeightAnimator != null && mExpanding && mUpdateExpandOnLayout && !mJustPeeked) { final int maxPanelHeight = getMaxPanelHeight(); final PropertyValuesHolder[] values = mHeightAnimator.getValues(); values[0].setFloatValues(maxPanelHeight); @@ -121,6 +124,9 @@ public abstract class PanelView extends FrameLayout { private boolean mPeekPending; private boolean mCollapseAfterPeek; + private boolean mShowExpandHint; + private boolean mShowUnlockHint; + private boolean mScreenOnHintAnimationRunning; /** * Speed-up factor to be used when {@link #mFlingCollapseRunnable} runs the next time. @@ -674,7 +680,7 @@ public abstract class PanelView extends FrameLayout { } mUpdateExpandOnLayout = isFullyCollapsed(); mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight()); - if (expandBecauseOfFalsing) { + if (expandBecauseOfFalsing && vel == 0) { animator.setDuration(350); } } else { @@ -928,6 +934,7 @@ public abstract class PanelView extends FrameLayout { private void abortAnimations() { cancelPeek(); cancelHeightAnimator(); + mKeyguardBottomArea.getIndicationView().animate().cancel(); removeCallbacks(mPostCollapseRunnable); removeCallbacks(mFlingCollapseRunnable); } @@ -946,6 +953,8 @@ public abstract class PanelView extends FrameLayout { } cancelPeek(); notifyExpandingStarted(); + mKeyguardBottomArea.getIndicationView().animate().cancel(); + mStatusBar.onUnlockHintStarted(); startUnlockHintAnimationPhase1(new Runnable() { @Override public void run() { @@ -954,8 +963,8 @@ public abstract class PanelView extends FrameLayout { mHintAnimationRunning = false; } }); - mStatusBar.onUnlockHintStarted(); mHintAnimationRunning = true; + mShowExpandHint = false; } /** @@ -989,6 +998,7 @@ public abstract class PanelView extends FrameLayout { mKeyguardBottomArea.getIndicationView().animate() .translationY(-mHintDistance) .setDuration(250) + .setStartDelay(0) .setInterpolator(mFastOutSlowInInterpolator) .withEndAction(new Runnable() { @Override @@ -1011,17 +1021,213 @@ public abstract class PanelView extends FrameLayout { animator.setDuration(450); animator.setInterpolator(mBounceInterpolator); animator.addListener(new AnimatorListenerAdapter() { + private boolean mCancelled; + + @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + @Override public void onAnimationEnd(Animator animation) { mHeightAnimator = null; - onAnimationFinished.run(); - notifyBarPanelExpansionChanged(); + if (mCancelled) { + onAnimationFinished.run(); + } else { + if (mShowExpandHint) { + startUnlockHintFadeOutAnimationPhase(onAnimationFinished); + } else { + onAnimationFinished.run(); + notifyBarPanelExpansionChanged(); + } + } } }); animator.start(); mHeightAnimator = animator; } + /** + * Fade in unlock hint + */ + private void startUnlockHintFadeInAnimationPhase(final Runnable onAnimationFinished) { + mStatusBar.onUnlockHintStarted(); + mKeyguardBottomArea.getIndicationView().animate() + .alpha(1) + .setDuration(ANIMATION_FADE_DURATION) + .setStartDelay(0) + .setInterpolator(null) + .withEndAction(new Runnable() { + @Override + public void run() { + if (mShowExpandHint) { + startUnlockHintFadeOutAnimationPhase(onAnimationFinished); + } else { + onAnimationFinished.run(); + notifyBarPanelExpansionChanged(); + } + } + }) + .start(); + } + + /** + * Fade out unlock hint + */ + private void startUnlockHintFadeOutAnimationPhase(final Runnable onAnimationFinished) { + mKeyguardBottomArea.getIndicationView().animate() + .alpha(0) + .setDuration(ANIMATION_FADE_DURATION) + .setStartDelay(HINT_DELAY_DURATION) + .setInterpolator(null) + .withEndAction(new Runnable() { + @Override + public void run() { + startExpandHintAnimation(onAnimationFinished); + } + }) + .start(); + } + + /** + * Fade in Lls hint + */ + private void startLlsHintFadeInAnimationPhase(final Runnable onAnimationFinished) { + mKeyguardBottomArea.getIndicationView().setAlpha(0); + mKeyguardBottomArea.getIndicationView().animate() + .alpha(1) + .setDuration(ANIMATION_FADE_DURATION) + .setStartDelay(0) + .setInterpolator(null) + .withEndAction(new Runnable() { + @Override + public void run() { + if (mShowUnlockHint || mShowExpandHint) { + startLlsHintFadeOutAnimationPhase(onAnimationFinished); + } else { + onAnimationFinished.run();; + } + } + }) + .start(); + } + + /** + * Fade out Lls hint + */ + private void startLlsHintFadeOutAnimationPhase(final Runnable onAnimationFinished) { + mKeyguardBottomArea.getIndicationView().animate() + .alpha(0) + .setDuration(ANIMATION_FADE_DURATION) + .setStartDelay(HINT_DELAY_DURATION) + .setInterpolator(null) + .withEndAction(new Runnable() { + @Override + public void run() { + if (mShowUnlockHint) { + startUnlockHintFadeInAnimationPhase(onAnimationFinished); + } else if (mShowExpandHint) { + startExpandHintAnimation(onAnimationFinished); + } else { + onAnimationFinished.run(); + } + } + }) + .start(); + } + + /** + * Fade in expand hint + */ + private void startExpandHintAnimation(final Runnable onAnimationFinished) { + mStatusBar.onExpandHintStarted(); + mKeyguardBottomArea.getIndicationView().animate() + .alpha(1) + .setDuration(ANIMATION_FADE_DURATION) + .setStartDelay(0) + .setInterpolator(null) + .withEndAction(new Runnable() { + @Override + public void run() { + onAnimationFinished.run(); + } + }) + .start(); + } + + /** + * Show notifications hint (swipe right hint) + */ + protected void startShowNotificationsHintAnimation() { + cancelPeek(); + mStatusBar.onNotificationsHintStarted(); + mHintAnimationRunning = true; + mKeyguardBottomArea.getIndicationView().setAlpha(0); + mKeyguardBottomArea.getIndicationView().animate() + .alpha(1) + .setDuration(ANIMATION_FADE_DURATION) + .setInterpolator(null) + .withEndAction(new Runnable() { + @Override + public void run() { + mStatusBar.onHintFinished(); + mHintAnimationRunning = false; + } + }) + .start(); + } + + protected void startScreenOnHintAnimation(boolean showSwipeLeftHint, boolean showUnlockHint, + boolean showExpandHint) { + // We don't need to hint the user if an animation is already running or the user is changing + // the expansion. + if (mHintAnimationRunning || mScreenOnHintAnimationRunning) return; + + final View indicationView = mKeyguardBottomArea.getIndicationView(); + indicationView.animate().cancel(); + indicationView.animate().setListener( + new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { } + + @Override + public void onAnimationEnd(Animator animation) { } + + @Override + public void onAnimationCancel(Animator animation) { + mScreenOnHintAnimationRunning = false; + indicationView.setAlpha(1f); + } + + @Override + public void onAnimationRepeat(Animator animation) { } + }); + + Runnable r = new Runnable() { + @Override + public void run() { + mStatusBar.onHintFinished(); + mScreenOnHintAnimationRunning = false; + } + }; + if (showSwipeLeftHint) { + mStatusBar.onLlsHintStarted(); + startLlsHintFadeInAnimationPhase(r); + } else if (showUnlockHint) { + mStatusBar.onUnlockHintStarted(); + startUnlockHintFadeInAnimationPhase(r); + } else if (showExpandHint) { + mStatusBar.onExpandHintStarted(); + startExpandHintAnimation(r); + } else { + return; + } + indicationView.setAlpha(0); + mShowUnlockHint = showUnlockHint; + mShowExpandHint = showExpandHint; + mScreenOnHintAnimationRunning = true; + } + private ValueAnimator createHeightAnimator(float targetHeight) { ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 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 adff973..7df8346 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -17,14 +17,11 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.settings.BrightnessController.BRIGHTNESS_ADJ_RESOLUTION; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManagerNative; -import android.app.ActivityOptions; import android.app.IActivityManager; import android.app.Notification; import android.app.PendingIntent; @@ -103,13 +100,13 @@ import android.view.ViewGroup.LayoutParams; import android.view.ViewStub; import android.view.ViewTreeObserver; import android.view.WindowManager; -import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import android.view.animation.PathInterpolator; +import android.widget.AdapterView; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; @@ -136,10 +133,7 @@ import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.qs.QSDragPanel; -import com.android.systemui.qs.QSPanel; -import com.android.systemui.qs.QSTile; import com.android.systemui.recents.ScreenPinningRequest; -import com.android.systemui.settings.BrightnessController; import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.BackDropView; import com.android.systemui.statusbar.BaseStatusBar; @@ -150,6 +144,7 @@ import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.KeyguardIndicationController; +import com.android.systemui.statusbar.MediaExpandableNotificationRow; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.NotificationOverflowContainer; @@ -173,6 +168,7 @@ import com.android.systemui.statusbar.policy.HotspotControllerImpl; import com.android.systemui.statusbar.policy.KeyButtonView; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; +import com.android.systemui.statusbar.policy.LiveLockScreenController; import com.android.systemui.statusbar.policy.LocationControllerImpl; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NetworkControllerImpl; @@ -321,6 +317,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, WeatherControllerImpl mWeatherController; SuControllerImpl mSuController; FingerprintUnlockController mFingerprintUnlockController; + LiveLockScreenController mLiveLockScreenController; int mNaturalBarHeight = -1; @@ -329,7 +326,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, StatusBarWindowView mStatusBarWindow; FrameLayout mStatusBarWindowContent; - PhoneStatusBarView mStatusBarView; + private PhoneStatusBarView mStatusBarView; private int mStatusBarWindowState = WINDOW_STATE_SHOWING; private StatusBarWindowManager mStatusBarWindowManager; private UnlockMethodCache mUnlockMethodCache; @@ -497,6 +494,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } + public void setStatusBarViewVisibility(boolean visible) { + mStatusBarView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); + } + class DevForceNavbarObserver extends UserContentObserver { DevForceNavbarObserver(Handler handler) { super(handler); @@ -506,14 +507,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, protected void observe() { super.observe(); ContentResolver resolver = mContext.getContentResolver(); - resolver.registerContentObserver(CMSettings.Secure.getUriFor( - CMSettings.Secure.DEV_FORCE_SHOW_NAVBAR), false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(CMSettings.Global.getUriFor( + CMSettings.Global.DEV_FORCE_SHOW_NAVBAR), false, this, UserHandle.USER_ALL); } @Override public void update() { - boolean visible = CMSettings.Secure.getIntForUser(mContext.getContentResolver(), - CMSettings.Secure.DEV_FORCE_SHOW_NAVBAR, 0, UserHandle.USER_CURRENT) == 1; + boolean visible = CMSettings.Global.getIntForUser(mContext.getContentResolver(), + CMSettings.Global.DEV_FORCE_SHOW_NAVBAR, 0, UserHandle.USER_CURRENT) == 1; if (visible) { forceAddNavigationBar(); @@ -539,7 +540,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mNavigationBarView.setDisabledFlags(mDisabled1); mNavigationBarView.setBar(this); - addNavigationBar(); + addNavigationBar(true); // dynamically adding nav bar, reset System UI visibility! } // ensure quick settings is disabled until the current user makes it through the setup wizard @@ -792,6 +793,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private RankingMap mLatestRankingMap; private boolean mNoAnimationOnNextBarModeChange; + public ScrimController getScrimController() { + return mScrimController; + } + @Override public void start() { mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) @@ -809,7 +814,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mStatusBarWindow = new StatusBarWindowView(mContext, null); mStatusBarWindow.setService(this); - + super.start(); // calls createAndAddWindows() mMediaSessionManager @@ -817,7 +822,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // TODO: use MediaSessionManager.SessionListener to hook us up to future updates // in session state - addNavigationBar(); + addNavigationBar(false); // Developer options - Force Navigation bar try { @@ -882,9 +887,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, Resources res = context.getResources(); - mScreenWidth = (float) context.getResources().getDisplayMetrics().widthPixels; - mMinBrightness = context.getResources().getInteger( - com.android.internal.R.integer.config_screenBrightnessDim); + mScreenWidth = (float) res.getDisplayMetrics().widthPixels; + mMinBrightness = res.getInteger(com.android.internal.R.integer.config_screenBrightnessDim); updateDisplaySize(); // populates mDisplayMetrics updateResources(null); @@ -920,6 +924,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mNotificationPanel.setBackground(new FastColorDrawable(context.getColor( R.color.notification_panel_solid_background))); } + mLiveLockScreenController = new LiveLockScreenController(mContext, this, + mNotificationPanel); + mNotificationPanel.setLiveController(mLiveLockScreenController); + if (mStatusBarWindowManager != null) { + mStatusBarWindowManager.setLiveLockscreenController(mLiveLockScreenController); + } if (mHeadsUpManager == null) { mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow); @@ -1032,12 +1042,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mHeader.setActivityStarter(this); mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindowContent.findViewById(R.id.keyguard_header); mKeyguardStatusView = mStatusBarWindowContent.findViewById(R.id.keyguard_status_view); - mKeyguardBottomArea = - (KeyguardBottomAreaView) mStatusBarWindowContent.findViewById(R.id.keyguard_bottom_area); + mKeyguardBottomArea = mNotificationPanel.getKeyguardBottomArea(); + mKeyguardBottomArea.setActivityStarter(this); mKeyguardBottomArea.setAssistManager(mAssistManager); mKeyguardIndicationController = new KeyguardIndicationController(mContext, - (KeyguardIndicationTextView) mStatusBarWindowContent.findViewById( + (KeyguardIndicationTextView) mKeyguardBottomArea.findViewById( R.id.keyguard_indication_text), mKeyguardBottomArea.getLockIcon()); mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController); @@ -1181,7 +1191,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mNetworkController, mZenModeController, mHotspotController, mCastController, mFlashlightController, mUserSwitcherController, mKeyguardMonitor, - mSecurityController); + mSecurityController, mBatteryController); } mQSPanel.setHost(mQSTileHost); if (mBrightnessMirrorController == null) { @@ -1315,10 +1325,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } - mVisualizerView.setKeyguardMonitor(mKeyguardMonitor); mHeader.setNextAlarmController(mNextAlarmController); mHeader.setWeatherController(mWeatherController); + mNotificationPanel.setWeatherController(mWeatherController); + PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mBroadcastReceiver.onReceive(mContext, new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF)); @@ -1490,7 +1501,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public void run() { boolean isUpdate = mQSPanel.getHost().getCustomTileData() - .get(sbc.getKey()) != null; + .get(sbc.persistableKey()) != null; if (isUpdate) { mQSPanel.getHost().updateCustomTile(sbc); } else { @@ -1506,7 +1517,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mHandler.post(new Runnable() { @Override public void run() { - mQSPanel.getHost().removeCustomTileSysUi(sbc.getKey()); + mQSPanel.getHost().removeCustomTileSysUi(sbc.persistableKey()); } }); } @@ -1568,16 +1579,26 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } - private void prepareNavigationBarView() { + private void prepareNavigationBarView(boolean forceReset) { mNavigationBarView.reorient(); mNavigationBarView.setListeners(mRecentsClickListener, mRecentsPreloadOnTouchListener, mLongPressBackRecentsListener, mHomeActionListener, mLongPressHomeListener); mAssistManager.onConfigurationChanged(); + if (forceReset) { + // Nav Bar was added dynamically - we need to reset the mSystemUiVisibility and call + // setSystemUiVisibility so that mNavigationBarMode is set to the correct value + int newVal = mSystemUiVisibility; + mSystemUiVisibility = 0; + setSystemUiVisibility(newVal, SYSTEM_UI_VISIBILITY_MASK); + checkBarMode(mNavigationBarMode, + mNavigationBarWindowState, mNavigationBarView.getBarTransitions(), + mNoAnimationOnNextBarModeChange); + } } // For small-screen devices (read: phones) that lack hardware navigation buttons - private void addNavigationBar() { + private void addNavigationBar(boolean forceReset) { if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView); if (mNavigationBarView == null) return; @@ -1588,7 +1609,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return; } - prepareNavigationBarView(); + prepareNavigationBarView(forceReset); mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams()); } @@ -1604,7 +1625,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private void repositionNavigationBar() { if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return; - prepareNavigationBarView(); + prepareNavigationBarView(false); mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams()); } @@ -1966,7 +1987,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - protected void updateRowStates() { + public void updateRowStates() { super.updateRowStates(); mNotificationPanel.notifyVisibleChildrenChanged(); } @@ -2080,6 +2101,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: " + mMediaMetadata); } + if (mediaNotification != null + && mediaNotification.row != null + && mediaNotification.row instanceof MediaExpandableNotificationRow) { + ((MediaExpandableNotificationRow) mediaNotification.row) + .setMediaController(controller); + } if (mediaNotification != null) { mMediaNotificationKey = mediaNotification.notification.getKey(); @@ -2195,7 +2222,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } // apply user lockscreen image - if (backdropBitmap == null && !mNotificationPanel.hasExternalKeyguardView()) { + if (backdropBitmap == null && !mLiveLockScreenController.isShowingLiveLockScreenView()) { backdropBitmap = mKeyguardWallpaper; } @@ -2476,6 +2503,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return mMediaNotificationKey; } + @Override + protected MediaController getCurrentMediaController() { + return mMediaController; + } + public boolean isScrimSrcModeEnabled() { return mScrimSrcModeEnabled; } @@ -2938,7 +2970,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); } } - if (mBrightnessChanged && upOrCancel) { + if (mBrightnessChanged && upOrCancel && !isQsExpanded()) { mBrightnessChanged = false; if (mJustPeeked && mExpandedVisible) { mNotificationPanel.fling(10, false); @@ -3433,6 +3465,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mStatusBarWindowManager = new StatusBarWindowManager(mContext, mKeyguardMonitor); mStatusBarWindowManager.setShowingMedia(mKeyguardShowingMedia); mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); + if (mLiveLockScreenController != null) { + mStatusBarWindowManager.setLiveLockscreenController(mLiveLockScreenController); + } } // called by makeStatusbar and also by PhoneStatusBarView @@ -3565,10 +3600,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } else if (cyanogenmod.content.Intent.ACTION_SCREEN_CAMERA_GESTURE.equals(action)) { boolean userSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 0) != 0; - if (!userSetupComplete) { - if (DEBUG) Log.d(TAG, String.format( - "userSetupComplete = %s, ignoring camera launch gesture.", - userSetupComplete)); + if (!userSetupComplete || !isDeviceProvisioned()) { + if (DEBUG) { + Log.d(TAG, String.format("userSetupComplete = $1%s, " + + "deviceProvisioned = $2%s, ignoring camera launch gesture.", + userSetupComplete, isDeviceProvisioned())); + } return; } @@ -3661,6 +3698,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mIconController.updateResources(); mScreenPinningRequest.onConfigurationChanged(); mNetworkController.onConfigurationChanged(); + mStatusBarWindowManager.onConfigurationChanged(newConfig); } @Override @@ -3686,6 +3724,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } + public void hideHeadsUp() { + if (mUseHeadsUp && mHeadsUpManager != null) { + mHeadsUpManager.releaseAllImmediately(); + } + } + private void setControllerUsers() { if (mZenModeController != null) { mZenModeController.setUserId(mCurrentUserId); @@ -3753,7 +3797,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (mKeyguardIndicationController != null) { mKeyguardIndicationController.cleanup(); } + if (mLiveLockScreenController != null) { + mLiveLockScreenController.cleanup(); + } + mKeyguardBottomArea.cleanup(); mStatusBarWindow.removeContent(mStatusBarWindowContent); mStatusBarWindow.clearDisappearingChildren(); @@ -3836,6 +3884,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (mState == StatusBarState.KEYGUARD) { // this will make sure the keyguard is showing showKeyguard(); + // make sure to hide the notification icon area and system iconography + // to avoid overlap (CYNGNOS-2253) + mIconController.hideNotificationIconArea(false); + mIconController.hideSystemIconArea(false); } // update mLastThemeChangeTime @@ -3854,7 +3906,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, removeAllViews((ViewGroup) child); } } - parent.removeAllViews(); + + // AdapterView does not support removeAllViews so check before calling + if (!(parent instanceof AdapterView)) parent.removeAllViews(); } /** @@ -4307,8 +4361,16 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mDraggedDownRow = null; } mAssistManager.onLockscreenShown(); - if (mNotificationPanel.hasExternalKeyguardView()) { - mNotificationPanel.getExternalKeyguardView().onKeyguardShowing( + mKeyguardBottomArea.requestFocus(); + try { + WindowManagerGlobal.getWindowManagerService() + .setLiveLockscreenEdgeDetector(false); + } catch (RemoteException e){ + e.printStackTrace(); + } + if (mLiveLockScreenController.isShowingLiveLockScreenView()) { + mLiveLockScreenController.onLiveLockScreenFocusChanged(false); + mLiveLockScreenController.getLiveLockScreenView().onKeyguardShowing( mStatusBarKeyguardViewManager.isScreenTurnedOn()); } } @@ -4461,8 +4523,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mNotificationPanel.onAffordanceLaunchEnded(); mNotificationPanel.animate().cancel(); mNotificationPanel.setAlpha(1f); - if ( mNotificationPanel.getExternalKeyguardView() != null) { - mNotificationPanel.getExternalKeyguardView().onKeyguardDismissed(); + if (mLiveLockScreenController.isShowingLiveLockScreenView()) { + mLiveLockScreenController.getLiveLockScreenView().onKeyguardDismissed(); } return staying; } @@ -4477,6 +4539,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return mStatusBarKeyguardViewManager != null && mStatusBarKeyguardViewManager.isSecure(); } + public boolean isKeyguardInputRestricted() { + return mStatusBarKeyguardViewManager != null && mStatusBarKeyguardViewManager.isInputRestricted(); + } + public long calculateGoingToFullShadeDelay() { return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration; } @@ -4552,6 +4618,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mIconPolicy.setKeyguardShowing(false); } mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade); + mLiveLockScreenController.setBarState(mState); updateDozingState(); updatePublicMode(); updateStackScrollerState(goingToFullShade); @@ -4640,24 +4707,28 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return false; } - protected void showBouncer() { - if (!mRecreating && - (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) { + public void showBouncer() { + if (!mRecreating && mNotificationPanel.mCanDismissKeyguard + && (mState != StatusBarState.SHADE || mLiveLockScreenController.getLiveLockScreenHasFocus())) { mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing(); mStatusBarKeyguardViewManager.dismiss(); } } protected void showBouncerOrFocusKeyguardExternalView() { - if (mNotificationPanel.hasExternalKeyguardView() && !isKeyguardShowingMedia() && - mNotificationPanel.isExternalKeyguardViewInteractive()) { + if (mLiveLockScreenController.isShowingLiveLockScreenView() && !isKeyguardShowingMedia() && + mLiveLockScreenController.isLiveLockScreenInteractive()) { focusKeyguardExternalView(); } else { showBouncer(); } } - protected void focusKeyguardExternalView() { + protected void unfocusKeyguardExternalView() { + mStatusBarKeyguardViewManager.setKeyguardExternalViewFocus(false); + } + + public void focusKeyguardExternalView() { mStatusBarView.collapseAllPanels(/*animate=*/ false, false /* delayed*/, 1.0f /* speedUpFactor */); mStatusBarKeyguardViewManager.setKeyguardExternalViewFocus(true); @@ -4701,6 +4772,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, clearNotificationEffects(); } mState = state; + mVisualizerView.setStatusBarState(state); mGroupManager.setStatusBarState(state); mStatusBarWindowManager.setStatusBarState(state); updateDozing(); @@ -4723,12 +4795,33 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public void onUnlockHintStarted() { - mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock); + mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock, + mNotificationPanel.shouldShowScreenOnHints() ? + KeyguardIndicationController.IndicationDirection.UP : + KeyguardIndicationController.IndicationDirection.NONE); + } + + public void onLlsHintStarted() { + String llsName = mLiveLockScreenController.getLiveLockScreenName(); + mKeyguardIndicationController.showTransientIndication( + mContext.getString(R.string.swipe_left_hint, llsName), + KeyguardIndicationController.IndicationDirection.LEFT); + } + + public void onExpandHintStarted() { + mKeyguardIndicationController.showTransientIndication(R.string.expand_hint, + KeyguardIndicationController.IndicationDirection.DOWN); + } + + public void onNotificationsHintStarted() { + mKeyguardIndicationController.showTransientIndication(R.string.swipe_right_hint, + KeyguardIndicationController.IndicationDirection.RIGHT); } public void onHintFinished() { // Delay the reset a bit so the user can read the text. mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS); + mKeyguardBottomArea.expand(false); } public void onCameraHintStarted(String hint) { @@ -4741,19 +4834,22 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void onTrackingStopped(boolean expand) { if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { - if (!expand && (!mUnlockMethodCache.canSkipBouncer() || - mNotificationPanel.hasExternalKeyguardView())) { - showBouncerOrFocusKeyguardExternalView(); + if (!expand && !mUnlockMethodCache.canSkipBouncer()) { + showBouncer(); } - } else if (expand && mStatusBarWindowManager.keyguardExternalViewHasFocus()) { - mStatusBarKeyguardViewManager.setKeyguardExternalViewFocus(false); - setBarState(StatusBarState.KEYGUARD); } } @Override protected int getMaxKeyguardNotifications() { - return mKeyguardMaxNotificationCount; + int max = mKeyguardMaxNotificationCount; + // When an interactive live lockscreen is showing + // we want to limit the number of maximum notifications + // by 1 so there is additional space for the user to dismiss keygard + if (mLiveLockScreenController.isLiveLockScreenInteractive()) { + max--; + } + return max; } public NavigationBarView getNavigationBarView() { @@ -4911,15 +5007,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mScreenTurningOn = false; mDozeScrimController.onScreenTurnedOn(); mVisualizerView.setVisible(true); - if (mNotificationPanel.hasExternalKeyguardView()) { - mNotificationPanel.getExternalKeyguardView().onScreenTurnedOn(); + if (mLiveLockScreenController.isShowingLiveLockScreenView()) { + mLiveLockScreenController.onScreenTurnedOn(); } } public void onScreenTurnedOff() { mVisualizerView.setVisible(false); - if (mNotificationPanel.hasExternalKeyguardView()) { - mNotificationPanel.getExternalKeyguardView().onScreenTurnedOff(); + if (mLiveLockScreenController.isShowingLiveLockScreenView()) { + mLiveLockScreenController.onScreenTurnedOff(); } } @@ -5326,6 +5422,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return mVisualizerView; } + public boolean isShowingLiveLockScreenView() { + return mLiveLockScreenController.isShowingLiveLockScreenView(); + } + + public void slideNotificationPanelIn() { + mNotificationPanel.slideLockScreenIn(); + } + private final class ShadeUpdates { private final ArraySet<String> mVisibleNotifications = new ArraySet<String>(); private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>(); @@ -5478,4 +5582,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } } + + public boolean isAffordanceSwipeInProgress() { + return mNotificationPanel.isAffordanceSwipeInProgress(); + } } 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 ed4880b..1395ff6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -22,6 +22,8 @@ import android.app.AlarmManager.AlarmClockInfo; import android.app.IUserSwitchObserver; import android.app.PendingIntent; import android.app.StatusBarManager; +import android.bluetooth.BluetoothAssignedNumbers; +import android.bluetooth.BluetoothHeadset; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -103,6 +105,7 @@ public class PhoneStatusBarPolicy implements Callback { private boolean mZenVisible; private boolean mVolumeVisible; private boolean mCurrentUserSetup; + private Float mBluetoothBatteryLevel = null; private int mZen; @@ -129,6 +132,9 @@ public class PhoneStatusBarPolicy implements Callback { else if (action.equals(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED)) { updateTTY(intent); } + else if (action.equals(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT)) { + updateBluetoothBattery(intent); + } } }; @@ -166,6 +172,9 @@ public class PhoneStatusBarPolicy implements Callback { filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED); + filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT); + filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + + "." + Integer.toString(BluetoothAssignedNumbers.APPLE)); mContext.registerReceiver(mIntentReceiver, filter, null, mHandler); // listen for user / profile change. @@ -357,6 +366,27 @@ public class PhoneStatusBarPolicy implements Callback { updateBluetooth(); } + private void updateBluetoothBattery(Intent intent) { + if (intent.hasExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD)) { + String command = intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD); + if ("+IPHONEACCEV".equals(command)) { + Object[] args = (Object[]) intent.getSerializableExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS); + if (args.length >= 3 && args[0] instanceof Integer && ((Integer)args[0])*2+1<=args.length) { + for (int i=0;i<((Integer)args[0]);i++) { + if (!(args[i*2+1] instanceof Integer) || !(args[i*2+2] instanceof Integer)) { + continue; + } + if (args[i*2+1].equals(1)) { + mBluetoothBatteryLevel = (((Integer)args[i*2+2])+1)/10.0f; + updateBluetooth(); + break; + } + } + } + } + } + } + private final void updateBluetooth() { int iconId = R.drawable.stat_sys_data_bluetooth; String contentDescription = @@ -365,8 +395,24 @@ public class PhoneStatusBarPolicy implements Callback { if (mBluetooth != null) { bluetoothEnabled = mBluetooth.isBluetoothEnabled(); if (mBluetooth.isBluetoothConnected()) { - iconId = R.drawable.stat_sys_data_bluetooth_connected; + if (mBluetoothBatteryLevel == null) { + iconId = R.drawable.stat_sys_data_bluetooth_connected; + } else { + if (mBluetoothBatteryLevel<=0.15f) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_1; + } else if (mBluetoothBatteryLevel<=0.375f) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_2; + } else if (mBluetoothBatteryLevel<=0.625f) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_3; + } else if (mBluetoothBatteryLevel<=0.85f) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_4; + } else { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_5; + } + } contentDescription = mContext.getString(R.string.accessibility_bluetooth_connected); + } else { + mBluetoothBatteryLevel = null; } } @@ -474,6 +520,12 @@ public class PhoneStatusBarPolicy implements Callback { @Override public void onUserSwitching(int newUserId, IRemoteCallback reply) { mUserInfoController.reloadUserInfo(); + if (reply != null) { + try { + reply.sendResult(null); + } catch (RemoteException e) { + } + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 3b068d6..8c9daee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -111,6 +111,7 @@ public class PhoneStatusBarView extends PanelBar { @Override public void onPanelPeeked() { super.onPanelPeeked(); + removePendingHideExpandedRunnables(); mBar.makeExpandedVisible(false); } 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 b776a9f..e88ed73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -54,7 +54,6 @@ import com.android.systemui.qs.tiles.FlashlightTile; import com.android.systemui.qs.tiles.HeadsUpTile; import com.android.systemui.qs.tiles.HotspotTile; import com.android.systemui.qs.tiles.IntentTile; -import com.android.systemui.qs.tiles.LiveDisplayTile; import com.android.systemui.qs.tiles.LocationTile; import com.android.systemui.qs.tiles.LockscreenToggleTile; import com.android.systemui.qs.tiles.NfcTile; @@ -67,6 +66,7 @@ import com.android.systemui.qs.tiles.UsbTetherTile; import com.android.systemui.qs.tiles.VolumeTile; import com.android.systemui.qs.tiles.WifiTile; import com.android.systemui.statusbar.CustomTileData; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.FlashlightController; @@ -115,6 +115,7 @@ public class QSTileHost implements QSTile.Host, Tunable { private final UserSwitcherController mUserSwitcherController; private final KeyguardMonitor mKeyguard; private final SecurityController mSecurity; + private final BatteryController mBattery; private CustomTileData mCustomTileData; private CustomTileListenerService mCustomTileListenerService; @@ -127,7 +128,7 @@ public class QSTileHost implements QSTile.Host, Tunable { ZenModeController zen, HotspotController hotspot, CastController cast, FlashlightController flashlight, UserSwitcherController userSwitcher, KeyguardMonitor keyguard, - SecurityController security) { + SecurityController security, BatteryController battery) { mContext = context; mStatusBar = statusBar; mBluetooth = bluetooth; @@ -141,6 +142,7 @@ public class QSTileHost implements QSTile.Host, Tunable { mUserSwitcherController = userSwitcher; mKeyguard = keyguard; mSecurity = security; + mBattery = battery; mCustomTileData = new CustomTileData(); final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(), @@ -281,6 +283,11 @@ public class QSTileHost implements QSTile.Host, Tunable { return mKeyguard; } + @Override + public BatteryController getBatteryController() { + return mBattery; + } + public UserSwitcherController getUserSwitcherController() { return mUserSwitcherController; } @@ -311,10 +318,12 @@ public class QSTileHost implements QSTile.Host, Tunable { if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec); try { if (mCustomTileData.get(tileSpec) != null) { - newTiles.put(tileSpec, new CustomQSTile(this, - mCustomTileData.get(tileSpec).sbc)); + final CustomQSTile value = new CustomQSTile(this, + mCustomTileData.get(tileSpec).sbc); + newTiles.put(tileSpec, value); } else { - newTiles.put(tileSpec, createTile(tileSpec)); + final QSTile<?> tile = createTile(tileSpec); + newTiles.put(tileSpec, tile); } } catch (Throwable t) { Log.w(TAG, "Error creating tile for spec: " + tileSpec, t); @@ -361,12 +370,16 @@ public class QSTileHost implements QSTile.Host, Tunable { else if (tileSpec.equals("performance")) return new PerfProfileTile(this); else if (tileSpec.equals("lockscreen")) return new LockscreenToggleTile(this); else if (tileSpec.equals("ambient_display")) return new AmbientDisplayTile(this); - else if (tileSpec.equals("live_display")) return new LiveDisplayTile(this); else if (tileSpec.equals("heads_up")) return new HeadsUpTile(this); else if (tileSpec.equals("battery_saver")) return new BatterySaverTile(this); else if (tileSpec.equals("caffeine")) return new CaffeineTile(this); else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec); - else throw new IllegalArgumentException("Bad tile spec: " + tileSpec); + else if (TextUtils.split(tileSpec, "\\|").length == 3) { + /** restores placeholder for + * {@link cyanogenmod.app.StatusBarPanelCustomTile#persistableKey()} **/ + return new CustomQSTile(this, tileSpec); + } else + throw new IllegalArgumentException("Bad tile spec: " + tileSpec); } protected List<String> loadTileSpecs(String tileList) { @@ -393,11 +406,10 @@ public class QSTileHost implements QSTile.Host, Tunable { tiles.add(tile); } } - // ensure edit tile is present - if (tiles.size() < TILES_PER_PAGE && !tiles.contains("edit")) { + // ensure edit tile is present, default placement should be handled in the default + // tile list. + if (!tiles.contains("edit")) { tiles.add("edit"); - } else if (tiles.size() > TILES_PER_PAGE && !tiles.contains("edit")) { - tiles.add((TILES_PER_PAGE - 1), "edit"); } return tiles; } @@ -455,7 +467,6 @@ public class QSTileHost implements QSTile.Host, Tunable { else if (spec.equals("performance")) return R.string.qs_tile_performance; else if (spec.equals("lockscreen")) return R.string.quick_settings_lockscreen_label; else if (spec.equals("ambient_display")) return R.string.quick_settings_ambient_display_label; - else if (spec.equals("live_display")) return R.string.live_display_title; else if (spec.equals("heads_up")) return R.string.quick_settings_heads_up_label; else if (spec.equals("battery_saver")) return R.string.quick_settings_battery_saver_label; else if (spec.equals("caffeine")) return R.string.quick_settings_caffeine_label; @@ -486,7 +497,6 @@ public class QSTileHost implements QSTile.Host, Tunable { else if (spec.equals("performance")) return R.drawable.ic_qs_perf_profile; else if (spec.equals("lockscreen")) return R.drawable.ic_qs_lock_screen_on; else if (spec.equals("ambient_display")) return R.drawable.ic_qs_ambientdisplay_on; - else if (spec.equals("live_display")) return R.drawable.ic_livedisplay_auto; else if (spec.equals("heads_up")) return R.drawable.ic_qs_heads_up_on; else if (spec.equals("battery_saver")) return R.drawable.ic_qs_battery_saver_on; else if (spec.equals("caffeine")) return R.drawable.ic_qs_caffeine_on; @@ -495,8 +505,8 @@ public class QSTileHost implements QSTile.Host, Tunable { void updateCustomTile(StatusBarPanelCustomTile sbc) { synchronized (mTiles) { - if (mTiles.containsKey(sbc.getKey())) { - QSTile<?> tile = mTiles.get(sbc.getKey()); + if (mTiles.containsKey(sbc.persistableKey())) { + QSTile<?> tile = mTiles.get(sbc.persistableKey()); if (tile instanceof CustomQSTile) { CustomQSTile qsTile = (CustomQSTile) tile; qsTile.update(sbc); @@ -508,8 +518,8 @@ public class QSTileHost implements QSTile.Host, Tunable { void addCustomTile(StatusBarPanelCustomTile sbc) { synchronized (mTiles) { mCustomTileData.add(new CustomTileData.Entry(sbc)); - mTileSpecs.add(sbc.getKey()); - mTiles.put(sbc.getKey(), new CustomQSTile(this, sbc)); + mTileSpecs.add(sbc.persistableKey()); + mTiles.put(sbc.persistableKey(), new CustomQSTile(this, sbc)); if (mCallback != null) { mCallback.onTilesChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index b9e9292..975cb77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -45,9 +45,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, public static final long ANIMATION_DURATION = 220; public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR = new PathInterpolator(0f, 0, 0.7f, 1f); + public static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.45f; private static final float SCRIM_BEHIND_ALPHA = 0.62f; - private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.45f; private static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f; private static final float SCRIM_IN_FRONT_ALPHA = 0.75f; private static final int TAG_KEY_ANIM = R.id.scrim; @@ -255,7 +255,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } } - private void setScrimBehindColor(float alpha) { + public void setScrimBehindColor(float alpha) { setScrimColor(mScrimBehind, alpha); } 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 339d469..f9b1f38 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -76,6 +76,7 @@ import java.text.NumberFormat; import cyanogenmod.app.StatusBarPanelCustomTile; import cyanogenmod.providers.CMSettings; +import cyanogenmod.weather.util.WeatherUtils; import org.cyanogenmod.internal.logging.CMMetricsLogger; /** @@ -280,6 +281,10 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL mClockExpandedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size); mClockCollapsedScaleFactor = (float) mClockCollapsedSize / (float) mClockExpandedSize; + if (mEditTileDoneText != null) { + mEditTileDoneText.setText(R.string.quick_settings_done); + } + updateClockScale(); updateClockCollapsedMargin(); } @@ -515,12 +520,12 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL @Override public void onWeatherChanged(WeatherController.WeatherInfo info) { - if (info.temp == null || info.condition == null) { + if (Double.isNaN(info.temp) || info.condition == null) { mWeatherLine1.setText(null); } else { mWeatherLine1.setText(mContext.getString( R.string.status_bar_expanded_header_weather_format, - info.temp, + WeatherUtils.formatTemperature(info.temp, info.tempUnit), info.condition)); } mWeatherLine2.setText(info.city); 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 19f2b09..96cf093 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -83,8 +83,6 @@ public class StatusBarKeyguardViewManager { private boolean mDeviceWillWakeUp; private boolean mDeferScrimFadeOut; - private View mUnlockFab; - public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback, LockPatternUtils lockPatternUtils) { mContext = context; @@ -103,7 +101,7 @@ public class StatusBarKeyguardViewManager { if (mBouncer != null) mBouncer.removeView(); mFingerprintUnlockController = fingerprintUnlockController; mBouncer = new KeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils, - mStatusBarWindowManager, container); + mStatusBarWindowManager, container, mPhoneStatusBar); } /** @@ -257,8 +255,8 @@ public class StatusBarKeyguardViewManager { mOccluded = occluded; mStatusBarWindowManager.setKeyguardOccluded(occluded); mPhoneStatusBar.getVisualizer().setOccluded(occluded); - if (mUnlockFab != null && mUnlockFab.isAttachedToWindow() && !occluded) { - hideUnlockFab(); + if (!occluded) { + mPhoneStatusBar.mKeyguardBottomArea.setVisibility(View.GONE); } reset(false); } @@ -393,20 +391,9 @@ public class StatusBarKeyguardViewManager { } } - /** - * Dismisses the keyguard by going to the next screen or making it gone. - */ public void dismiss() { - dismiss(false); - } - - public void dismiss(boolean focusKeyguardExternalView) { - if ((mDeviceInteractive || mDeviceWillWakeUp) && !focusKeyguardExternalView) { + if ((mDeviceInteractive || mDeviceWillWakeUp)) { showBouncer(); - hideUnlockFab(); - } else if (focusKeyguardExternalView) { - showUnlockFab(); - mStatusBarWindowManager.setKeyguardExternalViewFocus(true); } } @@ -542,7 +529,7 @@ public class StatusBarKeyguardViewManager { public boolean shouldDisableWindowAnimationsForUnlock() { return mPhoneStatusBar.isInLaunchTransition() || - mPhoneStatusBar.mNotificationPanel.hasExternalKeyguardView(); + mPhoneStatusBar.isShowingLiveLockScreenView(); } public boolean isGoingToNotificationShade() { @@ -566,7 +553,7 @@ public class StatusBarKeyguardViewManager { false /* delayed */, speedUpFactor); if (mStatusBarWindowManager.keyguardExternalViewHasFocus()) { mStatusBarWindowManager.setKeyguardExternalViewFocus(false); - dismiss(false); + dismiss(); } } @@ -591,50 +578,6 @@ public class StatusBarKeyguardViewManager { } public void setKeyguardExternalViewFocus(boolean hasFocus) { - if (hasFocus) { - showUnlockFab(); - } else { - hideUnlockFab(); - } mStatusBarWindowManager.setKeyguardExternalViewFocus(hasFocus); } - - private void showUnlockFab() { - if (mUnlockFab == null) { - mUnlockFab = View.inflate(mContext, R.layout.unlock_fab, null); - } - if (!mUnlockFab.isAttachedToWindow()) { - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, - WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH - | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS - | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, - PixelFormat.TRANSLUCENT); - lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; - lp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; - lp.setTitle("UnlockFab"); - lp.packageName = mContext.getPackageName(); - lp.width = lp.height = - mContext.getResources().getDimensionPixelSize(R.dimen.unlock_fab_size); - WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - wm.addView(mUnlockFab, lp); - mUnlockFab.setOnClickListener(mUnlockFabClickListener); - } - } - - private void hideUnlockFab() { - if (mUnlockFab != null && mUnlockFab.isAttachedToWindow()) { - WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - wm.removeViewImmediate(mUnlockFab); - } - } - - private View.OnClickListener mUnlockFabClickListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - mStatusBarWindowManager.setKeyguardExternalViewFocus(false); - dismiss(false); - } - }; } 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 9a991f4..f0d7828 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java @@ -18,11 +18,14 @@ package com.android.systemui.statusbar.phone; import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.content.res.Resources; +import android.database.ContentObserver; import android.graphics.Point; import android.graphics.PixelFormat; +import android.os.Handler; import android.os.SystemProperties; -import android.util.Log; +import android.provider.Settings; import android.view.Gravity; import android.view.Display; import android.view.SurfaceSession; @@ -35,8 +38,8 @@ import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.LiveLockScreenController; import cyanogenmod.providers.CMSettings; -import org.cyanogenmod.internal.util.CmLockPatternUtils; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -53,8 +56,9 @@ public class StatusBarWindowManager implements KeyguardMonitor.Callback { private WindowManager.LayoutParams mLp; private WindowManager.LayoutParams mLpChanged; private int mBarHeight; - private final boolean mKeyguardScreenRotation; + private boolean mKeyguardScreenRotation; private final float mScreenBrightnessDoze; + private final boolean mBlurSupported; private boolean mKeyguardBlurEnabled; private boolean mShowingMedia; @@ -62,6 +66,7 @@ public class StatusBarWindowManager implements KeyguardMonitor.Callback { private final SurfaceSession mFxSession; private final KeyguardMonitor mKeyguardMonitor; + private int mCurrentOrientation; private static final int TYPE_LAYER_MULTIPLIER = 10000; // refer to WindowManagerService.TYPE_LAYER_MULTIPLIER private static final int TYPE_LAYER_OFFSET = 1000; // refer to WindowManagerService.TYPE_LAYER_OFFSET @@ -69,6 +74,7 @@ public class StatusBarWindowManager implements KeyguardMonitor.Callback { private static final int STATUS_BAR_LAYER = 16 * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; private final State mCurrentState = new State(); + private LiveLockScreenController mLiveLockScreenController; public StatusBarWindowManager(Context context, KeyguardMonitor kgm) { mContext = context; @@ -76,18 +82,23 @@ public class StatusBarWindowManager implements KeyguardMonitor.Callback { mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation(); mScreenBrightnessDoze = mContext.getResources().getInteger( com.android.internal.R.integer.config_screenBrightnessDoze) / 255f; + mBlurSupported = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_ui_blur_enabled); mKeyguardMonitor = kgm; mKeyguardMonitor.addCallback(this); - mKeyguardBlurEnabled = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_ui_blur_enabled); mFxSession = new SurfaceSession(); } private boolean shouldEnableKeyguardScreenRotation() { Resources res = mContext.getResources(); + boolean enableAccelerometerRotation = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.ACCELEROMETER_ROTATION, 1) != 0; + boolean enableLockScreenRotation = CMSettings.System.getInt(mContext.getContentResolver(), + CMSettings.System.LOCKSCREEN_ROTATION, 0) != 0; return SystemProperties.getBoolean("lockscreen.rot_override", false) - || res.getBoolean(R.bool.config_enableLockScreenRotation); + || (res.getBoolean(R.bool.config_enableLockScreenRotation) + && (enableLockScreenRotation && enableAccelerometerRotation)); } /** @@ -121,27 +132,34 @@ public class StatusBarWindowManager implements KeyguardMonitor.Callback { mLpChanged = new WindowManager.LayoutParams(); mLpChanged.copyFrom(mLp); - if (mKeyguardBlurEnabled) { + mKeyguardBlurEnabled = mBlurSupported ? + CMSettings.Secure.getInt(mContext.getContentResolver(), + CMSettings.Secure.LOCK_SCREEN_BLUR_ENABLED, 1) == 1 : false; + if (mBlurSupported) { Display display = mWindowManager.getDefaultDisplay(); Point xy = new Point(); display.getRealSize(xy); + mCurrentOrientation = mContext.getResources().getConfiguration().orientation; mKeyguardBlur = new BlurLayer(mFxSession, xy.x, xy.y, "KeyGuard"); if (mKeyguardBlur != null) { mKeyguardBlur.setLayer(STATUS_BAR_LAYER - 2); } } + + SettingsObserver observer = new SettingsObserver(new Handler()); + observer.observe(mContext); } private void applyKeyguardFlags(State state) { if (state.keyguardShowing) { mLpChanged.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; - if (!mKeyguardBlurEnabled) { + if (!mKeyguardBlurEnabled || mShowingMedia) { mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; } } else { mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; mLpChanged.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; - if (mKeyguardBlurEnabled) { + if (mKeyguardBlurEnabled && mKeyguardBlur != null) { mKeyguardBlur.hide(); } } @@ -260,8 +278,7 @@ public class StatusBarWindowManager implements KeyguardMonitor.Callback { boolean isblur = false; if (mCurrentState.keyguardShowing && mKeyguardBlurEnabled && !mCurrentState.keyguardOccluded - && !mShowingMedia - && !isShowingLiveLockScreen()) { + && !mShowingMedia) { isblur = true; } if (mKeyguardBlur != null) { @@ -341,11 +358,21 @@ public class StatusBarWindowManager implements KeyguardMonitor.Callback { } public void setKeyguardExternalViewFocus(boolean hasFocus) { - mCurrentState.keyguardExternalViewHasFocus = hasFocus; + mLiveLockScreenController.onLiveLockScreenFocusChanged(hasFocus); // make the keyguard occluded so the external view gets full focus setKeyguardOccluded(hasFocus); } + public void onConfigurationChanged(Configuration newConfig) { + if (mKeyguardBlur != null && newConfig.orientation != mCurrentOrientation) { + Display display = mWindowManager.getDefaultDisplay(); + Point xy = new Point(); + display.getRealSize(xy); + mKeyguardBlur.setSize(xy.x, xy.y); + mCurrentOrientation = newConfig.orientation; + } + } + /** * @param state The {@link StatusBarState} of the status bar. */ @@ -394,14 +421,11 @@ public class StatusBarWindowManager implements KeyguardMonitor.Callback { } public boolean keyguardExternalViewHasFocus() { - return mCurrentState.keyguardExternalViewHasFocus; + return mLiveLockScreenController.getLiveLockScreenHasFocus(); } - private boolean isShowingLiveLockScreen() { - CmLockPatternUtils lockPatternUtils = new CmLockPatternUtils(mContext); - return (CMSettings.Secure.getInt(mContext.getContentResolver(), - CMSettings.Secure.LIVE_LOCK_SCREEN_ENABLED, 0) == 1) - && lockPatternUtils.isThirdPartyKeyguardEnabled(); + public void setLiveLockscreenController(LiveLockScreenController liveLockScreenController) { + mLiveLockScreenController = liveLockScreenController; } private static class State { @@ -418,7 +442,6 @@ public class StatusBarWindowManager implements KeyguardMonitor.Callback { boolean forceStatusBarVisible; boolean forceCollapsed; boolean forceDozeBrightness; - boolean keyguardExternalViewHasFocus; /** * The {@link BaseStatusBar} state from the status bar. @@ -455,4 +478,39 @@ public class StatusBarWindowManager implements KeyguardMonitor.Callback { return result.toString(); } } + + private class SettingsObserver extends ContentObserver { + public SettingsObserver(Handler handler) { + super(handler); + } + + public void observe(Context context) { + context.getContentResolver().registerContentObserver( + CMSettings.Secure.getUriFor(CMSettings.Secure.LOCK_SCREEN_BLUR_ENABLED), + false, + this); + context.getContentResolver().registerContentObserver( + Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), + false, + this); + context.getContentResolver().registerContentObserver( + CMSettings.System.getUriFor(CMSettings.System.LOCKSCREEN_ROTATION), + false, + this); + } + + public void unobserve(Context context) { + context.getContentResolver().unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange) { + mKeyguardBlurEnabled = mBlurSupported ? + CMSettings.Secure.getInt(mContext.getContentResolver(), + CMSettings.Secure.LOCK_SCREEN_BLUR_ENABLED, 1) == 1 : false; + mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation(); + // update the state + apply(mCurrentState); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ViewLinker.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ViewLinker.java new file mode 100644 index 0000000..48457c6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ViewLinker.java @@ -0,0 +1,76 @@ +package com.android.systemui.statusbar.phone; + +import android.view.View; + +/* + Allows mirroring of view states such as alpha, translation...etc + */ +public class ViewLinker<T extends View & ViewLinker.ViewLinkerParent> { + + public static final int LINK_ALPHA = 0x1; + public static final int LINK_TRANSLATION = 0x2; + + private final LinkInfo[] mLinkedViews; + private final T mParent; + + public interface ViewLinkerCallback { + void onAlphaChanged(float alpha); + void onTranslationXChanged(float translationX); + } + + public interface ViewLinkerParent { + void registerLinker(ViewLinkerCallback callback); + } + + public static class LinkInfo { + private View mView; + private int mFlags; + public LinkInfo(View v, int linkFlags) { + mView = v; + mFlags = linkFlags; + } + private boolean supportsFlag(int flag) { + return (mFlags & flag) != 0; + } + } + + private ViewLinkerCallback mCallback = new ViewLinkerCallback() { + @Override + public void onAlphaChanged(float alpha) { + for (LinkInfo v : mLinkedViews) { + if (v.supportsFlag(LINK_ALPHA)) { + v.mView.setAlpha(alpha); + } + } + } + + @Override + public void onTranslationXChanged(float translationX) { + for (LinkInfo v : mLinkedViews) { + if (v.supportsFlag(LINK_TRANSLATION)) { + v.mView.setTranslationX(translationX); + } + } + } + }; + + public ViewLinker(T parent, LinkInfo... viewsToLink) { + mLinkedViews = viewsToLink; + mParent = parent; + ensureParentNotInLink(); + parent.registerLinker(mCallback); + } + + private void ensureParentNotInLink() { + for (LinkInfo v : mLinkedViews) { + if (v.mView == mParent) { + throw new IllegalStateException("Parent cannot be" + + "one of the linked views"); + } + } + } + + public View getParent() { + return mParent; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LiveLockScreenController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LiveLockScreenController.java new file mode 100644 index 0000000..2f290cc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LiveLockScreenController.java @@ -0,0 +1,349 @@ +package com.android.systemui.statusbar.policy; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.EventLog; + +import android.view.View; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.EventLogTags; +import com.android.systemui.SystemUIApplication; +import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.phone.NotificationPanelView; +import com.android.systemui.statusbar.phone.PhoneStatusBar; + +import cyanogenmod.app.CMContextConstants; +import cyanogenmod.app.ILiveLockScreenChangeListener; +import cyanogenmod.app.ILiveLockScreenManager; +import cyanogenmod.app.LiveLockScreenInfo; +import cyanogenmod.externalviews.KeyguardExternalView; + +import java.util.Objects; + +public class LiveLockScreenController { + private static final String TAG = LiveLockScreenController.class.getSimpleName(); + + private ILiveLockScreenManager mLLSM; + private Context mContext; + private PhoneStatusBar mBar; + private NotificationPanelView mPanelView; + private ComponentName mLiveLockScreenComponentName; + private KeyguardExternalView mLiveLockScreenView; + private Handler mHandler; + + private int mStatusBarState; + + private PowerManager mPowerManager; + + private boolean mLlsHasFocus = false; + + private boolean mScreenOnAndInteractive; + + private String mLlsName; + private KeyguardViewMediator mKeyguardViewMediator; + + public LiveLockScreenController(Context context, PhoneStatusBar bar, + NotificationPanelView panelView) { + mContext = context; + mHandler = new Handler(Looper.getMainLooper()); + + mLLSM = ILiveLockScreenManager.Stub.asInterface(ServiceManager.getService( + CMContextConstants.CM_LIVE_LOCK_SCREEN_SERVICE)); + mBar = bar; + mPanelView = panelView; + mPowerManager = context.getSystemService(PowerManager.class); + mKeyguardViewMediator = ((SystemUIApplication) + mContext.getApplicationContext()).getComponent(KeyguardViewMediator.class); + registerListener(); + try { + LiveLockScreenInfo llsInfo = mLLSM.getCurrentLiveLockScreen(); + if (llsInfo != null && llsInfo.component != null) { + updateLiveLockScreenView(llsInfo.component); + } + } catch (RemoteException e) { + /* ignore */ + } + } + + public void cleanup() { + unregisterListener(); + mPanelView = null; + if (mLiveLockScreenView != null) { + mLiveLockScreenView.setProviderComponent(null); + } + mLiveLockScreenView = null; + mLiveLockScreenComponentName = null; + } + + public void setBarState(int statusBarState) { + if (mStatusBarState != StatusBarState.SHADE && statusBarState == StatusBarState.SHADE) { + // going from KEYGUARD or SHADE_LOCKED to SHADE so device has been unlocked + onKeyguardDismissed(); + } + + if (statusBarState == StatusBarState.KEYGUARD) { + mBar.getScrimController().forceHideScrims(false); + } + + mStatusBarState = statusBarState; + if (statusBarState == StatusBarState.KEYGUARD || + statusBarState == StatusBarState.SHADE_LOCKED) { + if (mLiveLockScreenComponentName != null) { + if (mLiveLockScreenView == null) { + mLiveLockScreenView = + getExternalKeyguardView(mLiveLockScreenComponentName); + if (mLiveLockScreenView != null) { + mLiveLockScreenView.registerKeyguardExternalViewCallback( + mExternalKeyguardViewCallbacks); + } + } + if (mLiveLockScreenView != null && !mLiveLockScreenView.isAttachedToWindow()) { + mBar.updateRowStates(); + mPanelView.addView(mLiveLockScreenView, 0); + } + } + } else { + if (isShowingLiveLockScreenView() && !mBar.isKeyguardInputRestricted()) { + mPanelView.removeView(mLiveLockScreenView); + } + mLlsHasFocus = false; + } + } + + private ILiveLockScreenChangeListener mChangeListener = + new ILiveLockScreenChangeListener.Stub() { + @Override + public void onLiveLockScreenChanged(LiveLockScreenInfo llsInfo) throws RemoteException { + if (mPanelView != null) { + updateLiveLockScreenView(llsInfo != null ? llsInfo.component : null); + } + } + }; + + private void registerListener() { + try { + mLLSM.registerChangeListener(mChangeListener); + } catch (RemoteException e) { + /* ignore */ + } + } + + private void unregisterListener() { + try { + mLLSM.unregisterChangeListener(mChangeListener); + } catch (RemoteException e) { + /* ignore */ + } + } + + private KeyguardExternalView getExternalKeyguardView(ComponentName componentName) { + try { + return new KeyguardExternalView(mContext, null, componentName); + } catch (Exception e) { + // just return null below and move on + } + return null; + } + + private KeyguardExternalView.KeyguardExternalViewCallbacks mExternalKeyguardViewCallbacks = + new KeyguardExternalView.KeyguardExternalViewCallbacks() { + @Override + public boolean requestDismiss() { + if (isShowingLiveLockScreenView()) { + mHandler.post(new Runnable() { + @Override + public void run() { + mBar.showKeyguard(); + mBar.showBouncer(); + } + }); + return true; + } + return false; + } + + @Override + public boolean requestDismissAndStartActivity(final Intent intent) { + if (isShowingLiveLockScreenView()) { + mHandler.post(new Runnable() { + @Override + public void run() { + mBar.startActivityDismissingKeyguard(intent, false, true, true, + null); + } + }); + return true; + } + return false; + } + + @Override + public void providerDied() { + mLiveLockScreenView.unregisterKeyguardExternalViewCallback( + mExternalKeyguardViewCallbacks); + mLiveLockScreenView = null; + // make sure we're showing the notification panel if the LLS crashed while it had focus + if (mLlsHasFocus) { + mLlsHasFocus = false; + mHandler.post(new Runnable() { + @Override + public void run() { + mBar.showKeyguard(); + } + }); + } + } + + @Override + public void slideLockscreenIn() { + if (mLlsHasFocus) { + mHandler.post(new Runnable() { + @Override + public void run() { + mBar.showKeyguard(); + } + }); + } + } + }; + + public boolean isShowingLiveLockScreenView() { + return mLiveLockScreenView != null && mLiveLockScreenView.isAttachedToWindow(); + } + + public boolean isLiveLockScreenInteractive() { + return mLiveLockScreenView != null && mLiveLockScreenView.isInteractive(); + } + + public KeyguardExternalView getLiveLockScreenView() { + return mLiveLockScreenView; + } + + public void onScreenTurnedOn() { + mScreenOnAndInteractive = mPowerManager.isInteractive(); + if (mScreenOnAndInteractive) { + if (mLiveLockScreenView != null) mLiveLockScreenView.onScreenTurnedOn(); + EventLog.writeEvent(EventLogTags.SYSUI_LLS_KEYGUARD_SHOWING, 1); + } + } + + public void onScreenTurnedOff() { + if (mScreenOnAndInteractive) { + if (mLiveLockScreenView != null) mLiveLockScreenView.onScreenTurnedOff(); + if (mStatusBarState != StatusBarState.SHADE) { + EventLog.writeEvent(EventLogTags.SYSUI_LLS_KEYGUARD_SHOWING, 0); + } + mScreenOnAndInteractive = false; + } + } + + public void onLiveLockScreenFocusChanged(boolean hasFocus) { + mKeyguardViewMediator.notifyKeyguardPanelFocusChanged(hasFocus); + if (mLiveLockScreenView != null) { + // make sure the LLS knows where the notification panel is + mLiveLockScreenView.onLockscreenSlideOffsetChanged(hasFocus ? 0f : 1f); + } + // don't log focus changes when screen is not interactive + if (hasFocus != mLlsHasFocus && mPowerManager.isInteractive()) { + EventLog.writeEvent(EventLogTags.SYSUI_LLS_NOTIFICATION_PANEL_SHOWN, + hasFocus ? 0 : 1); + } + // Hide statusbar and scrim if live lockscreen + // currently has focus + mBar.setStatusBarViewVisibility(!hasFocus); + mBar.getScrimController().forceHideScrims(hasFocus); + mLlsHasFocus = hasFocus; + } + + public void onKeyguardDismissed() { + if (mLiveLockScreenView != null) mLiveLockScreenView.onKeyguardDismissed(); + EventLog.writeEvent(EventLogTags.SYSUI_LLS_KEYGUARD_DISMISSED, mLlsHasFocus ? 1 : 0); + // Ensure we reset visibility when keyguard is dismissed + mBar.setStatusBarViewVisibility(true); + mBar.getScrimController().forceHideScrims(false); + } + + public boolean getLiveLockScreenHasFocus() { + return mLlsHasFocus; + } + + public String getLiveLockScreenName() { + return mLlsName; + } + + private String getLlsNameFromComponentName(ComponentName cn) { + if (cn == null) return null; + + PackageManager pm = mContext.getPackageManager(); + Intent intent = new Intent(); + intent.setComponent(cn); + ResolveInfo ri = pm.resolveService(intent, 0); + return ri != null ? ri.serviceInfo.loadLabel(pm).toString() : null; + } + + private Runnable mAddNewLiveLockScreenRunnable = new Runnable() { + @Override + public void run() { + if (mLiveLockScreenComponentName != null) { + mLiveLockScreenView = + getExternalKeyguardView(mLiveLockScreenComponentName); + mLiveLockScreenView.registerKeyguardExternalViewCallback( + mExternalKeyguardViewCallbacks); + if (mStatusBarState != StatusBarState.SHADE) { + mPanelView.addView(mLiveLockScreenView); + mLiveLockScreenView.onKeyguardShowing(true); + } + } else { + mLiveLockScreenView = null; + } + } + }; + + private void updateLiveLockScreenView(final ComponentName cn) { + mHandler.post(new Runnable() { + @Override + public void run() { + // If mThirdPartyKeyguardViewComponent differs from cn, go ahead and update + if (!Objects.equals(mLiveLockScreenComponentName, cn)) { + mLiveLockScreenComponentName = cn; + mLlsName = getLlsNameFromComponentName(cn); + if (mLiveLockScreenView != null) { + mLiveLockScreenView.unregisterKeyguardExternalViewCallback( + mExternalKeyguardViewCallbacks); + // setProviderComponent(null) will unbind the existing service + mLiveLockScreenView.setProviderComponent(null); + if (mPanelView.indexOfChild(mLiveLockScreenView) >= 0) { + mLiveLockScreenView.registerOnWindowAttachmentChangedListener( + new KeyguardExternalView.OnWindowAttachmentChangedListener() { + @Override + public void onAttachedToWindow() { + } + + @Override + public void onDetachedFromWindow() { + mLiveLockScreenView + .unregisterOnWindowAttachmentChangedListener( + this); + mHandler.post(mAddNewLiveLockScreenRunnable); + } + } + ); + mPanelView.removeView(mLiveLockScreenView); + } else { + mAddNewLiveLockScreenRunnable.run(); + } + } + } + } + }); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index a8e977f..f7d6f85 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -279,13 +279,15 @@ public class MobileSignalController extends SignalController< } private boolean isRoaming() { - if (isCdma()) { + if (mServiceState == null) { + return false; + } else if (isCdma()) { final int iconMode = mServiceState.getCdmaEriIconMode(); return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH); } else { - return mServiceState != null && mServiceState.getRoaming(); + return mServiceState.getRoaming(); } } @@ -395,7 +397,8 @@ public class MobileSignalController extends SignalController< mCurrentState.iconGroup = mDefaultIcons; } mCurrentState.dataConnected = mCurrentState.connected - && mDataState == TelephonyManager.DATA_CONNECTED; + && mDataState == TelephonyManager.DATA_CONNECTED + && mCurrentState.dataSim; mCurrentState.showSeparateRoaming = false; if (isCarrierNetworkChangeActive()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WeatherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WeatherController.java index 1fa4956..0f71bcc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WeatherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WeatherController.java @@ -25,8 +25,9 @@ public interface WeatherController { void onWeatherChanged(WeatherInfo temp); } public static class WeatherInfo { - public String temp = null; + public double temp = Double.NaN; public String city = null; public String condition = null; + public int tempUnit; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WeatherControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WeatherControllerImpl.java index 288bc7e..1a798f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WeatherControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WeatherControllerImpl.java @@ -16,43 +16,49 @@ package com.android.systemui.statusbar.policy; -import android.content.BroadcastReceiver; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Handler; -import android.provider.Settings; import android.util.Log; +import cyanogenmod.providers.CMSettings; +import cyanogenmod.providers.WeatherContract; +import cyanogenmod.weather.CMWeatherManager; +import cyanogenmod.weather.util.WeatherUtils; import java.util.ArrayList; +import static cyanogenmod.providers.WeatherContract.WeatherColumns.CURRENT_CITY; +import static cyanogenmod.providers.WeatherContract.WeatherColumns.CURRENT_CONDITION; +import static cyanogenmod.providers.WeatherContract.WeatherColumns.CURRENT_TEMPERATURE; +import static cyanogenmod.providers.WeatherContract.WeatherColumns.CURRENT_TEMPERATURE_UNIT; +import static cyanogenmod.providers.WeatherContract.WeatherColumns.TempUnit.CELSIUS; +import static cyanogenmod.providers.WeatherContract.WeatherColumns.TempUnit.FAHRENHEIT; + public class WeatherControllerImpl implements WeatherController { private static final String TAG = WeatherController.class.getSimpleName(); private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private WeatherContentObserver mWeatherContentObserver; + private Handler mHandler; + private int mWeatherUnit; + private Uri mWeatherTempetarureUri; public static final ComponentName COMPONENT_WEATHER_FORECAST = new ComponentName( "com.cyanogenmod.lockclock", "com.cyanogenmod.lockclock.weather.ForecastActivity"); - public static final String ACTION_UPDATE_FINISHED - = "com.cyanogenmod.lockclock.action.WEATHER_UPDATE_FINISHED"; - public static final String EXTRA_UPDATE_CANCELLED = "update_cancelled"; public static final String ACTION_FORCE_WEATHER_UPDATE = "com.cyanogenmod.lockclock.action.FORCE_WEATHER_UPDATE"; - public static final Uri CURRENT_WEATHER_URI - = Uri.parse("content://com.cyanogenmod.lockclock.weather.provider/weather/current"); - public static final String[] WEATHER_PROJECTION = new String[]{ - "temperature", - "city", - "condition" + private static final String[] WEATHER_PROJECTION = new String[]{ + CURRENT_TEMPERATURE, + CURRENT_TEMPERATURE_UNIT, + CURRENT_CITY, + CURRENT_CONDITION }; private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); - private final Receiver mReceiver = new Receiver(); private final Context mContext; private WeatherInfo mCachedInfo = new WeatherInfo(); @@ -60,10 +66,16 @@ public class WeatherControllerImpl implements WeatherController { public WeatherControllerImpl(Context context) { mContext = context; mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + mHandler = new Handler(); + mWeatherContentObserver = new WeatherContentObserver(mHandler); + mWeatherTempetarureUri + = CMSettings.Global.getUriFor(CMSettings.Global.WEATHER_TEMPERATURE_UNIT); + mContext.getContentResolver().registerContentObserver( + WeatherContract.WeatherColumns.CURRENT_WEATHER_URI,true, mWeatherContentObserver); + mContext.getContentResolver().registerContentObserver(mWeatherTempetarureUri, true, + mWeatherContentObserver); + queryWeatherTempUnit(); queryWeather(); - final IntentFilter filter = new IntentFilter(); - filter.addAction(ACTION_UPDATE_FINISHED); - mContext.registerReceiver(mReceiver, filter); } public void addCallback(Callback callback) { @@ -85,17 +97,29 @@ public class WeatherControllerImpl implements WeatherController { } private void queryWeather() { - Cursor c = mContext.getContentResolver().query(CURRENT_WEATHER_URI, WEATHER_PROJECTION, + Cursor c = mContext.getContentResolver().query( + WeatherContract.WeatherColumns.CURRENT_WEATHER_URI, WEATHER_PROJECTION, null, null, null); if (c == null) { if(DEBUG) Log.e(TAG, "cursor was null for temperature, forcing weather update"); + //LockClock keeps track of the user settings (temp unit, search by geo location/city) + //so we delegate the responsibility of handling a weather update to LockClock mContext.sendBroadcast(new Intent(ACTION_FORCE_WEATHER_UPDATE)); } else { try { c.moveToFirst(); - mCachedInfo.temp = c.getString(0); - mCachedInfo.city = c.getString(1); - mCachedInfo.condition = c.getString(2); + double temp = c.getDouble(0); + int reportedUnit = c.getInt(1); + if (reportedUnit == CELSIUS && mWeatherUnit == FAHRENHEIT) { + temp = WeatherUtils.celsiusToFahrenheit(temp); + } else if (reportedUnit == FAHRENHEIT && mWeatherUnit == CELSIUS) { + temp = WeatherUtils.fahrenheitToCelsius(temp); + } + + mCachedInfo.temp = temp; + mCachedInfo.tempUnit = mWeatherUnit; + mCachedInfo.city = c.getString(2); + mCachedInfo.condition = c.getString(3); } finally { c.close(); } @@ -108,19 +132,53 @@ public class WeatherControllerImpl implements WeatherController { } } - private final class Receiver extends BroadcastReceiver { + private class WeatherContentObserver extends ContentObserver { + + public WeatherContentObserver(Handler handler) { + super(handler); + } + @Override - public void onReceive(Context context, Intent intent) { - if (DEBUG) Log.d(TAG, "onReceive " + intent.getAction()); - if (intent.hasExtra(EXTRA_UPDATE_CANCELLED)) { - if (intent.getBooleanExtra(EXTRA_UPDATE_CANCELLED, false)) { - // no update - return; + public void onChange(boolean selfChange, Uri uri) { + if (uri != null) { + if (uri.compareTo(WeatherContract.WeatherColumns.CURRENT_WEATHER_URI) == 0) { + queryWeather(); + fireCallback(); + } else if (uri.compareTo(mWeatherTempetarureUri) == 0) { + queryWeatherTempUnit(); + fixCachedWeatherInfo(); + fireCallback(); + } else { + super.onChange(selfChange, uri); } } - queryWeather(); - fireCallback(); + } + + @Override + public void onChange(boolean selfChange) { + onChange(selfChange, null); } } + private void queryWeatherTempUnit() { + try { + mWeatherUnit = CMSettings.Global.getInt(mContext.getContentResolver(), + CMSettings.Global.WEATHER_TEMPERATURE_UNIT); + } catch (CMSettings.CMSettingNotFoundException e) { + //CMSettingsProvider should have taken care of setting a default value for this setting + //so how is that we ended up here?? We need to set a valid temp unit anyway to keep + //this going + mWeatherUnit = WeatherContract.WeatherColumns.TempUnit.CELSIUS; + } + } + + private void fixCachedWeatherInfo() { + if (mCachedInfo.tempUnit == CELSIUS && mWeatherUnit == FAHRENHEIT) { + mCachedInfo.temp = WeatherUtils.celsiusToFahrenheit(mCachedInfo.temp); + mCachedInfo.tempUnit = FAHRENHEIT; + } else if (mCachedInfo.tempUnit == FAHRENHEIT && mWeatherUnit == CELSIUS) { + mCachedInfo.temp = WeatherUtils.fahrenheitToCelsius(mCachedInfo.temp); + mCachedInfo.tempUnit = CELSIUS; + } + } } 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 2f04b42..b6e131a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -50,6 +50,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.ViewLinker; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ScrollAdapter; @@ -63,7 +64,8 @@ import java.util.HashSet; */ public class NotificationStackScrollLayout extends ViewGroup implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter, - ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener { + ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener, + ViewLinker.ViewLinkerParent { private static final String TAG = "NotificationStackScrollLayout"; private static final boolean DEBUG = false; @@ -234,6 +236,7 @@ public class NotificationStackScrollLayout extends ViewGroup private boolean mForceNoOverlappingRendering; private NotificationOverflowContainer mOverflowContainer; private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>(); + private ViewLinker.ViewLinkerCallback mLinkerCallback; public NotificationStackScrollLayout(Context context) { this(context, null); @@ -2879,6 +2882,23 @@ public class NotificationStackScrollLayout extends ViewGroup return !mForceNoOverlappingRendering && super.hasOverlappingRendering(); } + @Override + public void registerLinker(ViewLinker.ViewLinkerCallback callback) { + mLinkerCallback = callback; + } + + @Override + public void setAlpha(float alpha) { + super.setAlpha(alpha); + mLinkerCallback.onAlphaChanged(alpha); + } + + @Override + public void setTranslationX(float translationX) { + super.setTranslationX(translationX); + mLinkerCallback.onTranslationXChanged(translationX); + } + /** * A listener that is notified when some child locations might have changed. */ diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java index 9d63d08..53fbef7 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java @@ -163,7 +163,7 @@ public class QsTuner extends Fragment implements Callback { public CustomHost(Context context) { super(context, null, null, null, null, null, null, null, null, null, - null, null, new BlankSecurityController()); + null, null, new BlankSecurityController(), null); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java index b2c90be..3e7477c 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java @@ -132,6 +132,12 @@ public class TunerService extends SystemUI { public void reloadSetting(Uri uri) { String key = mListeningUris.get(uri); + + // Handle possible null keys + if (TextUtils.isEmpty(key)) { + return; + } + String value; if (uri.getAuthority().equals(CMSettings.AUTHORITY)) { value = CMSettings.Secure.getStringForUser(mContentResolver, key, mCurrentUser); diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java index 7d34cdc..baa5321 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java +++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java @@ -327,7 +327,7 @@ public class StorageNotification extends SystemUI { // Don't annoy when user dismissed in past. (But make sure the disk is adoptable; we // used to allow snoozing non-adoptable disks too.) - if (rec.isSnoozed() && disk.isAdoptable()) { + if (rec == null || (rec.isSnoozed() && disk.isAdoptable())) { return null; } @@ -364,6 +364,11 @@ public class StorageNotification extends SystemUI { .setContentIntent(browseIntent) .setCategory(Notification.CATEGORY_SYSTEM) .setPriority(Notification.PRIORITY_LOW); + // USB disks notification can be persistent + if (disk.isUsb()) { + builder.setOngoing(mContext.getResources().getBoolean( + R.bool.config_persistUsbDriveNotification)); + } // Non-adoptable disks can't be snoozed. if (disk.isAdoptable()) { builder.setDeleteIntent(buildSnoozeIntent(vol.getFsUuid())); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java index 27c6601..9fda531 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java @@ -106,7 +106,6 @@ public class VolumeDialog { private final SpTexts mSpTexts; private final SparseBooleanArray mDynamic = new SparseBooleanArray(); private final KeyguardManager mKeyguard; - private final AudioManager mAudioManager; private final int mExpandButtonAnimationDuration; private final ZenFooter mZenFooter; private final LayoutTransition mLayoutTransition; @@ -139,7 +138,6 @@ public class VolumeDialog { mCallback = callback; mSpTexts = new SpTexts(mContext); mKeyguard = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); - mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mDialog = new CustomDialog(mContext); @@ -666,8 +664,7 @@ public class VolumeDialog { private void updateFooterH() { if (D.BUG) Log.d(TAG, "updateFooterH"); final boolean wasVisible = mZenFooter.getVisibility() == View.VISIBLE; - final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF - && mAudioManager.isStreamAffectedByRingerMode(mActiveStream); + final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF; if (wasVisible != visible && !visible) { prepareForCollapse(); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java index 9494e27..9269c1c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java @@ -371,6 +371,7 @@ public class VolumeDialogController { updateZenModeW(); updateEffectsSuppressorW(mNoMan.getEffectsSuppressor()); updateZenModeConfigW(); + updateLinkNotificationConfigW(); mCallbacks.onStateChanged(mState); } |