diff options
Diffstat (limited to 'packages/SystemUI/src')
46 files changed, 1707 insertions, 968 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java index 198a4e6cedb8..b1463a3c53ea 100644 --- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java +++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java @@ -143,9 +143,6 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { mSeparatedView.setBackground(mSeparatedViewBackground); updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding()); mOldHeight = mList.getMeasuredHeight(); - mList.addOnLayoutChangeListener( - (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> - updatePosition()); updateRotation(); } else { return; @@ -155,6 +152,8 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { if (newHeight != mOldHeight) { animateChild(mOldHeight, newHeight); } + + post(() -> updatePaddingAndGravityIfTooTall()); post(() -> updatePosition()); } @@ -241,7 +240,7 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { separatedViewLayoutParams.gravity = rotateGravityRight(separatedViewLayoutParams.gravity); mSeparatedView.setLayoutParams(separatedViewLayoutParams); - setGravity(p.gravity); + setGravity(rotateGravityRight(getGravity())); } private void swapDimens(View v) { @@ -299,7 +298,7 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { separatedViewLayoutParams.gravity = rotateGravityLeft(separatedViewLayoutParams.gravity); mSeparatedView.setLayoutParams(separatedViewLayoutParams); - setGravity(p.gravity); + setGravity(rotateGravityLeft(getGravity())); } private int rotateGravityLeft(int gravity) { @@ -447,6 +446,46 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { mAnimator.start(); } + // If current power menu height larger then screen height, remove padding to break power menu + // alignment and set menu center vertical within the screen. + private void updatePaddingAndGravityIfTooTall() { + int defaultTopPadding; + int viewsTotalHeight; + int separatedViewTopMargin; + int screenHeight; + int totalHeight; + int targetGravity; + MarginLayoutParams params = (MarginLayoutParams) mSeparatedView.getLayoutParams(); + switch (RotationUtils.getRotation(getContext())) { + case RotationUtils.ROTATION_LANDSCAPE: + defaultTopPadding = getPaddingLeft(); + viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth(); + separatedViewTopMargin = mHasSeparatedButton ? params.leftMargin : 0; + screenHeight = getMeasuredWidth(); + targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.TOP; + break; + case RotationUtils.ROTATION_SEASCAPE: + defaultTopPadding = getPaddingRight(); + viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth(); + separatedViewTopMargin = mHasSeparatedButton ? params.leftMargin : 0; + screenHeight = getMeasuredWidth(); + targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM; + break; + default: // Portrait + defaultTopPadding = getPaddingTop(); + viewsTotalHeight = mList.getMeasuredHeight() + mSeparatedView.getMeasuredHeight(); + separatedViewTopMargin = mHasSeparatedButton ? params.topMargin : 0; + screenHeight = getMeasuredHeight(); + targetGravity = Gravity.CENTER_VERTICAL|Gravity.RIGHT; + break; + } + totalHeight = defaultTopPadding + viewsTotalHeight + separatedViewTopMargin; + if (totalHeight >= screenHeight) { + setPadding(0, 0, 0, 0); + setGravity(targetGravity); + } + } + @Override public ViewOutlineProvider getOutlineProvider() { return super.getOutlineProvider(); diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index d351c4f3e3e6..1bf87506ab0c 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -259,6 +259,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis @Override public void onServiceConnected(ComponentName name, IBinder service) { mHandler.removeCallbacks(mDeferredConnectionCallback); + mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser(); mConnectionBackoffAttempts = 0; mOverviewProxy = IOverviewProxy.Stub.asInterface(service); // Listen for launcher's death @@ -269,7 +270,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } try { mOverviewProxy.onBind(mSysUiProxy); - mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser(); } catch (RemoteException e) { mCurrentBoundedUserId = -1; Log.e(TAG_OPS, "Failed to call onBind()", e); diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 4d24d82bd7ee..3007b6e68b78 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -736,14 +736,19 @@ public class ScreenDecorations extends SystemUI implements Tunable { switch (gravity) { case Gravity.TOP: out.set(displayCutout.getBoundingRectTop()); + break; case Gravity.LEFT: out.set(displayCutout.getBoundingRectLeft()); + break; case Gravity.BOTTOM: out.set(displayCutout.getBoundingRectBottom()); + break; case Gravity.RIGHT: out.set(displayCutout.getBoundingRectRight()); + break; + default: + out.setEmpty(); } - out.setEmpty(); } private void localBounds(Rect out) { diff --git a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java index 6d790668995e..449ed8c3bcdb 100644 --- a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java @@ -14,7 +14,7 @@ package com.android.systemui; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; +import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import android.app.Activity; import android.app.AlertDialog; @@ -69,7 +69,7 @@ public class SlicePermissionActivity extends Activity implements OnClickListener .setPositiveButton(R.string.slice_permission_allow, this) .setOnDismissListener(this) .create(); - dialog.getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + dialog.getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); dialog.show(); TextView t1 = dialog.getWindow().getDecorView().findViewById(R.id.text1); t1.setText(getString(R.string.slice_permission_text_1, app2)); diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 0215fda81485..3fe99445f49d 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -87,6 +87,8 @@ public class SwipeHelper implements Gefingerpoken { private Runnable mWatchLongPress; private final long mLongPressTimeout; + protected boolean mSwipingInProgress; + final private int[] mTmpPos = new int[2]; private final int mFalsingThreshold; private boolean mTouchAboveFalsingThreshold; @@ -127,6 +129,10 @@ public class SwipeHelper implements Gefingerpoken { mDisableHwLayers = disableHwLayers; } + public boolean isSwipingInProgress() { + return mSwipingInProgress; + } + private float getPos(MotionEvent ev) { return mSwipeDirection == X ? ev.getX() : ev.getY(); } @@ -318,6 +324,7 @@ public class SwipeHelper implements Gefingerpoken { if (Math.abs(delta) > mPagingTouchSlop && Math.abs(delta) > Math.abs(deltaPerpendicular)) { if (mCallback.canChildBeDragged(mCurrView)) { + mSwipingInProgress = true; mCallback.onBeginDrag(mCurrView); mDragging = true; mInitialTouchPos = getPos(ev); @@ -437,6 +444,7 @@ public class SwipeHelper implements Gefingerpoken { wasRemoved = row.isRemoved(); } if (!mCancelled || wasRemoved) { + mSwipingInProgress = false; mCallback.onChildDismissed(animView); } if (endAction != null) { @@ -626,6 +634,7 @@ public class SwipeHelper implements Gefingerpoken { !swipedFastEnough() /* useAccelerateInterpolator */); } else { // snappity + mSwipingInProgress = false; mCallback.onDragCancelled(mCurrView); snapChild(mCurrView, 0 /* leftTarget */, velocity); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 701394763fbd..77f7ad4f3a9e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -16,6 +16,8 @@ package com.android.systemui.doze; +import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN; + import android.annotation.AnyThread; import android.app.ActivityManager; import android.app.AlarmManager; @@ -39,8 +41,10 @@ import android.util.Log; import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.systemui.plugins.SensorManagerPlugin; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.AlarmTimeout; +import com.android.systemui.util.AsyncSensorManager; import com.android.systemui.util.wakelock.WakeLock; import java.io.PrintWriter; @@ -112,8 +116,8 @@ public class DozeSensors { DozeLog.PULSE_REASON_SENSOR_LONG_PRESS, true /* reports touch coordinates */, true /* touchscreen */), - new TriggerSensor( - findSensorWithType(config.wakeLockScreenSensorType()), + new PluginTriggerSensor( + new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN), Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, true /* configured */, DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, @@ -375,7 +379,7 @@ public class DozeSensors { mHandler.post(mWakeLock.wrap(() -> { if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event)); boolean sensorPerformsProxCheck = false; - if (mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) { + if (mSensor != null && mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) { int subType = (int) event.values[0]; MetricsLogger.action( mContext, MetricsProto.MetricsEvent.ACTION_AMBIENT_GESTURE, @@ -418,6 +422,49 @@ public class DozeSensors { } } + /** + * A Sensor that is injected via plugin. + */ + private class PluginTriggerSensor extends TriggerSensor { + + private final SensorManagerPlugin.Sensor mPluginSensor; + private final SensorManagerPlugin.TriggerEventListener mTriggerEventListener = (event) -> { + onTrigger(null); + }; + + PluginTriggerSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured, + int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen) { + super(null, setting, configured, pulseReason, reportsTouchCoordinates, + requiresTouchscreen); + mPluginSensor = sensor; + } + + @Override + public void updateListener() { + if (!mConfigured) return; + AsyncSensorManager asyncSensorManager = (AsyncSensorManager) mSensorManager; + if (mRequested && !mDisabled && enabledBySetting() && !mRegistered) { + asyncSensorManager.requestPluginTriggerSensor(mPluginSensor, mTriggerEventListener); + mRegistered = true; + if (DEBUG) Log.d(TAG, "requestPluginTriggerSensor"); + } else if (mRegistered) { + asyncSensorManager.cancelPluginTriggerSensor(mPluginSensor, mTriggerEventListener); + mRegistered = false; + if (DEBUG) Log.d(TAG, "cancelPluginTriggerSensor"); + } + } + + @Override + public String toString() { + return new StringBuilder("{mRegistered=").append(mRegistered) + .append(", mRequested=").append(mRequested) + .append(", mDisabled=").append(mDisabled) + .append(", mConfigured=").append(mConfigured) + .append(", mSensor=").append(mPluginSensor).append("}").toString(); + } + + } + private class WakeScreenSensor extends TriggerSensor { WakeScreenSensor() { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java index 4a6786832df0..df763151cdd7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java @@ -16,7 +16,7 @@ package com.android.systemui.media; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; +import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import android.app.Activity; import android.app.AlertDialog; @@ -151,7 +151,7 @@ public class MediaProjectionPermissionActivity extends Activity ((CheckBox) mDialog.findViewById(R.id.remember)).setOnCheckedChangeListener(this); final Window w = mDialog.getWindow(); w.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); - w.addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + w.addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); mDialog.show(); } diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java index 774567ef8bb1..95029c013ab6 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java @@ -26,6 +26,10 @@ import com.android.systemui.shared.plugins.PluginManagerImpl; public class PluginInitializerImpl implements PluginInitializer { + /** + * True if WTFs should lead to crashes + */ + private static final boolean WTFS_SHOULD_CRASH = false; private boolean mWtfsSet; @Override @@ -52,7 +56,7 @@ public class PluginInitializerImpl implements PluginInitializer { @Override public void handleWtfs() { - if (!mWtfsSet) { + if (WTFS_SHOULD_CRASH && !mWtfsSet) { mWtfsSet = true; Log.setWtfHandler(new Log.TerribleFailureHandler() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 92f5cae4e165..15d2e66a82ce 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -308,6 +308,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta @Override public void onClick(View v) { int position = holder.getAdapterPosition(); + if (position == RecyclerView.NO_POSITION) return; if (mAccessibilityAction != ACTION_NONE) { selectPosition(position, v); } else { @@ -561,6 +562,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta if (viewHolder == mCurrentDrag) return; if (mCurrentDrag != null) { int position = mCurrentDrag.getAdapterPosition(); + if (position == RecyclerView.NO_POSITION) return; TileInfo info = mTiles.get(position); mCurrentDrag.mTileView.setShowAppLabel( position > mEditIndex && !info.isSystem); @@ -582,13 +584,14 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta @Override public boolean canDropOver(RecyclerView recyclerView, ViewHolder current, ViewHolder target) { - if (target.getAdapterPosition() == 0){ + final int position = target.getAdapterPosition(); + if (position == 0 || position == RecyclerView.NO_POSITION){ return false; } if (!canRemoveTiles() && current.getAdapterPosition() < mEditIndex) { - return target.getAdapterPosition() < mEditIndex; + return position < mEditIndex; } - return target.getAdapterPosition() <= mEditIndex + 1; + return position <= mEditIndex + 1; } @Override @@ -610,6 +613,10 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) { int from = viewHolder.getAdapterPosition(); int to = target.getAdapterPosition(); + if (from == 0 || from == RecyclerView.NO_POSITION || + to == 0 || to == RecyclerView.NO_POSITION) { + return false; + } return move(from, to, target.itemView); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index ed78048c8746..921db6901626 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -46,6 +46,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.NetworkController; import java.util.LinkedHashMap; import java.util.Set; @@ -58,16 +59,18 @@ public class CastTile extends QSTileImpl<BooleanState> { private final CastController mController; private final CastDetailAdapter mDetailAdapter; private final KeyguardMonitor mKeyguard; + private final NetworkController mNetworkController; private final Callback mCallback = new Callback(); private final ActivityStarter mActivityStarter; private Dialog mDialog; - private boolean mRegistered; + private boolean mWifiConnected; public CastTile(QSHost host) { super(host); mController = Dependency.get(CastController.class); mDetailAdapter = new CastDetailAdapter(); mKeyguard = Dependency.get(KeyguardMonitor.class); + mNetworkController = Dependency.get(NetworkController.class); mActivityStarter = Dependency.get(ActivityStarter.class); } @@ -87,10 +90,12 @@ public class CastTile extends QSTileImpl<BooleanState> { if (listening) { mController.addCallback(mCallback); mKeyguard.addCallback(mCallback); + mNetworkController.addCallback(mSignalCallback); } else { mController.setDiscovering(false); mController.removeCallback(mCallback); mKeyguard.removeCallback(mCallback); + mNetworkController.removeCallback(mSignalCallback); } } @@ -112,6 +117,9 @@ public class CastTile extends QSTileImpl<BooleanState> { @Override protected void handleClick() { + if (getState().state == Tile.STATE_UNAVAILABLE) { + return; + } if (mKeyguard.isSecure() && !mKeyguard.canSkipBouncer()) { mActivityStarter.postQSRunnableDismissingKeyguard(() -> { showDetail(true); @@ -164,13 +172,22 @@ public class CastTile extends QSTileImpl<BooleanState> { if (!state.value && connecting) { state.label = mContext.getString(R.string.quick_settings_connecting); } - state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; state.icon = ResourceIcon.get(state.value ? R.drawable.ic_qs_cast_on : R.drawable.ic_qs_cast_off); + if (mWifiConnected) { + state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; + state.secondaryLabel = ""; + state.contentDescription = state.contentDescription + "," + + mContext.getString(R.string.accessibility_quick_settings_open_details); + state.expandedAccessibilityClassName = Button.class.getName(); + } else { + state.state = Tile.STATE_UNAVAILABLE; + String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi); + state.secondaryLabel = noWifi; + state.contentDescription = state.contentDescription + ", " + mContext.getString( + R.string.accessibility_quick_settings_not_available, noWifi); + } mDetailAdapter.updateItems(devices); - state.expandedAccessibilityClassName = Button.class.getName(); - state.contentDescription = state.contentDescription + "," - + mContext.getString(R.string.accessibility_quick_settings_open_details); } @Override @@ -192,6 +209,22 @@ public class CastTile extends QSTileImpl<BooleanState> { : mContext.getString(R.string.quick_settings_cast_device_default_name); } + private final NetworkController.SignalCallback mSignalCallback = + new NetworkController.SignalCallback() { + @Override + public void setWifiIndicators(boolean enabled, + NetworkController.IconState statusIcon, + NetworkController.IconState qsIcon, boolean activityIn, boolean activityOut, + String description, boolean isTransient, String statusLabel) { + // statusIcon.visible has the connected status information + boolean enabledAndConnected = enabled && qsIcon.visible; + if (enabledAndConnected != mWifiConnected) { + mWifiConnected = enabledAndConnected; + refreshState(); + } + } + }; + private final class Callback implements CastController.Callback, KeyguardMonitor.Callback { @Override public void onCastDevicesChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java index 2c384d0f4d80..21a33b0271db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT; + import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; @@ -85,6 +87,7 @@ public final class AmbientPulseManager extends AlertingNotificationManager { for (OnAmbientChangedListener listener : mListeners) { listener.onAmbientStateChanged(entry, false); } + entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 0c5f39198b4f..a00eac4adea0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -19,14 +19,14 @@ package com.android.systemui.statusbar; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.admin.DevicePolicyManager; -import android.hardware.biometrics.BiometricSourceType; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Resources; import android.content.res.ColorStateList; +import android.content.res.Resources; import android.graphics.Color; +import android.hardware.biometrics.BiometricSourceType; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.BatteryManager; @@ -106,6 +106,7 @@ public class KeyguardIndicationController { private final DevicePolicyManager mDevicePolicyManager; private boolean mDozing; + private float mDarkAmount; /** * Creates a new KeyguardIndicationController and registers callbacks. @@ -298,6 +299,15 @@ public class KeyguardIndicationController { if (mVisible) { // Walk down a precedence-ordered list of what indication // should be shown based on user or device state + if (mDozing) { + if (!TextUtils.isEmpty(mTransientIndication)) { + mTextView.setTextColor(Color.WHITE); + mTextView.switchIndication(mTransientIndication); + } + updateAlphas(); + return; + } + KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); int userId = KeyguardUpdateMonitor.getCurrentUser(); String trustGrantedIndication = getTrustGrantedIndication(); @@ -335,6 +345,14 @@ public class KeyguardIndicationController { } } + private void updateAlphas() { + if (!TextUtils.isEmpty(mTransientIndication)) { + mTextView.setAlpha(1f); + } else { + mTextView.setAlpha(1f - mDarkAmount); + } + } + // animates textView - textView moves up and bounces down private void animateText(KeyguardIndicationTextView textView, String indication) { int yTranslation = mContext.getResources().getInteger( @@ -492,6 +510,14 @@ public class KeyguardIndicationController { pw.println(" computePowerIndication(): " + computePowerIndication()); } + public void setDarkAmount(float darkAmount) { + if (mDarkAmount == darkAmount) { + return; + } + mDarkAmount = darkAmount; + updateAlphas(); + } + protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback { public static final int HIDE_DELAY_MS = 5000; private int mLastSuccessiveErrorMessage = -1; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index e89e6e89bc07..2db99453e36c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -256,9 +256,9 @@ public class NotificationMediaManager implements Dumpable { private boolean isMediaNotification(NotificationData.Entry entry) { // TODO: confirm that there's a valid media key - return entry.getExpandedContentView() != null && - entry.getExpandedContentView() - .findViewById(com.android.internal.R.id.media_actions) != null; + return entry.row.getExpandedContentView() != null + && entry.row.getExpandedContentView().findViewById( + com.android.internal.R.id.media_actions) != null; } private void clearCurrentMediaNotificationSession() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index f69ad43ed79c..5b3082b04d58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -327,6 +327,17 @@ public class NotificationViewHierarchyManager { entry.notification) && !entry.row.isRemoved(); boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry .notification); + if (!showOnKeyguard) { + // min priority notifications should show if their summary is showing + if (mGroupManager.isChildInGroupWithSummary(entry.notification)) { + ExpandableNotificationRow summary = mGroupManager.getLogicalGroupSummary( + entry.notification); + if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard( + summary.getStatusBarNotification())) { + showOnKeyguard = true; + } + } + } if (suppressedSummary || mLockscreenUserManager.shouldHideNotifications(userId) || (isLocked && !showOnKeyguard)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java index d6719f0a03e1..78a5817c32b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java @@ -22,6 +22,7 @@ import android.annotation.IntDef; import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.GuardedBy; +import com.android.systemui.statusbar.phone.StatusBar; import java.lang.annotation.Retention; import java.util.ArrayList; import java.util.Comparator; @@ -39,6 +40,7 @@ public class StatusBarStateController { = (o1, o2) -> Integer.compare(o1.rank, o2.rank); private final ArrayList<RankedListener> mListeners = new ArrayList<>(); + private boolean mIsDozing; private int mState; private int mLastState; private boolean mLeaveOpenOnKeyguardHide; @@ -57,6 +59,11 @@ public class StatusBarStateController { return mState; } + /** + * Update the status bar state + * @param state see {@link StatusBarState} for valid options + * @return {@code true} if the state changed, else {@code false} + */ public boolean setState(int state) { if (state > MAX_STATE || state < MIN_STATE) { throw new IllegalArgumentException("Invalid state " + state); @@ -82,6 +89,32 @@ public class StatusBarStateController { return true; } + public boolean isDozing() { + return mIsDozing; + } + + /** + * Update the dozing state from {@link StatusBar}'s perspective + * @param isDozing well, are we dozing? + * @return {@code true} if the state changed, else {@code false} + */ + @SuppressWarnings("UnusedReturnValue") + public boolean setIsDozing(boolean isDozing) { + if (mIsDozing == isDozing) { + return false; + } + + mIsDozing = isDozing; + + synchronized (mListeners) { + for (RankedListener rl : new ArrayList<>(mListeners)) { + rl.listener.onDozingChanged(isDozing); + } + } + + return true; + } + public boolean goingToFullShade() { return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide; } @@ -144,23 +177,49 @@ public class StatusBarStateController { return StatusBarState.toShortString(state); } + private class RankedListener { + private final StateListener listener; + private final int rank; + + private RankedListener(StateListener l, int r) { + listener = l; + rank = r; + } + } + + /** + * Listener for StatusBarState updates + */ public interface StateListener { + + /** + * Callback before the new state is applied, for those who need to preempt the change + * @param oldState state before the change + * @param newState new state to be applied in {@link #onStateChanged} + */ public default void onStatePreChange(int oldState, int newState) { } + /** + * Callback after all listeners have had a chance to update based on the state change + */ public default void onStatePostChange() { } + /** + * Required callback. Get the new state and do what you will with it. Keep in mind that + * other listeners are typically unordered and don't rely on your work being done before + * other peers + * + * Only called if the state is actually different + * @param newState the new {@link StatusBarState} + */ public void onStateChanged(int newState); - } - - private class RankedListener { - private final StateListener listener; - private final int rank; - private RankedListener(StateListener l, int r) { - listener = l; - rank = r; - } + /** + * Callback to be notified when Dozing changes. Dozing is stored separately from state. + * @param isDozing {@code true} if dozing according to {@link StatusBar} + */ + public default void onDozingChanged(boolean isDozing) {} } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 2450e448c4f7..24665eac76a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -491,6 +491,10 @@ public class CarStatusBar extends StatusBar implements @Override public void onStateChanged(int newState) { super.onStateChanged(newState); + if (mFullscreenUserSwitcher == null) { + return; // Not using the full screen user switcher. + } + if (newState == StatusBarState.FULLSCREEN_USER_SWITCHER) { if (!mFullscreenUserSwitcher.isVisible()) { // Current execution path continues to set state after this, thus we deffer the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java index d097c8e706ba..fbf12ed39561 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java @@ -50,7 +50,6 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.view.View; import android.widget.ImageView; -import android.widget.RemoteViews; import androidx.annotation.Nullable; @@ -102,11 +101,6 @@ public class NotificationData { public boolean autoRedacted; // whether the redacted notification was generated by us public int targetSdk; private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET; - public RemoteViews cachedContentView; - public RemoteViews cachedBigContentView; - public RemoteViews cachedHeadsUpContentView; - public RemoteViews cachedPublicContentView; - public RemoteViews cachedAmbientContentView; public CharSequence remoteInputText; public List<SnoozeCriterion> snoozeCriteria; public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL; @@ -178,14 +172,6 @@ public class NotificationData { } } - public View getExpandedContentView() { - return row.getPrivateLayout().getExpandedChild(); - } - - public View getPublicContentView() { - return row.getPublicLayout().getContractedChild(); - } - public void notifyFullScreenIntentLaunched() { setInterruption(); lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index a3e982e77522..28d339aaeab2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -18,6 +18,10 @@ package com.android.systemui.statusbar.notification; import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT; import static com.android.systemui.statusbar.NotificationRemoteInputManager .FORCE_REMOTE_INPUT_HISTORY; +import static com.android.systemui.statusbar.notification.row.NotificationInflater + .FLAG_CONTENT_VIEW_AMBIENT; +import static com.android.systemui.statusbar.notification.row.NotificationInflater + .FLAG_CONTENT_VIEW_HEADS_UP; import android.annotation.Nullable; import android.app.Notification; @@ -71,6 +75,7 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.NotificationUpdateHandler; import com.android.systemui.statusbar.notification.row.NotificationInflater; +import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; import com.android.systemui.statusbar.notification.row.RowInflaterTask; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -440,25 +445,48 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } private void addEntry(NotificationData.Entry shadeEntry) { - if (shouldHeadsUp(shadeEntry)) { - mHeadsUpManager.showNotification(shadeEntry); - // Mark as seen immediately - setNotificationShown(shadeEntry.notification); - } - if (shouldPulse(shadeEntry)) { - mAmbientPulseManager.showNotification(shadeEntry); - } addNotificationViews(shadeEntry); mCallback.onNotificationAdded(shadeEntry); } + /** + * Adds the entry to the respective alerting manager if the content view was inflated and + * the entry should still alert. + * + * @param entry entry to add + * @param inflatedFlags flags representing content views that were inflated + */ + private void showAlertingView(NotificationData.Entry entry, + @InflationFlag int inflatedFlags) { + if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) { + // Possible for shouldHeadsUp to change between the inflation starting and ending. + // If it does and we no longer need to heads up, we should free the view. + if (shouldHeadsUp(entry)) { + mHeadsUpManager.showNotification(entry); + // Mark as seen immediately + setNotificationShown(entry.notification); + } else { + entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP); + } + } + if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) { + if (shouldPulse(entry)) { + mAmbientPulseManager.showNotification(entry); + } else { + entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT); + } + } + } + @Override - public void onAsyncInflationFinished(NotificationData.Entry entry) { + public void onAsyncInflationFinished(NotificationData.Entry entry, + @InflationFlag int inflatedFlags) { mPendingNotifications.remove(entry.key); // If there was an async task started after the removal, we don't want to add it back to // the list, otherwise we might get leaks. boolean isNew = mNotificationData.get(entry.key) == null; if (isNew && !entry.row.isRemoved()) { + showAlertingView(entry, inflatedFlags); addEntry(entry); } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) { mVisualStabilityManager.onLowPriorityUpdated(entry); @@ -636,7 +664,11 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight); row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp); row.setSmartActions(entry.smartActions); - row.updateNotification(entry); + row.setEntry(entry); + + row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, shouldHeadsUp(entry)); + row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, shouldPulse(entry)); + row.inflateViews(); } protected void addNotificationViews(NotificationData.Entry entry) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java index 81208c4330c5..53ebe747c2e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java @@ -24,7 +24,10 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow public interface VisibilityLocationProvider { /** - * @return whether the view is in a visible location right now. + * Returns whether an ExpandableNotificationRow is in a visible location or not. + * + * @param row + * @return true if row is in a visible location */ boolean isInVisibleLocation(ExpandableNotificationRow row); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index bce613a33859..8110c1c98dec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -17,12 +17,19 @@ package com.android.systemui.statusbar.notification.row; import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; +import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT; +import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP; +import static com.android.systemui.statusbar.notification.row.NotificationInflater + .FLAG_CONTENT_VIEW_AMBIENT; +import static com.android.systemui.statusbar.notification.row.NotificationInflater + .FLAG_CONTENT_VIEW_HEADS_UP; import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationChannel; @@ -83,6 +90,7 @@ import com.android.systemui.statusbar.notification.AboveShelfChangedListener; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.logging.NotificationCounters; import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -94,6 +102,9 @@ import com.android.systemui.statusbar.notification.stack.ExpandableViewState; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer; import com.android.systemui.statusbar.notification.stack.StackScrollState; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.function.BooleanSupplier; @@ -429,12 +440,59 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } - public void updateNotification(NotificationData.Entry entry) { + /** + * Set the entry for the row. + * + * @param entry the entry this row is tied to + */ + public void setEntry(@NonNull NotificationData.Entry entry) { mEntry = entry; mStatusBarNotification = entry.notification; + cacheIsSystemNotification(); + } + + /** + * Inflate views based off the inflation flags set. Inflation happens asynchronously. + */ + public void inflateViews() { mNotificationInflater.inflateNotificationViews(); + } - cacheIsSystemNotification(); + /** + * Marks a content view as freeable, setting it so that future inflations do not reinflate + * and ensuring that the view is freed when it is safe to remove. + * + * @param inflationFlag flag corresponding to the content view to be freed + */ + public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) { + // View should not be reinflated in the future + updateInflationFlag(inflationFlag, false); + Runnable freeViewRunnable = () -> + mNotificationInflater.freeNotificationView(inflationFlag); + switch (inflationFlag) { + case FLAG_CONTENT_VIEW_HEADS_UP: + getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP, + freeViewRunnable); + break; + case FLAG_CONTENT_VIEW_AMBIENT: + getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_AMBIENT, + freeViewRunnable); + getPublicLayout().performWhenContentInactive(VISIBLE_TYPE_AMBIENT, + freeViewRunnable); + break; + default: + break; + } + } + + /** + * Update whether or not a content view should be inflated. + * + * @param flag the flag corresponding to the content view + * @param shouldInflate true if it should be inflated, false if it should not + */ + public void updateInflationFlag(@InflationFlag int flag, boolean shouldInflate) { + mNotificationInflater.updateInflationFlag(flag, shouldInflate); } /** @@ -581,7 +639,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView headsUpHeight = mMaxHeadsUpHeight; } NotificationViewWrapper headsUpWrapper = layout.getVisibleWrapper( - NotificationContentView.VISIBLE_TYPE_HEADSUP); + VISIBLE_TYPE_HEADSUP); if (headsUpWrapper != null) { headsUpHeight = Math.max(headsUpHeight, headsUpWrapper.getMinLayoutHeight()); } @@ -2616,6 +2674,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return shouldShowPublic() ? mPublicLayout : mPrivateLayout; } + public View getExpandedContentView() { + return getPrivateLayout().getExpandedChild(); + } + public void setLegacy(boolean legacy) { for (NotificationContentView l : mLayouts) { l.setLegacy(legacy); @@ -3017,6 +3079,36 @@ public class ExpandableNotificationRow extends ActivatableNotificationView boolean onClick(View v, int x, int y, MenuItem item); } + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + super.dump(fd, pw, args); + pw.println(" Notification: " + getStatusBarNotification().getKey()); + pw.print(" visibility: " + getVisibility()); + pw.print(", alpha: " + getAlpha()); + pw.print(", translation: " + getTranslation()); + pw.print(", removed: " + isRemoved()); + pw.print(", privateShowing: " + (getShowingLayout() == mPrivateLayout)); + pw.println(); + pw.print(" "); + if (mNotificationViewState != null) { + mNotificationViewState.dump(fd, pw, args); + } else { + pw.print("no viewState!!!"); + } + pw.println(); + pw.println(); + if (mIsSummaryWithChildren) { + List<ExpandableNotificationRow> notificationChildren = getNotificationChildren(); + pw.println(" Children: " + notificationChildren.size()); + pw.println(" {"); + for(ExpandableNotificationRow child : notificationChildren) { + child.dump(fd, pw, args); + } + pw.println(" }"); + pw.println(); + } + } + /** * Background task for executing IPCs to check if the notification is a system notification. The * output is used for both the blocking helper and the notification info. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index 46019e3b48ea..38d657b967a0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -25,16 +25,19 @@ import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import com.android.systemui.Dumpable; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.StackScrollState; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; /** * An abstract view for expandable views. */ -public abstract class ExpandableView extends FrameLayout { +public abstract class ExpandableView extends FrameLayout implements Dumpable { public static final float NO_ROUNDNESS = -1; protected OnHeightChangedListener mOnHeightChangedListener; @@ -559,6 +562,10 @@ public abstract class ExpandableView extends FrameLayout { return false; } + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + } + /** * A listener notifying when {@link #getActualHeight} changes. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 4ef8dbb19318..78564515a2c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -23,6 +23,7 @@ import android.content.Context; import android.graphics.Rect; import android.os.Build; import android.service.notification.StatusBarNotification; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; @@ -108,6 +109,10 @@ public class NotificationContentView extends FrameLayout { private NotificationGroupManager mGroupManager; private RemoteInputController mRemoteInputController; private Runnable mExpandedVisibleListener; + /** + * List of listeners for when content views become inactive (i.e. not the showing view). + */ + private final ArrayMap<View, Runnable> mOnContentViewInactiveListeners = new ArrayMap<>(); private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener = new ViewTreeObserver.OnPreDrawListener() { @@ -517,6 +522,14 @@ public class NotificationContentView extends FrameLayout { removeView(mAmbientChild); } if (child == null) { + mAmbientChild = null; + mAmbientWrapper = null; + if (mVisibleType == VISIBLE_TYPE_AMBIENT) { + mVisibleType = VISIBLE_TYPE_CONTRACTED; + } + if (mTransformationStartVisibleType == VISIBLE_TYPE_AMBIENT) { + mTransformationStartVisibleType = UNDEFINED; + } return; } addView(child); @@ -1163,6 +1176,7 @@ public class NotificationContentView extends FrameLayout { public void onNotificationUpdated(NotificationData.Entry entry) { mStatusBarNotification = entry.notification; + mOnContentViewInactiveListeners.clear(); mBeforeN = entry.targetSdk < Build.VERSION_CODES.N; updateAllSingleLineViews(); if (mContractedChild != null) { @@ -1620,6 +1634,58 @@ public class NotificationContentView extends FrameLayout { fireExpandedVisibleListenerIfVisible(); } + /** + * Set a one-shot listener to run when a given content view becomes inactive. + * + * @param visibleType visible type corresponding to the content view to listen + * @param listener runnable to run once when the content view becomes inactive + */ + public void performWhenContentInactive(int visibleType, Runnable listener) { + View view = getViewForVisibleType(visibleType); + // View is already inactive + if (view == null || isContentViewInactive(visibleType)) { + listener.run(); + return; + } + mOnContentViewInactiveListeners.put(view, listener); + } + + /** + * Whether or not the content view is inactive. This means it should not be visible + * or the showing content as removing it would cause visual jank. + * + * @param visibleType visible type corresponding to the content view to be removed + * @return true if the content view is inactive, false otherwise + */ + public boolean isContentViewInactive(int visibleType) { + View view = getViewForVisibleType(visibleType); + return isContentViewInactive(view); + } + + /** + * Whether or not the content view is inactive. + * + * @param view view to see if its inactive + * @return true if the view is inactive, false o/w + */ + private boolean isContentViewInactive(View view) { + if (view == null) { + return true; + } + return view.getVisibility() != VISIBLE && getViewForVisibleType(mVisibleType) != view; + } + + @Override + protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) { + super.onChildVisibilityChanged(child, oldVisibility, newVisibility); + if (isContentViewInactive(child)) { + Runnable listener = mOnContentViewInactiveListeners.remove(child); + if (listener != null) { + listener.run(); + } + } + } + public void setIsLowPriority(boolean isLowPriority) { mIsLowPriority = isLowPriority; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java index aa4765a349b4..ea1892be1b1f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java @@ -16,12 +16,17 @@ package com.android.systemui.statusbar.notification.row; +import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT; +import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP; + +import android.annotation.IntDef; import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.os.AsyncTask; import android.os.CancellationSignal; import android.service.notification.StatusBarNotification; +import android.util.ArrayMap; import android.util.Log; import android.view.View; import android.widget.RemoteViews; @@ -35,6 +40,8 @@ import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.Assert; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -52,14 +59,64 @@ import java.util.concurrent.atomic.AtomicInteger; public class NotificationInflater { public static final String TAG = "NotificationInflater"; - @VisibleForTesting - static final int FLAG_REINFLATE_ALL = ~0; - private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0; - @VisibleForTesting - static final int FLAG_REINFLATE_EXPANDED_VIEW = 1<<1; - private static final int FLAG_REINFLATE_HEADS_UP_VIEW = 1<<2; - private static final int FLAG_REINFLATE_PUBLIC_VIEW = 1<<3; - private static final int FLAG_REINFLATE_AMBIENT_VIEW = 1<<4; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, + prefix = {"FLAG_CONTENT_VIEW_"}, + value = { + FLAG_CONTENT_VIEW_CONTRACTED, + FLAG_CONTENT_VIEW_EXPANDED, + FLAG_CONTENT_VIEW_HEADS_UP, + FLAG_CONTENT_VIEW_AMBIENT, + FLAG_CONTENT_VIEW_PUBLIC, + FLAG_CONTENT_VIEW_ALL}) + public @interface InflationFlag {} + /** + * The default, contracted view. Seen when the shade is pulled down and in the lock screen + * if there is no worry about content sensitivity. + */ + public static final int FLAG_CONTENT_VIEW_CONTRACTED = 1; + + /** + * The expanded view. Seen when the user expands a notification. + */ + public static final int FLAG_CONTENT_VIEW_EXPANDED = 1 << 1; + + /** + * The heads up view. Seen when a high priority notification peeks in from the top. + */ + public static final int FLAG_CONTENT_VIEW_HEADS_UP = 1 << 2; + + /** + * The ambient view. Seen when a high priority notification is received and the phone + * is dozing. + */ + public static final int FLAG_CONTENT_VIEW_AMBIENT = 1 << 3; + + /** + * The public view. This is a version of the contracted view that hides sensitive + * information and is used on the lock screen if we determine that the notification's + * content should be hidden. + */ + public static final int FLAG_CONTENT_VIEW_PUBLIC = 1 << 4; + + public static final int FLAG_CONTENT_VIEW_ALL = ~0; + + /** + * Content views that must be inflated at all times. + */ + @InflationFlag + private static final int REQUIRED_INFLATION_FLAGS = + FLAG_CONTENT_VIEW_CONTRACTED + | FLAG_CONTENT_VIEW_EXPANDED + | FLAG_CONTENT_VIEW_PUBLIC; + + /** + * The set of content views to inflate. + */ + @InflationFlag + private int mInflationFlags = REQUIRED_INFLATION_FLAGS; + private static final InflationExecutor EXECUTOR = new InflationExecutor(); private final ExpandableNotificationRow mRow; @@ -71,6 +128,7 @@ public class NotificationInflater { private InflationCallback mCallback; private boolean mRedactAmbient; private List<Notification.Action> mSmartActions; + private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>(); public NotificationInflater(ExpandableNotificationRow row) { mRow = row; @@ -89,10 +147,10 @@ public class NotificationInflater { if (childInGroup != mIsChildInGroup) { mIsChildInGroup = childInGroup; if (mIsLowPriority) { - int flags = FLAG_REINFLATE_CONTENT_VIEW | FLAG_REINFLATE_EXPANDED_VIEW; + int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED; inflateNotificationViews(flags); } - } ; + } } public void setUsesIncreasedHeight(boolean usesIncreasedHeight) { @@ -117,38 +175,67 @@ public class NotificationInflater { if (mRow.getEntry() == null) { return; } - inflateNotificationViews(FLAG_REINFLATE_AMBIENT_VIEW); + inflateNotificationViews(FLAG_CONTENT_VIEW_AMBIENT); } } /** + * Set whether or not a particular content view is needed and whether or not it should be + * inflated. These flags will be used when we inflate or reinflate. + * + * @param flag the {@link InflationFlag} corresponding to the view that should/should not be + * inflated + * @param shouldInflate true if the view should be inflated, false otherwise + */ + public void updateInflationFlag(@InflationFlag int flag, boolean shouldInflate) { + if (shouldInflate) { + mInflationFlags |= flag; + } else if ((REQUIRED_INFLATION_FLAGS & flag) == 0) { + mInflationFlags &= ~flag; + } + } + + /** + * Add flags for which content views should be inflated in addition to those already set. + * + * @param flags a set of {@link InflationFlag} corresponding to content views that should be + * inflated + */ + public void addInflationFlags(@InflationFlag int flags) { + mInflationFlags |= flags; + } + + /** * Inflate all views of this notification on a background thread. This is asynchronous and will * notify the callback once it's finished. */ public void inflateNotificationViews() { - inflateNotificationViews(FLAG_REINFLATE_ALL); + inflateNotificationViews(mInflationFlags); } /** - * Reinflate all views for the specified flags on a background thread. This is asynchronous and - * will notify the callback once it's finished. + * Inflate all views for the specified flags on a background thread. This is asynchronous and + * will notify the callback once it's finished. If the content view is already inflated, this + * will reinflate it. * - * @param reInflateFlags flags which views should be reinflated. Use {@link #FLAG_REINFLATE_ALL} - * to reinflate all of views. + * @param reInflateFlags flags which views should be inflated. Should be a subset of + * {@link NotificationInflater#mInflationFlags} as only those will be + * inflated/reinflated. */ - @VisibleForTesting - void inflateNotificationViews(int reInflateFlags) { + private void inflateNotificationViews(@InflationFlag int reInflateFlags) { if (mRow.isRemoved()) { // We don't want to reinflate anything for removed notifications. Otherwise views might // be readded to the stack, leading to leaks. This may happen with low-priority groups // where the removal of already removed children can lead to a reinflation. return; } + // Only inflate the ones that are set. + reInflateFlags |= mInflationFlags; StatusBarNotification sbn = mRow.getEntry().notification; - AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mRow, - mIsLowPriority, - mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient, - mCallback, mRemoteViewClickHandler, mSmartActions); + AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mCachedContentViews, + mRow, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight, + mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler, + mSmartActions); if (mCallback != null && mCallback.doInflateSynchronous()) { task.onPostExecute(task.doInBackground()); } else { @@ -157,38 +244,80 @@ public class NotificationInflater { } @VisibleForTesting - InflationProgress inflateNotificationViews(int reInflateFlags, + InflationProgress inflateNotificationViews(@InflationFlag int reInflateFlags, Notification.Builder builder, Context packageContext) { InflationProgress result = createRemoteViews(reInflateFlags, builder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient, packageContext); - apply(result, reInflateFlags, mRow, mRedactAmbient, mRemoteViewClickHandler, null); + apply(result, reInflateFlags, mCachedContentViews, mRow, mRedactAmbient, + mRemoteViewClickHandler, null); return result; } - private static InflationProgress createRemoteViews(int reInflateFlags, + /** + * Frees the content view associated with the inflation flag. Will only succeed if the + * view is safe to remove. + * + * @param inflateFlag the flag corresponding to the content view which should be freed + */ + public void freeNotificationView(@InflationFlag int inflateFlag) { + if ((mInflationFlags & inflateFlag) != 0) { + // The view should still be inflated. + return; + } + switch (inflateFlag) { + case FLAG_CONTENT_VIEW_HEADS_UP: + if (mRow.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) { + mRow.getPrivateLayout().setHeadsUpChild(null); + mCachedContentViews.remove(FLAG_CONTENT_VIEW_HEADS_UP); + } + break; + case FLAG_CONTENT_VIEW_AMBIENT: + boolean privateSafeToRemove = mRow.getPrivateLayout().isContentViewInactive( + VISIBLE_TYPE_AMBIENT); + boolean publicSafeToRemove = mRow.getPublicLayout().isContentViewInactive( + VISIBLE_TYPE_AMBIENT); + if (privateSafeToRemove) { + mRow.getPrivateLayout().setAmbientChild(null); + } + if (publicSafeToRemove) { + mRow.getPublicLayout().setAmbientChild(null); + } + if (privateSafeToRemove && publicSafeToRemove) { + mCachedContentViews.remove(FLAG_CONTENT_VIEW_AMBIENT); + } + break; + case FLAG_CONTENT_VIEW_CONTRACTED: + case FLAG_CONTENT_VIEW_EXPANDED: + case FLAG_CONTENT_VIEW_PUBLIC: + default: + break; + } + } + + private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags, Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup, boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient, Context packageContext) { InflationProgress result = new InflationProgress(); isLowPriority = isLowPriority && !isChildInGroup; - if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) { + if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) { result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight); } - if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) { + if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) { result.newExpandedView = createExpandedView(builder, isLowPriority); } - if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) { + if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) { result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight); } - if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) { + if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) { result.newPublicView = builder.makePublicContentView(); } - if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) { + if ((reInflateFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) { result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification() : builder.makeAmbientNotification(); } @@ -199,18 +328,20 @@ public class NotificationInflater { return result; } - public static CancellationSignal apply(InflationProgress result, int reInflateFlags, + public static CancellationSignal apply(InflationProgress result, + @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews, ExpandableNotificationRow row, boolean redactAmbient, RemoteViews.OnClickHandler remoteViewClickHandler, @Nullable InflationCallback callback) { - NotificationData.Entry entry = row.getEntry(); NotificationContentView privateLayout = row.getPrivateLayout(); NotificationContentView publicLayout = row.getPublicLayout(); final HashMap<Integer, CancellationSignal> runningInflations = new HashMap<>(); - int flag = FLAG_REINFLATE_CONTENT_VIEW; + int flag = FLAG_CONTENT_VIEW_CONTRACTED; if ((reInflateFlags & flag) != 0) { - boolean isNewView = !canReapplyRemoteView(result.newContentView, entry.cachedContentView); + boolean isNewView = + !canReapplyRemoteView(result.newContentView, + cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED)); ApplyCallback applyCallback = new ApplyCallback() { @Override public void setResultView(View v) { @@ -222,18 +353,19 @@ public class NotificationInflater { return result.newContentView; } }; - applyRemoteView(result, reInflateFlags, flag, row, redactAmbient, - isNewView, remoteViewClickHandler, callback, entry, privateLayout, + applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, redactAmbient, + isNewView, remoteViewClickHandler, callback, privateLayout, privateLayout.getContractedChild(), privateLayout.getVisibleWrapper( NotificationContentView.VISIBLE_TYPE_CONTRACTED), runningInflations, applyCallback); } - flag = FLAG_REINFLATE_EXPANDED_VIEW; + flag = FLAG_CONTENT_VIEW_EXPANDED; if ((reInflateFlags & flag) != 0) { if (result.newExpandedView != null) { - boolean isNewView = !canReapplyRemoteView(result.newExpandedView, - entry.cachedBigContentView); + boolean isNewView = + !canReapplyRemoteView(result.newExpandedView, + cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED)); ApplyCallback applyCallback = new ApplyCallback() { @Override public void setResultView(View v) { @@ -245,8 +377,8 @@ public class NotificationInflater { return result.newExpandedView; } }; - applyRemoteView(result, reInflateFlags, flag, row, - redactAmbient, isNewView, remoteViewClickHandler, callback, entry, + applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, + redactAmbient, isNewView, remoteViewClickHandler, callback, privateLayout, privateLayout.getExpandedChild(), privateLayout.getVisibleWrapper( NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations, @@ -254,11 +386,12 @@ public class NotificationInflater { } } - flag = FLAG_REINFLATE_HEADS_UP_VIEW; + flag = FLAG_CONTENT_VIEW_HEADS_UP; if ((reInflateFlags & flag) != 0) { if (result.newHeadsUpView != null) { - boolean isNewView = !canReapplyRemoteView(result.newHeadsUpView, - entry.cachedHeadsUpContentView); + boolean isNewView = + !canReapplyRemoteView(result.newHeadsUpView, + cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP)); ApplyCallback applyCallback = new ApplyCallback() { @Override public void setResultView(View v) { @@ -270,19 +403,20 @@ public class NotificationInflater { return result.newHeadsUpView; } }; - applyRemoteView(result, reInflateFlags, flag, row, - redactAmbient, isNewView, remoteViewClickHandler, callback, entry, + applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, + redactAmbient, isNewView, remoteViewClickHandler, callback, privateLayout, privateLayout.getHeadsUpChild(), privateLayout.getVisibleWrapper( - NotificationContentView.VISIBLE_TYPE_HEADSUP), runningInflations, + VISIBLE_TYPE_HEADSUP), runningInflations, applyCallback); } } - flag = FLAG_REINFLATE_PUBLIC_VIEW; + flag = FLAG_CONTENT_VIEW_PUBLIC; if ((reInflateFlags & flag) != 0) { - boolean isNewView = !canReapplyRemoteView(result.newPublicView, - entry.cachedPublicContentView); + boolean isNewView = + !canReapplyRemoteView(result.newPublicView, + cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC)); ApplyCallback applyCallback = new ApplyCallback() { @Override public void setResultView(View v) { @@ -294,18 +428,19 @@ public class NotificationInflater { return result.newPublicView; } }; - applyRemoteView(result, reInflateFlags, flag, row, - redactAmbient, isNewView, remoteViewClickHandler, callback, entry, + applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, + redactAmbient, isNewView, remoteViewClickHandler, callback, publicLayout, publicLayout.getContractedChild(), publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED), runningInflations, applyCallback); } - flag = FLAG_REINFLATE_AMBIENT_VIEW; + flag = FLAG_CONTENT_VIEW_AMBIENT; if ((reInflateFlags & flag) != 0) { NotificationContentView newParent = redactAmbient ? publicLayout : privateLayout; - boolean isNewView = !canReapplyAmbient(row, redactAmbient) || - !canReapplyRemoteView(result.newAmbientView, entry.cachedAmbientContentView); + boolean isNewView = (!canReapplyAmbient(row, redactAmbient) + || !canReapplyRemoteView(result.newAmbientView, + cachedContentViews.get(FLAG_CONTENT_VIEW_AMBIENT))); ApplyCallback applyCallback = new ApplyCallback() { @Override public void setResultView(View v) { @@ -317,15 +452,15 @@ public class NotificationInflater { return result.newAmbientView; } }; - applyRemoteView(result, reInflateFlags, flag, row, - redactAmbient, isNewView, remoteViewClickHandler, callback, entry, + applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, + redactAmbient, isNewView, remoteViewClickHandler, callback, newParent, newParent.getAmbientChild(), newParent.getVisibleWrapper( NotificationContentView.VISIBLE_TYPE_AMBIENT), runningInflations, applyCallback); } // Let's try to finish, maybe nobody is even inflating anything - finishIfDone(result, reInflateFlags, runningInflations, callback, row, + finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, callback, row, redactAmbient); CancellationSignal cancellationSignal = new CancellationSignal(); cancellationSignal.setOnCancelListener( @@ -335,11 +470,11 @@ public class NotificationInflater { @VisibleForTesting static void applyRemoteView(final InflationProgress result, - final int reInflateFlags, int inflationId, - final ExpandableNotificationRow row, - final boolean redactAmbient, boolean isNewView, + final @InflationFlag int reInflateFlags, @InflationFlag int inflationId, + final ArrayMap<Integer, RemoteViews> cachedContentViews, + final ExpandableNotificationRow row, final boolean redactAmbient, boolean isNewView, RemoteViews.OnClickHandler remoteViewClickHandler, - @Nullable final InflationCallback callback, NotificationData.Entry entry, + @Nullable final InflationCallback callback, NotificationContentView parentLayout, View existingView, NotificationViewWrapper existingWrapper, final HashMap<Integer, CancellationSignal> runningInflations, @@ -362,7 +497,7 @@ public class NotificationInflater { existingWrapper.onReinflated(); } } catch (Exception e) { - handleInflationError(runningInflations, e, entry.notification, callback); + handleInflationError(runningInflations, e, row.getStatusBarNotification(), callback); // Add a running inflation to make sure we don't trigger callbacks. // Safe to do because only happens in tests. runningInflations.put(inflationId, new CancellationSignal()); @@ -381,8 +516,8 @@ public class NotificationInflater { existingWrapper.onReinflated(); } runningInflations.remove(inflationId); - finishIfDone(result, reInflateFlags, runningInflations, callback, row, - redactAmbient); + finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, + callback, row, redactAmbient); } @Override @@ -407,7 +542,8 @@ public class NotificationInflater { onViewApplied(newView); } catch (Exception anotherException) { runningInflations.remove(inflationId); - handleInflationError(runningInflations, e, entry.notification, callback); + handleInflationError(runningInflations, e, row.getStatusBarNotification(), + callback); } } }; @@ -430,8 +566,9 @@ public class NotificationInflater { runningInflations.put(inflationId, cancellationSignal); } - private static void handleInflationError(HashMap<Integer, CancellationSignal> runningInflations, - Exception e, StatusBarNotification notification, @Nullable InflationCallback callback) { + private static void handleInflationError( + HashMap<Integer, CancellationSignal> runningInflations, Exception e, + StatusBarNotification notification, @Nullable InflationCallback callback) { Assert.isMainThread(); runningInflations.values().forEach(CancellationSignal::cancel); if (callback != null) { @@ -444,7 +581,8 @@ public class NotificationInflater { * * @return true if the inflation was finished */ - private static boolean finishIfDone(InflationProgress result, int reInflateFlags, + private static boolean finishIfDone(InflationProgress result, + @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews, HashMap<Integer, CancellationSignal> runningInflations, @Nullable InflationCallback endListener, ExpandableNotificationRow row, boolean redactAmbient) { @@ -453,40 +591,40 @@ public class NotificationInflater { NotificationContentView privateLayout = row.getPrivateLayout(); NotificationContentView publicLayout = row.getPublicLayout(); if (runningInflations.isEmpty()) { - if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) { + if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) { if (result.inflatedContentView != null) { privateLayout.setContractedChild(result.inflatedContentView); } - entry.cachedContentView = result.newContentView; + cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView); } - if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) { + if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) { if (result.inflatedExpandedView != null) { privateLayout.setExpandedChild(result.inflatedExpandedView); } else if (result.newExpandedView == null) { privateLayout.setExpandedChild(null); } - entry.cachedBigContentView = result.newExpandedView; + cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView); row.setExpandable(result.newExpandedView != null); } - if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) { + if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) { if (result.inflatedHeadsUpView != null) { privateLayout.setHeadsUpChild(result.inflatedHeadsUpView); } else if (result.newHeadsUpView == null) { privateLayout.setHeadsUpChild(null); } - entry.cachedHeadsUpContentView = result.newHeadsUpView; + cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView); } - if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) { + if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) { if (result.inflatedPublicView != null) { publicLayout.setContractedChild(result.inflatedPublicView); } - entry.cachedPublicContentView = result.newPublicView; + cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView); } - if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) { + if ((reInflateFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) { if (result.inflatedAmbientView != null) { NotificationContentView newParent = redactAmbient ? publicLayout : privateLayout; @@ -495,12 +633,12 @@ public class NotificationInflater { newParent.setAmbientChild(result.inflatedAmbientView); otherParent.setAmbientChild(null); } - entry.cachedAmbientContentView = result.newAmbientView; + cachedContentViews.put(FLAG_CONTENT_VIEW_AMBIENT, result.newAmbientView); } entry.headsUpStatusBarText = result.headsUpStatusBarText; entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic; if (endListener != null) { - endListener.onAsyncInflationFinished(row.getEntry()); + endListener.onAsyncInflationFinished(row.getEntry(), reInflateFlags); } return true; } @@ -552,7 +690,15 @@ public class NotificationInflater { public interface InflationCallback { void handleInflationException(StatusBarNotification notification, Exception e); - void onAsyncInflationFinished(NotificationData.Entry entry); + + /** + * Callback for after the content views finish inflating. + * + * @param entry the entry with the content views set + * @param inflatedFlags the flags associated with the content views that were inflated + */ + void onAsyncInflationFinished(NotificationData.Entry entry, + @InflationFlag int inflatedFlags); /** * Used to disable async-ness for tests. Should only be used for tests. @@ -563,18 +709,13 @@ public class NotificationInflater { } public void clearCachesAndReInflate() { - NotificationData.Entry entry = mRow.getEntry(); - entry.cachedAmbientContentView = null; - entry.cachedBigContentView = null; - entry.cachedContentView = null; - entry.cachedHeadsUpContentView = null; - entry.cachedPublicContentView = null; + mCachedContentViews.clear(); inflateNotificationViews(); } private static boolean canReapplyAmbient(ExpandableNotificationRow row, boolean redactAmbient) { NotificationContentView ambientView = redactAmbient ? row.getPublicLayout() - : row.getPrivateLayout(); ; + : row.getPrivateLayout(); return ambientView.getAmbientChild() != null; } @@ -589,7 +730,8 @@ public class NotificationInflater { private final InflationCallback mCallback; private final boolean mUsesIncreasedHeadsUpHeight; private final boolean mRedactAmbient; - private int mReInflateFlags; + private @InflationFlag int mReInflateFlags; + private final ArrayMap<Integer, RemoteViews> mCachedContentViews; private ExpandableNotificationRow mRow; private Exception mError; private RemoteViews.OnClickHandler mRemoteViewClickHandler; @@ -597,15 +739,16 @@ public class NotificationInflater { private List<Notification.Action> mSmartActions; private AsyncInflationTask(StatusBarNotification notification, - int reInflateFlags, ExpandableNotificationRow row, boolean isLowPriority, - boolean isChildInGroup, boolean usesIncreasedHeight, + @InflationFlag int reInflateFlags, + ArrayMap<Integer, RemoteViews> cachedContentViews, ExpandableNotificationRow row, + boolean isLowPriority, boolean isChildInGroup, boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient, - InflationCallback callback, - RemoteViews.OnClickHandler remoteViewClickHandler, + InflationCallback callback, RemoteViews.OnClickHandler remoteViewClickHandler, List<Notification.Action> smartActions) { mRow = row; mSbn = notification; mReInflateFlags = reInflateFlags; + mCachedContentViews = cachedContentViews; mContext = mRow.getContext(); mIsLowPriority = isLowPriority; mIsChildInGroup = isChildInGroup; @@ -622,6 +765,7 @@ public class NotificationInflater { } @VisibleForTesting + @InflationFlag public int getReInflateFlags() { return mReInflateFlags; } @@ -642,10 +786,9 @@ public class NotificationInflater { packageContext); processor.processNotification(notification, recoveredBuilder); } - return createRemoteViews(mReInflateFlags, - recoveredBuilder, mIsLowPriority, mIsChildInGroup, - mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient, - packageContext); + return createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority, + mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, + mRedactAmbient, packageContext); } catch (Exception e) { mError = e; return null; @@ -655,8 +798,8 @@ public class NotificationInflater { @Override protected void onPostExecute(InflationProgress result) { if (mError == null) { - mCancellationSignal = apply(result, mReInflateFlags, mRow, mRedactAmbient, - mRemoteViewClickHandler, this); + mCancellationSignal = apply(result, mReInflateFlags, mCachedContentViews, mRow, + mRedactAmbient, mRemoteViewClickHandler, this); } else { handleError(mError); } @@ -706,10 +849,11 @@ public class NotificationInflater { } @Override - public void onAsyncInflationFinished(NotificationData.Entry entry) { + public void onAsyncInflationFinished(NotificationData.Entry entry, + @InflationFlag int inflatedFlags) { mRow.getEntry().onInflationTaskFinished(); mRow.onNotificationUpdated(); - mCallback.onAsyncInflationFinished(mRow.getEntry()); + mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 2ca7282041cc..f76284dd1ffc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.row.wrapper; import android.content.Context; +import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.view.NotificationHeaderView; @@ -76,8 +77,11 @@ public abstract class NotificationViewWrapper implements TransformableView { } Drawable background = mView.getBackground(); if (background instanceof ColorDrawable) { - mBackgroundColor = ((ColorDrawable) background).getColor(); - mView.setBackground(null); + int backgroundColor = ((ColorDrawable) background).getColor(); + if (backgroundColor != Color.TRANSPARENT) { + mBackgroundColor = backgroundColor; + mView.setBackground(new ColorDrawable(Color.TRANSPARENT)); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java index fa75c7131e09..cfb6d990a9a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java @@ -22,6 +22,7 @@ import android.view.View; import android.view.ViewGroup; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; +import com.android.systemui.statusbar.notification.VisibilityLocationProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.NotificationData; @@ -31,7 +32,8 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger; * Interface representing the entity that contains notifications. It can have * notification views added and removed from it, and will manage displaying them to the user. */ -public interface NotificationListContainer { +public interface NotificationListContainer extends ExpandableView.OnHeightChangedListener, + VisibilityLocationProvider { /** * Called when a child is being transferred. @@ -128,14 +130,6 @@ public interface NotificationListContainer { ViewGroup getViewParentForNotification(NotificationData.Entry entry); /** - * Called when the height of an expandable view changes. - * - * @param view view whose height changed - * @param animate whether this change should be animated - */ - void onHeightChanged(ExpandableView view, boolean animate); - - /** * Resets the currently exposed menu view. * * @param animate whether to animate the closing/change of menu view @@ -158,13 +152,6 @@ public interface NotificationListContainer { */ void cleanUpViewState(View view); - /** - * Returns whether an ExpandableNotificationRow is in a visible location or not. - * - * @param row - * @return true if row is in a visible location - */ - boolean isInVisibleLocation(ExpandableNotificationRow row); /** * Sets a listener to listen for changes in notification locations. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 33ac390de29f..0bc54a33347c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -25,6 +25,8 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.TimeAnimator; import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WallpaperManager; import android.content.Context; @@ -43,10 +45,6 @@ import android.os.ServiceManager; import android.provider.Settings; import android.service.notification.StatusBarNotification; -import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; -import androidx.core.graphics.ColorUtils; - import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; @@ -69,6 +67,8 @@ import android.view.animation.Interpolator; import android.widget.OverScroller; import android.widget.ScrollView; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.graphics.ColorUtils; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.statusbar.IStatusBarService; @@ -84,6 +84,7 @@ import com.android.systemui.classifier.FalsingManager; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper.DragDownCallback; @@ -117,7 +118,7 @@ import com.android.systemui.statusbar.notification.VisibilityLocationProvider; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone.AnimationStateHandler; +import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener; @@ -142,10 +143,8 @@ import java.util.function.BiConsumer; /** * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack. */ -public class NotificationStackScrollLayout extends ViewGroup - implements ExpandHelper.Callback, ScrollAdapter, OnHeightChangedListener, - OnGroupChangeListener, VisibilityLocationProvider, NotificationListContainer, - ConfigurationListener, DragDownCallback, AnimationStateHandler, Dumpable { +public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter, + NotificationListContainer, ConfigurationListener, Dumpable { public static final float BACKGROUND_ALPHA_DIMMED = 0.7f; private static final String TAG = "StackScroller"; @@ -160,7 +159,6 @@ public class NotificationStackScrollLayout extends ViewGroup private ExpandHelper mExpandHelper; private final NotificationSwipeHelper mSwipeHelper; - private boolean mSwipingInProgress; private int mCurrentStackHeight = Integer.MAX_VALUE; private final Paint mBackgroundPaint = new Paint(); private final boolean mShouldDrawNotificationBackground; @@ -344,7 +342,7 @@ public class NotificationStackScrollLayout extends ViewGroup private float mDimAmount; private ValueAnimator mDimAnimator; private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>(); - private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() { + private final Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mDimAnimator = null; @@ -485,12 +483,12 @@ public class NotificationStackScrollLayout extends ViewGroup mBgColor = context.getColor(R.color.notification_shade_background_color); int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height); - mExpandHelper = new ExpandHelper(getContext(), this, + mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback, minHeight, maxHeight); mExpandHelper.setEventSource(this); mExpandHelper.setScrollAdapter(this); - mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, new SwipeHelperCallback(), - getContext(), new NotificationMenuListener()); + mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback, + getContext(), mMenuEventListener); mStackScrollAlgorithm = createStackScrollAlgorithm(context); initView(context); mFalsingManager = FalsingManager.getInstance(context); @@ -530,7 +528,7 @@ public class NotificationStackScrollLayout extends ViewGroup inflateEmptyShadeView(); inflateFooterView(); - mVisualStabilityManager.setVisibilityLocationProvider(this); + mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation); setLongPressListener(mEntryManager.getNotificationLongClicker()); } @@ -589,7 +587,7 @@ public class NotificationStackScrollLayout extends ViewGroup return false; } - @ShadeViewRefactor(RefactorComponent.INPUT) + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public RemoteInputController.Delegate createDelegate() { return new RemoteInputController.Delegate() { public void setRemoteInputActive(NotificationData.Entry entry, @@ -628,7 +626,7 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public NotificationSwipeActionHelper getSwipeActionHelper() { return mSwipeHelper; } @@ -1245,11 +1243,6 @@ public class NotificationStackScrollLayout extends ViewGroup return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize; } - @ShadeViewRefactor(RefactorComponent.INPUT) - public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) { - mLongPressListener = listener; - } - @ShadeViewRefactor(RefactorComponent.ADAPTER) public void setQsContainer(ViewGroup qsContainer) { mQsContainer = qsContainer; @@ -1273,7 +1266,7 @@ public class NotificationStackScrollLayout extends ViewGroup return false; } - @ShadeViewRefactor(RefactorComponent.INPUT) + @ShadeViewRefactor(RefactorComponent.COORDINATOR) public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) { getLocationOnScreen(mTempInt2); float localTouchY = touchY - mTempInt2[1]; @@ -1303,16 +1296,8 @@ public class NotificationStackScrollLayout extends ViewGroup return closestChild; } - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public ExpandableView getChildAtRawPosition(float touchX, float touchY) { - getLocationOnScreen(mTempInt2); - return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]); - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public ExpandableView getChildAtPosition(float touchX, float touchY) { + @ShadeViewRefactor(RefactorComponent.COORDINATOR) + private ExpandableView getChildAtPosition(float touchX, float touchY) { return getChildAtPosition(touchX, touchY, true /* requireMinHeight */); } @@ -1325,7 +1310,7 @@ public class NotificationStackScrollLayout extends ViewGroup * @param requireMinHeight Whether a minimum height is required for a child to be returned. * @return the child at the given location. */ - @ShadeViewRefactor(RefactorComponent.INPUT) + @ShadeViewRefactor(RefactorComponent.COORDINATOR) private ExpandableView getChildAtPosition(float touchX, float touchY, boolean requireMinHeight) { // find the view under the pointer, accounting for GONE views @@ -1365,71 +1350,9 @@ public class NotificationStackScrollLayout extends ViewGroup return null; } - @Override - @ShadeViewRefactor(RefactorComponent.ADAPTER) - public boolean canChildBeExpanded(View v) { - return v instanceof ExpandableNotificationRow - && ((ExpandableNotificationRow) v).isExpandable() - && !((ExpandableNotificationRow) v).areGutsExposed() - && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned()); - } - - /* Only ever called as a consequence of an expansion gesture in the shade. */ - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setUserExpandedChild(View v, boolean userExpanded) { - if (v instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) v; - if (userExpanded && onKeyguard()) { - // Due to a race when locking the screen while touching, a notification may be - // expanded even after we went back to keyguard. An example of this happens if - // you click in the empty space while expanding a group. - - // We also need to un-user lock it here, since otherwise the content height - // calculated might be wrong. We also can't invert the two calls since - // un-userlocking it will trigger a layout switch in the content view. - row.setUserLocked(false); - updateContentHeight(); - notifyHeightChangeListener(row); - return; - } - row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */); - row.onExpandedByGesture(userExpanded); - } - } - - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setExpansionCancelled(View v) { - if (v instanceof ExpandableNotificationRow) { - ((ExpandableNotificationRow) v).setGroupExpansionChanging(false); - } - } - - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setUserLockedChild(View v, boolean userLocked) { - if (v instanceof ExpandableNotificationRow) { - ((ExpandableNotificationRow) v).setUserLocked(userLocked); - } - cancelLongPress(); - requestDisallowInterceptTouchEvent(true); - } - - @Override - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - public void expansionStateChanged(boolean isExpanding) { - mExpandingNotification = isExpanding; - if (!mExpandedInThisMotion) { - mMaxScrollAfterExpand = mOwnScrollY; - mExpandedInThisMotion = true; - } - } - - @Override - @ShadeViewRefactor(RefactorComponent.COORDINATOR) - public int getMaxExpandHeight(ExpandableView view) { - return view.getMaxContentHeight(); + private ExpandableView getChildAtRawPosition(float touchX, float touchY) { + getLocationOnScreen(mTempInt2); + return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]); } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @@ -1526,14 +1449,6 @@ public class NotificationStackScrollLayout extends ViewGroup return mStatusBarState == StatusBarState.KEYGUARD; } - @ShadeViewRefactor(RefactorComponent.INPUT) - private void setSwipingInProgress(boolean isSwiped) { - mSwipingInProgress = isSwiped; - if (isSwiped) { - requestDisallowInterceptTouchEvent(true); - } - } - @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) protected void onConfigurationChanged(Configuration newConfig) { @@ -1567,249 +1482,6 @@ public class NotificationStackScrollLayout extends ViewGroup return this; } - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public boolean onTouchEvent(MotionEvent ev) { - boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL - || ev.getActionMasked() == MotionEvent.ACTION_UP; - handleEmptySpaceClick(ev); - boolean expandWantsIt = false; - if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) { - if (isCancelOrUp) { - mExpandHelper.onlyObserveMovements(false); - } - boolean wasExpandingBefore = mExpandingNotification; - expandWantsIt = mExpandHelper.onTouchEvent(ev); - if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore - && !mDisallowScrollingInThisMotion) { - dispatchDownEventToScroller(ev); - } - } - boolean scrollerWantsIt = false; - if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification - && !mDisallowScrollingInThisMotion) { - scrollerWantsIt = onScrollTouch(ev); - } - boolean horizontalSwipeWantsIt = false; - if (!mIsBeingDragged - && !mExpandingNotification - && !mExpandedInThisMotion - && !mOnlyScrollingInThisMotion - && !mDisallowDismissInThisMotion) { - horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev); - } - - // Check if we need to clear any snooze leavebehinds - NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); - if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts) - && guts.getGutsContent() instanceof NotificationSnooze) { - NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent(); - if ((ns.isExpanded() && isCancelOrUp) - || (!horizontalSwipeWantsIt && scrollerWantsIt)) { - // If the leavebehind is expanded we clear it on the next up event, otherwise we - // clear it on the next non-horizontal swipe or expand event. - checkSnoozeLeavebehind(); - } - } - if (ev.getActionMasked() == MotionEvent.ACTION_UP) { - mCheckForLeavebehind = true; - } - return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev); - } - - @ShadeViewRefactor(RefactorComponent.INPUT) - private void dispatchDownEventToScroller(MotionEvent ev) { - MotionEvent downEvent = MotionEvent.obtain(ev); - downEvent.setAction(MotionEvent.ACTION_DOWN); - onScrollTouch(downEvent); - downEvent.recycle(); - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public boolean onGenericMotionEvent(MotionEvent event) { - if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification - || mDisallowScrollingInThisMotion) { - return false; - } - if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { - switch (event.getAction()) { - case MotionEvent.ACTION_SCROLL: { - if (!mIsBeingDragged) { - final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL); - if (vscroll != 0) { - final int delta = (int) (vscroll * getVerticalScrollFactor()); - final int range = getScrollRange(); - int oldScrollY = mOwnScrollY; - int newScrollY = oldScrollY - delta; - if (newScrollY < 0) { - newScrollY = 0; - } else if (newScrollY > range) { - newScrollY = range; - } - if (newScrollY != oldScrollY) { - setOwnScrollY(newScrollY); - return true; - } - } - } - } - } - } - return super.onGenericMotionEvent(event); - } - - @ShadeViewRefactor(RefactorComponent.INPUT) - private boolean onScrollTouch(MotionEvent ev) { - if (!isScrollingEnabled()) { - return false; - } - if (isInsideQsContainer(ev) && !mIsBeingDragged) { - return false; - } - mForcedScroll = null; - initVelocityTrackerIfNotExists(); - mVelocityTracker.addMovement(ev); - - final int action = ev.getAction(); - - switch (action & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: { - if (getChildCount() == 0 || !isInContentBounds(ev)) { - return false; - } - boolean isBeingDragged = !mScroller.isFinished(); - setIsBeingDragged(isBeingDragged); - /* - * If being flinged and user touches, stop the fling. isFinished - * will be false if being flinged. - */ - if (!mScroller.isFinished()) { - mScroller.forceFinished(true); - } - - // Remember where the motion event started - mLastMotionY = (int) ev.getY(); - mDownX = (int) ev.getX(); - mActivePointerId = ev.getPointerId(0); - break; - } - case MotionEvent.ACTION_MOVE: - final int activePointerIndex = ev.findPointerIndex(mActivePointerId); - if (activePointerIndex == -1) { - Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent"); - break; - } - - final int y = (int) ev.getY(activePointerIndex); - final int x = (int) ev.getX(activePointerIndex); - int deltaY = mLastMotionY - y; - final int xDiff = Math.abs(x - mDownX); - final int yDiff = Math.abs(deltaY); - if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) { - setIsBeingDragged(true); - if (deltaY > 0) { - deltaY -= mTouchSlop; - } else { - deltaY += mTouchSlop; - } - } - if (mIsBeingDragged) { - // Scroll to follow the motion event - mLastMotionY = y; - int range = getScrollRange(); - if (mExpandedInThisMotion) { - range = Math.min(range, mMaxScrollAfterExpand); - } - - float scrollAmount; - if (deltaY < 0) { - scrollAmount = overScrollDown(deltaY); - } else { - scrollAmount = overScrollUp(deltaY, range); - } - - // Calling customOverScrollBy will call onCustomOverScrolled, which - // sets the scrolling if applicable. - if (scrollAmount != 0.0f) { - // The scrolling motion could not be compensated with the - // existing overScroll, we have to scroll the view - customOverScrollBy((int) scrollAmount, mOwnScrollY, - range, getHeight() / 2); - // If we're scrolling, leavebehinds should be dismissed - checkSnoozeLeavebehind(); - } - } - break; - case MotionEvent.ACTION_UP: - if (mIsBeingDragged) { - final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId); - - if (shouldOverScrollFling(initialVelocity)) { - onOverScrollFling(true, initialVelocity); - } else { - if (getChildCount() > 0) { - if ((Math.abs(initialVelocity) > mMinimumVelocity)) { - float currentOverScrollTop = getCurrentOverScrollAmount(true); - if (currentOverScrollTop == 0.0f || initialVelocity > 0) { - fling(-initialVelocity); - } else { - onOverScrollFling(false, initialVelocity); - } - } else { - if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, - getScrollRange())) { - animateScroll(); - } - } - } - } - mActivePointerId = INVALID_POINTER; - endDrag(); - } - - break; - case MotionEvent.ACTION_CANCEL: - if (mIsBeingDragged && getChildCount() > 0) { - if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) { - animateScroll(); - } - mActivePointerId = INVALID_POINTER; - endDrag(); - } - break; - case MotionEvent.ACTION_POINTER_DOWN: { - final int index = ev.getActionIndex(); - mLastMotionY = (int) ev.getY(index); - mDownX = (int) ev.getX(index); - mActivePointerId = ev.getPointerId(index); - break; - } - case MotionEvent.ACTION_POINTER_UP: - onSecondaryPointerUp(ev); - mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId)); - mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId)); - break; - } - return true; - } - - @ShadeViewRefactor(RefactorComponent.INPUT) - protected boolean isInsideQsContainer(MotionEvent ev) { - return ev.getY() < mQsContainer.getBottom(); - } - - @ShadeViewRefactor(RefactorComponent.INPUT) - private void onOverScrollFling(boolean open, int initialVelocity) { - if (mOverscrollTopChangedListener != null) { - mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open); - } - mDontReportNextOverScroll = true; - setOverScrollAmount(0.0f, true, false); - } - /** * Perform a scroll upwards and adapt the overscroll amounts accordingly * @@ -1817,7 +1489,7 @@ public class NotificationStackScrollLayout extends ViewGroup * @return The amount of scrolling to be performed by the scroller, * not handled by the overScroll amount. */ - @ShadeViewRefactor(RefactorComponent.INPUT) + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private float overScrollUp(int deltaY, int range) { deltaY = Math.max(deltaY, 0); float currentTopAmount = getCurrentOverScrollAmount(true); @@ -1876,24 +1548,6 @@ public class NotificationStackScrollLayout extends ViewGroup return scrollAmount; } - @ShadeViewRefactor(RefactorComponent.INPUT) - private void onSecondaryPointerUp(MotionEvent ev) { - final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> - MotionEvent.ACTION_POINTER_INDEX_SHIFT; - final int pointerId = ev.getPointerId(pointerIndex); - if (pointerId == mActivePointerId) { - // This was our active pointer going up. Choose a new - // active pointer and adjust accordingly. - // TODO: Make this decision more intelligent. - final int newPointerIndex = pointerIndex == 0 ? 1 : 0; - mLastMotionY = (int) ev.getY(newPointerIndex); - mActivePointerId = ev.getPointerId(newPointerIndex); - if (mVelocityTracker != null) { - mVelocityTracker.clear(); - } - } - } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void initVelocityTrackerIfNotExists() { if (mVelocityTracker == null) { @@ -2636,7 +2290,7 @@ public class NotificationStackScrollLayout extends ViewGroup * numbers mean that the finger/cursor is moving down the screen, * which means we want to scroll towards the top. */ - @ShadeViewRefactor(RefactorComponent.INPUT) + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) protected void fling(int velocityY) { if (getChildCount() > 0) { int scrollRange = getScrollRange(); @@ -2674,7 +2328,7 @@ public class NotificationStackScrollLayout extends ViewGroup * @return Whether a fling performed on the top overscroll edge lead to the expanded * overScroll view (i.e QS). */ - @ShadeViewRefactor(RefactorComponent.INPUT) + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private boolean shouldOverScrollFling(int initialVelocity) { float topOverScroll = getCurrentOverScrollAmount(true); return mScrolledToTopOnFirstDown @@ -2757,7 +2411,7 @@ public class NotificationStackScrollLayout extends ViewGroup return Math.max(desiredPadding, mIntrinsicPadding); } - @ShadeViewRefactor(RefactorComponent.INPUT) + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private float getRubberBandFactor(boolean onTop) { if (!onTop) { return RUBBER_BAND_FACTOR_NORMAL; @@ -2777,99 +2431,13 @@ public class NotificationStackScrollLayout extends ViewGroup * rubberbanded, false if it is technically an overscroll but rather a motion to expand the * overscroll view (e.g. expand QS). */ - @ShadeViewRefactor(RefactorComponent.INPUT) + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private boolean isRubberbanded(boolean onTop) { return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking || !mScrolledToTopOnFirstDown; } - @ShadeViewRefactor(RefactorComponent.INPUT) - private void endDrag() { - setIsBeingDragged(false); - - recycleVelocityTracker(); - - if (getCurrentOverScrollAmount(true /* onTop */) > 0) { - setOverScrollAmount(0, true /* onTop */, true /* animate */); - } - if (getCurrentOverScrollAmount(false /* onTop */) > 0) { - setOverScrollAmount(0, false /* onTop */, true /* animate */); - } - } - - @ShadeViewRefactor(RefactorComponent.INPUT) - private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) { - ev.offsetLocation(sourceView.getX(), sourceView.getY()); - ev.offsetLocation(-targetView.getX(), -targetView.getY()); - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public boolean onInterceptTouchEvent(MotionEvent ev) { - initDownStates(ev); - handleEmptySpaceClick(ev); - boolean expandWantsIt = false; - if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) { - expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev); - } - boolean scrollWantsIt = false; - if (!mSwipingInProgress && !mExpandingNotification) { - scrollWantsIt = onInterceptTouchEventScroll(ev); - } - boolean swipeWantsIt = false; - if (!mIsBeingDragged - && !mExpandingNotification - && !mExpandedInThisMotion - && !mOnlyScrollingInThisMotion - && !mDisallowDismissInThisMotion) { - swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev); - } - // Check if we need to clear any snooze leavebehinds - boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP; - NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); - if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt && - !expandWantsIt && !scrollWantsIt) { - mCheckForLeavebehind = false; - mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, - false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, - false /* resetMenu */); - } - if (ev.getActionMasked() == MotionEvent.ACTION_UP) { - mCheckForLeavebehind = true; - } - return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev); - } - - @ShadeViewRefactor(RefactorComponent.INPUT) - private void handleEmptySpaceClick(MotionEvent ev) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_MOVE: - if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop - || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) { - mTouchIsClick = false; - } - break; - case MotionEvent.ACTION_UP: - if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick && - isBelowLastNotification(mInitialTouchX, mInitialTouchY)) { - mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY); - } - break; - } - } - @ShadeViewRefactor(RefactorComponent.INPUT) - private void initDownStates(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mExpandedInThisMotion = false; - mOnlyScrollingInThisMotion = !mScroller.isFinished(); - mDisallowScrollingInThisMotion = false; - mDisallowDismissInThisMotion = false; - mTouchIsClick = true; - mInitialTouchX = ev.getX(); - mInitialTouchY = ev.getY(); - } - } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setChildTransferInProgress(boolean childTransferInProgress) { @@ -2896,15 +2464,6 @@ public class NotificationStackScrollLayout extends ViewGroup mCurrentStackScrollState.removeViewStateForView(child); } - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { - super.requestDisallowInterceptTouchEvent(disallowIntercept); - if (disallowIntercept) { - cancelLongPress(); - } - } - @ShadeViewRefactor(RefactorComponent.COORDINATOR) private void onViewRemovedInternal(View child, ViewGroup container) { if (mChangePositionInProgress) { @@ -3600,6 +3159,385 @@ public class NotificationStackScrollLayout extends ViewGroup mGoToFullShadeNeedsAnimation = false; } + @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM) + protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) { + return new StackScrollAlgorithm(context); + } + + /** + * @return Whether a y coordinate is inside the content. + */ + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) + public boolean isInContentBounds(float y) { + return y < getHeight() - getEmptyBottomMargin(); + } + + @ShadeViewRefactor(RefactorComponent.INPUT) + public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) { + mLongPressListener = listener; + } + + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public boolean onTouchEvent(MotionEvent ev) { + boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL + || ev.getActionMasked() == MotionEvent.ACTION_UP; + handleEmptySpaceClick(ev); + boolean expandWantsIt = false; + boolean swipingInProgress = mSwipeHelper.isSwipingInProgress(); + if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) { + if (isCancelOrUp) { + mExpandHelper.onlyObserveMovements(false); + } + boolean wasExpandingBefore = mExpandingNotification; + expandWantsIt = mExpandHelper.onTouchEvent(ev); + if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore + && !mDisallowScrollingInThisMotion) { + dispatchDownEventToScroller(ev); + } + } + boolean scrollerWantsIt = false; + if (mIsExpanded && !swipingInProgress && !mExpandingNotification + && !mDisallowScrollingInThisMotion) { + scrollerWantsIt = onScrollTouch(ev); + } + boolean horizontalSwipeWantsIt = false; + if (!mIsBeingDragged + && !mExpandingNotification + && !mExpandedInThisMotion + && !mOnlyScrollingInThisMotion + && !mDisallowDismissInThisMotion) { + horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev); + } + + // Check if we need to clear any snooze leavebehinds + NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); + if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts) + && guts.getGutsContent() instanceof NotificationSnooze) { + NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent(); + if ((ns.isExpanded() && isCancelOrUp) + || (!horizontalSwipeWantsIt && scrollerWantsIt)) { + // If the leavebehind is expanded we clear it on the next up event, otherwise we + // clear it on the next non-horizontal swipe or expand event. + checkSnoozeLeavebehind(); + } + } + if (ev.getActionMasked() == MotionEvent.ACTION_UP) { + mCheckForLeavebehind = true; + } + return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev); + } + + @ShadeViewRefactor(RefactorComponent.INPUT) + private void dispatchDownEventToScroller(MotionEvent ev) { + MotionEvent downEvent = MotionEvent.obtain(ev); + downEvent.setAction(MotionEvent.ACTION_DOWN); + onScrollTouch(downEvent); + downEvent.recycle(); + } + + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public boolean onGenericMotionEvent(MotionEvent event) { + if (!isScrollingEnabled() || !mIsExpanded || mSwipeHelper.isSwipingInProgress() || mExpandingNotification + || mDisallowScrollingInThisMotion) { + return false; + } + if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { + switch (event.getAction()) { + case MotionEvent.ACTION_SCROLL: { + if (!mIsBeingDragged) { + final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL); + if (vscroll != 0) { + final int delta = (int) (vscroll * getVerticalScrollFactor()); + final int range = getScrollRange(); + int oldScrollY = mOwnScrollY; + int newScrollY = oldScrollY - delta; + if (newScrollY < 0) { + newScrollY = 0; + } else if (newScrollY > range) { + newScrollY = range; + } + if (newScrollY != oldScrollY) { + setOwnScrollY(newScrollY); + return true; + } + } + } + } + } + } + return super.onGenericMotionEvent(event); + } + + @ShadeViewRefactor(RefactorComponent.INPUT) + private boolean onScrollTouch(MotionEvent ev) { + if (!isScrollingEnabled()) { + return false; + } + if (isInsideQsContainer(ev) && !mIsBeingDragged) { + return false; + } + mForcedScroll = null; + initVelocityTrackerIfNotExists(); + mVelocityTracker.addMovement(ev); + + final int action = ev.getAction(); + + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: { + if (getChildCount() == 0 || !isInContentBounds(ev)) { + return false; + } + boolean isBeingDragged = !mScroller.isFinished(); + setIsBeingDragged(isBeingDragged); + /* + * If being flinged and user touches, stop the fling. isFinished + * will be false if being flinged. + */ + if (!mScroller.isFinished()) { + mScroller.forceFinished(true); + } + + // Remember where the motion event started + mLastMotionY = (int) ev.getY(); + mDownX = (int) ev.getX(); + mActivePointerId = ev.getPointerId(0); + break; + } + case MotionEvent.ACTION_MOVE: + final int activePointerIndex = ev.findPointerIndex(mActivePointerId); + if (activePointerIndex == -1) { + Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent"); + break; + } + + final int y = (int) ev.getY(activePointerIndex); + final int x = (int) ev.getX(activePointerIndex); + int deltaY = mLastMotionY - y; + final int xDiff = Math.abs(x - mDownX); + final int yDiff = Math.abs(deltaY); + if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) { + setIsBeingDragged(true); + if (deltaY > 0) { + deltaY -= mTouchSlop; + } else { + deltaY += mTouchSlop; + } + } + if (mIsBeingDragged) { + // Scroll to follow the motion event + mLastMotionY = y; + int range = getScrollRange(); + if (mExpandedInThisMotion) { + range = Math.min(range, mMaxScrollAfterExpand); + } + + float scrollAmount; + if (deltaY < 0) { + scrollAmount = overScrollDown(deltaY); + } else { + scrollAmount = overScrollUp(deltaY, range); + } + + // Calling customOverScrollBy will call onCustomOverScrolled, which + // sets the scrolling if applicable. + if (scrollAmount != 0.0f) { + // The scrolling motion could not be compensated with the + // existing overScroll, we have to scroll the view + customOverScrollBy((int) scrollAmount, mOwnScrollY, + range, getHeight() / 2); + // If we're scrolling, leavebehinds should be dismissed + checkSnoozeLeavebehind(); + } + } + break; + case MotionEvent.ACTION_UP: + if (mIsBeingDragged) { + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId); + + if (shouldOverScrollFling(initialVelocity)) { + onOverScrollFling(true, initialVelocity); + } else { + if (getChildCount() > 0) { + if ((Math.abs(initialVelocity) > mMinimumVelocity)) { + float currentOverScrollTop = getCurrentOverScrollAmount(true); + if (currentOverScrollTop == 0.0f || initialVelocity > 0) { + fling(-initialVelocity); + } else { + onOverScrollFling(false, initialVelocity); + } + } else { + if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, + getScrollRange())) { + animateScroll(); + } + } + } + } + mActivePointerId = INVALID_POINTER; + endDrag(); + } + + break; + case MotionEvent.ACTION_CANCEL: + if (mIsBeingDragged && getChildCount() > 0) { + if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) { + animateScroll(); + } + mActivePointerId = INVALID_POINTER; + endDrag(); + } + break; + case MotionEvent.ACTION_POINTER_DOWN: { + final int index = ev.getActionIndex(); + mLastMotionY = (int) ev.getY(index); + mDownX = (int) ev.getX(index); + mActivePointerId = ev.getPointerId(index); + break; + } + case MotionEvent.ACTION_POINTER_UP: + onSecondaryPointerUp(ev); + mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId)); + mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId)); + break; + } + return true; + } + + @ShadeViewRefactor(RefactorComponent.INPUT) + protected boolean isInsideQsContainer(MotionEvent ev) { + return ev.getY() < mQsContainer.getBottom(); + } + + @ShadeViewRefactor(RefactorComponent.INPUT) + private void onOverScrollFling(boolean open, int initialVelocity) { + if (mOverscrollTopChangedListener != null) { + mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open); + } + mDontReportNextOverScroll = true; + setOverScrollAmount(0.0f, true, false); + } + + + @ShadeViewRefactor(RefactorComponent.INPUT) + private void onSecondaryPointerUp(MotionEvent ev) { + final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> + MotionEvent.ACTION_POINTER_INDEX_SHIFT; + final int pointerId = ev.getPointerId(pointerIndex); + if (pointerId == mActivePointerId) { + // This was our active pointer going up. Choose a new + // active pointer and adjust accordingly. + // TODO: Make this decision more intelligent. + final int newPointerIndex = pointerIndex == 0 ? 1 : 0; + mLastMotionY = (int) ev.getY(newPointerIndex); + mActivePointerId = ev.getPointerId(newPointerIndex); + if (mVelocityTracker != null) { + mVelocityTracker.clear(); + } + } + } + + @ShadeViewRefactor(RefactorComponent.INPUT) + private void endDrag() { + setIsBeingDragged(false); + + recycleVelocityTracker(); + + if (getCurrentOverScrollAmount(true /* onTop */) > 0) { + setOverScrollAmount(0, true /* onTop */, true /* animate */); + } + if (getCurrentOverScrollAmount(false /* onTop */) > 0) { + setOverScrollAmount(0, false /* onTop */, true /* animate */); + } + } + + @ShadeViewRefactor(RefactorComponent.INPUT) + private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) { + ev.offsetLocation(sourceView.getX(), sourceView.getY()); + ev.offsetLocation(-targetView.getX(), -targetView.getY()); + } + + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public boolean onInterceptTouchEvent(MotionEvent ev) { + initDownStates(ev); + handleEmptySpaceClick(ev); + boolean expandWantsIt = false; + boolean swipingInProgress = mSwipeHelper.isSwipingInProgress(); + if (!swipingInProgress && !mOnlyScrollingInThisMotion) { + expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev); + } + boolean scrollWantsIt = false; + if (!swipingInProgress && !mExpandingNotification) { + scrollWantsIt = onInterceptTouchEventScroll(ev); + } + boolean swipeWantsIt = false; + if (!mIsBeingDragged + && !mExpandingNotification + && !mExpandedInThisMotion + && !mOnlyScrollingInThisMotion + && !mDisallowDismissInThisMotion) { + swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev); + } + // Check if we need to clear any snooze leavebehinds + boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP; + NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); + if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt && + !expandWantsIt && !scrollWantsIt) { + mCheckForLeavebehind = false; + mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, + false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, + false /* resetMenu */); + } + if (ev.getActionMasked() == MotionEvent.ACTION_UP) { + mCheckForLeavebehind = true; + } + return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev); + } + + @ShadeViewRefactor(RefactorComponent.INPUT) + private void handleEmptySpaceClick(MotionEvent ev) { + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_MOVE: + if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop + || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) { + mTouchIsClick = false; + } + break; + case MotionEvent.ACTION_UP: + if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick && + isBelowLastNotification(mInitialTouchX, mInitialTouchY)) { + mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY); + } + break; + } + } + + @ShadeViewRefactor(RefactorComponent.INPUT) + private void initDownStates(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mExpandedInThisMotion = false; + mOnlyScrollingInThisMotion = !mScroller.isFinished(); + mDisallowScrollingInThisMotion = false; + mDisallowDismissInThisMotion = false; + mTouchIsClick = true; + mInitialTouchX = ev.getX(); + mInitialTouchY = ev.getY(); + } + } + + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + super.requestDisallowInterceptTouchEvent(disallowIntercept); + if (disallowIntercept) { + cancelLongPress(); + } + } + @ShadeViewRefactor(RefactorComponent.INPUT) private boolean onInterceptTouchEventScroll(MotionEvent ev) { if (!isScrollingEnabled()) { @@ -3710,11 +3648,6 @@ public class NotificationStackScrollLayout extends ViewGroup return mIsBeingDragged; } - @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM) - protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) { - return new StackScrollAlgorithm(context); - } - /** * @return Whether the specified motion event is actually happening over the content. */ @@ -3723,13 +3656,6 @@ public class NotificationStackScrollLayout extends ViewGroup return isInContentBounds(event.getY()); } - /** - * @return Whether a y coordinate is inside the content. - */ - @ShadeViewRefactor(RefactorComponent.INPUT) - public boolean isInContentBounds(float y) { - return y < getHeight() - getEmptyBottomMargin(); - } @VisibleForTesting @ShadeViewRefactor(RefactorComponent.INPUT) @@ -3742,6 +3668,83 @@ public class NotificationStackScrollLayout extends ViewGroup } } + @ShadeViewRefactor(RefactorComponent.INPUT) + public void requestDisallowLongPress() { + cancelLongPress(); + } + + @ShadeViewRefactor(RefactorComponent.INPUT) + public void requestDisallowDismiss() { + mDisallowDismissInThisMotion = true; + } + + @ShadeViewRefactor(RefactorComponent.INPUT) + public void cancelLongPress() { + mSwipeHelper.cancelLongPress(); + } + + @ShadeViewRefactor(RefactorComponent.INPUT) + public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) { + mOnEmptySpaceClickListener = listener; + } + + /** @hide */ + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public boolean performAccessibilityActionInternal(int action, Bundle arguments) { + if (super.performAccessibilityActionInternal(action, arguments)) { + return true; + } + if (!isEnabled()) { + return false; + } + int direction = -1; + switch (action) { + case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: + // fall through + case android.R.id.accessibilityActionScrollDown: + direction = 1; + // fall through + case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: + // fall through + case android.R.id.accessibilityActionScrollUp: + final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop + - mShelf.getIntrinsicHeight(); + final int targetScrollY = Math.max(0, + Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange())); + if (targetScrollY != mOwnScrollY) { + mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY); + animateScroll(); + return true; + } + break; + } + return false; + } + + @ShadeViewRefactor(RefactorComponent.INPUT) + public void closeControlsIfOutsideTouch(MotionEvent ev) { + NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); + NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow(); + View translatingParentView = mSwipeHelper.getTranslatingParentView(); + View view = null; + if (guts != null && !guts.getGutsContent().isLeavebehind()) { + // Only close visible guts if they're not a leavebehind. + view = guts; + } else if (menuRow != null && menuRow.isMenuVisible() + && translatingParentView != null) { + // Checking menu + view = translatingParentView; + } + if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) { + // Touch was outside visible guts / menu notification, close what's visible + mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */, + false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */, + false /* resetMenu */); + resetExposedMenuView(true /* animate */, true /* force */); + } + } + @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void onWindowFocusChanged(boolean hasWindowFocus) { @@ -3760,21 +3763,6 @@ public class NotificationStackScrollLayout extends ViewGroup } } - @ShadeViewRefactor(RefactorComponent.INPUT) - public void requestDisallowLongPress() { - cancelLongPress(); - } - - @ShadeViewRefactor(RefactorComponent.INPUT) - public void requestDisallowDismiss() { - mDisallowDismissInThisMotion = true; - } - - @ShadeViewRefactor(RefactorComponent.INPUT) - public void cancelLongPress() { - mSwipeHelper.cancelLongPress(); - } - @Override @ShadeViewRefactor(RefactorComponent.COORDINATOR) public boolean isScrolledToTop() { @@ -3916,7 +3904,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void onHeightChanged(ExpandableView view, boolean needsAnimation) { updateContentHeight(); updateScrollPositionOnExpandInBottom(view); @@ -3936,7 +3923,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void onReset(ExpandableView view) { updateAnimationState(view); updateChronometerForChild(view); @@ -3969,13 +3955,8 @@ public class NotificationStackScrollLayout extends ViewGroup @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setOnHeightChangedListener( - ExpandableView.OnHeightChangedListener mOnHeightChangedListener) { - this.mOnHeightChangedListener = mOnHeightChangedListener; - } - - @ShadeViewRefactor(RefactorComponent.INPUT) - public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) { - mOnEmptySpaceClickListener = listener; + ExpandableView.OnHeightChangedListener onHeightChangedListener) { + this.mOnHeightChangedListener = onHeightChangedListener; } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -4465,7 +4446,7 @@ public class NotificationStackScrollLayout extends ViewGroup @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setGroupManager(NotificationGroupManager groupManager) { this.mGroupManager = groupManager; - mGroupManager.setOnGroupChangeListener(this); + mGroupManager.setOnGroupChangeListener(mOnGroupChangeListener); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -4508,33 +4489,6 @@ public class NotificationStackScrollLayout extends ViewGroup return touchY > mTopPadding + mStackTranslation; } - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) { - boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled - && (mIsExpanded || changedRow.isPinned()); - if (animated) { - mExpandedGroupView = changedRow; - mNeedsAnimation = true; - } - changedRow.setChildrenExpanded(expanded, animated); - if (!mGroupExpandedForMeasure) { - onHeightChanged(changedRow, false /* needsAnimation */); - } - runAfterAnimationFinished(new Runnable() { - @Override - public void run() { - changedRow.onFinishedExpansionChange(); - } - }); - } - - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) { - mStatusBar.requestNotificationUpdate(); - } - /** @hide */ @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @@ -4567,46 +4521,6 @@ public class NotificationStackScrollLayout extends ViewGroup info.setClassName(ScrollView.class.getName()); } - /** @hide */ - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public boolean performAccessibilityActionInternal(int action, Bundle arguments) { - if (super.performAccessibilityActionInternal(action, arguments)) { - return true; - } - if (!isEnabled()) { - return false; - } - int direction = -1; - switch (action) { - case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: - // fall through - case android.R.id.accessibilityActionScrollDown: - direction = 1; - // fall through - case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: - // fall through - case android.R.id.accessibilityActionScrollUp: - final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop - - mShelf.getIntrinsicHeight(); - final int targetScrollY = Math.max(0, - Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange())); - if (targetScrollY != mOwnScrollY) { - mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY); - animateScroll(); - return true; - } - break; - } - return false; - } - - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void onGroupsChanged() { - mStatusBar.requestNotificationUpdate(); - } - @ShadeViewRefactor(RefactorComponent.COORDINATOR) public void generateChildOrderChangedEvent() { if (mIsExpanded && mAnimationsEnabled) { @@ -4649,7 +4563,7 @@ public class NotificationStackScrollLayout extends ViewGroup public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { mHeadsUpManager = headsUpManager; mHeadsUpManager.addListener(mRoundnessManager); - mHeadsUpManager.setAnimationStateHandler(this); + mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -4912,6 +4826,34 @@ public class NotificationStackScrollLayout extends ViewGroup mMaxTopPadding, mShouldShowShelfOnly ? "T" : "f", mQsExpansionFraction)); + int childCount = getChildCount(); + pw.println(" Number of children: " + childCount); + pw.println(); + + for (int i = 0; i < childCount; i++) { + ExpandableView child = (ExpandableView) getChildAt(i); + child.dump(fd, pw, args); + if (!(child instanceof ExpandableNotificationRow)) { + pw.println(" " + child.getClass().getSimpleName()); + // Notifications dump it's viewstate as part of their dump to support children + ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView( + child); + if (viewState == null) { + pw.println(" no viewState!!!"); + } else { + pw.print(" "); + viewState.dump(fd, pw, args); + pw.println(); + pw.println(); + } + } + } + pw.println(" Transient Views: " + childCount); + int transientViewCount = getTransientViewCount(); + for (int i = 0; i < transientViewCount; i++) { + ExpandableView child = (ExpandableView) getTransientView(i); + child.dump(fd, pw, args); + } } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @@ -5140,67 +5082,7 @@ public class NotificationStackScrollLayout extends ViewGroup return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty(); } - // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------ - - - /* Only ever called as a consequence of a lockscreen expansion gesture. */ - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public boolean onDraggedDown(View startingChild, int dragLengthY) { - if (mStatusBarState == StatusBarState.KEYGUARD - && hasActiveNotifications() && (!mStatusBar.isDozing() || mStatusBar.isPulsing())) { - mLockscreenGestureLogger.write( - MetricsEvent.ACTION_LS_SHADE, - (int) (dragLengthY / mDisplayMetrics.density), - 0 /* velocityDp - N/A */); - - // We have notifications, go to locked shade. - mStatusBar.goToLockedShade(startingChild); - if (startingChild instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild; - row.onExpandedByGesture(true /* drag down is always an open */); - } - return true; - } else { - // abort gesture. - return false; - } - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public void onDragDownReset() { - setDimmed(true /* dimmed */, true /* animated */); - resetScrollPosition(); - resetCheckSnoozeLeavebehind(); - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public void onCrossedThreshold(boolean above) { - setDimmed(!above /* dimmed */, true /* animate */); - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public void onTouchSlopExceeded() { - cancelLongPress(); - checkSnoozeLeavebehind(); - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public void setEmptyDragAmount(float amount) { - mNotificationPanel.setEmptyDragAmount(amount); - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public boolean isFalsingCheckNeeded() { - return mStatusBarState == StatusBarState.KEYGUARD; - } - - @ShadeViewRefactor(RefactorComponent.INPUT) + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void updateSpeedBumpIndex() { int speedBumpIndex = 0; int currentIndex = 0; @@ -5241,30 +5123,6 @@ public class NotificationStackScrollLayout extends ViewGroup mSwipeHelper.resetExposedMenuView(animate, force); } - - @ShadeViewRefactor(RefactorComponent.INPUT) - public void closeControlsIfOutsideTouch(MotionEvent ev) { - NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); - NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow(); - View translatingParentView = mSwipeHelper.getTranslatingParentView(); - View view = null; - if (guts != null && !guts.getGutsContent().isLeavebehind()) { - // Only close visible guts if they're not a leavebehind. - view = guts; - } else if (menuRow != null && menuRow.isMenuVisible() - && translatingParentView != null) { - // Checking menu - view = translatingParentView; - } - if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) { - // Touch was outside visible guts / menu notification, close what's visible - mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */, - false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */, - false /* resetMenu */); - resetExposedMenuView(true /* animate */, true /* force */); - } - } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) static class AnimationEvent { @@ -5586,9 +5444,9 @@ public class NotificationStackScrollLayout extends ViewGroup } }; - class NotificationMenuListener implements NotificationMenuRowPlugin.OnMenuEventListener { + @ShadeViewRefactor(RefactorComponent.INPUT) + private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() { @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public void onMenuClicked(View view, int x, int y, MenuItem item) { if (mLongPressListener == null) { return; @@ -5602,7 +5460,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public void onMenuReset(View row) { View translatingParentView = mSwipeHelper.getTranslatingParentView(); if (translatingParentView != null && row == translatingParentView) { @@ -5612,7 +5469,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public void onMenuShown(View row) { if (row instanceof ExpandableNotificationRow) { MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR, @@ -5621,9 +5477,11 @@ public class NotificationStackScrollLayout extends ViewGroup } mSwipeHelper.onMenuShown(row); } - } + }; - class SwipeHelperCallback implements NotificationSwipeHelper.NotificationCallback { + @ShadeViewRefactor(RefactorComponent.INPUT) + private final NotificationSwipeHelper.NotificationCallback mNotificationCallback = + new NotificationSwipeHelper.NotificationCallback() { @Override public void onDismiss() { mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, @@ -5643,10 +5501,8 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public void onDragCancelled(View v) { mFalsingManager.onNotificatonStopDismissing(); - setSwipingInProgress(false); } /** @@ -5654,7 +5510,6 @@ public class NotificationStackScrollLayout extends ViewGroup * re-invoking dismiss logic in case the notification has not made its way out yet). */ @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void onChildDismissed(View view) { ExpandableNotificationRow row = (ExpandableNotificationRow) view; if (!row.isDismissed()) { @@ -5673,7 +5528,6 @@ public class NotificationStackScrollLayout extends ViewGroup * @param view view (e.g. notification) to dismiss from the layout */ - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void handleChildViewDismissed(View view) { if (mDismissAllInProgress) { return; @@ -5681,7 +5535,6 @@ public class NotificationStackScrollLayout extends ViewGroup boolean isBlockingHelperShown = false; - setSwipingInProgress(false); if (mDragAnimPendingChildren.contains(view)) { // We start the swipe and finish it in the same frame; we don't want a drag // animation. @@ -5715,13 +5568,11 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public boolean isAntiFalsingNeeded() { return onKeyguard(); } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public View getChildAtPosition(MotionEvent ev) { View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(), ev.getY()); @@ -5744,10 +5595,8 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public void onBeginDrag(View v) { mFalsingManager.onNotificatonStartDismissing(); - setSwipingInProgress(true); mAmbientState.onBeginDrag(v); updateContinuousShadowDrawing(); if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) { @@ -5758,7 +5607,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void onChildSnappedBack(View animView, float targetLeft) { mAmbientState.onDragFinished(animView); updateContinuousShadowDrawing(); @@ -5780,7 +5628,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) { // Returning true prevents alpha fading. @@ -5788,7 +5635,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public float getFalsingThresholdFactor() { return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; } @@ -5797,5 +5643,197 @@ public class NotificationStackScrollLayout extends ViewGroup public boolean canChildBeDismissed(View v) { return NotificationStackScrollLayout.this.canChildBeDismissed(v); } + }; + + // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------ + + @ShadeViewRefactor(RefactorComponent.INPUT) + private final DragDownCallback mDragDownCallback = new DragDownCallback() { + + /* Only ever called as a consequence of a lockscreen expansion gesture. */ + @Override + public boolean onDraggedDown(View startingChild, int dragLengthY) { + if (mStatusBarState == StatusBarState.KEYGUARD + && hasActiveNotifications() && (!mStatusBar.isDozing() + || mStatusBar.isPulsing())) { + mLockscreenGestureLogger.write( + MetricsEvent.ACTION_LS_SHADE, + (int) (dragLengthY / mDisplayMetrics.density), + 0 /* velocityDp - N/A */); + + // We have notifications, go to locked shade. + mStatusBar.goToLockedShade(startingChild); + if (startingChild instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild; + row.onExpandedByGesture(true /* drag down is always an open */); + } + return true; + } else { + // abort gesture. + return false; + } + } + + @Override + public void onDragDownReset() { + setDimmed(true /* dimmed */, true /* animated */); + resetScrollPosition(); + resetCheckSnoozeLeavebehind(); + } + + @Override + public void onCrossedThreshold(boolean above) { + setDimmed(!above /* dimmed */, true /* animate */); + } + + @Override + public void onTouchSlopExceeded() { + cancelLongPress(); + checkSnoozeLeavebehind(); + } + + @Override + public void setEmptyDragAmount(float amount) { + mNotificationPanel.setEmptyDragAmount(amount); + } + + @Override + public boolean isFalsingCheckNeeded() { + return mStatusBarState == StatusBarState.KEYGUARD; + } + }; + + public DragDownCallback getDragDownCallback() { return mDragDownCallback; } + + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) + private final HeadsUpTouchHelper.Callback mHeadsUpCallback = new HeadsUpTouchHelper.Callback() { + @Override + public ExpandableView getChildAtRawPosition(float touchX, float touchY) { + return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY); + } + + @Override + public boolean isExpanded() { + return mIsExpanded; + } + + @Override + public Context getContext() { + return mContext; + } + }; + + public HeadsUpTouchHelper.Callback getHeadsUpCallback() { return mHeadsUpCallback; } + + + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) + private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() { + @Override + public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) { + boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled + && (mIsExpanded || changedRow.isPinned()); + if (animated) { + mExpandedGroupView = changedRow; + mNeedsAnimation = true; + } + changedRow.setChildrenExpanded(expanded, animated); + if (!mGroupExpandedForMeasure) { + onHeightChanged(changedRow, false /* needsAnimation */); + } + runAfterAnimationFinished(new Runnable() { + @Override + public void run() { + changedRow.onFinishedExpansionChange(); + } + }); + } + + @Override + public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) { + mStatusBar.requestNotificationUpdate(); + } + + @Override + public void onGroupsChanged() { + mStatusBar.requestNotificationUpdate(); + } + }; + + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) + private ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() { + @Override + public ExpandableView getChildAtPosition(float touchX, float touchY) { + return NotificationStackScrollLayout.this.getChildAtPosition(touchX, touchY); + } + + @Override + public ExpandableView getChildAtRawPosition(float touchX, float touchY) { + return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY); + } + + @Override + public boolean canChildBeExpanded(View v) { + return v instanceof ExpandableNotificationRow + && ((ExpandableNotificationRow) v).isExpandable() + && !((ExpandableNotificationRow) v).areGutsExposed() + && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned()); + } + + /* Only ever called as a consequence of an expansion gesture in the shade. */ + @Override + public void setUserExpandedChild(View v, boolean userExpanded) { + if (v instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) v; + if (userExpanded && onKeyguard()) { + // Due to a race when locking the screen while touching, a notification may be + // expanded even after we went back to keyguard. An example of this happens if + // you click in the empty space while expanding a group. + + // We also need to un-user lock it here, since otherwise the content height + // calculated might be wrong. We also can't invert the two calls since + // un-userlocking it will trigger a layout switch in the content view. + row.setUserLocked(false); + updateContentHeight(); + notifyHeightChangeListener(row); + return; + } + row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */); + row.onExpandedByGesture(userExpanded); + } + } + + @Override + public void setExpansionCancelled(View v) { + if (v instanceof ExpandableNotificationRow) { + ((ExpandableNotificationRow) v).setGroupExpansionChanging(false); + } + } + + @Override + public void setUserLockedChild(View v, boolean userLocked) { + if (v instanceof ExpandableNotificationRow) { + ((ExpandableNotificationRow) v).setUserLocked(userLocked); + } + cancelLongPress(); + requestDisallowInterceptTouchEvent(true); + } + + @Override + public void expansionStateChanged(boolean isExpanding) { + mExpandingNotification = isExpanding; + if (!mExpandedInThisMotion) { + mMaxScrollAfterExpand = mOwnScrollY; + mExpandedInThisMotion = true; + } + } + + @Override + public int getMaxExpandHeight(ExpandableView view) { + return view.getMaxContentHeight(); + } + }; + + public ExpandHelper.Callback getExpandHelperCallback() { + return mExpandHelperCallback; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index 028957d233ff..599da3b280be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -31,11 +31,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.SwipeHelper; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; -import com.android.systemui.statusbar.notification.ShadeViewRefactor; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; -@ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.INPUT) class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeActionHelper { @VisibleForTesting @@ -229,6 +227,7 @@ class NotificationSwipeHelper extends SwipeHelper if (mCallback.isExpanded()) { // We don't want to quick-dismiss when it's a heads up as this might lead to closing // of the panel early. + mSwipingInProgress = false; mCallback.handleChildViewDismissed(view); } mCallback.onDismiss(); @@ -248,6 +247,7 @@ class NotificationSwipeHelper extends SwipeHelper @Override public void snapChild(final View animView, final float targetLeft, float velocity) { superSnapChild(animView, targetLeft, velocity); + mSwipingInProgress = false; mCallback.onDragCancelled(animView); if (targetLeft == 0) { handleMenuCoveredOrDismissed(); @@ -354,6 +354,7 @@ class NotificationSwipeHelper extends SwipeHelper public void onMenuShown(View animView) { setExposedMenuView(getTranslatingParentView()); + mSwipingInProgress = false; mCallback.onDragCancelled(animView); Handler handler = getHandler(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java index 1f3244f2177d..a15fd7083017 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java @@ -25,6 +25,7 @@ import android.util.Property; import android.view.View; import android.view.animation.Interpolator; +import com.android.systemui.Dumpable; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -32,12 +33,17 @@ import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.policy.HeadsUpUtil; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + /** * A state of a view. This can be used to apply a set of view properties to a view with * {@link com.android.systemui.statusbar.notification.stack.StackScrollState} or start * animations with {@link com.android.systemui.statusbar.notification.stack.StackStateAnimator}. */ -public class ViewState { +public class ViewState implements Dumpable { /** * Some animation properties that can be used to update running animations but not creating @@ -710,4 +716,39 @@ public class ViewState { animator.cancel(); } } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + StringBuilder result = new StringBuilder(); + result.append("ViewState { "); + + boolean first = true; + Class currentClass = this.getClass(); + while (currentClass != null) { + Field[] fields = currentClass.getDeclaredFields(); + // Print field names paired with their values + for (Field field : fields) { + int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) || field.isSynthetic() + || Modifier.isTransient(modifiers)) { + continue; + } + if (!first) { + result.append(", "); + } + try { + result.append(field.getName()); + result.append(": "); + //requires access to private field: + field.setAccessible(true); + result.append(field.get(this)); + } catch (IllegalAccessException ex) { + } + first = false; + } + currentClass = currentClass.getSuperclass(); + } + result.append(" }"); + pw.print(result); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 9acaf21c41f5..c66bbb1696ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -48,6 +48,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, private final NotificationStackScrollLayout mStackScroller; private final HeadsUpStatusBarView mHeadsUpStatusBarView; private final View mClockView; + private final View mOperatorNameView; private final DarkIconDispatcher mDarkIconDispatcher; private final NotificationPanelView mPanelView; private final Consumer<ExpandableNotificationRow> @@ -65,8 +66,10 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, private final View.OnLayoutChangeListener mStackScrollLayoutChangeListener = (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> updatePanelTranslation(); + private boolean mAnimationsEnabled = true; Point mPoint; + public HeadsUpAppearanceController( NotificationIconAreaController notificationIconAreaController, HeadsUpManagerPhone headsUpManager, @@ -75,7 +78,8 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, statusbarView.findViewById(R.id.heads_up_status_bar_view), statusbarView.findViewById(R.id.notification_stack_scroller), statusbarView.findViewById(R.id.notification_panel), - statusbarView.findViewById(R.id.clock)); + statusbarView.findViewById(R.id.clock), + statusbarView.findViewById(R.id.operator_name_frame)); } @VisibleForTesting @@ -85,7 +89,8 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, HeadsUpStatusBarView headsUpStatusBarView, NotificationStackScrollLayout stackScroller, NotificationPanelView panelView, - View clockView) { + View clockView, + View operatorNameView) { mNotificationIconAreaController = notificationIconAreaController; mHeadsUpManager = headsUpManager; mHeadsUpManager.addListener(this); @@ -101,6 +106,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener); mStackScroller.setHeadsUpAppearanceController(this); mClockView = clockView; + mOperatorNameView = operatorNameView; mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class); mDarkIconDispatcher.addDarkReceiver(this); @@ -230,20 +236,52 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, mShown = isShown; if (isShown) { mHeadsUpStatusBarView.setVisibility(View.VISIBLE); - CrossFadeHelper.fadeIn(mHeadsUpStatusBarView, CONTENT_FADE_DURATION /* duration */, - CONTENT_FADE_DELAY /* delay */); - CrossFadeHelper.fadeOut(mClockView, CONTENT_FADE_DURATION/* duration */, - 0 /* delay */, () -> mClockView.setVisibility(View.INVISIBLE)); + show(mHeadsUpStatusBarView); + hide(mClockView, View.INVISIBLE); + if (mOperatorNameView != null) { + hide(mOperatorNameView, View.INVISIBLE); + } } else { - CrossFadeHelper.fadeIn(mClockView, CONTENT_FADE_DURATION /* duration */, - CONTENT_FADE_DELAY /* delay */); - CrossFadeHelper.fadeOut(mHeadsUpStatusBarView, CONTENT_FADE_DURATION/* duration */, - 0 /* delay */, () -> mHeadsUpStatusBarView.setVisibility(View.GONE)); - + show(mClockView); + if (mOperatorNameView != null) { + show(mOperatorNameView); + } + hide(mHeadsUpStatusBarView, View.GONE); } } } + /** + * Hides the view and sets the state to endState when finished. + * + * @param view The view to hide. + * @param endState One of {@link View#INVISIBLE} or {@link View#GONE}. + * @see View#setVisibility(int) + * + */ + private void hide(View view, int endState) { + if (mAnimationsEnabled) { + CrossFadeHelper.fadeOut(view, CONTENT_FADE_DURATION /* duration */, + 0 /* delay */, () -> view.setVisibility(endState)); + } else { + view.setVisibility(endState); + } + } + + private void show(View view) { + if (mAnimationsEnabled) { + CrossFadeHelper.fadeIn(view, CONTENT_FADE_DURATION /* duration */, + CONTENT_FADE_DELAY /* delay */); + } else { + view.setVisibility(View.VISIBLE); + } + } + + @VisibleForTesting + void setAnimationsEnabled(boolean enabled) { + mAnimationsEnabled = enabled; + } + @VisibleForTesting public boolean isShown() { return mShown; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java index 4df1e3bda1a5..e4a5caaf7d71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java @@ -32,7 +32,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll public class HeadsUpTouchHelper implements Gefingerpoken { private HeadsUpManagerPhone mHeadsUpManager; - private NotificationStackScrollLayout mStackScroller; + private Callback mCallback; private int mTrackingPointer; private float mTouchSlop; private float mInitialTouchX; @@ -44,12 +44,12 @@ public class HeadsUpTouchHelper implements Gefingerpoken { private ExpandableNotificationRow mPickedChild; public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager, - NotificationStackScrollLayout stackScroller, + Callback callback, NotificationPanelView notificationPanelView) { mHeadsUpManager = headsUpManager; - mStackScroller = stackScroller; + mCallback = callback; mPanel = notificationPanelView; - Context context = stackScroller.getContext(); + Context context = mCallback.getContext(); final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = configuration.getScaledTouchSlop(); } @@ -75,13 +75,13 @@ public class HeadsUpTouchHelper implements Gefingerpoken { mInitialTouchY = y; mInitialTouchX = x; setTrackingHeadsUp(false); - ExpandableView child = mStackScroller.getChildAtRawPosition(x, y); + ExpandableView child = mCallback.getChildAtRawPosition(x, y); mTouchingHeadsUpView = false; if (child instanceof ExpandableNotificationRow) { mPickedChild = (ExpandableNotificationRow) child; - mTouchingHeadsUpView = !mStackScroller.isExpanded() + mTouchingHeadsUpView = !mCallback.isExpanded() && mPickedChild.isHeadsUp() && mPickedChild.isPinned(); - } else if (child == null && !mStackScroller.isExpanded()) { + } else if (child == null && !mCallback.isExpanded()) { // We might touch above the visible heads up child, but then we still would // like to capture it. NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry(); @@ -174,4 +174,10 @@ public class HeadsUpTouchHelper implements Gefingerpoken { mPickedChild = null; mTouchingHeadsUpView = false; } + + public interface Callback { + ExpandableView getChildAtRawPosition(float touchX, float touchY); + boolean isExpanded(); + Context getContext(); + } } 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 087753afb68c..46bf8cb62dc0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -565,7 +565,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL return; } mDarkAmount = darkAmount; - mIndicationArea.setAlpha(1f - darkAmount); + mIndicationController.setDarkAmount(darkAmount); mLockIcon.setDarkAmount(darkAmount); } 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 2a4595b980ac..8ac867727e65 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -361,6 +361,8 @@ public class KeyguardBouncer { } else if (fraction == EXPANSION_HIDDEN && oldExpansion != EXPANSION_HIDDEN) { onFullyHidden(); mExpansionCallback.onFullyHidden(); + } else if (fraction != EXPANSION_VISIBLE && oldExpansion == EXPANSION_VISIBLE) { + mExpansionCallback.onStartingToHide(); } } @@ -481,6 +483,7 @@ public class KeyguardBouncer { public interface BouncerExpansionCallback { void onFullyShown(); + void onStartingToHide(); void onFullyHidden(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 7c84df91fffa..e85ff8ef22c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -519,7 +519,14 @@ public class KeyguardStatusBarView extends RelativeLayout mStatusIconContainer.setAlpha(alpha); mStatusIconContainer.setVisibility(visibility); - mSystemIconsContainer.setTranslationX(-mCurrentBurnInOffsetX * mDarkAmount); + float iconsX = -mCurrentBurnInOffsetX; + if (mMultiUserSwitch.getVisibility() == VISIBLE) { + // Squared alpha to add a nice easing curve and avoid overlap during animation. + mMultiUserAvatar.setAlpha(alpha * alpha); + iconsX += mMultiUserAvatar.getPaddingLeft() + mMultiUserAvatar.getWidth() + + mMultiUserAvatar.getPaddingRight(); + } + mSystemIconsContainer.setTranslationX(iconsX * mDarkAmount); mSystemIconsContainer.setTranslationY(mCurrentBurnInOffsetY * mDarkAmount); updateIconsAndTextColors(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 6bccf31b04a6..80f35060b737 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -236,7 +236,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { try { WindowManagerGlobal.getWindowManagerService() - .watchRotation(mRotationWatcher, getContext().getDisplay().getDisplayId()); + .watchRotation(mRotationWatcher, getContext().getDisplayId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -284,8 +284,8 @@ public class NavigationBarFragment extends Fragment implements Callbacks { super.onViewCreated(view, savedInstanceState); mNavigationBarView = (NavigationBarView) view; - mNavigationBarView.setDisabledFlags(mDisabledFlags1); mNavigationBarView.setComponents(mStatusBar.getPanel()); + mNavigationBarView.setDisabledFlags(mDisabledFlags1); mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged); mNavigationBarView.setOnTouchListener(this::onNavigationTouch); if (savedInstanceState != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index c4efa9431317..e92656ae0c02 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -60,7 +60,6 @@ import com.android.systemui.DockedStackExistsListener; import com.android.systemui.Interpolators; import com.android.systemui.OverviewProxyService; import com.android.systemui.R; -import com.android.systemui.RecentsComponent; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.plugins.PluginManager; @@ -71,7 +70,6 @@ import com.android.systemui.recents.RecentsOnboarding; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.NavigationBarCompat; import com.android.systemui.shared.system.WindowManagerWrapper; -import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.policy.DeadZone; import com.android.systemui.statusbar.policy.KeyButtonDrawable; @@ -140,6 +138,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>(); private final ContextualButtonGroup mContextualButtonGroup; private Configuration mConfiguration; + private Configuration mTmpLastConfiguration; private NavigationBarInflaterView mNavigationInflaterView; private RecentsOnboarding mRecentsOnboarding; @@ -286,6 +285,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService); mConfiguration = new Configuration(); + mTmpLastConfiguration = new Configuration(); mConfiguration.updateFrom(context.getResources().getConfiguration()); mScreenPinningNotify = new ScreenPinningNotify(mContext); @@ -445,13 +445,13 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } private void reloadNavIcons() { - updateIcons(Configuration.EMPTY, mConfiguration); + updateIcons(Configuration.EMPTY); } - private void updateIcons(Configuration oldConfig, Configuration newConfig) { - final boolean orientationChange = oldConfig.orientation != newConfig.orientation; - final boolean densityChange = oldConfig.densityDpi != newConfig.densityDpi; - final boolean dirChange = oldConfig.getLayoutDirection() != newConfig.getLayoutDirection(); + private void updateIcons(Configuration oldConfig) { + final boolean orientationChange = oldConfig.orientation != mConfiguration.orientation; + final boolean densityChange = oldConfig.densityDpi != mConfiguration.densityDpi; + final boolean dirChange = oldConfig.getLayoutDirection() != mConfiguration.getLayoutDirection(); if (orientationChange || densityChange) { mDockedIcon = getDrawable(R.drawable.ic_sysbar_docked); @@ -485,7 +485,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private void orientBackButton(KeyButtonDrawable drawable) { final boolean useAltBack = (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; - final boolean isRtl = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; float degrees = useAltBack ? (isRtl ? 270 : -90) : (isRtl ? 180 : 0); @@ -946,26 +946,27 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - boolean uiCarModeChanged = updateCarMode(newConfig); + mTmpLastConfiguration.updateFrom(mConfiguration); + mConfiguration.updateFrom(newConfig); + boolean uiCarModeChanged = updateCarMode(); updateTaskSwitchHelper(); - updateIcons(mConfiguration, newConfig); + updateIcons(mTmpLastConfiguration); updateRecentsIcon(); - mRecentsOnboarding.onConfigurationChanged(newConfig); - if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi - || mConfiguration.getLayoutDirection() != newConfig.getLayoutDirection()) { + mRecentsOnboarding.onConfigurationChanged(mConfiguration); + if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi + || mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) { // If car mode or density changes, we need to reset the icons. updateNavButtonIcons(); } - mConfiguration.updateFrom(newConfig); } /** * If the configuration changed, update the carmode and return that it was updated. */ - private boolean updateCarMode(Configuration newConfig) { + private boolean updateCarMode() { boolean uiCarModeChanged = false; - if (newConfig != null) { - int uiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK; + if (mConfiguration != null) { + int uiMode = mConfiguration.uiMode & Configuration.UI_MODE_TYPE_MASK; final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR); if (isCarMode != mInCarMode) { @@ -1100,10 +1101,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav visibilityToString(getCurrentView().getVisibility()), getCurrentView().getAlpha())); - pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s", + pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s darkIntensity=%.2f", mDisabledFlags, mVertical ? "true" : "false", - getMenuButton().isVisible() ? "true" : "false")); + getMenuButton().isVisible() ? "true" : "false", + getLightTransitionsController().getCurrentDarkIntensity())); dumpButton(pw, "back", getBackButton()); dumpButton(pw, "home", getHomeButton()); 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 6d53cd373d05..75077029c16b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -2521,8 +2521,8 @@ public class NotificationPanelView extends PanelView implements @Override public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { super.setHeadsUpManager(headsUpManager); - mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller, - this); + mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, + mNotificationStackScroller.getHeadsUpCallback(), this); } public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 56bef2e8ea59..cc9adb86a6b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -3756,9 +3756,6 @@ public class StatusBar extends SystemUI implements DemoMode, Trace.beginSection("StatusBar#updateKeyguardState"); if (mState == StatusBarState.KEYGUARD) { mKeyguardIndicationController.setVisible(true); - boolean dozingAnimated = mDozingRequested - && DozeParameters.getInstance(mContext).shouldControlScreenOff(); - mNotificationPanel.resetViews(dozingAnimated); if (mKeyguardUserSwitcher != null) { mKeyguardUserSwitcher.setKeyguard(true, mStatusBarStateController.fromShadeLocked()); @@ -3790,6 +3787,47 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override + public void onDozingChanged(boolean isDozing) { + Trace.beginSection("StatusBar#updateDozing"); + mDozing = isDozing; + + // Collapse the notification panel if open + boolean dozingAnimated = mDozingRequested + && DozeParameters.getInstance(mContext).shouldControlScreenOff(); + mNotificationPanel.resetViews(dozingAnimated); + + mKeyguardViewMediator.setAodShowing(mDozing); + + //TODO: make these folks listeners of StatusBarStateController.onDozingChanged + mStatusBarWindowController.setDozing(mDozing); + mStatusBarKeyguardViewManager.setDozing(mDozing); + if (mAmbientIndicationContainer instanceof DozeReceiver) { + ((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing); + } + + mEntryManager.updateNotifications(); + updateDozingState(); + updateScrimController(); + updateReportRejectedTouchVisibility(); + Trace.endSection(); + } + + private void updateDozing() { + // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked. + boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD + || mBiometricUnlockController.getMode() + == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING; + // When in wake-and-unlock we may not have received a change to mState + // but we still should not be dozing, manually set to false. + if (mBiometricUnlockController.getMode() == + BiometricUnlockController.MODE_WAKE_AND_UNLOCK) { + dozing = false; + } + + mStatusBarStateController.setIsDozing(dozing); + } + + @Override public void onActivationReset(ActivatableNotificationView view) { if (view == mNotificationPanel.getActivatedChild()) { mNotificationPanel.setActivatedChild(null); @@ -4341,34 +4379,6 @@ public class StatusBar extends SystemUI implements DemoMode, updateScrimController(); } - private void updateDozing() { - Trace.beginSection("StatusBar#updateDozing"); - // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked. - boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD - || mBiometricUnlockController.getMode() - == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING; - // When in wake-and-unlock we may not have received a change to mState - // but we still should not be dozing, manually set to false. - if (mBiometricUnlockController.getMode() == - mBiometricUnlockController.MODE_WAKE_AND_UNLOCK) { - dozing = false; - } - if (mDozing != dozing) { - mDozing = dozing; - mKeyguardViewMediator.setAodShowing(mDozing); - mStatusBarWindowController.setDozing(mDozing); - mStatusBarKeyguardViewManager.setDozing(mDozing); - if (mAmbientIndicationContainer instanceof DozeReceiver) { - ((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing); - } - mEntryManager.updateNotifications(); - updateDozingState(); - updateScrimController(); - updateReportRejectedTouchVisibility(); - } - Trace.endSection(); - } - @VisibleForTesting void updateScrimController() { Trace.beginSection("StatusBar#updateScrimController"); @@ -5267,8 +5277,7 @@ public class StatusBar extends SystemUI implements DemoMode, (Runnable saveImportance, StatusBarNotification sbn) -> { // If the user has security enabled, show challenge if the setting is changed. if (mLockscreenUserManager.isLockscreenPublicMode(sbn.getUser().getIdentifier()) - && (mState == StatusBarState.KEYGUARD || - mState == StatusBarState.SHADE_LOCKED)) { + && mKeyguardManager.isKeyguardLocked()) { onLockedNotificationImportanceChange(() -> { saveImportance.run(); return true; 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 3db1456446a0..ac3608bc622a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -84,6 +84,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } @Override + public void onStartingToHide() { + updateStates(); + } + + @Override public void onFullyHidden() { updateStates(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index 57c7e285e29f..0d37b550d4e0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -173,9 +173,9 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat } if (state.dozing) { - mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; + mLpChanged.privateFlags |= LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; } else { - mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; + mLpChanged.privateFlags &= ~LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index 45b32c7abae0..ad9b9b30fafc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -58,6 +58,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.view.FloatingActionMode; import com.android.internal.widget.FloatingToolbar; import com.android.systemui.Dependency; +import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.statusbar.DragDownHelper; @@ -182,6 +183,11 @@ public class StatusBarWindowView extends FrameLayout { } } + @VisibleForTesting + protected NotificationStackScrollLayout getStackScrollLayout() { + return mStackScrollLayout; + } + @Override public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); @@ -215,8 +221,11 @@ public class StatusBarWindowView extends FrameLayout { public void setService(StatusBar service) { mService = service; - setDragDownHelper(new DragDownHelper(getContext(), this, mStackScrollLayout, - mStackScrollLayout)); + NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); + ExpandHelper.Callback expandHelperCallback = stackScrollLayout.getExpandHelperCallback(); + DragDownHelper.DragDownCallback dragDownCallback = stackScrollLayout.getDragDownCallback(); + setDragDownHelper(new DragDownHelper(getContext(), this, expandHelperCallback, + dragDownCallback)); } @VisibleForTesting @@ -309,7 +318,7 @@ public class StatusBarWindowView extends FrameLayout { } } if (isDown) { - mStackScrollLayout.closeControlsIfOutsideTouch(ev); + getStackScrollLayout().closeControlsIfOutsideTouch(ev); } if (mService.isDozing()) { mService.mDozeScrimController.extendPulse(); @@ -331,13 +340,14 @@ public class StatusBarWindowView extends FrameLayout { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mService.isDozing() && !mStackScrollLayout.hasPulsingNotifications()) { + NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); + if (mService.isDozing() && !stackScrollLayout.hasPulsingNotifications()) { // Capture all touch events in always-on. return true; } boolean intercept = false; if (mNotificationPanel.isFullyExpanded() - && mStackScrollLayout.getVisibility() == View.VISIBLE + && stackScrollLayout.getVisibility() == View.VISIBLE && mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !mService.isBouncerShowing() && !mService.isDozing()) { @@ -349,7 +359,7 @@ public class StatusBarWindowView extends FrameLayout { if (intercept) { MotionEvent cancellation = MotionEvent.obtain(ev); cancellation.setAction(MotionEvent.ACTION_CANCEL); - mStackScrollLayout.onInterceptTouchEvent(cancellation); + stackScrollLayout.onInterceptTouchEvent(cancellation); mNotificationPanel.onInterceptTouchEvent(cancellation); cancellation.recycle(); } @@ -391,8 +401,9 @@ public class StatusBarWindowView extends FrameLayout { } public void cancelExpandHelper() { - if (mStackScrollLayout != null) { - mStackScrollLayout.cancelExpandHelper(); + NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); + if (stackScrollLayout != null) { + stackScrollLayout.cancelExpandHelper(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index d477587f8ecb..b4d24d16113e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy; +import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -151,6 +153,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { for (OnHeadsUpChangedListener listener : mListeners) { listener.onHeadsUpStateChanged(entry, false); } + entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP); } protected void updatePinnedMode() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 59dd56066652..846c0cd96229 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -465,6 +465,8 @@ public class NetworkControllerImpl extends BroadcastReceiver MobileSignalController controller = mMobileSignalControllers.valueAt(i); controller.handleBroadcast(intent); } + mConfig = Config.readConfig(mContext); + mReceiverHandler.post(this::handleConfigurationChanged); break; case TelephonyIntents.ACTION_SIM_STATE_CHANGED: // Avoid rebroadcast because SysUI is direct boot aware. @@ -1051,18 +1053,23 @@ public class NetworkControllerImpl extends BroadcastReceiver config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G); config.alwaysShowCdmaRssi = res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi); - config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE); config.hspaDataDistinguishable = res.getBoolean(R.bool.config_hspa_data_distinguishable); - config.hideLtePlus = res.getBoolean(R.bool.config_hideLtePlus); config.inflateSignalStrengths = res.getBoolean(R.bool.config_inflateSignalStrength); CarrierConfigManager configMgr = (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); - PersistableBundle b = configMgr.getConfig(); + // Handle specific carrier config values for the default data SIM + int defaultDataSubId = SubscriptionManager.from(context) + .getDefaultDataSubscriptionId(); + PersistableBundle b = configMgr.getConfigForSubId(defaultDataSubId); if (b != null) { config.alwaysShowDataRatIcon = b.getBoolean( CarrierConfigManager.KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL); + config.show4gForLte = b.getBoolean( + CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL); + config.hideLtePlus = b.getBoolean( + CarrierConfigManager.KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL); } config.readIconsFromXml = res.getBoolean(R.bool.config_read_icons_from_xml); config.showRsrpSignalLevelforLTE = diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java b/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java index af99236cc393..e85dee841715 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java @@ -51,7 +51,9 @@ public class TunablePadding implements Tunable { public void onTuningChanged(String key, String newValue) { int dimen = mDefaultSize; if (newValue != null) { - dimen = (int) (Integer.parseInt(newValue) * mDensity); + try { + dimen = (int) (Integer.parseInt(newValue) * mDensity); + } catch (NumberFormatException ex) {} } int left = mView.isLayoutRtl() ? FLAG_END : FLAG_START; int right = mView.isLayoutRtl() ? FLAG_START : FLAG_END; diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java index 66d5ee1a276f..4102e63f7330 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java @@ -54,7 +54,7 @@ public class UsbDebuggingActivity extends AlertActivity @Override public void onCreate(Bundle icicle) { Window window = getWindow(); - window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + window.addSystemFlags(WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); super.onCreate(icicle); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index a97effd3a023..e20e267336ea 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -956,11 +956,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa changed |= onVolumeChangedW(stream, 0); } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1); + if (isInitialStickyBroadcast()) mState.ringerModeExternal = rm; if (D.BUG) Log.d(TAG, "onReceive RINGER_MODE_CHANGED_ACTION rm=" + Util.ringerModeToString(rm)); changed = updateRingerModeExternalW(rm); } else if (action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) { final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1); + if (isInitialStickyBroadcast()) mState.ringerModeInternal = rm; if (D.BUG) Log.d(TAG, "onReceive INTERNAL_RINGER_MODE_CHANGED_ACTION rm=" + Util.ringerModeToString(rm)); changed = updateRingerModeInternalW(rm); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 4810b0b91c10..798f8bcd7938 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -135,10 +135,6 @@ public class VolumeDialogImpl implements VolumeDialog { private final AccessibilityManagerWrapper mAccessibilityMgr; private final Object mSafetyWarningLock = new Object(); private final Accessibility mAccessibility = new Accessibility(); - private ColorStateList mActiveTint; - private int mActiveAlpha; - private ColorStateList mInactiveTint; - private int mInactiveAlpha; private boolean mShowing; private boolean mShowA11yStream; @@ -238,11 +234,6 @@ public class VolumeDialogImpl implements VolumeDialog { lp.gravity = ((FrameLayout.LayoutParams) mDialogView.getLayoutParams()).gravity; mWindow.setAttributes(lp); - mActiveTint = Utils.getColorAccent(mContext); - mActiveAlpha = Color.alpha(mActiveTint.getDefaultColor()); - mInactiveTint = Utils.getColorAttr(mContext, android.R.attr.colorForeground); - mInactiveAlpha = getAlphaAttr(android.R.attr.secondaryContentAlpha); - mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows); mRinger = mDialog.findViewById(R.id.ringer); if (mRinger != null) { @@ -556,14 +547,15 @@ public class VolumeDialogImpl implements VolumeDialog { mHandler.removeMessages(H.SHOW); mHandler.removeMessages(H.DISMISS); rescheduleTimeoutH(); - mShowing = true; if (mConfigChanged) { - initDialog(); + initDialog(); // resets mShowing to false mConfigurableTexts.update(); mConfigChanged = false; } + initSettingsH(); + mShowing = true; mDialog.show(); Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked()); mController.notifyVisible(true); @@ -941,8 +933,12 @@ public class VolumeDialogImpl implements VolumeDialog { row.slider.requestFocus(); } boolean useActiveColoring = isActive && row.slider.isEnabled(); - final ColorStateList tint = useActiveColoring ? mActiveTint : mInactiveTint; - final int alpha = useActiveColoring ? mActiveAlpha : mInactiveAlpha; + final ColorStateList tint = useActiveColoring + ? Utils.getColorAccent(mContext) + : Utils.getColorAttr(mContext, android.R.attr.colorForeground); + final int alpha = useActiveColoring + ? Color.alpha(tint.getDefaultColor()) + : getAlphaAttr(android.R.attr.secondaryContentAlpha); if (tint == row.cachedTint) return; row.slider.setProgressTintList(tint); row.slider.setThumbTintList(tint); |