diff options
79 files changed, 6339 insertions, 5304 deletions
diff --git a/Android.bp b/Android.bp index 93b37f261665..477f0272efa0 100644 --- a/Android.bp +++ b/Android.bp @@ -1137,6 +1137,7 @@ filegroup { srcs: [ ":framework-annotations", "core/java/android/net/InterfaceConfiguration.java", + "core/java/android/os/BasicShellCommandHandler.java", "core/java/android/os/HandlerExecutor.java", "core/java/android/util/BackupUtils.java", "core/java/android/util/LocalLog.java", diff --git a/apex/statsd/aidl/android/os/IStatsManagerService.aidl b/apex/statsd/aidl/android/os/IStatsManagerService.aidl index 2a3665ca9b5d..dec56345ec2f 100644 --- a/apex/statsd/aidl/android/os/IStatsManagerService.aidl +++ b/apex/statsd/aidl/android/os/IStatsManagerService.aidl @@ -107,4 +107,22 @@ interface IStatsManagerService { * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. */ byte[] getData(in long key, in String packageName); + + /** + * Sets a configuration with the specified config id and subscribes to updates for this + * configuration id. Broadcasts will be sent if this configuration needs to be collected. + * The configuration must be a wire-encoded StatsdConfig. The receiver for this data is + * registered in a separate function. + * + * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. + */ + void addConfiguration(in long configId, in byte[] config, in String packageName); + + /** + * Removes the configuration with the matching config id. No-op if this config id does not + * exist. + * + * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. + */ + void removeConfiguration(in long configId, in String packageName); }
\ No newline at end of file diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl index c08abdd4cebe..c409f516de1b 100644 --- a/apex/statsd/aidl/android/os/IStatsd.aidl +++ b/apex/statsd/aidl/android/os/IStatsd.aidl @@ -99,14 +99,14 @@ interface IStatsd { byte[] getMetadata(); /** - * Sets a configuration with the specified config key and subscribes to updates for this + * Sets a configuration with the specified config id and subscribes to updates for this * configuration key. Broadcasts will be sent if this configuration needs to be collected. * The configuration must be a wire-encoded StatsdConfig. The receiver for this data is * registered in a separate function. * * Requires Manifest.permission.DUMP. */ - void addConfiguration(in long configKey, in byte[] config, in String packageName); + void addConfiguration(in long configId, in byte[] config, in int callingUid); /** * Registers the given pending intent for this config key. This intent is invoked when the @@ -143,12 +143,12 @@ interface IStatsd { void removeActiveConfigsChangedOperation(int callingUid); /** - * Removes the configuration with the matching config key. No-op if this config key does not + * Removes the configuration with the matching config id. No-op if this config id does not * exist. * * Requires Manifest.permission.DUMP. */ - void removeConfiguration(in long configKey, in String packageName); + void removeConfiguration(in long configId, in int callingUid); /** * Set the PendingIntentRef to be used when broadcasting subscriber diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java index eff9bda618f9..b27d0f7699fc 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java @@ -301,6 +301,48 @@ public class StatsManagerService extends IStatsManagerService.Stub { throw new IllegalStateException("Failed to connect to statsd to getData"); } + @Override + public void addConfiguration(long configId, byte[] config, String packageName) + throws IllegalStateException { + enforceDumpAndUsageStatsPermission(packageName); + int callingUid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + IStatsd statsd = waitForStatsd(); + if (statsd != null) { + statsd.addConfiguration(configId, config, callingUid); + return; + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to addConfiguration with statsd"); + throw new IllegalStateException(e.getMessage(), e); + } finally { + Binder.restoreCallingIdentity(token); + } + throw new IllegalStateException("Failed to connect to statsd to addConfig"); + } + + @Override + public void removeConfiguration(long configId, String packageName) + throws IllegalStateException { + enforceDumpAndUsageStatsPermission(packageName); + int callingUid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + IStatsd statsd = waitForStatsd(); + if (statsd != null) { + statsd.removeConfiguration(configId, callingUid); + return; + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to removeConfiguration with statsd"); + throw new IllegalStateException(e.getMessage(), e); + } finally { + Binder.restoreCallingIdentity(token); + } + throw new IllegalStateException("Failed to connect to statsd to removeConfig"); + } + void setStatsCompanionService(StatsCompanionService statsCompanionService) { mStatsCompanionService = statsCompanionService; } diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 7b96ce92e307..118a508a9071 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -126,29 +126,28 @@ cc_defaults { ], static_libs: [ - "libhealthhalutils", - "libplatformprotos", - ], - - shared_libs: [ "android.frameworks.stats@1.0", - "android.hardware.health@2.0", "android.hardware.power.stats@1.0", "android.hardware.power@1.0", "android.hardware.power@1.1", "libbase", - "libbinder", "libcutils", - "libgraphicsenv", - "libhidlbase", - "libincident", + "libhealthhalutils", "liblog", + "libplatformprotos", "libprotoutil", - "libservices", "libstatslog", - "libstatsmetadata", "libstatssocket", "libsysutils", + ], + shared_libs: [ + "android.hardware.health@2.0", + "libbinder", + "libgraphicsenv", + "libhidlbase", + "libincident", + "libservices", + "libstatsmetadata", "libtimestats_proto", "libutils", ], diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index b2a5b50bb6eb..694c99df2194 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1155,11 +1155,10 @@ Status StatsService::getMetadata(vector<uint8_t>* output) { } Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config, - const String16& packageName) { - ENFORCE_DUMP_AND_USAGE_STATS(packageName); + const int32_t callingUid) { + ENFORCE_UID(AID_SYSTEM); - IPCThreadState* ipc = IPCThreadState::self(); - if (addConfigurationChecked(ipc->getCallingUid(), key, config)) { + if (addConfigurationChecked(callingUid, key, config)) { return Status::ok(); } else { ALOGE("Could not parse malformatted StatsdConfig"); @@ -1224,11 +1223,10 @@ Status StatsService::removeActiveConfigsChangedOperation(const int32_t callingUi return Status::ok(); } -Status StatsService::removeConfiguration(int64_t key, const String16& packageName) { - ENFORCE_DUMP_AND_USAGE_STATS(packageName); +Status StatsService::removeConfiguration(int64_t key, const int32_t callingUid) { + ENFORCE_UID(AID_SYSTEM); - IPCThreadState* ipc = IPCThreadState::self(); - ConfigKey configKey(ipc->getCallingUid(), key); + ConfigKey configKey(callingUid, key); mConfigManager->RemoveConfig(configKey); SubscriberReporter::getInstance().removeConfig(configKey); return Status::ok(); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 56d87f25b7e5..c9a9072ecb92 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -115,7 +115,7 @@ public: */ virtual Status addConfiguration(int64_t key, const vector<uint8_t>& config, - const String16& packageName) override; + const int32_t callingUid) override; /** * Binder call to let clients register the data fetch operation for a configuration. @@ -145,7 +145,7 @@ public: * Binder call to allow clients to remove the specified configuration. */ virtual Status removeConfiguration(int64_t key, - const String16& packageName) override; + const int32_t callingUid) override; /** * Binder call to associate the given config's subscriberId with the given pendingIntentRef. diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp index 0bc3ebb81ce6..16b51d99535b 100644 --- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp @@ -28,16 +28,16 @@ namespace statsd { #ifdef __ANDROID__ -const string kAndroid = "android"; const string kApp1 = "app1.sharing.1"; const int kConfigKey = 789130123; // Randomly chosen to avoid collisions with existing configs. +const int kCallingUid = 0; // Randomly chosen void SendConfig(StatsService& service, const StatsdConfig& config) { string str; config.SerializeToString(&str); std::vector<uint8_t> configAsVec(str.begin(), str.end()); bool success; - service.addConfiguration(kConfigKey, configAsVec, String16(kAndroid.c_str())); + service.addConfiguration(kConfigKey, configAsVec, kCallingUid); } ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp, @@ -50,7 +50,7 @@ ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestam ConfigMetricsReportList reports; reports.ParseFromArray(output.data(), output.size()); EXPECT_EQ(1, reports.reports_size()); - return reports.reports(0); + return reports.reports(kCallingUid); } StatsdConfig MakeConfig() { diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index 4e50a3fa17c1..84263749232d 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -157,11 +157,11 @@ public final class StatsManager { public void addConfig(long configKey, byte[] config) throws StatsUnavailableException { synchronized (sLock) { try { - IStatsd service = getIStatsdLocked(); + IStatsManagerService service = getIStatsManagerServiceLocked(); // can throw IllegalArgumentException service.addConfiguration(configKey, config, mContext.getOpPackageName()); } catch (RemoteException e) { - Slog.e(TAG, "Failed to connect to statsd when adding configuration"); + Slog.e(TAG, "Failed to connect to statsmanager when adding configuration"); throw new StatsUnavailableException("could not connect", e); } catch (SecurityException e) { throw new StatsUnavailableException(e.getMessage(), e); @@ -194,10 +194,10 @@ public final class StatsManager { public void removeConfig(long configKey) throws StatsUnavailableException { synchronized (sLock) { try { - IStatsd service = getIStatsdLocked(); + IStatsManagerService service = getIStatsManagerServiceLocked(); service.removeConfiguration(configKey, mContext.getOpPackageName()); } catch (RemoteException e) { - Slog.e(TAG, "Failed to connect to statsd when removing configuration"); + Slog.e(TAG, "Failed to connect to statsmanager when removing configuration"); throw new StatsUnavailableException("could not connect", e); } catch (SecurityException e) { throw new StatsUnavailableException(e.getMessage(), e); diff --git a/core/java/android/view/IDisplayWindowInsetsController.aidl b/core/java/android/view/IDisplayWindowInsetsController.aidl new file mode 100644 index 000000000000..429c3aeba9b3 --- /dev/null +++ b/core/java/android/view/IDisplayWindowInsetsController.aidl @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.view.InsetsSourceControl; +import android.view.InsetsState; + +/** + * Singular controller of insets to use when there isn't another obvious controller available. + * Specifically, this will take over insets control in multi-window. + * @hide + */ +oneway interface IDisplayWindowInsetsController { + + /** + * @see IWindow#insetsChanged + */ + void insetsChanged(in InsetsState insetsState); + + /** + * @see IWindow#insetsControlChanged + */ + void insetsControlChanged(in InsetsState insetsState, in InsetsSourceControl[] activeControls); + + /** + * @see IWindow#showInsets + */ + void showInsets(int types, boolean fromIme); + + /** + * @see IWindow#hideInsets + */ + void hideInsets(int types, boolean fromIme); +} diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 9496827b1a84..993bdc4d6543 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -35,6 +35,7 @@ import android.os.ParcelFileDescriptor; import android.view.IApplicationToken; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IDockedStackListener; +import android.view.IDisplayWindowInsetsController; import android.view.IDisplayWindowListener; import android.view.IDisplayFoldListener; import android.view.IDisplayWindowRotationController; @@ -49,6 +50,7 @@ import android.view.IWindowSession; import android.view.IWindowSessionCallback; import android.view.KeyEvent; import android.view.InputEvent; +import android.view.InsetsState; import android.view.MagnificationSpec; import android.view.MotionEvent; import android.view.InputChannel; @@ -711,4 +713,16 @@ interface IWindowManager * @return true if the display was successfully mirrored. */ boolean mirrorDisplay(int displayId, out SurfaceControl outSurfaceControl); + + /** + * When in multi-window mode, the provided displayWindowInsetsController will control insets + * animations. + */ + void setDisplayWindowInsetsController( + int displayId, in IDisplayWindowInsetsController displayWindowInsetsController); + + /** + * Called when a remote process modifies insets on a display window container. + */ + void modifyDisplayWindowInsets(int displayId, in InsetsState state); } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index e3fed3a5dce3..ae1e579da8f6 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -19,10 +19,15 @@ package android.view; import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME; import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; +import static android.view.WindowInsets.Type.IME; import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES; import static android.view.WindowInsets.Type.SIZE; import static android.view.WindowInsets.Type.SYSTEM_GESTURES; +import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.indexOf; +import static android.view.WindowInsets.Type.systemBars; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import android.annotation.IntDef; import android.annotation.Nullable; @@ -156,11 +161,10 @@ public class InsetsState implements Parcelable { && source.getType() != ITYPE_IME; boolean skipSystemBars = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR); - boolean skipIme = source.getType() == ITYPE_IME - && (legacySoftInputMode & LayoutParams.SOFT_INPUT_ADJUST_RESIZE) == 0; boolean skipLegacyTypes = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE - && (toPublicType(type) & Type.compatSystemInsets()) != 0; - if (skipSystemBars || skipIme || skipLegacyTypes || skipNonImeInImeMode) { + && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR + || type == ITYPE_IME); + if (skipSystemBars || skipLegacyTypes || skipNonImeInImeMode) { typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible(); continue; } @@ -175,8 +179,11 @@ public class InsetsState implements Parcelable { typeMaxInsetsMap, null /* typeSideMap */, null /* typeVisibilityMap */); } } + final int softInputAdjustMode = legacySoftInputMode & SOFT_INPUT_MASK_ADJUST; return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound, - alwaysConsumeSystemBars, cutout); + alwaysConsumeSystemBars, cutout, softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE + ? systemBars() | ime() + : systemBars()); } private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility, diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 52ea2b2142ad..17825444a524 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -379,7 +379,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall * This gets called on a RenderThread worker thread, so members accessed here must * be protected by a lock. */ - final boolean useBLAST = ViewRootImpl.USE_BLAST_BUFFERQUEUE; + final boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER; viewRoot.registerRtFrameCallback(frame -> { try { final SurfaceControl.Transaction t = useBLAST ? @@ -1107,7 +1107,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t, Rect position, long frameNumber) { - if (frameNumber > 0 && ViewRootImpl.USE_BLAST_BUFFERQUEUE == false) { + if (frameNumber > 0 && !WindowManagerGlobal.USE_BLAST_ADAPTER) { final ViewRootImpl viewRoot = getViewRootImpl(); t.deferTransactionUntilSurface(surface, viewRoot.mSurface, @@ -1125,7 +1125,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } private void setParentSpaceRectangle(Rect position, long frameNumber) { - final boolean useBLAST = ViewRootImpl.USE_BLAST_BUFFERQUEUE; + final boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER; final ViewRootImpl viewRoot = getViewRootImpl(); final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() : mRtTransaction; @@ -1186,7 +1186,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @Override public void positionLost(long frameNumber) { - boolean useBLAST = ViewRootImpl.USE_BLAST_BUFFERQUEUE; + boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER; if (DEBUG) { Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d", System.identityHashCode(this), frameNumber)); @@ -1524,7 +1524,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @Override public void invalidate(boolean invalidateCache) { super.invalidate(invalidateCache); - if (ViewRootImpl.USE_BLAST_BUFFERQUEUE == false) { + if (!WindowManagerGlobal.USE_BLAST_ADAPTER) { return; } final ViewRootImpl viewRoot = getViewRootImpl(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 522ff9a3bcb5..bf8dc65abe28 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -192,11 +192,6 @@ public final class ViewRootImpl implements ViewParent, private static final boolean MT_RENDERER_AVAILABLE = true; /** - * @hide - */ - public static final boolean USE_BLAST_BUFFERQUEUE = false; - - /** * If set to 2, the view system will switch from using rectangles retrieved from window to * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets * directly from the full configuration, enabling richer information about the insets state, as @@ -1312,7 +1307,7 @@ public final class ViewRootImpl implements ViewParent, } mWindowAttributes.privateFlags |= compatibleWindowFlag; - if (USE_BLAST_BUFFERQUEUE) { + if (WindowManagerGlobal.USE_BLAST_ADAPTER) { mWindowAttributes.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST; } @@ -7273,7 +7268,7 @@ public final class ViewRootImpl implements ViewParent, mPendingStableInsets, mPendingBackDropFrame, mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets); if (mSurfaceControl.isValid()) { - if (USE_BLAST_BUFFERQUEUE == false) { + if (!WindowManagerGlobal.USE_BLAST_ADAPTER) { mSurface.copyFrom(mSurfaceControl); } else { mSurface.transferFrom(getOrCreateBLASTSurface( diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index a9cc50f9e65e..9df131de6754 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -27,8 +27,8 @@ import static android.view.WindowInsets.Type.STATUS_BARS; import static android.view.WindowInsets.Type.SYSTEM_GESTURES; import static android.view.WindowInsets.Type.TAPPABLE_ELEMENT; import static android.view.WindowInsets.Type.all; -import static android.view.WindowInsets.Type.compatSystemInsets; import static android.view.WindowInsets.Type.indexOf; +import static android.view.WindowInsets.Type.systemBars; import android.annotation.IntDef; import android.annotation.IntRange; @@ -87,6 +87,8 @@ public final class WindowInsets { private final boolean mStableInsetsConsumed; private final boolean mDisplayCutoutConsumed; + private final int mCompatInsetTypes; + /** * Since new insets may be added in the future that existing apps couldn't * know about, this fully empty constant shouldn't be made available to apps @@ -112,7 +114,7 @@ public final class WindowInsets { boolean isRound, boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) { this(createCompatTypeMap(systemWindowInsetsRect), createCompatTypeMap(stableInsetsRect), createCompatVisibilityMap(createCompatTypeMap(systemWindowInsetsRect)), - isRound, alwaysConsumeSystemBars, displayCutout); + isRound, alwaysConsumeSystemBars, displayCutout, systemBars()); } /** @@ -131,7 +133,7 @@ public final class WindowInsets { @Nullable Insets[] typeMaxInsetsMap, boolean[] typeVisibilityMap, boolean isRound, - boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) { + boolean alwaysConsumeSystemBars, DisplayCutout displayCutout, int compatInsetTypes) { mSystemWindowInsetsConsumed = typeInsetsMap == null; mTypeInsetsMap = mSystemWindowInsetsConsumed ? new Insets[SIZE] @@ -145,6 +147,7 @@ public final class WindowInsets { mTypeVisibilityMap = typeVisibilityMap; mIsRound = isRound; mAlwaysConsumeSystemBars = alwaysConsumeSystemBars; + mCompatInsetTypes = compatInsetTypes; mDisplayCutoutConsumed = displayCutout == null; mDisplayCutout = (mDisplayCutoutConsumed || displayCutout.isEmpty()) @@ -160,7 +163,8 @@ public final class WindowInsets { this(src.mSystemWindowInsetsConsumed ? null : src.mTypeInsetsMap, src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap, src.mTypeVisibilityMap, src.mIsRound, - src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src)); + src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src), + src.mCompatInsetTypes); } private static DisplayCutout displayCutoutCopyConstructorArgument(WindowInsets w) { @@ -211,7 +215,8 @@ public final class WindowInsets { /** @hide */ @UnsupportedAppUsage public WindowInsets(Rect systemWindowInsets) { - this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null); + this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null, + systemBars()); } /** @@ -280,7 +285,7 @@ public final class WindowInsets { */ @NonNull public Insets getSystemWindowInsets() { - return getInsets(mTypeInsetsMap, compatSystemInsets()); + return getInsets(mTypeInsetsMap, mCompatInsetTypes); } /** @@ -439,7 +444,8 @@ public final class WindowInsets { mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap, mIsRound, mAlwaysConsumeSystemBars, - null /* displayCutout */); + null /* displayCutout */, + mCompatInsetTypes); } @@ -485,7 +491,8 @@ public final class WindowInsets { return new WindowInsets(null, mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap, mIsRound, mAlwaysConsumeSystemBars, - displayCutoutCopyConstructorArgument(this)); + displayCutoutCopyConstructorArgument(this), + mCompatInsetTypes); } // TODO(b/119190588): replace @code with @link below @@ -555,7 +562,7 @@ public final class WindowInsets { */ @NonNull public Insets getStableInsets() { - return getInsets(mTypeMaxInsetsMap, compatSystemInsets()); + return getInsets(mTypeMaxInsetsMap, mCompatInsetTypes); } /** @@ -733,7 +740,8 @@ public final class WindowInsets { public WindowInsets consumeStableInsets() { return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap, null, mTypeVisibilityMap, mIsRound, mAlwaysConsumeSystemBars, - displayCutoutCopyConstructorArgument(this)); + displayCutoutCopyConstructorArgument(this), + mCompatInsetTypes); } /** @@ -817,7 +825,8 @@ public final class WindowInsets { ? null : mDisplayCutout == null ? DisplayCutout.NO_CUTOUT - : mDisplayCutout.inset(left, top, right, bottom)); + : mDisplayCutout.inset(left, top, right, bottom), + mCompatInsetTypes); } @Override @@ -1134,7 +1143,8 @@ public final class WindowInsets { public WindowInsets build() { return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap, mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap, - mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout); + mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout, + systemBars()); } } @@ -1271,15 +1281,6 @@ public final class WindowInsets { } /** - * @return Inset types representing the list of bars that traditionally were denoted as - * system insets. - * @hide - */ - static @InsetsType int compatSystemInsets() { - return STATUS_BARS | NAVIGATION_BARS | IME; - } - - /** * @return All inset types combined. * * TODO: Figure out if this makes sense at all, mixing e.g {@link #systemGestures()} and diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 95780023563d..7d5564e1c8be 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -57,6 +57,12 @@ public final class WindowManagerGlobal { private static final String TAG = "WindowManager"; /** + * This flag controls whether ViewRootImpl will utilize the Blast Adapter + * to send buffer updates to SurfaceFlinger + */ + public static final boolean USE_BLAST_ADAPTER = false; + + /** * The user is navigating with keys (not the touch screen), so * navigational focus should be shown. */ diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index 27c2478bd420..67cfc3a7dd49 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -121,7 +121,11 @@ public class LatencyTracker { } public static boolean isEnabled(Context ctx) { - return Build.IS_DEBUGGABLE && getInstance(ctx).mEnabled; + return getInstance(ctx).isEnabled(); + } + + public boolean isEnabled() { + return Build.IS_DEBUGGABLE && mEnabled; } /** diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index 60620881b6f2..fa2ffccaaa63 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -23,6 +23,7 @@ import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.WindowInsets.Type.ime; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static org.junit.Assert.assertEquals; @@ -116,14 +117,18 @@ public class InsetsStateTest { @Test public void testCalculateInsets_imeIgnoredWithoutAdjustResize() { - mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100)); - mState.getSource(ITYPE_STATUS_BAR).setVisible(true); - mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300)); - mState.getSource(ITYPE_IME).setVisible(true); - WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, - DisplayCutout.NO_CUTOUT, null, null, 0, null); - assertEquals(0, insets.getSystemWindowInsetBottom()); - assertTrue(insets.isVisible(ime())); + try (final InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { + mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(ITYPE_STATUS_BAR).setVisible(true); + mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300)); + mState.getSource(ITYPE_IME).setVisible(true); + WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false, + DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_NOTHING, null); + assertEquals(0, insets.getSystemWindowInsetBottom()); + assertEquals(100, insets.getInsets(ime()).bottom); + assertTrue(insets.isVisible(ime())); + } } @Test diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java index 8c7b28af2e78..e5a4f6d5b3be 100644 --- a/core/tests/coretests/src/android/view/WindowInsetsTest.java +++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java @@ -63,7 +63,8 @@ public class WindowInsetsTest { b.setInsets(navigationBars(), Insets.of(0, 0, 0, 100)); b.setInsets(ime(), Insets.of(0, 0, 0, 300)); WindowInsets insets = b.build(); - assertEquals(300, insets.getSystemWindowInsets().bottom); + assertEquals(100, insets.getSystemWindowInsets().bottom); + assertEquals(300, insets.getInsets(ime()).bottom); } // TODO: Move this to CTS once API made public diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml index 37cd1d49cb85..0b346260896a 100644 --- a/packages/CarSystemUI/res/layout/super_status_bar.xml +++ b/packages/CarSystemUI/res/layout/super_status_bar.xml @@ -93,7 +93,7 @@ android:layout_height="match_parent" android:visibility="invisible"/> - <ViewStub android:id="@+id/status_bar_expanded" + <include layout="@layout/status_bar_expanded" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible"/> diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 77db54c067c6..3c3ebe2fe228 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -128,11 +128,11 @@ import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.phone.StatusBarComponent; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter; import com.android.systemui.statusbar.phone.StatusBarWindowController; +import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -465,7 +465,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt super.start(); - mNotificationPanel.setScrollingEnabled(true); + mNotificationPanelViewController.setScrollingEnabled(true); mSettleOpenPercentage = mContext.getResources().getInteger( R.integer.notification_settle_open_percentage); mSettleClosePercentage = mContext.getResources().getInteger( diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java index 1ebaef702286..a1eccceea771 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java @@ -86,11 +86,11 @@ import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; -import com.android.systemui.statusbar.phone.StatusBarComponent; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter; import com.android.systemui.statusbar.phone.StatusBarWindowController; +import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 4869be11a906..479f255b7e44 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -17,9 +17,14 @@ */ --> -<merge + +<com.android.systemui.statusbar.phone.NotificationPanelView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res-auto"> + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:id="@+id/notification_panel" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/transparent"> <FrameLayout android:id="@+id/big_clock_container" android:layout_width="match_parent" @@ -97,4 +102,4 @@ android:background="@drawable/qs_navbar_scrim" /> <include layout="@layout/status_bar_expanded_plugin_frame"/> -</merge> +</com.android.systemui.statusbar.phone.NotificationPanelView> diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml index 57834da76285..9716a00a7f72 100644 --- a/packages/SystemUI/res/layout/super_status_bar.xml +++ b/packages/SystemUI/res/layout/super_status_bar.xml @@ -64,7 +64,7 @@ sysui:ignoreRightInset="true" /> - <ViewStub android:id="@+id/status_bar_expanded" + <include layout="@layout/status_bar_expanded" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible" /> diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index eecc54c678c0..bbe972dea11f 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -121,6 +121,7 @@ import com.android.systemui.util.leak.GarbageMonitor; import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.leak.LeakReporter; import com.android.systemui.util.sensors.AsyncSensorManager; +import com.android.systemui.wm.DisplayImeController; import com.android.systemui.wm.DisplayWindowController; import com.android.systemui.wm.SystemWindows; @@ -321,6 +322,7 @@ public class Dependency { @Inject Lazy<StatusBar> mStatusBar; @Inject Lazy<DisplayWindowController> mDisplayWindowController; @Inject Lazy<SystemWindows> mSystemWindows; + @Inject Lazy<DisplayImeController> mDisplayImeController; @Inject public Dependency() { @@ -509,6 +511,7 @@ public class Dependency { mProviders.put(StatusBar.class, mStatusBar::get); mProviders.put(DisplayWindowController.class, mDisplayWindowController::get); mProviders.put(SystemWindows.class, mSystemWindows::get); + mProviders.put(DisplayImeController.class, mDisplayImeController::get); // TODO(b/118592525): to support multi-display , we start to add something which is // per-display, while others may be global. I think it's time to add diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java index 73b658494f76..26337b1f24b1 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java @@ -46,6 +46,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.LatencyTracker; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.PackageManagerWrapper; @@ -73,6 +74,19 @@ public class SystemServicesModule { @Provides @Singleton + static ActivityManager provideActivityManager(Context context) { + return context.getSystemService(ActivityManager.class); + } + + + @Provides + @DisplayId + static int provideDisplayId(Context context) { + return context.getDisplayId(); + } + + @Provides + @Singleton static DevicePolicyManager provideDevicePolicyManager(Context context) { return context.getSystemService(DevicePolicyManager.class); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 442313d763ec..58ddda9ce720 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -35,7 +35,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.NotifL import com.android.systemui.statusbar.notification.people.PeopleHubModule; import com.android.systemui.statusbar.phone.KeyguardLiftController; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.phone.StatusBarComponent; +import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.concurrency.ConcurrencyModule; import com.android.systemui.util.sensors.AsyncSensorManager; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java new file mode 100644 index 000000000000..155a6d224130 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface DisplayId { +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 8f2c3ed9e62c..e13c3e087893 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -87,7 +87,7 @@ import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationPanelView; +import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarWindowController; @@ -2105,7 +2105,7 @@ public class KeyguardViewMediator extends SystemUI { } public StatusBarKeyguardViewManager registerStatusBar(StatusBar statusBar, - ViewGroup container, NotificationPanelView panelView, + ViewGroup container, NotificationPanelViewController panelView, BiometricUnlockController biometricUnlockController, ViewGroup lockIconContainer, View notificationContainer, KeyguardBypassController bypassController) { mStatusBarKeyguardViewManagerLazy.get().registerStatusBar(statusBar, container, panelView, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java index 525b5b795a03..12749fd8fce2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java @@ -368,13 +368,14 @@ public class FlingAnimationUtils { public static class Builder { private final DisplayMetrics mDisplayMetrics; float mMaxLengthSeconds; - float mSpeedUpFactor = 0.0f; - float mX2 = -1.0f; - float mY2 = 1.0f; + float mSpeedUpFactor; + float mX2; + float mY2; @Inject public Builder(DisplayMetrics displayMetrics) { mDisplayMetrics = displayMetrics; + reset(); } public Builder setMaxLengthSeconds(float maxLengthSeconds) { @@ -397,6 +398,15 @@ public class FlingAnimationUtils { return this; } + public Builder reset() { + mMaxLengthSeconds = 0; + mSpeedUpFactor = 0.0f; + mX2 = -1.0f; + mY2 = 1.0f; + + return this; + } + public FlingAnimationUtils build() { return new FlingAnimationUtils(mDisplayMetrics, mMaxLengthSeconds, mSpeedUpFactor, mX2, mY2); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java index 9b312341c583..66605690b106 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -37,7 +37,7 @@ import com.android.systemui.Interpolators; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; -import com.android.systemui.statusbar.phone.NotificationPanelView; +import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.StatusBarWindowViewController; /** @@ -53,7 +53,7 @@ public class ActivityLaunchAnimator { CollapsedStatusBarFragment.FADE_IN_DURATION - CollapsedStatusBarFragment.FADE_IN_DELAY - 16; private static final long LAUNCH_TIMEOUT = 500; - private final NotificationPanelView mNotificationPanel; + private final NotificationPanelViewController mNotificationPanel; private final NotificationListContainer mNotificationContainer; private final float mWindowCornerRadius; private final StatusBarWindowViewController mStatusBarWindowViewController; @@ -69,7 +69,7 @@ public class ActivityLaunchAnimator { public ActivityLaunchAnimator( StatusBarWindowViewController statusBarWindowViewController, Callback callback, - NotificationPanelView notificationPanel, + NotificationPanelViewController notificationPanel, NotificationListContainer container) { mNotificationPanel = notificationPanel; mNotificationContainer = container; 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 71342c597bfa..823dd5a73ca6 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 @@ -134,7 +134,7 @@ import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener; import com.android.systemui.statusbar.phone.NotificationIconAreaController; -import com.android.systemui.statusbar.phone.NotificationPanelView; +import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; @@ -496,8 +496,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd protected boolean mClearAllEnabled; private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN; - private NotificationPanelView mNotificationPanel; - private final ShadeController mShadeController = Dependency.get(ShadeController.class); + private NotificationPanelViewController mNotificationPanelController; private final NotificationGutsManager mNotificationGutsManager; private final NotificationSectionsManager mSectionsManager; @@ -5519,7 +5518,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd if (viewsToRemove.isEmpty()) { if (closeShade) { - mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); + Dependency.get(ShadeController.class).animateCollapsePanels( + CommandQueue.FLAG_EXCLUDE_NONE); } return; } @@ -5577,11 +5577,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd final Runnable onSlideAwayAnimationComplete = () -> { if (closeShade) { - mShadeController.addPostCollapseAction(() -> { + Dependency.get(ShadeController.class).addPostCollapseAction(() -> { setDismissAllInProgress(false); onAnimationComplete.run(); }); - mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); + Dependency.get(ShadeController.class).animateCollapsePanels( + CommandQueue.FLAG_EXCLUDE_NONE); } else { setDismissAllInProgress(false); onAnimationComplete.run(); @@ -5657,8 +5658,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setNotificationPanel(NotificationPanelView notificationPanelView) { - mNotificationPanel = notificationPanelView; + public void setNotificationPanelController( + NotificationPanelViewController notificationPanelViewController) { + mNotificationPanelController = notificationPanelViewController; } public void updateIconAreaViews() { @@ -6402,7 +6404,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd if (!mAmbientState.isDozing() || startingChild != null) { // We have notifications, go to locked shade. - mShadeController.goToLockedShade(startingChild); + Dependency.get(ShadeController.class).goToLockedShade(startingChild); if (startingChild instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild; row.onExpandedByGesture(true /* drag down is always an open */); @@ -6441,7 +6443,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override public void setEmptyDragAmount(float amount) { - mNotificationPanel.setEmptyDragAmount(amount); + mNotificationPanelController.setEmptyDragAmount(amount); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index 8b31da4e54bd..e03db2c8b9c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -230,7 +230,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue // The shelf will be hidden when dozing with a custom clock, we must show notification // icons in this occasion. if (mStatusBarStateController.isDozing() - && mStatusBarComponent.getPanel().hasCustomClock()) { + && mStatusBarComponent.getPanelController().hasCustomClock()) { state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java index ce1123ee67e0..accd2a4fcc0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java @@ -91,7 +91,7 @@ public final class DozeServiceHost implements DozeHost { private final LockscreenLockIconController mLockscreenLockIconController; private NotificationIconAreaController mNotificationIconAreaController; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - private NotificationPanelView mNotificationPanel; + private NotificationPanelViewController mNotificationPanel; private View mAmbientIndicationContainer; private StatusBar mStatusBar; @@ -141,7 +141,7 @@ public final class DozeServiceHost implements DozeHost { NotificationIconAreaController notificationIconAreaController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, StatusBarWindowViewController statusBarWindowViewController, - NotificationPanelView notificationPanel, View ambientIndicationContainer) { + NotificationPanelViewController notificationPanel, View ambientIndicationContainer) { mStatusBar = statusBar; mNotificationIconAreaController = notificationIconAreaController; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 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 8e5a9126f08a..7b20a7b7037c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -58,7 +58,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, private final View mClockView; private final View mOperatorNameView; private final DarkIconDispatcher mDarkIconDispatcher; - private final NotificationPanelView mPanelView; + private final NotificationPanelViewController mNotificationPanelViewController; private final Consumer<ExpandableNotificationRow> mSetTrackingHeadsUp = this::setTrackingHeadsUp; private final Runnable mUpdatePanelTranslation = this::updatePanelTranslation; @@ -96,13 +96,14 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, SysuiStatusBarStateController statusBarStateController, KeyguardBypassController keyguardBypassController, KeyguardStateController keyguardStateController, - NotificationWakeUpCoordinator wakeUpCoordinator, CommandQueue commandQueue) { + NotificationWakeUpCoordinator wakeUpCoordinator, CommandQueue commandQueue, + NotificationPanelViewController notificationPanelViewController) { this(notificationIconAreaController, headsUpManager, statusBarStateController, keyguardBypassController, wakeUpCoordinator, keyguardStateController, commandQueue, statusbarView.findViewById(R.id.heads_up_status_bar_view), statusbarView.findViewById(R.id.notification_stack_scroller), - statusbarView.findViewById(R.id.notification_panel), + notificationPanelViewController, statusbarView.findViewById(R.id.clock), statusbarView.findViewById(R.id.operator_name_frame), statusbarView.findViewById(R.id.centered_icon_area)); @@ -119,7 +120,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, CommandQueue commandQueue, HeadsUpStatusBarView headsUpStatusBarView, NotificationStackScrollLayout stackScroller, - NotificationPanelView panelView, + NotificationPanelViewController notificationPanelViewController, View clockView, View operatorNameView, View centeredIconView) { @@ -131,10 +132,10 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, headsUpStatusBarView.setOnDrawingRectChangedListener( () -> updateIsolatedIconLocation(true /* requireUpdate */)); mStackScroller = stackScroller; - mPanelView = panelView; - panelView.addTrackingHeadsUpListener(mSetTrackingHeadsUp); - panelView.addVerticalTranslationListener(mUpdatePanelTranslation); - panelView.setHeadsUpAppearanceController(this); + mNotificationPanelViewController = notificationPanelViewController; + notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp); + notificationPanelViewController.addVerticalTranslationListener(mUpdatePanelTranslation); + notificationPanelViewController.setHeadsUpAppearanceController(this); mStackScroller.addOnExpandedHeightChangedListener(mSetExpandedHeight); mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener); mStackScroller.setHeadsUpAppearanceController(this); @@ -169,9 +170,9 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, mHeadsUpManager.removeListener(this); mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null); mWakeUpCoordinator.removeListener(this); - mPanelView.removeTrackingHeadsUpListener(mSetTrackingHeadsUp); - mPanelView.removeVerticalTranslationListener(mUpdatePanelTranslation); - mPanelView.setHeadsUpAppearanceController(null); + mNotificationPanelViewController.removeTrackingHeadsUpListener(mSetTrackingHeadsUp); + mNotificationPanelViewController.removeVerticalTranslationListener(mUpdatePanelTranslation); + mNotificationPanelViewController.setHeadsUpAppearanceController(null); mStackScroller.removeOnExpandedHeightChangedListener(mSetExpandedHeight); mStackScroller.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener); mDarkIconDispatcher.removeDarkReceiver(this); 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 ac06d9de92a3..c282cb8a5cb5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java @@ -39,12 +39,12 @@ public class HeadsUpTouchHelper implements Gefingerpoken { private boolean mTouchingHeadsUpView; private boolean mTrackingHeadsUp; private boolean mCollapseSnoozes; - private NotificationPanelView mPanel; + private NotificationPanelViewController mPanel; private ExpandableNotificationRow mPickedChild; public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager, Callback callback, - NotificationPanelView notificationPanelView) { + NotificationPanelViewController notificationPanelView) { mHeadsUpManager = headsUpManager; mCallback = callback; mPanel = notificationPanelView; 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 d95d2b7db4c9..d3e44eaf17b1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -155,7 +155,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL }; private boolean mLeftIsVoiceAssist; - private AssistManager mAssistManager; private Drawable mLeftAssistIcon; private IntentButton mRightButton = new DefaultRightButton(); @@ -254,7 +253,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mActivityStarter = Dependency.get(ActivityStarter.class); mFlashlightController = Dependency.get(FlashlightController.class); mAccessibilityController = Dependency.get(AccessibilityController.class); - mAssistManager = Dependency.get(AssistManager.class); mActivityIntentHelper = new ActivityIntentHelper(getContext()); updateLeftAffordance(); } @@ -551,7 +549,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL Runnable runnable = new Runnable() { @Override public void run() { - mAssistManager.launchVoiceAssistFromKeyguard(); + Dependency.get(AssistManager.class).launchVoiceAssistFromKeyguard(); } }; if (!mKeyguardStateController.canDismissLockScreen()) { @@ -565,7 +563,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } private boolean canLaunchVoiceAssist() { - return mAssistManager.canVoiceAssistBeLaunchedFromKeyguard(); + return Dependency.get(AssistManager.class).canVoiceAssistBeLaunchedFromKeyguard(); } private void launchPhone() { @@ -647,7 +645,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } if (mLeftIsVoiceAssist) { mLeftPreview = mPreviewInflater.inflatePreviewFromService( - mAssistManager.getVoiceInteractorComponentName()); + Dependency.get(AssistManager.class).getVoiceInteractorComponentName()); } else { mLeftPreview = mPreviewInflater.inflatePreview(mLeftButton.getIntent()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 4e91e4c84e99..a3f14ba28dcb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -90,7 +90,7 @@ public class KeyguardClockPositionAlgorithm { private int mContainerTopPadding; /** - * @see NotificationPanelView#getExpandedFraction() + * @see NotificationPanelViewController#getExpandedFraction() */ private float mPanelExpansion; 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 d4cf272e8077..a3b1b5f8360b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -350,7 +350,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mIsOnDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY; } - mNavigationBarView.setComponents(mStatusBarLazy.get().getPanel(), mAssistManager); + mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController()); mNavigationBarView.setDisabledFlags(mDisabledFlags1); mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged); mNavigationBarView.setOnTouchListener(this::onNavigationTouch); 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 5a1b20dd1e71..ba9ba6c9c702 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -67,7 +67,6 @@ import com.android.systemui.DockedStackExistsListener; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.assist.AssistHandleViewController; -import com.android.systemui.assist.AssistManager; import com.android.systemui.model.SysUiState; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; @@ -148,7 +147,7 @@ public class NavigationBarView extends FrameLayout implements private NavigationBarInflaterView mNavigationInflaterView; private RecentsOnboarding mRecentsOnboarding; - private NotificationPanelView mPanelView; + private NotificationPanelViewController mPanelView; private FloatingRotationButton mFloatingRotationButton; private RotationButtonController mRotationButtonController; @@ -349,7 +348,7 @@ public class NavigationBarView extends FrameLayout implements return mBarTransitions.getLightTransitionsController(); } - public void setComponents(NotificationPanelView panel, AssistManager assistManager) { + public void setComponents(NotificationPanelViewController panel) { mPanelView = panel; updatePanelSystemUiStateFlags(); } 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 c74286dfe3ff..0f3af095f7be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -16,118 +16,15 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; -import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; -import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.app.ActivityManager; -import android.app.Fragment; -import android.app.StatusBarManager; import android.content.Context; -import android.content.pm.ResolveInfo; -import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; -import android.graphics.PointF; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; -import android.graphics.Rect; -import android.graphics.Region; -import android.hardware.biometrics.BiometricSourceType; -import android.os.PowerManager; -import android.os.SystemClock; -import android.provider.DeviceConfig; -import android.provider.Settings; import android.util.AttributeSet; -import android.util.Log; -import android.util.MathUtils; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowInsets; -import android.view.accessibility.AccessibilityManager; -import android.widget.FrameLayout; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.keyguard.KeyguardClockSwitch; -import com.android.keyguard.KeyguardStatusView; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.systemui.DejankUtils; -import com.android.systemui.Dependency; -import com.android.systemui.Interpolators; -import com.android.systemui.R; -import com.android.systemui.doze.DozeLog; -import com.android.systemui.fragments.FragmentHostManager; -import com.android.systemui.fragments.FragmentHostManager.FragmentListener; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.HomeControlsPlugin; -import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.qs.QS; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; -import com.android.systemui.qs.QSFragment; -import com.android.systemui.shared.plugins.PluginManager; -import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.FlingAnimationUtils; -import com.android.systemui.statusbar.GestureRecorder; -import com.android.systemui.statusbar.KeyguardAffordanceView; -import com.android.systemui.statusbar.KeyguardIndicationController; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationShelf; -import com.android.systemui.statusbar.PulseExpansionHandler; -import com.android.systemui.statusbar.RemoteInputController; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; -import com.android.systemui.statusbar.notification.AnimatableProperty; -import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.PropertyAnimator; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.row.ExpandableView; -import com.android.systemui.statusbar.notification.stack.AnimationProperties; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; -import com.android.systemui.statusbar.notification.stack.StackStateAnimator; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; -import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.systemui.util.InjectionInflationController; -import com.android.systemui.util.Utils; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; - -import javax.inject.Inject; -import javax.inject.Named; -public class NotificationPanelView extends PanelView implements - ExpandableView.OnHeightChangedListener, - View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener, - KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener, - OnHeadsUpChangedListener, QS.HeightListener, ZenModeController.Callback, - ConfigurationController.ConfigurationListener, StateListener, - PulseExpansionHandler.ExpansionCallback, DynamicPrivacyController.Listener, - NotificationWakeUpCoordinator.WakeUpListener { +public class NotificationPanelView extends PanelView { private static final boolean DEBUG = false; @@ -136,2873 +33,34 @@ public class NotificationPanelView extends PanelView implements */ public static final int FLING_EXPAND = 0; - /** - * Fling collapsing QS, potentially stopping when QS becomes QQS. - */ - public static final int FLING_COLLAPSE = 1; - - /** - * Fling until QS is completely hidden. - */ - public static final int FLING_HIDE = 2; - private final DozeParameters mDozeParameters; - - private double mQqsSplitFraction; - - // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is - // changed. - private static final int CAP_HEIGHT = 1456; - private static final int FONT_HEIGHT = 2163; - - /** - * Maximum time before which we will expand the panel even for slow motions when getting a - * touch passed over from launcher. - */ - private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300; - static final String COUNTER_PANEL_OPEN = "panel_open"; static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs"; - private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek"; - - private static final Rect mDummyDirtyRect = new Rect(0, 0, 1, 1); - private static final Rect mEmptyRect = new Rect(); - - private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = new AnimationProperties() - .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); - private static final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT - = AnimatableProperty.from("KEYGUARD_HEADS_UP_SHOWING_AMOUNT", - NotificationPanelView::setKeyguardHeadsUpShowingAmount, - NotificationPanelView::getKeyguardHeadsUpShowingAmount, - R.id.keyguard_hun_animator_tag, - R.id.keyguard_hun_animator_end_tag, - R.id.keyguard_hun_animator_start_tag); - private static final AnimationProperties KEYGUARD_HUN_PROPERTIES = - new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); - @VisibleForTesting - final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback = - new KeyguardUpdateMonitorCallback() { - - @Override - public void onBiometricAuthenticated(int userId, - BiometricSourceType biometricSourceType) { - if (mFirstBypassAttempt && mUpdateMonitor.isUnlockingWithBiometricAllowed()) { - mDelayShowingKeyguardStatusBar = true; - } - } - - @Override - public void onBiometricRunningStateChanged(boolean running, - BiometricSourceType biometricSourceType) { - boolean keyguardOrShadeLocked = mBarState == StatusBarState.KEYGUARD - || mBarState == StatusBarState.SHADE_LOCKED; - if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing - && !mDelayShowingKeyguardStatusBar) { - mFirstBypassAttempt = false; - animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); - } - } - - @Override - public void onFinishedGoingToSleep(int why) { - mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); - mDelayShowingKeyguardStatusBar = false; - } - }; - private final KeyguardStateController.Callback mKeyguardMonitorCallback = - new KeyguardStateController.Callback() { - @Override - public void onKeyguardFadingAwayChanged() { - if (!mKeyguardStateController.isKeyguardFadingAway()) { - mFirstBypassAttempt = false; - mDelayShowingKeyguardStatusBar = false; - } - } - }; - - private final InjectionInflationController mInjectionInflationController; - private final PowerManager mPowerManager; - private final AccessibilityManager mAccessibilityManager; - private final NotificationWakeUpCoordinator mWakeUpCoordinator; - private final PulseExpansionHandler mPulseExpansionHandler; - private final KeyguardBypassController mKeyguardBypassController; - private final KeyguardUpdateMonitor mUpdateMonitor; - - @VisibleForTesting - protected KeyguardAffordanceHelper mAffordanceHelper; - private KeyguardUserSwitcher mKeyguardUserSwitcher; - @VisibleForTesting - protected KeyguardStatusBarView mKeyguardStatusBar; - @VisibleForTesting - protected ViewGroup mBigClockContainer; - private QS mQs; - @VisibleForTesting - protected FrameLayout mQsFrame; - @VisibleForTesting - protected KeyguardStatusView mKeyguardStatusView; - private View mQsNavbarScrim; - protected NotificationsQuickSettingsContainer mNotificationContainerParent; - protected NotificationStackScrollLayout mNotificationStackScroller; - protected FrameLayout mHomeControlsLayout; - private boolean mAnimateNextPositionUpdate; - - private int mTrackingPointer; - private VelocityTracker mQsVelocityTracker; - private boolean mQsTracking; - - /** - * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and - * the expansion for quick settings. - */ - private boolean mConflictingQsExpansionGesture; - - /** - * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't - * intercepted yet. - */ - private boolean mIntercepting; - private boolean mPanelExpanded; - private boolean mQsExpanded; - private boolean mQsExpandedWhenExpandingStarted; - private boolean mQsFullyExpanded; - private boolean mKeyguardShowing; - private boolean mDozing; - private boolean mDozingOnDown; - protected int mBarState; - private float mInitialHeightOnTouch; - private float mInitialTouchX; - private float mInitialTouchY; - private float mLastTouchX; - private float mLastTouchY; - protected float mQsExpansionHeight; - protected int mQsMinExpansionHeight; - protected int mQsMaxExpansionHeight; - private int mQsPeekHeight; - private boolean mStackScrollerOverscrolling; - private boolean mQsExpansionFromOverscroll; - private float mLastOverscroll; - protected boolean mQsExpansionEnabled = true; - private ValueAnimator mQsExpansionAnimator; - private FlingAnimationUtils mFlingAnimationUtils; - private int mStatusBarMinHeight; - private int mNotificationsHeaderCollideDistance; - private int mUnlockMoveDistance; - private float mEmptyDragAmount; - private float mDownX; - private float mDownY; - - private final KeyguardClockPositionAlgorithm mClockPositionAlgorithm = - new KeyguardClockPositionAlgorithm(); - private final KeyguardClockPositionAlgorithm.Result mClockPositionResult = - new KeyguardClockPositionAlgorithm.Result(); - private boolean mIsExpanding; - - private boolean mBlockTouches; - // Used for two finger gesture as well as accessibility shortcut to QS. - private boolean mQsExpandImmediate; - private boolean mTwoFingerQsExpandPossible; - - /** - * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still - * need to take this into account in our panel height calculation. - */ - private boolean mQsAnimatorExpand; - private boolean mIsLaunchTransitionFinished; - private boolean mIsLaunchTransitionRunning; - private Runnable mLaunchAnimationEndRunnable; - private boolean mOnlyAffordanceInThisMotion; - private boolean mKeyguardStatusViewAnimating; - private ValueAnimator mQsSizeChangeAnimator; - - private boolean mShowEmptyShadeView; - - private boolean mQsScrimEnabled = true; - private boolean mLastAnnouncementWasQuickSettings; - private boolean mQsTouchAboveFalsingThreshold; - private int mQsFalsingThreshold; - private float mKeyguardStatusBarAnimateAlpha = 1f; - private int mOldLayoutDirection; - private HeadsUpTouchHelper mHeadsUpTouchHelper; - private boolean mIsExpansionFromHeadsUp; - private boolean mListenForHeadsUp; - private int mNavigationBarBottomHeight; - private boolean mExpandingFromHeadsUp; - private boolean mCollapsedOnDown; - private int mPositionMinSideMargin; - private int mMaxFadeoutHeight; - private int mLastOrientation = -1; - private boolean mClosingWithAlphaFadeOut; - private boolean mHeadsUpAnimatingAway; - private boolean mLaunchingAffordance; - private boolean mAffordanceHasPreview; - private FalsingManager mFalsingManager; - private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; - - private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() { - @Override - public void run() { - setHeadsUpAnimatingAway(false); - notifyBarPanelExpansionChanged(); - } - }; - private NotificationGroupManager mGroupManager; - private boolean mShowIconsWhenExpanded; - private int mIndicationBottomPadding; - private int mAmbientIndicationBottomPadding; - private boolean mIsFullWidth; - private boolean mBlockingExpansionForCurrentTouch; - - /** - * Following variables maintain state of events when input focus transfer may occur. - */ - private boolean mExpectingSynthesizedDown; // expecting to see synthesized DOWN event - private boolean mLastEventSynthesizedDown; // last event was synthesized DOWN event - - /** - * Current dark amount that follows regular interpolation curve of animation. - */ - private float mInterpolatedDarkAmount; - - /** - * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the - * interpolation curve is different. - */ - private float mLinearDarkAmount; - - private boolean mPulsing; - private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); - private boolean mNoVisibleNotifications = true; - private boolean mUserSetupComplete; - private int mQsNotificationTopPadding; - private float mExpandOffset; - private boolean mHideIconsDuringNotificationLaunch = true; - private int mStackScrollerMeasuringPass; - private ArrayList<Consumer<ExpandableNotificationRow>> mTrackingHeadsUpListeners - = new ArrayList<>(); - private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>(); - private HeadsUpAppearanceController mHeadsUpAppearanceController; - - private int mPanelAlpha; private int mCurrentPanelAlpha; private final Paint mAlphaPaint = new Paint(); - private Runnable mPanelAlphaEndAction; - private float mBottomAreaShadeAlpha; - private final ValueAnimator mBottomAreaShadeAlphaAnimator; - private AnimatorListenerAdapter mAnimatorListenerAdapter = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (mPanelAlphaEndAction != null) { - mPanelAlphaEndAction.run(); - } - } - }; - private final AnimatableProperty PANEL_ALPHA = AnimatableProperty.from( - "panelAlpha", - NotificationPanelView::setPanelAlphaInternal, - NotificationPanelView::getCurrentPanelAlpha, - R.id.panel_alpha_animator_tag, - R.id.panel_alpha_animator_start_tag, - R.id.panel_alpha_animator_end_tag); - private final AnimationProperties PANEL_ALPHA_OUT_PROPERTIES = new AnimationProperties() - .setDuration(150) - .setCustomInterpolator(PANEL_ALPHA.getProperty(), Interpolators.ALPHA_OUT); - private final AnimationProperties PANEL_ALPHA_IN_PROPERTIES = new AnimationProperties() - .setDuration(200) - .setAnimationFinishListener(mAnimatorListenerAdapter) - .setCustomInterpolator(PANEL_ALPHA.getProperty(), Interpolators.ALPHA_IN); - private final NotificationEntryManager mEntryManager; - - private final CommandQueue mCommandQueue; - private final NotificationLockscreenUserManager mLockscreenUserManager; - private final ShadeController mShadeController; - private int mDisplayId; - - /** - * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged. - * - * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary - * work, check the current id with the cached id. - */ - private int mThemeResId; - private KeyguardIndicationController mKeyguardIndicationController; - private Consumer<Boolean> mAffordanceLaunchListener; - private int mShelfHeight; - private Runnable mOnReinflationListener; - private int mDarkIconSize; - private int mHeadsUpInset; - private boolean mHeadsUpPinnedMode; - private float mKeyguardHeadsUpShowingAmount = 0.0f; - private boolean mShowingKeyguardHeadsUp; - private boolean mAllowExpandForSmallExpansion; - private Runnable mExpandAfterLayoutRunnable; - - /** - * If face auth with bypass is running for the first time after you turn on the screen. - * (From aod or screen off) - */ - private boolean mFirstBypassAttempt; - /** - * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until - * the keyguard is dismissed to show the status bar. - */ - private boolean mDelayShowingKeyguardStatusBar; - - private PluginManager mPluginManager; - private FrameLayout mPluginFrame; - private NPVPluginManager mNPVPluginManager; + private boolean mDozing; + private RtlChangeListener mRtlChangeListener; - @Inject - public NotificationPanelView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - InjectionInflationController injectionInflationController, - NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, - DynamicPrivacyController dynamicPrivacyController, - KeyguardBypassController bypassController, FalsingManager falsingManager, - PluginManager pluginManager, ShadeController shadeController, - NotificationLockscreenUserManager notificationLockscreenUserManager, - NotificationEntryManager notificationEntryManager, - KeyguardStateController keyguardStateController, - StatusBarStateController statusBarStateController, DozeLog dozeLog, - DozeParameters dozeParameters, CommandQueue commandQueue) { - super(context, attrs, falsingManager, dozeLog, keyguardStateController, - (SysuiStatusBarStateController) statusBarStateController); + public NotificationPanelView(Context context, AttributeSet attrs) { + super(context, attrs); setWillNotDraw(!DEBUG); - mInjectionInflationController = injectionInflationController; - mFalsingManager = falsingManager; - mPowerManager = context.getSystemService(PowerManager.class); - mWakeUpCoordinator = coordinator; - mAccessibilityManager = context.getSystemService(AccessibilityManager.class); - setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); mAlphaPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); - setPanelAlpha(255, false /* animate */); - mCommandQueue = commandQueue; - mDisplayId = context.getDisplayId(); - mPulseExpansionHandler = pulseExpansionHandler; - mDozeParameters = dozeParameters; - pulseExpansionHandler.setPulseExpandAbortListener(() -> { - if (mQs != null) { - mQs.animateHeaderSlidingOut(); - } - }); - mThemeResId = context.getThemeResId(); - mKeyguardBypassController = bypassController; - mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); - mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); - mKeyguardStateController.addCallback(mKeyguardMonitorCallback); - dynamicPrivacyController.addListener(this); - - mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0); - mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> { - mBottomAreaShadeAlpha = (float) animation.getAnimatedValue(); - updateKeyguardBottomAreaAlpha(); - }); - mBottomAreaShadeAlphaAnimator.setDuration(160); - mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT); - mPluginManager = pluginManager; - mShadeController = shadeController; - mLockscreenUserManager = notificationLockscreenUserManager; - mEntryManager = notificationEntryManager; setBackgroundColor(Color.TRANSPARENT); } - /** - * Returns if there's a custom clock being presented. - */ - public boolean hasCustomClock() { - return mKeyguardStatusView.hasCustomClock(); - } - - private void setStatusBar(StatusBar bar) { - mStatusBar = bar; - mKeyguardBottomArea.setStatusBar(mStatusBar); - } - - /** - * Call after this view has been fully inflated and had its children attached. - */ - public void onChildrenAttached() { - loadDimens(); - mKeyguardStatusBar = findViewById(R.id.keyguard_header); - mKeyguardStatusView = findViewById(R.id.keyguard_status_view); - - KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container); - mBigClockContainer = findViewById(R.id.big_clock_container); - keyguardClockSwitch.setBigClockContainer(mBigClockContainer); - - mHomeControlsLayout = findViewById(R.id.home_controls_layout); - mNotificationContainerParent = findViewById(R.id.notification_container_parent); - mNotificationStackScroller = findViewById(R.id.notification_stack_scroller); - mNotificationStackScroller.setOnHeightChangedListener(this); - mNotificationStackScroller.setOverscrollTopChangedListener(this); - mNotificationStackScroller.setOnEmptySpaceClickListener(this); - addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp); - mKeyguardBottomArea = findViewById(R.id.keyguard_bottom_area); - mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim); - mLastOrientation = getResources().getConfiguration().orientation; - mPluginFrame = findViewById(R.id.plugin_frame); - if (Settings.System.getInt( - mContext.getContentResolver(), "npv_plugin_flag", 0) == 1) { - mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager); - } - - - initBottomArea(); - - mWakeUpCoordinator.setStackScroller(mNotificationStackScroller); - mQsFrame = findViewById(R.id.qs_frame); - mPulseExpansionHandler.setUp(mNotificationStackScroller, this, mShadeController); - mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() { - @Override - public void onFullyHiddenChanged(boolean isFullyHidden) { - updateKeyguardStatusBarForHeadsUp(); - } - - @Override - public void onPulseExpansionChanged(boolean expandingChanged) { - if (mKeyguardBypassController.getBypassEnabled()) { - // Position the notifications while dragging down while pulsing - requestScrollerTopPaddingUpdate(false /* animate */); - updateQSPulseExpansion(); - } - } - }); - - mPluginManager.addPluginListener( - new PluginListener<HomeControlsPlugin>() { - - @Override - public void onPluginConnected(HomeControlsPlugin plugin, - Context pluginContext) { - plugin.sendParentGroup(mHomeControlsLayout); - } - - @Override - public void onPluginDisconnected(HomeControlsPlugin plugin) { - - } - }, HomeControlsPlugin.class, false); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - FragmentHostManager.get(this).addTagListener(QS.TAG, mFragmentListener); - Dependency.get(StatusBarStateController.class).addCallback(this); - Dependency.get(ZenModeController.class).addCallback(this); - Dependency.get(ConfigurationController.class).addCallback(this); - mUpdateMonitor.registerCallback(mKeyguardUpdateCallback); - // Theme might have changed between inflating this view and attaching it to the window, so - // force a call to onThemeChanged - onThemeChanged(); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - FragmentHostManager.get(this).removeTagListener(QS.TAG, mFragmentListener); - Dependency.get(StatusBarStateController.class).removeCallback(this); - Dependency.get(ZenModeController.class).removeCallback(this); - Dependency.get(ConfigurationController.class).removeCallback(this); - mUpdateMonitor.removeCallback(mKeyguardUpdateCallback); - } - - @Override - protected void loadDimens() { - super.loadDimens(); - mFlingAnimationUtils = new FlingAnimationUtils(getResources().getDisplayMetrics(), 0.4f); - mStatusBarMinHeight = getResources().getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); - mQsPeekHeight = getResources().getDimensionPixelSize(R.dimen.qs_peek_height); - mNotificationsHeaderCollideDistance = - getResources().getDimensionPixelSize(R.dimen.header_notifications_collide_distance); - mUnlockMoveDistance = getResources().getDimensionPixelOffset(R.dimen.unlock_move_distance); - mClockPositionAlgorithm.loadDimens(getResources()); - mQsFalsingThreshold = getResources().getDimensionPixelSize( - R.dimen.qs_falsing_threshold); - mPositionMinSideMargin = getResources().getDimensionPixelSize( - R.dimen.notification_panel_min_side_margin); - mMaxFadeoutHeight = getResources().getDimensionPixelSize( - R.dimen.max_notification_fadeout_height); - mIndicationBottomPadding = getResources().getDimensionPixelSize( - R.dimen.keyguard_indication_bottom_padding); - mQsNotificationTopPadding = getResources().getDimensionPixelSize( - R.dimen.qs_notification_padding); - mShelfHeight = getResources().getDimensionPixelSize(R.dimen.notification_shelf_height); - mDarkIconSize = getResources().getDimensionPixelSize( - R.dimen.status_bar_icon_drawing_size_dark); - int statusbarHeight = getResources().getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); - mHeadsUpInset = statusbarHeight + getResources().getDimensionPixelSize( - R.dimen.heads_up_status_bar_padding); - mQqsSplitFraction = ((float) getResources().getInteger(R.integer.qqs_split_fraction)) / ( - getResources().getInteger(R.integer.qqs_split_fraction) - + getResources().getInteger(R.integer.qs_split_fraction)); - } - - /** - * @see #launchCamera(boolean, int) - * @see #setLaunchingAffordance(boolean) - */ - public void setLaunchAffordanceListener(Consumer<Boolean> listener) { - mAffordanceLaunchListener = listener; - } - - public void updateResources() { - Resources res = getResources(); - int qsWidth = res.getDimensionPixelSize(R.dimen.qs_panel_width); - int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity); - FrameLayout.LayoutParams lp = - (FrameLayout.LayoutParams) mQsFrame.getLayoutParams(); - if (lp.width != qsWidth || lp.gravity != panelGravity) { - lp.width = qsWidth; - lp.gravity = panelGravity; - mQsFrame.setLayoutParams(lp); - } - - int panelWidth = res.getDimensionPixelSize(R.dimen.notification_panel_width); - lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams(); - if (lp.width != panelWidth || lp.gravity != panelGravity) { - lp.width = panelWidth; - lp.gravity = panelGravity; - mNotificationStackScroller.setLayoutParams(lp); - } - int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings); - int topMargin = sideMargin; - lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams(); - if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin - || lp.rightMargin != sideMargin || lp.topMargin != topMargin) { - lp.width = qsWidth; - lp.gravity = panelGravity; - lp.leftMargin = sideMargin; - lp.rightMargin = sideMargin; - lp.topMargin = topMargin; - mPluginFrame.setLayoutParams(lp); - } - } - - @Override - public void onDensityOrFontScaleChanged() { - updateShowEmptyShadeView(); - } - - @Override - public void onThemeChanged() { - final int themeResId = getContext().getThemeResId(); - if (mThemeResId == themeResId) { - return; - } - mThemeResId = themeResId; - - reInflateViews(); - } - - @Override - public void onOverlayChanged() { - reInflateViews(); - } - - private void reInflateViews() { - updateShowEmptyShadeView(); - - // Re-inflate the status view group. - int index = indexOfChild(mKeyguardStatusView); - removeView(mKeyguardStatusView); - mKeyguardStatusView = (KeyguardStatusView) mInjectionInflationController - .injectable(LayoutInflater.from(mContext)).inflate( - R.layout.keyguard_status_view, - this, - false); - addView(mKeyguardStatusView, index); - - // Re-associate the clock container with the keyguard clock switch. - mBigClockContainer.removeAllViews(); - KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container); - keyguardClockSwitch.setBigClockContainer(mBigClockContainer); - - // Update keyguard bottom area - index = indexOfChild(mKeyguardBottomArea); - removeView(mKeyguardBottomArea); - KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea; - mKeyguardBottomArea = (KeyguardBottomAreaView) mInjectionInflationController - .injectable(LayoutInflater.from(mContext)).inflate( - R.layout.keyguard_bottom_area, - this, - false); - mKeyguardBottomArea.initFrom(oldBottomArea); - addView(mKeyguardBottomArea, index); - initBottomArea(); - mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); - onDozeAmountChanged(mStatusBarStateController.getDozeAmount(), - mStatusBarStateController.getInterpolatedDozeAmount()); - - if (mKeyguardStatusBar != null) { - mKeyguardStatusBar.onThemeChanged(); - } - - setKeyguardStatusViewVisibility(mBarState, false, false); - setKeyguardBottomAreaVisibility(mBarState, false); - if (mOnReinflationListener != null) { - mOnReinflationListener.run(); - } - reinflatePluginContainer(); - } - - @Override - public void onUiModeChanged() { - reinflatePluginContainer(); - } - - private void reinflatePluginContainer() { - int index = indexOfChild(mPluginFrame); - removeView(mPluginFrame); - mPluginFrame = (FrameLayout) mInjectionInflationController - .injectable(LayoutInflater.from(mContext)).inflate( - R.layout.status_bar_expanded_plugin_frame, - this, - false); - addView(mPluginFrame, index); - - Resources res = getResources(); - int qsWidth = res.getDimensionPixelSize(R.dimen.qs_panel_width); - int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity); - FrameLayout.LayoutParams lp; - int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings); - int topMargin = - res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height); - if (Utils.useQsMediaPlayer(mContext)) { - topMargin = res.getDimensionPixelOffset( - com.android.internal.R.dimen.quick_qs_total_height_with_media); - } - lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams(); - if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin - || lp.rightMargin != sideMargin || lp.topMargin != topMargin) { - lp.width = qsWidth; - lp.gravity = panelGravity; - lp.leftMargin = sideMargin; - lp.rightMargin = sideMargin; - lp.topMargin = topMargin; - mPluginFrame.setLayoutParams(lp); - } - - if (mNPVPluginManager != null) mNPVPluginManager.replaceFrameLayout(mPluginFrame); - } - - private void initBottomArea() { - mAffordanceHelper = new KeyguardAffordanceHelper(this, getContext(), mFalsingManager); - mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper); - mKeyguardBottomArea.setStatusBar(mStatusBar); - mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete); - } - - public void setKeyguardIndicationController(KeyguardIndicationController indicationController) { - mKeyguardIndicationController = indicationController; - mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - DejankUtils.startDetectingBlockingIpcs("NVP#onLayout"); - super.onLayout(changed, left, top, right, bottom); - setIsFullWidth(mNotificationStackScroller.getWidth() == getWidth()); - - // Update Clock Pivot - mKeyguardStatusView.setPivotX(getWidth() / 2); - mKeyguardStatusView.setPivotY((FONT_HEIGHT - CAP_HEIGHT) / 2048f * - mKeyguardStatusView.getClockTextSize()); - - // Calculate quick setting heights. - int oldMaxHeight = mQsMaxExpansionHeight; - if (mQs != null) { - mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight(); - if (mNPVPluginManager != null) { - mNPVPluginManager.setYOffset(mQsMinExpansionHeight); - mQsMinExpansionHeight += mNPVPluginManager.getHeight(); - } - mQsMaxExpansionHeight = mQs.getDesiredHeight(); - mNotificationStackScroller.setMaxTopPadding( - mQsMaxExpansionHeight + mQsNotificationTopPadding); - } - positionClockAndNotifications(); - if (mQsExpanded && mQsFullyExpanded) { - mQsExpansionHeight = mQsMaxExpansionHeight; - requestScrollerTopPaddingUpdate(false /* animate */); - requestPanelHeightUpdate(); - - // Size has changed, start an animation. - if (mQsMaxExpansionHeight != oldMaxHeight) { - startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight); - } - } else if (!mQsExpanded) { - setQsExpansion(mQsMinExpansionHeight + mLastOverscroll); - } - updateExpandedHeight(getExpandedHeight()); - updateHeader(); - - // If we are running a size change animation, the animation takes care of the height of - // the container. However, if we are not animating, we always need to make the QS container - // the desired height so when closing the QS detail, it stays smaller after the size change - // animation is finished but the detail view is still being animated away (this animation - // takes longer than the size change animation). - if (mQsSizeChangeAnimator == null && mQs != null) { - mQs.setHeightOverride(mQs.getDesiredHeight()); - } - updateMaxHeadsUpTranslation(); - updateGestureExclusionRect(); - if (mExpandAfterLayoutRunnable != null) { - mExpandAfterLayoutRunnable.run(); - mExpandAfterLayoutRunnable = null; - } - DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout"); - } - - private void updateGestureExclusionRect() { - Rect exclusionRect = calculateGestureExclusionRect(); - setSystemGestureExclusionRects(exclusionRect.isEmpty() - ? Collections.EMPTY_LIST - : Collections.singletonList(exclusionRect)); - } - - private Rect calculateGestureExclusionRect() { - Rect exclusionRect = null; - Region touchableRegion = mHeadsUpManager.calculateTouchableRegion(); - if (isFullyCollapsed() && touchableRegion != null) { - // Note: The heads up manager also calculates the non-pinned touchable region - exclusionRect = touchableRegion.getBounds(); - } - return exclusionRect != null - ? exclusionRect - : mEmptyRect; - } - - private void setIsFullWidth(boolean isFullWidth) { - mIsFullWidth = isFullWidth; - mNotificationStackScroller.setIsFullWidth(isFullWidth); - } - - private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) { - if (mQsSizeChangeAnimator != null) { - oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); - mQsSizeChangeAnimator.cancel(); - } - mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight); - mQsSizeChangeAnimator.setDuration(300); - mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - requestScrollerTopPaddingUpdate(false /* animate */); - requestPanelHeightUpdate(); - int height = (int) mQsSizeChangeAnimator.getAnimatedValue(); - mQs.setHeightOverride(height); - } - }); - mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mQsSizeChangeAnimator = null; - } - }); - mQsSizeChangeAnimator.start(); - } - - /** - * Positions the clock and notifications dynamically depending on how many notifications are - * showing. - */ - private void positionClockAndNotifications() { - boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending(); - boolean animateClock = animate || mAnimateNextPositionUpdate; - int stackScrollerPadding; - if (mBarState != StatusBarState.KEYGUARD) { - stackScrollerPadding = getUnlockedStackScrollerPadding(); - } else { - int totalHeight = getHeight(); - int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); - int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight); - boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); - final boolean hasVisibleNotifications = - !bypassEnabled && mNotificationStackScroller.getVisibleNotificationCount() != 0; - mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications); - mClockPositionAlgorithm.setup( - mStatusBarMinHeight, - totalHeight - bottomPadding, - mNotificationStackScroller.getIntrinsicContentHeight(), - getExpandedFraction(), - totalHeight, - (int) (mKeyguardStatusView.getHeight() - - mShelfHeight / 2.0f - mDarkIconSize / 2.0f), - clockPreferredY, - hasCustomClock(), - hasVisibleNotifications, - mInterpolatedDarkAmount, - mEmptyDragAmount, - bypassEnabled, - getUnlockedStackScrollerPadding()); - mClockPositionAlgorithm.run(mClockPositionResult); - PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X, - mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock); - PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y, - mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock); - updateNotificationTranslucency(); - updateClock(); - stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded; - } - mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding); - mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX); - - mStackScrollerMeasuringPass++; - requestScrollerTopPaddingUpdate(animate); - mStackScrollerMeasuringPass = 0; - mAnimateNextPositionUpdate = false; - } - - /** - * @return the padding of the stackscroller when unlocked - */ - private int getUnlockedStackScrollerPadding() { - return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight - + mQsNotificationTopPadding; - } - - /** - * @param maximum the maximum to return at most - * @return the maximum keyguard notifications that can fit on the screen - */ - public int computeMaxKeyguardNotifications(int maximum) { - float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(); - int notificationPadding = Math.max(1, getResources().getDimensionPixelSize( - R.dimen.notification_divider_height)); - NotificationShelf shelf = mNotificationStackScroller.getNotificationShelf(); - float shelfSize = shelf.getVisibility() == GONE ? 0 - : shelf.getIntrinsicHeight() + notificationPadding; - float availableSpace = mNotificationStackScroller.getHeight() - minPadding - shelfSize - - Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding) - - mKeyguardStatusView.getLogoutButtonHeight(); - int count = 0; - for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) { - ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i); - if (!(child instanceof ExpandableNotificationRow)) { - continue; - } - ExpandableNotificationRow row = (ExpandableNotificationRow) child; - boolean suppressedSummary = mGroupManager != null - && mGroupManager.isSummaryOfSuppressedGroup(row.getEntry().getSbn()); - if (suppressedSummary) { - continue; - } - if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) { - continue; - } - if (row.isRemoved()) { - continue; - } - availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */) - + notificationPadding; - if (availableSpace >= 0 && count < maximum) { - count++; - } else if (availableSpace > -shelfSize) { - // if we are exactly the last view, then we can show us still! - for (int j = i + 1; j < mNotificationStackScroller.getChildCount(); j++) { - if (mNotificationStackScroller.getChildAt(j) - instanceof ExpandableNotificationRow) { - return count; - } - } - count++; - return count; - } else { - return count; - } - } - return count; - } - - private void updateClock() { - if (!mKeyguardStatusViewAnimating) { - mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha); - } - } - - public void animateToFullShade(long delay) { - mNotificationStackScroller.goToFullShade(delay); - requestLayout(); - mAnimateNextPositionUpdate = true; - } - - public void setQsExpansionEnabled(boolean qsExpansionEnabled) { - mQsExpansionEnabled = qsExpansionEnabled; - if (mQs == null) return; - mQs.setHeaderClickable(qsExpansionEnabled); - } - - @Override - public void resetViews(boolean animate) { - mIsLaunchTransitionFinished = false; - mBlockTouches = false; - if (!mLaunchingAffordance) { - mAffordanceHelper.reset(false); - mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; - } - mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */, - true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */); - if (animate) { - animateCloseQs(true /* animateAway */); - } else { - closeQs(); - } - mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, animate, - !animate /* cancelAnimators */); - mNotificationStackScroller.resetScrollPosition(); - } - - @Override - public void collapse(boolean delayed, float speedUpFactor) { - if (!canPanelBeCollapsed()) { - return; - } - - if (mQsExpanded) { - mQsExpandImmediate = true; - mNotificationStackScroller.setShouldShowShelfOnly(true); - } - super.collapse(delayed, speedUpFactor); - } - - public void closeQs() { - cancelQsAnimation(); - setQsExpansion(mQsMinExpansionHeight); - } - - /** - * Animate QS closing by flinging it. - * If QS is expanded, it will collapse into QQS and stop. - * - * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore. - */ - public void animateCloseQs(boolean animateAway) { - if (mQsExpansionAnimator != null) { - if (!mQsAnimatorExpand) { - return; - } - float height = mQsExpansionHeight; - mQsExpansionAnimator.cancel(); - setQsExpansion(height); - } - flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE); - } - - public void expandWithQs() { - if (mQsExpansionEnabled) { - mQsExpandImmediate = true; - mNotificationStackScroller.setShouldShowShelfOnly(true); - } - if (isFullyCollapsed()) { - expand(true /* animate */); - } else { - flingSettings(0 /* velocity */, FLING_EXPAND); - } - } - - public void expandWithoutQs() { - if (isQsExpanded()) { - flingSettings(0 /* velocity */, FLING_COLLAPSE); - } else { - expand(true /* animate */); - } - } - - @Override - public void fling(float vel, boolean expand) { - GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder(); - if (gr != null) { - gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel); - } - super.fling(vel, expand); - } - - @Override - protected void flingToHeight(float vel, boolean expand, float target, - float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) { - mHeadsUpTouchHelper.notifyFling(!expand); - setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f); - super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent event) { - if (mBlockTouches || mQsFullyExpanded && mQs.onInterceptTouchEvent(event)) { - return false; - } - initDownStates(event); - // Do not let touches go to shade or QS if the bouncer is visible, - // but still let user swipe down to expand the panel, dismissing the bouncer. - if (mStatusBar.isBouncerShowing()) { - return true; - } - if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { - mIsExpansionFromHeadsUp = true; - MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1); - MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1); - return true; - } - if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0) - && mPulseExpansionHandler.onInterceptTouchEvent(event)) { - return true; - } - - if (!isFullyCollapsed() && onQsIntercept(event)) { - return true; - } - return super.onInterceptTouchEvent(event); - } - - private boolean onQsIntercept(MotionEvent event) { - int pointerIndex = event.findPointerIndex(mTrackingPointer); - if (pointerIndex < 0) { - pointerIndex = 0; - mTrackingPointer = event.getPointerId(pointerIndex); - } - final float x = event.getX(pointerIndex); - final float y = event.getY(pointerIndex); - - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - mIntercepting = true; - mInitialTouchY = y; - mInitialTouchX = x; - initVelocityTracker(); - trackMovement(event); - if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) { - getParent().requestDisallowInterceptTouchEvent(true); - } - if (mQsExpansionAnimator != null) { - onQsExpansionStarted(); - mInitialHeightOnTouch = mQsExpansionHeight; - mQsTracking = true; - mIntercepting = false; - mNotificationStackScroller.cancelLongPress(); - } - break; - case MotionEvent.ACTION_POINTER_UP: - final int upPointer = event.getPointerId(event.getActionIndex()); - if (mTrackingPointer == upPointer) { - // gesture is ongoing, find a new pointer to track - final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; - mTrackingPointer = event.getPointerId(newIndex); - mInitialTouchX = event.getX(newIndex); - mInitialTouchY = event.getY(newIndex); - } - break; - - case MotionEvent.ACTION_MOVE: - final float h = y - mInitialTouchY; - trackMovement(event); - if (mQsTracking) { - - // Already tracking because onOverscrolled was called. We need to update here - // so we don't stop for a frame until the next touch event gets handled in - // onTouchEvent. - setQsExpansion(h + mInitialHeightOnTouch); - trackMovement(event); - mIntercepting = false; - return true; - } - if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX) - && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) { - mQsTracking = true; - onQsExpansionStarted(); - notifyExpandingFinished(); - mInitialHeightOnTouch = mQsExpansionHeight; - mInitialTouchY = y; - mInitialTouchX = x; - mIntercepting = false; - mNotificationStackScroller.cancelLongPress(); - return true; - } - break; - - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - trackMovement(event); - if (mQsTracking) { - flingQsWithCurrentVelocity(y, - event.getActionMasked() == MotionEvent.ACTION_CANCEL); - mQsTracking = false; - } - mIntercepting = false; - break; - } - return false; - } - - @Override - protected boolean isInContentBounds(float x, float y) { - float stackScrollerX = mNotificationStackScroller.getX(); - return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y) - && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth(); - } - - private void initDownStates(MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - mOnlyAffordanceInThisMotion = false; - mQsTouchAboveFalsingThreshold = mQsFullyExpanded; - mDozingOnDown = isDozing(); - mDownX = event.getX(); - mDownY = event.getY(); - mCollapsedOnDown = isFullyCollapsed(); - mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp(); - mAllowExpandForSmallExpansion = mExpectingSynthesizedDown; - mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown; - if (mExpectingSynthesizedDown) { - mLastEventSynthesizedDown = true; - } else { - // down but not synthesized motion event. - mLastEventSynthesizedDown = false; - } - } else { - // not down event at all. - mLastEventSynthesizedDown = false; - } - } - - private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) { - float vel = getCurrentQSVelocity(); - final boolean expandsQs = flingExpandsQs(vel); - if (expandsQs) { - logQsSwipeDown(y); - } - flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE); - } - - private void logQsSwipeDown(float y) { - float vel = getCurrentQSVelocity(); - final int gesture = mBarState == StatusBarState.KEYGUARD - ? MetricsEvent.ACTION_LS_QS - : MetricsEvent.ACTION_SHADE_QS_PULL; - mLockscreenGestureLogger.write(gesture, - (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()), - (int) (vel / mStatusBar.getDisplayDensity())); - } - - private boolean flingExpandsQs(float vel) { - if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) { - return false; - } - if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { - return getQsExpansionFraction() > 0.5f; - } else { - return vel > 0; - } - } - - private boolean isFalseTouch() { - if (!needsAntiFalsing()) { - return false; - } - if (mFalsingManager.isClassifierEnabled()) { - return mFalsingManager.isFalseTouch(); - } - return !mQsTouchAboveFalsingThreshold; - } - - private float getQsExpansionFraction() { - return Math.min(1f, (mQsExpansionHeight - mQsMinExpansionHeight) - / (mQsMaxExpansionHeight - mQsMinExpansionHeight)); - } - - @Override - protected boolean shouldExpandWhenNotFlinging() { - if (super.shouldExpandWhenNotFlinging()) { - return true; - } - if (mAllowExpandForSmallExpansion) { - // When we get a touch that came over from launcher, the velocity isn't always correct - // Let's err on expanding if the gesture has been reasonably slow - long timeSinceDown = SystemClock.uptimeMillis() - mDownTime; - return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER; - } - return false; - } - - @Override - protected float getOpeningHeight() { - return mNotificationStackScroller.getOpeningHeight(); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (mBlockTouches || (mQs != null && mQs.isCustomizing())) { - return false; - } - - // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able to - // pull down QS or expand the shade. - if (mStatusBar.isBouncerShowingScrimmed()) { - return false; - } - - // Make sure the next touch won't the blocked after the current ends. - if (event.getAction() == MotionEvent.ACTION_UP - || event.getAction() == MotionEvent.ACTION_CANCEL) { - mBlockingExpansionForCurrentTouch = false; - } - // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately - // without any ACTION_MOVE event. - // In such case, simply expand the panel instead of being stuck at the bottom bar. - if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) { - expand(true /* animate */); - } - initDownStates(event); - if (!mIsExpanding && !shouldQuickSettingsIntercept(mDownX, mDownY, 0) - && mPulseExpansionHandler.onTouchEvent(event)) { - // We're expanding all the other ones shouldn't get this anymore - return true; - } - if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp() - && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { - mIsExpansionFromHeadsUp = true; - MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1); - } - boolean handled = false; - if ((!mIsExpanding || mHintAnimationRunning) - && !mQsExpanded - && mBarState != StatusBarState.SHADE - && !mDozing) { - handled |= mAffordanceHelper.onTouchEvent(event); - } - if (mOnlyAffordanceInThisMotion) { - return true; - } - handled |= mHeadsUpTouchHelper.onTouchEvent(event); - - if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) { - return true; - } - if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) { - MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1); - updateVerticalPanelPosition(event.getX()); - handled = true; - } - handled |= super.onTouchEvent(event); - return !mDozing || mPulsing || handled; - } - - private boolean handleQsTouch(MotionEvent event) { - final int action = event.getActionMasked(); - if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f - && mBarState != StatusBarState.KEYGUARD && !mQsExpanded - && mQsExpansionEnabled) { - - // Down in the empty area while fully expanded - go to QS. - mQsTracking = true; - mConflictingQsExpansionGesture = true; - onQsExpansionStarted(); - mInitialHeightOnTouch = mQsExpansionHeight; - mInitialTouchY = event.getX(); - mInitialTouchX = event.getY(); - } - if (!isFullyCollapsed()) { - handleQsDown(event); - } - if (!mQsExpandImmediate && mQsTracking) { - onQsTouch(event); - if (!mConflictingQsExpansionGesture) { - return true; - } - } - if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { - mConflictingQsExpansionGesture = false; - } - if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() - && mQsExpansionEnabled) { - mTwoFingerQsExpandPossible = true; - } - if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) - && event.getY(event.getActionIndex()) < mStatusBarMinHeight) { - MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_QS, 1); - mQsExpandImmediate = true; - mNotificationStackScroller.setShouldShowShelfOnly(true); - requestPanelHeightUpdate(); - - // Normally, we start listening when the panel is expanded, but here we need to start - // earlier so the state is already up to date when dragging down. - setListening(true); - } - if (isQsSplitEnabled() && !mKeyguardShowing) { - if (mQsExpandImmediate) { - mNotificationStackScroller.setVisibility(View.GONE); - mQsFrame.setVisibility(View.VISIBLE); - mHomeControlsLayout.setVisibility(View.VISIBLE); - } else { - mNotificationStackScroller.setVisibility(View.VISIBLE); - mQsFrame.setVisibility(View.GONE); - mHomeControlsLayout.setVisibility(View.GONE); - } - } - return false; - } - - private boolean isInQsArea(float x, float y) { - return (x >= mQsFrame.getX() - && x <= mQsFrame.getX() + mQsFrame.getWidth()) - && (y <= mNotificationStackScroller.getBottomMostNotificationBottom() - || y <= mQs.getView().getY() + mQs.getView().getHeight()); - } - - private boolean isOnQsEndArea(float x) { - if (!isQsSplitEnabled()) return false; - if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) { - return x >= mQsFrame.getX() + mQqsSplitFraction * mQsFrame.getWidth() - && x <= mQsFrame.getX() + mQsFrame.getWidth(); - } else { - return x >= mQsFrame.getX() - && x <= mQsFrame.getX() + (1 - mQqsSplitFraction) * mQsFrame.getWidth(); - } - } - - private boolean isOpenQsEvent(MotionEvent event) { - final int pointerCount = event.getPointerCount(); - final int action = event.getActionMasked(); - - final boolean twoFingerDrag = action == MotionEvent.ACTION_POINTER_DOWN - && pointerCount == 2; - - final boolean stylusButtonClickDrag = action == MotionEvent.ACTION_DOWN - && (event.isButtonPressed(MotionEvent.BUTTON_STYLUS_PRIMARY) - || event.isButtonPressed(MotionEvent.BUTTON_STYLUS_SECONDARY)); - - final boolean mouseButtonClickDrag = action == MotionEvent.ACTION_DOWN - && (event.isButtonPressed(MotionEvent.BUTTON_SECONDARY) - || event.isButtonPressed(MotionEvent.BUTTON_TERTIARY)); - - final boolean onHeaderRight = isOnQsEndArea(event.getX()); - - return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag || onHeaderRight; - } - - private void handleQsDown(MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN - && shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) { - mFalsingManager.onQsDown(); - mQsTracking = true; - onQsExpansionStarted(); - mInitialHeightOnTouch = mQsExpansionHeight; - mInitialTouchY = event.getX(); - mInitialTouchX = event.getY(); - - // If we interrupt an expansion gesture here, make sure to update the state correctly. - notifyExpandingFinished(); - } - } - - /** - * Input focus transfer is about to happen. - */ - public void startWaitingForOpenPanelGesture() { - if (!isFullyCollapsed()) { - return; - } - mExpectingSynthesizedDown = true; - onTrackingStarted(); - updatePanelExpanded(); - } - - /** - * Called when this view is no longer waiting for input focus transfer. - * - * There are two scenarios behind this function call. First, input focus transfer - * has successfully happened and this view already received synthetic DOWN event. - * (mExpectingSynthesizedDown == false). Do nothing. - * - * Second, before input focus transfer finished, user may have lifted finger - * in previous window and this window never received synthetic DOWN event. - * (mExpectingSynthesizedDown == true). - * In this case, we use the velocity to trigger fling event. - * - * @param velocity unit is in px / millis - */ - public void stopWaitingForOpenPanelGesture(final float velocity) { - if (mExpectingSynthesizedDown) { - mExpectingSynthesizedDown = false; - maybeVibrateOnOpening(); - Runnable runnable = () -> fling(velocity > 1f ? 1000f * velocity : 0, - true /* expand */); - if (mStatusBar.getStatusBarWindow().getHeight() - != mStatusBar.getStatusBarHeight()) { - // The panel is already expanded to its full size, let's expand directly - runnable.run(); - } else { - mExpandAfterLayoutRunnable = runnable; - } - onTrackingStopped(false); - } - } - - @Override - protected boolean flingExpands(float vel, float vectorVel, float x, float y) { - boolean expands = super.flingExpands(vel, vectorVel, x, y); - - // If we are already running a QS expansion, make sure that we keep the panel open. - if (mQsExpansionAnimator != null) { - expands = true; - } - return expands; - } - - @Override - protected boolean shouldGestureWaitForTouchSlop() { - if (mExpectingSynthesizedDown) { - mExpectingSynthesizedDown = false; - return false; - } - return isFullyCollapsed() || mBarState != StatusBarState.SHADE; - } - - @Override - protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) { - return !mAffordanceHelper.isOnAffordanceIcon(x, y); - } - - private void onQsTouch(MotionEvent event) { - int pointerIndex = event.findPointerIndex(mTrackingPointer); - if (pointerIndex < 0) { - pointerIndex = 0; - mTrackingPointer = event.getPointerId(pointerIndex); - } - final float y = event.getY(pointerIndex); - final float x = event.getX(pointerIndex); - final float h = y - mInitialTouchY; - - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - mQsTracking = true; - mInitialTouchY = y; - mInitialTouchX = x; - onQsExpansionStarted(); - mInitialHeightOnTouch = mQsExpansionHeight; - initVelocityTracker(); - trackMovement(event); - break; - - case MotionEvent.ACTION_POINTER_UP: - final int upPointer = event.getPointerId(event.getActionIndex()); - if (mTrackingPointer == upPointer) { - // gesture is ongoing, find a new pointer to track - final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; - final float newY = event.getY(newIndex); - final float newX = event.getX(newIndex); - mTrackingPointer = event.getPointerId(newIndex); - mInitialHeightOnTouch = mQsExpansionHeight; - mInitialTouchY = newY; - mInitialTouchX = newX; - } - break; - - case MotionEvent.ACTION_MOVE: - setQsExpansion(h + mInitialHeightOnTouch); - if (h >= getFalsingThreshold()) { - mQsTouchAboveFalsingThreshold = true; - } - trackMovement(event); - break; - - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mQsTracking = false; - mTrackingPointer = -1; - trackMovement(event); - float fraction = getQsExpansionFraction(); - if (fraction != 0f || y >= mInitialTouchY) { - flingQsWithCurrentVelocity(y, - event.getActionMasked() == MotionEvent.ACTION_CANCEL); - } - if (mQsVelocityTracker != null) { - mQsVelocityTracker.recycle(); - mQsVelocityTracker = null; - } - break; - } - } - - private int getFalsingThreshold() { - float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; - return (int) (mQsFalsingThreshold * factor); - } - - @Override - public void onOverscrollTopChanged(float amount, boolean isRubberbanded) { - cancelQsAnimation(); - if (!mQsExpansionEnabled) { - amount = 0f; - } - float rounded = amount >= 1f ? amount : 0f; - setOverScrolling(rounded != 0f && isRubberbanded); - mQsExpansionFromOverscroll = rounded != 0f; - mLastOverscroll = rounded; - updateQsState(); - setQsExpansion(mQsMinExpansionHeight + rounded); - } - - @Override - public void flingTopOverscroll(float velocity, boolean open) { - mLastOverscroll = 0f; - mQsExpansionFromOverscroll = false; - setQsExpansion(mQsExpansionHeight); - flingSettings(!mQsExpansionEnabled && open ? 0f : velocity, - open && mQsExpansionEnabled ? FLING_EXPAND : FLING_COLLAPSE, - new Runnable() { - @Override - public void run() { - mStackScrollerOverscrolling = false; - setOverScrolling(false); - updateQsState(); - } - }, false /* isClick */); - } - - private void setOverScrolling(boolean overscrolling) { - mStackScrollerOverscrolling = overscrolling; - if (mQs == null) return; - mQs.setOverscrolling(overscrolling); - } - - private void onQsExpansionStarted() { - onQsExpansionStarted(0); - } - - protected void onQsExpansionStarted(int overscrollAmount) { - cancelQsAnimation(); - cancelHeightAnimator(); - - // Reset scroll position and apply that position to the expanded height. - float height = mQsExpansionHeight - overscrollAmount; - setQsExpansion(height); - requestPanelHeightUpdate(); - mNotificationStackScroller.checkSnoozeLeavebehind(); - - // When expanding QS, let's authenticate the user if possible, - // this will speed up notification actions. - if (height == 0) { - mStatusBar.requestFaceAuth(); - } - } - - private void setQsExpanded(boolean expanded) { - boolean changed = mQsExpanded != expanded; - if (changed) { - mQsExpanded = expanded; - updateQsState(); - requestPanelHeightUpdate(); - mFalsingManager.setQsExpanded(expanded); - mStatusBar.setQsExpanded(expanded); - mNotificationContainerParent.setQsExpanded(expanded); - mPulseExpansionHandler.setQsExpanded(expanded); - mKeyguardBypassController.setQSExpanded(expanded); - } - } - - @Override - public void onStateChanged(int statusBarState) { - boolean goingToFullShade = mStatusBarStateController.goingToFullShade(); - boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway(); - int oldState = mBarState; - boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD; - setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade); - setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade); - - mBarState = statusBarState; - mKeyguardShowing = keyguardShowing; - if (mKeyguardShowing && isQsSplitEnabled()) { - mNotificationStackScroller.setVisibility(View.VISIBLE); - mQsFrame.setVisibility(View.VISIBLE); - mHomeControlsLayout.setVisibility(View.GONE); - } - - if (oldState == StatusBarState.KEYGUARD - && (goingToFullShade || statusBarState == StatusBarState.SHADE_LOCKED)) { - animateKeyguardStatusBarOut(); - long delay = mBarState == StatusBarState.SHADE_LOCKED - ? 0 : mKeyguardStateController.calculateGoingToFullShadeDelay(); - mQs.animateHeaderSlidingIn(delay); - } else if (oldState == StatusBarState.SHADE_LOCKED - && statusBarState == StatusBarState.KEYGUARD) { - animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); - mNotificationStackScroller.resetScrollPosition(); - // Only animate header if the header is visible. If not, it will partially animate out - // the top of QS - if (!mQsExpanded) { - mQs.animateHeaderSlidingOut(); - } - } else { - mKeyguardStatusBar.setAlpha(1f); - mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE); - ((PhoneStatusBarView) mBar).maybeShowDivider(keyguardShowing); - if (keyguardShowing && oldState != mBarState) { - if (mQs != null) { - mQs.hideImmediately(); - } - } - } - updateKeyguardStatusBarForHeadsUp(); - if (keyguardShowing) { - updateDozingVisibilities(false /* animate */); - } - // THe update needs to happen after the headerSlide in above, otherwise the translation - // would reset - updateQSPulseExpansion(); - maybeAnimateBottomAreaAlpha(); - resetHorizontalPanelPosition(); - updateQsState(); - } - - private void maybeAnimateBottomAreaAlpha() { - mBottomAreaShadeAlphaAnimator.cancel(); - if (mBarState == StatusBarState.SHADE_LOCKED) { - mBottomAreaShadeAlphaAnimator.start(); - } else { - mBottomAreaShadeAlpha = 1f; - } - } - - private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() { - @Override - public void run() { - mKeyguardStatusViewAnimating = false; - mKeyguardStatusView.setVisibility(View.INVISIBLE); - } - }; - - private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() { - @Override - public void run() { - mKeyguardStatusViewAnimating = false; - mKeyguardStatusView.setVisibility(View.GONE); - } - }; - - private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = new Runnable() { - @Override - public void run() { - mKeyguardStatusViewAnimating = false; - } - }; - - private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() { - @Override - public void run() { - mKeyguardStatusBar.setVisibility(View.INVISIBLE); - mKeyguardStatusBar.setAlpha(1f); - mKeyguardStatusBarAnimateAlpha = 1f; - } - }; - - private void animateKeyguardStatusBarOut() { - ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f); - anim.addUpdateListener(mStatusBarAnimateAlphaListener); - anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway() - ? mKeyguardStateController.getKeyguardFadingAwayDelay() - : 0); - - long duration; - if (mKeyguardStateController.isKeyguardFadingAway()) { - duration = mKeyguardStateController.getShortenedFadingAwayDuration(); - } else { - duration = StackStateAnimator.ANIMATION_DURATION_STANDARD; - } - anim.setDuration(duration); - - anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mAnimateKeyguardStatusBarInvisibleEndRunnable.run(); - } - }); - anim.start(); - } - - private final ValueAnimator.AnimatorUpdateListener mStatusBarAnimateAlphaListener = - new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue(); - updateHeaderKeyguardAlpha(); - } - }; - - private void animateKeyguardStatusBarIn(long duration) { - mKeyguardStatusBar.setVisibility(View.VISIBLE); - mKeyguardStatusBar.setAlpha(0f); - ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); - anim.addUpdateListener(mStatusBarAnimateAlphaListener); - anim.setDuration(duration); - anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); - anim.start(); - } - - private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() { - @Override - public void run() { - mKeyguardBottomArea.setVisibility(View.GONE); - } - }; - - private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) { - mKeyguardBottomArea.animate().cancel(); - if (goingToFullShade) { - mKeyguardBottomArea.animate() - .alpha(0f) - .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay()) - .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration()) - .setInterpolator(Interpolators.ALPHA_OUT) - .withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable) - .start(); - } else if (statusBarState == StatusBarState.KEYGUARD - || statusBarState == StatusBarState.SHADE_LOCKED) { - mKeyguardBottomArea.setVisibility(View.VISIBLE); - mKeyguardBottomArea.setAlpha(1f); - } else { - mKeyguardBottomArea.setVisibility(View.GONE); - } - } - - private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway, - boolean goingToFullShade) { - mKeyguardStatusView.animate().cancel(); - mKeyguardStatusViewAnimating = false; - if ((!keyguardFadingAway && mBarState == StatusBarState.KEYGUARD - && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) { - mKeyguardStatusViewAnimating = true; - mKeyguardStatusView.animate() - .alpha(0f) - .setStartDelay(0) - .setDuration(160) - .setInterpolator(Interpolators.ALPHA_OUT) - .withEndAction(mAnimateKeyguardStatusViewGoneEndRunnable); - if (keyguardFadingAway) { - mKeyguardStatusView.animate() - .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay()) - .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration()) - .start(); - } - } else if (mBarState == StatusBarState.SHADE_LOCKED - && statusBarState == StatusBarState.KEYGUARD) { - mKeyguardStatusView.setVisibility(View.VISIBLE); - mKeyguardStatusViewAnimating = true; - mKeyguardStatusView.setAlpha(0f); - mKeyguardStatusView.animate() - .alpha(1f) - .setStartDelay(0) - .setDuration(320) - .setInterpolator(Interpolators.ALPHA_IN) - .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable); - } else if (statusBarState == StatusBarState.KEYGUARD) { - if (keyguardFadingAway) { - mKeyguardStatusViewAnimating = true; - mKeyguardStatusView.animate() - .alpha(0) - .translationYBy(-getHeight() * 0.05f) - .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN) - .setDuration(125) - .setStartDelay(0) - .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable) - .start(); - } else { - mKeyguardStatusView.setVisibility(View.VISIBLE); - mKeyguardStatusView.setAlpha(1f); - } - } else { - mKeyguardStatusView.setVisibility(View.GONE); - mKeyguardStatusView.setAlpha(1f); - } - } - - private void updateQsState() { - mNotificationStackScroller.setQsExpanded(mQsExpanded); - mNotificationStackScroller.setScrollingEnabled( - mBarState != StatusBarState.KEYGUARD && (!mQsExpanded - || mQsExpansionFromOverscroll)); - updateEmptyShadeView(); - if (mNPVPluginManager != null) { - mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD) - ? View.VISIBLE - : View.INVISIBLE); - } - mQsNavbarScrim.setVisibility(mBarState == StatusBarState.SHADE && mQsExpanded - && !mStackScrollerOverscrolling && mQsScrimEnabled - ? View.VISIBLE - : View.INVISIBLE); - if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) { - mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */); - } - if (mQs == null) return; - mQs.setExpanded(mQsExpanded); - } - - private void setQsExpansion(float height) { - height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight); - mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0; - if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling - && !mDozing) { - setQsExpanded(true); - } else if (height <= mQsMinExpansionHeight && mQsExpanded) { - setQsExpanded(false); - } - mQsExpansionHeight = height; - updateQsExpansion(); - requestScrollerTopPaddingUpdate(false /* animate */); - updateHeaderKeyguardAlpha(); - if (mBarState == StatusBarState.SHADE_LOCKED - || mBarState == StatusBarState.KEYGUARD) { - updateKeyguardBottomAreaAlpha(); - updateBigClockAlpha(); - } - if (mBarState == StatusBarState.SHADE && mQsExpanded - && !mStackScrollerOverscrolling && mQsScrimEnabled) { - mQsNavbarScrim.setAlpha(getQsExpansionFraction()); - } - - if (mAccessibilityManager.isEnabled()) { - setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); - } - - if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded - && mFalsingManager.shouldEnforceBouncer()) { - mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */, - false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */); - } - for (int i = 0; i < mExpansionListeners.size(); i++) { - mExpansionListeners.get(i).onQsExpansionChanged(mQsMaxExpansionHeight != 0 - ? mQsExpansionHeight / mQsMaxExpansionHeight : 0); - } - if (DEBUG) { - invalidate(); - } - } - - protected void updateQsExpansion() { - if (mQs == null) return; - float qsExpansionFraction = getQsExpansionFraction(); - mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation()); - int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight(); - if (mNPVPluginManager != null) { - mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff); - } - mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction); - } - - private String determineAccessibilityPaneTitle() { - if (mQs != null && mQs.isCustomizing()) { - return getContext().getString(R.string.accessibility_desc_quick_settings_edit); - } else if (mQsExpansionHeight != 0.0f && mQsFullyExpanded) { - // Upon initialisation when we are not layouted yet we don't want to announce that we - // are fully expanded, hence the != 0.0f check. - return getContext().getString(R.string.accessibility_desc_quick_settings); - } else if (mBarState == StatusBarState.KEYGUARD) { - return getContext().getString(R.string.accessibility_desc_lock_screen); - } else { - return getContext().getString(R.string.accessibility_desc_notification_shade); - } - } - - private float calculateQsTopPadding() { - if (mKeyguardShowing - && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted)) { - - // Either QS pushes the notifications down when fully expanded, or QS is fully above the - // notifications (mostly on tablets). maxNotificationPadding denotes the normal top - // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings - // panel. We need to take the maximum and linearly interpolate with the panel expansion - // for a nice motion. - int maxNotificationPadding = getKeyguardNotificationStaticPadding(); - int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding; - int max = mBarState == StatusBarState.KEYGUARD - ? Math.max(maxNotificationPadding, maxQsPadding) - : maxQsPadding; - return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max, - getExpandedFraction()); - } else if (mQsSizeChangeAnimator != null) { - return Math.max((int) mQsSizeChangeAnimator.getAnimatedValue(), - getKeyguardNotificationStaticPadding()); - } else if (mKeyguardShowing) { - // We can only do the smoother transition on Keyguard when we also are not collapsing - // from a scrolled quick settings. - return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(), - (float) (mQsMaxExpansionHeight + mQsNotificationTopPadding), - getQsExpansionFraction()); - } else { - return mQsExpansionHeight + mQsNotificationTopPadding; - } - } - - /** - * @return the topPadding of notifications when on keyguard not respecting quick settings - * expansion - */ - private int getKeyguardNotificationStaticPadding() { - if (!mKeyguardShowing) { - return 0; - } - if (!mKeyguardBypassController.getBypassEnabled()) { - return mClockPositionResult.stackScrollerPadding; - } - int collapsedPosition = mHeadsUpInset; - if (!mNotificationStackScroller.isPulseExpanding()) { - return collapsedPosition; - } else { - int expandedPosition = mClockPositionResult.stackScrollerPadding; - return (int) MathUtils.lerp(collapsedPosition, expandedPosition, - mNotificationStackScroller.calculateAppearFractionBypass()); - } - } - - - protected void requestScrollerTopPaddingUpdate(boolean animate) { - mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate); - if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) { - // update the position of the header - updateQsExpansion(); - } - } - - - private void updateQSPulseExpansion() { - if (mQs != null) { - mQs.setShowCollapsedOnKeyguard(mKeyguardShowing - && mKeyguardBypassController.getBypassEnabled() - && mNotificationStackScroller.isPulseExpanding()); - } - } - - private void trackMovement(MotionEvent event) { - if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event); - mLastTouchX = event.getX(); - mLastTouchY = event.getY(); - } - - private void initVelocityTracker() { - if (mQsVelocityTracker != null) { - mQsVelocityTracker.recycle(); - } - mQsVelocityTracker = VelocityTracker.obtain(); - } - - private float getCurrentQSVelocity() { - if (mQsVelocityTracker == null) { - return 0; - } - mQsVelocityTracker.computeCurrentVelocity(1000); - return mQsVelocityTracker.getYVelocity(); - } - - private void cancelQsAnimation() { - if (mQsExpansionAnimator != null) { - mQsExpansionAnimator.cancel(); - } - } - - /** - * @see #flingSettings(float, int, Runnable, boolean) - */ - public void flingSettings(float vel, int type) { - flingSettings(vel, type, null, false /* isClick */); - } - - /** - * Animates QS or QQS as if the user had swiped up or down. - * - * @param vel Finger velocity or 0 when not initiated by touch events. - * @param type Either {@link #FLING_EXPAND}, {@link #FLING_COLLAPSE} or {@link #FLING_HIDE}. - * @param onFinishRunnable Runnable to be executed at the end of animation. - * @param isClick If originated by click (different interpolator and duration.) - */ - protected void flingSettings(float vel, int type, final Runnable onFinishRunnable, - boolean isClick) { - float target; - switch (type) { - case FLING_EXPAND: - target = mQsMaxExpansionHeight; - break; - case FLING_COLLAPSE: - target = mQsMinExpansionHeight; - break; - case FLING_HIDE: - default: - target = 0; - } - if (target == mQsExpansionHeight) { - if (onFinishRunnable != null) { - onFinishRunnable.run(); - } - return; - } - - // If we move in the opposite direction, reset velocity and use a different duration. - boolean oppositeDirection = false; - boolean expanding = type == FLING_EXPAND; - if (vel > 0 && !expanding || vel < 0 && expanding) { - vel = 0; - oppositeDirection = true; - } - ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target); - if (isClick) { - animator.setInterpolator(Interpolators.TOUCH_RESPONSE); - animator.setDuration(368); - } else { - mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel); - } - if (oppositeDirection) { - animator.setDuration(350); - } - animator.addUpdateListener(animation -> { - setQsExpansion((Float) animation.getAnimatedValue()); - }); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mNotificationStackScroller.resetCheckSnoozeLeavebehind(); - mQsExpansionAnimator = null; - if (onFinishRunnable != null) { - onFinishRunnable.run(); - } - } - }); - animator.start(); - mQsExpansionAnimator = animator; - mQsAnimatorExpand = expanding; - } - - /** - * @return Whether we should intercept a gesture to open Quick Settings. - */ - private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) { - if (!mQsExpansionEnabled || mCollapsedOnDown - || (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled())) { - return false; - } - View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader(); - final boolean onHeader = x >= mQsFrame.getX() - && x <= mQsFrame.getX() + mQsFrame.getWidth() - && y >= header.getTop() && y <= header.getBottom(); - if (mQsExpanded) { - return onHeader || (yDiff < 0 && isInQsArea(x, y)); - } else { - return onHeader; - } - } - - @Override - protected boolean isScrolledToBottom() { - if (!isInSettings()) { - return mBarState == StatusBarState.KEYGUARD - || mNotificationStackScroller.isScrolledToBottom(); - } else { - return true; - } - } - - @Override - protected int getMaxPanelHeight() { - if (mKeyguardBypassController.getBypassEnabled() && mBarState == StatusBarState.KEYGUARD) { - return getMaxPanelHeightBypass(); - } else { - return getMaxPanelHeightNonBypass(); - } - } - - private int getMaxPanelHeightNonBypass() { - int min = mStatusBarMinHeight; - if (!(mBarState == StatusBarState.KEYGUARD) - && mNotificationStackScroller.getNotGoneChildCount() == 0) { - int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount()); - min = Math.max(min, minHeight); - } - int maxHeight; - if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted - || mPulsing) { - maxHeight = calculatePanelHeightQsExpanded(); - } else { - maxHeight = calculatePanelHeightShade(); - } - maxHeight = Math.max(maxHeight, min); - return maxHeight; - } - - private int getMaxPanelHeightBypass() { - int position = mClockPositionAlgorithm.getExpandedClockPosition() - + mKeyguardStatusView.getHeight(); - if (mNotificationStackScroller.getVisibleNotificationCount() != 0) { - position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f; - } - return position; - } - - public boolean isInSettings() { - return mQsExpanded; - } - - public boolean isExpanding() { - return mIsExpanding; - } - - @Override - protected void onHeightUpdated(float expandedHeight) { - if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) { - // Updating the clock position will set the top padding which might - // trigger a new panel height and re-position the clock. - // This is a circular dependency and should be avoided, otherwise we'll have - // a stack overflow. - if (mStackScrollerMeasuringPass > 2) { - if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting."); - } else { - positionClockAndNotifications(); - } - } - if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null - && !mQsExpansionFromOverscroll) { - float t; - if (mKeyguardShowing) { - - // On Keyguard, interpolate the QS expansion linearly to the panel expansion - t = expandedHeight / (getMaxPanelHeight()); - } else { - // In Shade, interpolate linearly such that QS is closed whenever panel height is - // minimum QS expansion + minStackHeight - float panelHeightQsCollapsed = mNotificationStackScroller.getIntrinsicPadding() - + mNotificationStackScroller.getLayoutMinHeight(); - float panelHeightQsExpanded = calculatePanelHeightQsExpanded(); - t = (expandedHeight - panelHeightQsCollapsed) - / (panelHeightQsExpanded - panelHeightQsCollapsed); - } - float targetHeight = mQsMinExpansionHeight - + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight); - setQsExpansion(targetHeight); - mHomeControlsLayout.setTranslationY(targetHeight); - } - updateExpandedHeight(expandedHeight); - updateHeader(); - updateNotificationTranslucency(); - updatePanelExpanded(); - updateGestureExclusionRect(); - if (DEBUG) { - invalidate(); - } - } - - private void updatePanelExpanded() { - boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown; - if (mPanelExpanded != isExpanded) { - mHeadsUpManager.setIsPanelExpanded(isExpanded); - mStatusBar.setPanelExpanded(isExpanded); - mPanelExpanded = isExpanded; - } - } - - private int calculatePanelHeightShade() { - int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin(); - int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin; - maxHeight += mNotificationStackScroller.getTopPaddingOverflow(); - - if (mBarState == StatusBarState.KEYGUARD) { - int minKeyguardPanelBottom = mClockPositionAlgorithm.getExpandedClockPosition() - + mKeyguardStatusView.getHeight() - + mNotificationStackScroller.getIntrinsicContentHeight(); - return Math.max(maxHeight, minKeyguardPanelBottom); - } else { - return maxHeight; - } - } - - private int calculatePanelHeightQsExpanded() { - float notificationHeight = mNotificationStackScroller.getHeight() - - mNotificationStackScroller.getEmptyBottomMargin() - - mNotificationStackScroller.getTopPadding(); - - // When only empty shade view is visible in QS collapsed state, simulate that we would have - // it in expanded QS state as well so we don't run into troubles when fading the view in/out - // and expanding/collapsing the whole panel from/to quick settings. - if (mNotificationStackScroller.getNotGoneChildCount() == 0 - && mShowEmptyShadeView) { - notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight(); - } - int maxQsHeight = mQsMaxExpansionHeight; - - if (mKeyguardShowing) { - maxQsHeight += mQsNotificationTopPadding; - } - - // If an animation is changing the size of the QS panel, take the animated value. - if (mQsSizeChangeAnimator != null) { - maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); - } - float totalHeight = Math.max( - maxQsHeight, mBarState == StatusBarState.KEYGUARD - ? mClockPositionResult.stackScrollerPadding : 0) - + notificationHeight + mNotificationStackScroller.getTopPaddingOverflow(); - if (totalHeight > mNotificationStackScroller.getHeight()) { - float fullyCollapsedHeight = maxQsHeight - + mNotificationStackScroller.getLayoutMinHeight(); - totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight()); - } - return (int) totalHeight; - } - - private void updateNotificationTranslucency() { - float alpha = 1f; - if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp && - !mHeadsUpManager.hasPinnedHeadsUp()) { - alpha = getFadeoutAlpha(); - } - if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning - && !mKeyguardBypassController.getBypassEnabled()) { - alpha *= mClockPositionResult.clockAlpha; - } - mNotificationStackScroller.setAlpha(alpha); - } - - private float getFadeoutAlpha() { - float alpha; - if (mQsMinExpansionHeight == 0) { - return 1.0f; - } - alpha = getExpandedHeight() / mQsMinExpansionHeight; - alpha = Math.max(0, Math.min(alpha, 1)); - alpha = (float) Math.pow(alpha, 0.75); - return alpha; - } - - @Override - protected float getOverExpansionAmount() { - return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */); - } - - @Override - protected float getOverExpansionPixels() { - return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */); - } - - /** - * Hides the header when notifications are colliding with it. - */ - private void updateHeader() { - if (mBarState == StatusBarState.KEYGUARD) { - updateHeaderKeyguardAlpha(); - } - updateQsExpansion(); - } - - protected float getHeaderTranslation() { - if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) { - return -mQs.getQsMinExpansionHeight(); - } - float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight); - float startHeight = -mQsExpansionHeight; - if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard() - && mNotificationStackScroller.isPulseExpanding()) { - if (!mPulseExpansionHandler.isExpanding() - && !mPulseExpansionHandler.getLeavingLockscreen()) { - // If we aborted the expansion we need to make sure the header doesn't reappear - // again after the header has animated away - appearAmount = 0; - } else { - appearAmount = mNotificationStackScroller.calculateAppearFractionBypass(); - } - startHeight = -mQs.getQsMinExpansionHeight(); - if (mNPVPluginManager != null) startHeight -= mNPVPluginManager.getHeight(); - } - float translation = MathUtils.lerp(startHeight, 0, - Math.min(1.0f, appearAmount)) - + mExpandOffset; - return Math.min(0, translation); - } - - /** - * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area) - * during swiping up - */ - private float getKeyguardContentsAlpha() { - float alpha; - if (mBarState == StatusBarState.KEYGUARD) { - - // When on Keyguard, we hide the header as soon as we expanded close enough to the - // header - alpha = getExpandedHeight() - / - (mKeyguardStatusBar.getHeight() + mNotificationsHeaderCollideDistance); - } else { - - // In SHADE_LOCKED, the top card is already really close to the header. Hide it as - // soon as we start translating the stack. - alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight(); - } - alpha = MathUtils.saturate(alpha); - alpha = (float) Math.pow(alpha, 0.75); - return alpha; - } - - private void updateHeaderKeyguardAlpha() { - if (!mKeyguardShowing) { - return; - } - float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2); - float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion) - * mKeyguardStatusBarAnimateAlpha; - newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount; - mKeyguardStatusBar.setAlpha(newAlpha); - boolean hideForBypass = mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace() - || mDelayShowingKeyguardStatusBar; - mKeyguardStatusBar.setVisibility(newAlpha != 0f && !mDozing && !hideForBypass - ? VISIBLE : INVISIBLE); - } - - private void updateKeyguardBottomAreaAlpha() { - // There are two possible panel expansion behaviors: - // • User dragging up to unlock: we want to fade out as quick as possible - // (ALPHA_EXPANSION_THRESHOLD) to avoid seeing the bouncer over the bottom area. - // • User tapping on lock screen: bouncer won't be visible but panel expansion will - // change due to "unlock hint animation." In this case, fading out the bottom area - // would also hide the message that says "swipe to unlock," we don't want to do that. - float expansionAlpha = MathUtils.map(isUnlockHintRunning() - ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, - 0f, 1f, getExpandedFraction()); - float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction()); - alpha *= mBottomAreaShadeAlpha; - mKeyguardBottomArea.setAffordanceAlpha(alpha); - mKeyguardBottomArea.setImportantForAccessibility(alpha == 0f - ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS - : IMPORTANT_FOR_ACCESSIBILITY_AUTO); - View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer(); - if (ambientIndicationContainer != null) { - ambientIndicationContainer.setAlpha(alpha); - } - } - - /** - * Custom clock fades away when user drags up to unlock or pulls down quick settings. - * - * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See - * {@link updateKeyguardBottomAreaAlpha}. - */ - private void updateBigClockAlpha() { - float expansionAlpha = MathUtils.map(isUnlockHintRunning() - ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, getExpandedFraction()); - float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction()); - mBigClockContainer.setAlpha(alpha); - } - - @Override - protected void onExpandingStarted() { - super.onExpandingStarted(); - mNotificationStackScroller.onExpansionStarted(); - mIsExpanding = true; - mQsExpandedWhenExpandingStarted = mQsFullyExpanded; - if (mQsExpanded) { - onQsExpansionStarted(); - } - // Since there are QS tiles in the header now, we need to make sure we start listening - // immediately so they can be up to date. - if (mQs == null) return; - mQs.setHeaderListening(true); - } - - @Override - protected void onExpandingFinished() { - super.onExpandingFinished(); - mNotificationStackScroller.onExpansionStopped(); - mHeadsUpManager.onExpandingFinished(); - mIsExpanding = false; - if (isFullyCollapsed()) { - DejankUtils.postAfterTraversal(new Runnable() { - @Override - public void run() { - setListening(false); - } - }); - - // Workaround b/22639032: Make sure we invalidate something because else RenderThread - // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go - // ahead with rendering and we jank. - postOnAnimation(new Runnable() { - @Override - public void run() { - getParent().invalidateChild(NotificationPanelView.this, mDummyDirtyRect); - } - }); - } else { - setListening(true); - } - mQsExpandImmediate = false; - mNotificationStackScroller.setShouldShowShelfOnly(false); - mTwoFingerQsExpandPossible = false; - mIsExpansionFromHeadsUp = false; - notifyListenersTrackingHeadsUp(null); - mExpandingFromHeadsUp = false; - setPanelScrimMinFraction(0.0f); - } - - private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) { - for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) { - Consumer<ExpandableNotificationRow> listener - = mTrackingHeadsUpListeners.get(i); - listener.accept(pickedChild); - } - } - - private void setListening(boolean listening) { - mKeyguardStatusBar.setListening(listening); - if (mQs == null) return; - mQs.setListening(listening); - if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening); - } - - @Override - public void expand(boolean animate) { - super.expand(animate); - setListening(true); - } - - @Override - protected void setOverExpansion(float overExpansion, boolean isPixels) { - if (mConflictingQsExpansionGesture || mQsExpandImmediate) { - return; - } - if (mBarState != StatusBarState.KEYGUARD) { - mNotificationStackScroller.setOnHeightChangedListener(null); - if (isPixels) { - mNotificationStackScroller.setOverScrolledPixels( - overExpansion, true /* onTop */, false /* animate */); - } else { - mNotificationStackScroller.setOverScrollAmount( - overExpansion, true /* onTop */, false /* animate */); - } - mNotificationStackScroller.setOnHeightChangedListener(this); - } - } - - @Override - protected void onTrackingStarted() { - mFalsingManager.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen()); - super.onTrackingStarted(); - if (mQsFullyExpanded) { - mQsExpandImmediate = true; - mNotificationStackScroller.setShouldShowShelfOnly(true); - } - if (mBarState == StatusBarState.KEYGUARD - || mBarState == StatusBarState.SHADE_LOCKED) { - mAffordanceHelper.animateHideLeftRightIcon(); - } - mNotificationStackScroller.onPanelTrackingStarted(); - } - - @Override - protected void onTrackingStopped(boolean expand) { - mFalsingManager.onTrackingStopped(); - super.onTrackingStopped(expand); - if (expand) { - mNotificationStackScroller.setOverScrolledPixels( - 0.0f, true /* onTop */, true /* animate */); - } - mNotificationStackScroller.onPanelTrackingStopped(); - if (expand && (mBarState == StatusBarState.KEYGUARD - || mBarState == StatusBarState.SHADE_LOCKED)) { - if (!mHintAnimationRunning) { - mAffordanceHelper.reset(true); - } - } - } - - @Override - public void onHeightChanged(ExpandableView view, boolean needsAnimation) { - - // Block update if we are in quick settings and just the top padding changed - // (i.e. view == null). - if (view == null && mQsExpanded) { - return; - } - if (needsAnimation && mInterpolatedDarkAmount == 0) { - mAnimateNextPositionUpdate = true; - } - ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone(); - ExpandableNotificationRow firstRow = firstChildNotGone instanceof ExpandableNotificationRow - ? (ExpandableNotificationRow) firstChildNotGone - : null; - if (firstRow != null - && (view == firstRow || (firstRow.getNotificationParent() == firstRow))) { - requestScrollerTopPaddingUpdate(false /* animate */); - } - requestPanelHeightUpdate(); - } - - @Override - public void onReset(ExpandableView view) { - } - - public void onQsHeightChanged() { - mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0; - if (mQsExpanded && mQsFullyExpanded) { - mQsExpansionHeight = mQsMaxExpansionHeight; - requestScrollerTopPaddingUpdate(false /* animate */); - requestPanelHeightUpdate(); - } - if (mAccessibilityManager.isEnabled()) { - setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); - } - mNotificationStackScroller.setMaxTopPadding( - mQsMaxExpansionHeight + mQsNotificationTopPadding); - } - - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - mAffordanceHelper.onConfigurationChanged(); - if (newConfig.orientation != mLastOrientation) { - resetHorizontalPanelPosition(); - } - mLastOrientation = newConfig.orientation; - } - - @Override - public WindowInsets onApplyWindowInsets(WindowInsets insets) { - mNavigationBarBottomHeight = insets.getStableInsetBottom(); - updateMaxHeadsUpTranslation(); - return insets; - } - - private void updateMaxHeadsUpTranslation() { - mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight); - } - @Override public void onRtlPropertiesChanged(int layoutDirection) { - if (layoutDirection != mOldLayoutDirection) { - mAffordanceHelper.onRtlPropertiesChanged(); - mOldLayoutDirection = layoutDirection; - } - } - - @Override - public void onClick(View v) { - onQsExpansionStarted(); - if (mQsExpanded) { - flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */, - true /* isClick */); - } else if (mQsExpansionEnabled) { - mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0); - flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */, - true /* isClick */); - } - } - - @Override - public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) { - boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? rightPage : !rightPage; - mIsLaunchTransitionRunning = true; - mLaunchAnimationEndRunnable = null; - float displayDensity = mStatusBar.getDisplayDensity(); - int lengthDp = Math.abs((int) (translation / displayDensity)); - int velocityDp = Math.abs((int) (vel / displayDensity)); - if (start) { - mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp); - - mFalsingManager.onLeftAffordanceOn(); - if (mFalsingManager.shouldEnforceBouncer()) { - mStatusBar.executeRunnableDismissingKeyguard(new Runnable() { - @Override - public void run() { - mKeyguardBottomArea.launchLeftAffordance(); - } - }, null, true /* dismissShade */, false /* afterKeyguardGone */, - true /* deferred */); - } else { - mKeyguardBottomArea.launchLeftAffordance(); - } - } else { - if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals( - mLastCameraLaunchSource)) { - mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp); - } - mFalsingManager.onCameraOn(); - if (mFalsingManager.shouldEnforceBouncer()) { - mStatusBar.executeRunnableDismissingKeyguard(new Runnable() { - @Override - public void run() { - mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource); - } - }, null, true /* dismissShade */, false /* afterKeyguardGone */, - true /* deferred */); - } - else { - mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource); - } - } - mStatusBar.startLaunchTransitionTimeout(); - mBlockTouches = true; - } - - @Override - public void onAnimationToSideEnded() { - mIsLaunchTransitionRunning = false; - mIsLaunchTransitionFinished = true; - if (mLaunchAnimationEndRunnable != null) { - mLaunchAnimationEndRunnable.run(); - mLaunchAnimationEndRunnable = null; - } - mStatusBar.readyForKeyguardDone(); - } - - @Override - protected void startUnlockHintAnimation() { - if (mPowerManager.isPowerSaveMode()) { - onUnlockHintStarted(); - onUnlockHintFinished(); - return; - } - super.startUnlockHintAnimation(); - } - - @Override - public float getMaxTranslationDistance() { - return (float) Math.hypot(getWidth(), getHeight()); - } - - @Override - public void onSwipingStarted(boolean rightIcon) { - mFalsingManager.onAffordanceSwipingStarted(rightIcon); - boolean camera = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon - : rightIcon; - if (camera) { - mKeyguardBottomArea.bindCameraPrewarmService(); - } - requestDisallowInterceptTouchEvent(true); - mOnlyAffordanceInThisMotion = true; - mQsTracking = false; - } - - @Override - public void onSwipingAborted() { - mFalsingManager.onAffordanceSwipingAborted(); - mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */); - } - - @Override - public void onIconClicked(boolean rightIcon) { - if (mHintAnimationRunning) { - return; - } - mHintAnimationRunning = true; - mAffordanceHelper.startHintAnimation(rightIcon, new Runnable() { - @Override - public void run() { - mHintAnimationRunning = false; - mStatusBar.onHintFinished(); - } - }); - rightIcon = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon : rightIcon; - if (rightIcon) { - mStatusBar.onCameraHintStarted(); - } else { - if (mKeyguardBottomArea.isLeftVoiceAssist()) { - mStatusBar.onVoiceAssistHintStarted(); - } else { - mStatusBar.onPhoneHintStarted(); - } - } - } - - @Override - protected void onUnlockHintFinished() { - super.onUnlockHintFinished(); - mNotificationStackScroller.setUnlockHintRunning(false); - } - - @Override - protected void onUnlockHintStarted() { - super.onUnlockHintStarted(); - mNotificationStackScroller.setUnlockHintRunning(true); - } - - @Override - public KeyguardAffordanceView getLeftIcon() { - return getLayoutDirection() == LAYOUT_DIRECTION_RTL - ? mKeyguardBottomArea.getRightView() - : mKeyguardBottomArea.getLeftView(); - } - - @Override - public KeyguardAffordanceView getRightIcon() { - return getLayoutDirection() == LAYOUT_DIRECTION_RTL - ? mKeyguardBottomArea.getLeftView() - : mKeyguardBottomArea.getRightView(); - } - - @Override - public View getLeftPreview() { - return getLayoutDirection() == LAYOUT_DIRECTION_RTL - ? mKeyguardBottomArea.getRightPreview() - : mKeyguardBottomArea.getLeftPreview(); - } - - @Override - public View getRightPreview() { - return getLayoutDirection() == LAYOUT_DIRECTION_RTL - ? mKeyguardBottomArea.getLeftPreview() - : mKeyguardBottomArea.getRightPreview(); - } - - @Override - public float getAffordanceFalsingFactor() { - return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; - } - - @Override - public boolean needsAntiFalsing() { - return mBarState == StatusBarState.KEYGUARD; - } - - @Override - protected float getPeekHeight() { - if (mNotificationStackScroller.getNotGoneChildCount() > 0) { - return mNotificationStackScroller.getPeekHeight(); - } else { - return mQsMinExpansionHeight; + if (mRtlChangeListener != null) { + mRtlChangeListener.onRtlPropertielsChanged(layoutDirection); } } @Override - protected boolean shouldUseDismissingAnimation() { - return mBarState != StatusBarState.SHADE - && (mKeyguardStateController.canDismissLockScreen() || !isTracking()); - } - - @Override - protected boolean fullyExpandedClearAllVisible() { - return mNotificationStackScroller.isFooterViewNotGone() - && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate; - } - - @Override - protected boolean isClearAllVisible() { - return mNotificationStackScroller.isFooterViewContentVisible(); - } - - @Override - protected int getClearAllHeight() { - return mNotificationStackScroller.getFooterViewHeight(); - } - - @Override - protected boolean isTrackingBlocked() { - return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch; - } - - public boolean isQsExpanded() { - return mQsExpanded; - } - - public boolean isQsDetailShowing() { - return mQs.isShowingDetail(); - } - - public void closeQsDetail() { - mQs.closeDetail(); - } - - @Override public boolean shouldDelayChildPressedState() { return true; } - public boolean isLaunchTransitionFinished() { - return mIsLaunchTransitionFinished; - } - - public boolean isLaunchTransitionRunning() { - return mIsLaunchTransitionRunning; - } - - public void setLaunchTransitionEndRunnable(Runnable r) { - mLaunchAnimationEndRunnable = r; - } - - public void setEmptyDragAmount(float amount) { - mEmptyDragAmount = amount * 0.2f; - positionClockAndNotifications(); - } - - private void updateDozingVisibilities(boolean animate) { - mKeyguardBottomArea.setDozing(mDozing, animate); - if (!mDozing && animate) { - animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); - } - } - - @Override - public boolean isDozing() { - return mDozing; - } - - public void showEmptyShadeView(boolean emptyShadeViewVisible) { - mShowEmptyShadeView = emptyShadeViewVisible; - updateEmptyShadeView(); - } - - private void updateEmptyShadeView() { - // Hide "No notifications" in QS. - mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded); - } - - public void setQsScrimEnabled(boolean qsScrimEnabled) { - boolean changed = mQsScrimEnabled != qsScrimEnabled; - mQsScrimEnabled = qsScrimEnabled; - if (changed) { - updateQsState(); - } - } - - public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) { - mKeyguardUserSwitcher = keyguardUserSwitcher; - } - - public void onScreenTurningOn() { - mKeyguardStatusView.dozeTimeTick(); - } - - @Override - public void onEmptySpaceClicked(float x, float y) { - onEmptySpaceClick(x); - } - - @Override - protected boolean onMiddleClicked() { - switch (mBarState) { - case StatusBarState.KEYGUARD: - if (!mDozingOnDown) { - if (mKeyguardBypassController.getBypassEnabled()) { - mUpdateMonitor.requestFaceAuth(); - } else { - mLockscreenGestureLogger.write( - MetricsEvent.ACTION_LS_HINT, - 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); - startUnlockHintAnimation(); - } - } - return true; - case StatusBarState.SHADE_LOCKED: - if (!mQsExpanded) { - mStatusBarStateController.setState(StatusBarState.KEYGUARD); - } - return true; - case StatusBarState.SHADE: - - // This gets called in the middle of the touch handling, where the state is still - // that we are tracking the panel. Collapse the panel after this is done. - post(mPostCollapseRunnable); - return false; - default: - return true; - } - } - @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); @@ -3011,250 +69,18 @@ public class NotificationPanelView extends PanelView implements } } - public float getCurrentPanelAlpha() { + float getCurrentPanelAlpha() { return mCurrentPanelAlpha; } - public boolean setPanelAlpha(int alpha, boolean animate) { - if (mPanelAlpha != alpha) { - mPanelAlpha = alpha; - PropertyAnimator.setProperty(this, PANEL_ALPHA, alpha, - alpha == 255 ? PANEL_ALPHA_IN_PROPERTIES : PANEL_ALPHA_OUT_PROPERTIES, animate); - return true; - } - return false; - } - - public void setPanelAlphaInternal(float alpha) { + void setPanelAlphaInternal(float alpha) { mCurrentPanelAlpha = (int) alpha; mAlphaPaint.setARGB(mCurrentPanelAlpha, 255, 255, 255); invalidate(); } - public void setPanelAlphaEndAction(Runnable r) { - mPanelAlphaEndAction = r; - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - if (DEBUG) { - Paint p = new Paint(); - p.setColor(Color.RED); - p.setStrokeWidth(2); - p.setStyle(Paint.Style.STROKE); - canvas.drawLine(0, getMaxPanelHeight(), getWidth(), getMaxPanelHeight(), p); - p.setColor(Color.BLUE); - canvas.drawLine(0, getExpandedHeight(), getWidth(), getExpandedHeight(), p); - p.setColor(Color.GREEN); - canvas.drawLine(0, calculatePanelHeightQsExpanded(), getWidth(), - calculatePanelHeightQsExpanded(), p); - p.setColor(Color.YELLOW); - canvas.drawLine(0, calculatePanelHeightShade(), getWidth(), - calculatePanelHeightShade(), p); - p.setColor(Color.MAGENTA); - canvas.drawLine(0, calculateQsTopPadding(), getWidth(), - calculateQsTopPadding(), p); - p.setColor(Color.CYAN); - canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, getWidth(), - mNotificationStackScroller.getTopPadding(), p); - p.setColor(Color.GRAY); - canvas.drawLine(0, mClockPositionResult.clockY, getWidth(), - mClockPositionResult.clockY, p); - } - } - - @Override - public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) { - mNotificationStackScroller.setInHeadsUpPinnedMode(inPinnedMode); - if (inPinnedMode) { - mHeadsUpExistenceChangedRunnable.run(); - updateNotificationTranslucency(); - } else { - setHeadsUpAnimatingAway(true); - mNotificationStackScroller.runAfterAnimationFinished( - mHeadsUpExistenceChangedRunnable); - } - updateGestureExclusionRect(); - mHeadsUpPinnedMode = inPinnedMode; - updateHeadsUpVisibility(); - updateKeyguardStatusBarForHeadsUp(); - } - - private void updateKeyguardStatusBarForHeadsUp() { - boolean showingKeyguardHeadsUp = mKeyguardShowing - && mHeadsUpAppearanceController.shouldBeVisible(); - if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) { - mShowingKeyguardHeadsUp = showingKeyguardHeadsUp; - if (mKeyguardShowing) { - PropertyAnimator.setProperty(this, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, - showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES, - true /* animate */); - } else { - PropertyAnimator.applyImmediately(this, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f); - } - } - } - - private void setKeyguardHeadsUpShowingAmount(float amount) { - mKeyguardHeadsUpShowingAmount = amount; - updateHeaderKeyguardAlpha(); - } - - private float getKeyguardHeadsUpShowingAmount() { - return mKeyguardHeadsUpShowingAmount; - } - - public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { - mHeadsUpAnimatingAway = headsUpAnimatingAway; - mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway); - updateHeadsUpVisibility(); - } - - private void updateHeadsUpVisibility() { - ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode); - } - - @Override - public void onHeadsUpPinned(NotificationEntry entry) { - if (!isOnKeyguard()) { - mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), - true); - } - } - - @Override - public void onHeadsUpUnPinned(NotificationEntry entry) { - - // When we're unpinning the notification via active edge they remain heads-upped, - // we need to make sure that an animation happens in this case, otherwise the notification - // will stick to the top without any interaction. - if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) { - mNotificationStackScroller.generateHeadsUpAnimation( - entry.getHeadsUpAnimationView(), false); - entry.setHeadsUpIsVisible(); - } - } - - @Override - public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { - mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp); - } - - @Override - public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { - super.setHeadsUpManager(headsUpManager); - mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, - mNotificationStackScroller.getHeadsUpCallback(), this); - } - - public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) { - if (pickedChild != null) { - notifyListenersTrackingHeadsUp(pickedChild); - mExpandingFromHeadsUp = true; - } - // otherwise we update the state when the expansion is finished - } - - @Override - protected void onClosingFinished() { - super.onClosingFinished(); - resetHorizontalPanelPosition(); - setClosingWithAlphaFadeout(false); - } - - private void setClosingWithAlphaFadeout(boolean closing) { - mClosingWithAlphaFadeOut = closing; - mNotificationStackScroller.forceNoOverlappingRendering(closing); - } - - /** - * Updates the vertical position of the panel so it is positioned closer to the touch - * responsible for opening the panel. - * - * @param x the x-coordinate the touch event - */ - protected void updateVerticalPanelPosition(float x) { - if (mNotificationStackScroller.getWidth() * 1.75f > getWidth()) { - resetHorizontalPanelPosition(); - return; - } - float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2; - float rightMost = getWidth() - mPositionMinSideMargin - - mNotificationStackScroller.getWidth() / 2; - if (Math.abs(x - getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) { - x = getWidth() / 2; - } - x = Math.min(rightMost, Math.max(leftMost, x)); - float center = - mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2; - setHorizontalPanelTranslation(x - center); - } - - private void resetHorizontalPanelPosition() { - setHorizontalPanelTranslation(0f); - } - - protected void setHorizontalPanelTranslation(float translation) { - mNotificationStackScroller.setTranslationX(translation); - mQsFrame.setTranslationX(translation); - int size = mVerticalTranslationListener.size(); - for (int i = 0; i < size; i++) { - mVerticalTranslationListener.get(i).run(); - } - } - - protected void updateExpandedHeight(float expandedHeight) { - if (mTracking) { - mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity()); - } - if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) { - // The expandedHeight is always the full panel Height when bypassing - expandedHeight = getMaxPanelHeightNonBypass(); - } - mNotificationStackScroller.setExpandedHeight(expandedHeight); - updateKeyguardBottomAreaAlpha(); - updateBigClockAlpha(); - updateStatusBarIcons(); - } - - /** - * @return whether the notifications are displayed full width and don't have any margins on - * the side. - */ - public boolean isFullWidth() { - return mIsFullWidth; - } - - private void updateStatusBarIcons() { - boolean showIconsWhenExpanded = (isPanelVisibleBecauseOfHeadsUp() || isFullWidth()) - && getExpandedHeight() < getOpeningHeight(); - if (showIconsWhenExpanded && mNoVisibleNotifications && isOnKeyguard()) { - showIconsWhenExpanded = false; - } - if (showIconsWhenExpanded != mShowIconsWhenExpanded) { - mShowIconsWhenExpanded = showIconsWhenExpanded; - mCommandQueue.recomputeDisableFlags(mDisplayId, false); - } - } - - private boolean isOnKeyguard() { - return mBarState == StatusBarState.KEYGUARD; - } - - public void setPanelScrimMinFraction(float minFraction) { - mBar.panelScrimMinFractionChanged(minFraction); - } - - public void clearNotificationEffects() { - mStatusBar.clearNotificationEffects(); - } - - @Override - protected boolean isPanelVisibleBecauseOfHeadsUp() { - return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway) - && mBarState == StatusBarState.SHADE; + public void setDozing(boolean dozing) { + mDozing = dozing; } @Override @@ -3262,382 +88,11 @@ public class NotificationPanelView extends PanelView implements return !mDozing; } - public void launchCamera(boolean animate, int source) { - if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) { - mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP; - } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) { - mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE; - } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) { - mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER; - } else { - - // Default. - mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; - } - - // If we are launching it when we are occluded already we don't want it to animate, - // nor setting these flags, since the occluded state doesn't change anymore, hence it's - // never reset. - if (!isFullyCollapsed()) { - setLaunchingAffordance(true); - } else { - animate = false; - } - mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null; - mAffordanceHelper.launchAffordance(animate, getLayoutDirection() == LAYOUT_DIRECTION_RTL); - } - - public void onAffordanceLaunchEnded() { - setLaunchingAffordance(false); - } - - /** - * Set whether we are currently launching an affordance. This is currently only set when - * launched via a camera gesture. - */ - private void setLaunchingAffordance(boolean launchingAffordance) { - mLaunchingAffordance = launchingAffordance; - getLeftIcon().setLaunchingAffordance(launchingAffordance); - getRightIcon().setLaunchingAffordance(launchingAffordance); - mKeyguardBypassController.setLaunchingAffordance(launchingAffordance); - if (mAffordanceLaunchListener != null) { - mAffordanceLaunchListener.accept(launchingAffordance); - } - } - - /** - * Return true when a bottom affordance is launching an occluded activity with a splash screen. - */ - public boolean isLaunchingAffordanceWithPreview() { - return mLaunchingAffordance && mAffordanceHasPreview; - } - - /** - * Whether the camera application can be launched for the camera launch gesture. - */ - public boolean canCameraGestureBeLaunched() { - if (!mStatusBar.isCameraAllowedByAdmin()) { - return false; - } - - ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent(); - String packageToLaunch = (resolveInfo == null || resolveInfo.activityInfo == null) - ? null : resolveInfo.activityInfo.packageName; - return packageToLaunch != null && - (mBarState != StatusBarState.SHADE || !isForegroundApp(packageToLaunch)) - && !mAffordanceHelper.isSwipingInProgress(); - } - - /** - * Return true if the applications with the package name is running in foreground. - * - * @param pkgName application package name. - */ - private boolean isForegroundApp(String pkgName) { - ActivityManager am = getContext().getSystemService(ActivityManager.class); - List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1); - return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); - } - - private void setGroupManager(NotificationGroupManager groupManager) { - mGroupManager = groupManager; - } - - public boolean hideStatusBarIconsWhenExpanded() { - if (mLaunchingNotification) { - return mHideIconsDuringNotificationLaunch; - } - if (mHeadsUpAppearanceController != null - && mHeadsUpAppearanceController.shouldBeVisible()) { - return false; - } - return !isFullWidth() || !mShowIconsWhenExpanded; - } - - private final FragmentListener mFragmentListener = new FragmentListener() { - @Override - public void onFragmentViewCreated(String tag, Fragment fragment) { - mQs = (QS) fragment; - mQs.setPanelView(NotificationPanelView.this); - mQs.setExpandClickListener(NotificationPanelView.this); - mQs.setHeaderClickable(mQsExpansionEnabled); - updateQSPulseExpansion(); - mQs.setOverscrolling(mStackScrollerOverscrolling); - - // recompute internal state when qspanel height changes - mQs.getView().addOnLayoutChangeListener( - (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { - final int height = bottom - top; - final int oldHeight = oldBottom - oldTop; - if (height != oldHeight) { - onQsHeightChanged(); - } - }); - mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView()); - if (mQs instanceof QSFragment) { - mKeyguardStatusBar.setQSPanel(((QSFragment) mQs).getQsPanel()); - } - updateQsExpansion(); - } - - @Override - public void onFragmentViewDestroyed(String tag, Fragment fragment) { - // Manual handling of fragment lifecycle is only required because this bridges - // non-fragment and fragment code. Once we are using a fragment for the notification - // panel, mQs will not need to be null cause it will be tied to the same lifecycle. - if (fragment == mQs) { - mQs = null; - } - } - }; - - @Override - public void setTouchAndAnimationDisabled(boolean disabled) { - super.setTouchAndAnimationDisabled(disabled); - if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) { - mAffordanceHelper.reset(false /* animate */); - } - mNotificationStackScroller.setAnimationsEnabled(!disabled); - } - - /** - * Sets the dozing state. - * - * @param dozing {@code true} when dozing. - * @param animate if transition should be animated. - * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor. - */ - public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) { - if (dozing == mDozing) return; - mDozing = dozing; - mNotificationStackScroller.setDozing(mDozing, animate, wakeUpTouchLocation); - mKeyguardBottomArea.setDozing(mDozing, animate); - - if (dozing) { - mBottomAreaShadeAlphaAnimator.cancel(); - } - - if (mBarState == StatusBarState.KEYGUARD - || mBarState == StatusBarState.SHADE_LOCKED) { - updateDozingVisibilities(animate); - } - - final float dozeAmount = dozing ? 1 : 0; - mStatusBarStateController.setDozeAmount(dozeAmount, animate); - } - - @Override - public void onDozeAmountChanged(float linearAmount, float amount) { - mInterpolatedDarkAmount = amount; - mLinearDarkAmount = linearAmount; - mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount); - mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount); - positionClockAndNotifications(); - } - - public void setPulsing(boolean pulsing) { - mPulsing = pulsing; - final boolean animatePulse = !mDozeParameters.getDisplayNeedsBlanking() - && mDozeParameters.getAlwaysOn(); - if (animatePulse) { - mAnimateNextPositionUpdate = true; - } - // Do not animate the clock when waking up from a pulse. - // The height callback will take care of pushing the clock to the right position. - if (!mPulsing && !mDozing) { - mAnimateNextPositionUpdate = false; - } - mNotificationStackScroller.setPulsing(pulsing, animatePulse); - mKeyguardStatusView.setPulsing(pulsing); - } - - public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) { - if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) { - mAmbientIndicationBottomPadding = ambientIndicationBottomPadding; - mStatusBar.updateKeyguardMaxNotifications(); - } - } - - public void dozeTimeTick() { - mKeyguardBottomArea.dozeTimeTick(); - mKeyguardStatusView.dozeTimeTick(); - if (mInterpolatedDarkAmount > 0) { - positionClockAndNotifications(); - } - } - - public void setStatusAccessibilityImportance(int mode) { - mKeyguardStatusView.setImportantForAccessibility(mode); - } - - /** - * TODO: this should be removed. - * It's not correct to pass this view forward because other classes will end up adding - * children to it. Theme will be out of sync. - * - * @return bottom area view - */ - public KeyguardBottomAreaView getKeyguardBottomAreaView() { - return mKeyguardBottomArea; - } - - public void setUserSetupComplete(boolean userSetupComplete) { - mUserSetupComplete = userSetupComplete; - mKeyguardBottomArea.setUserSetupComplete(userSetupComplete); - } - - public void applyExpandAnimationParams(ExpandAnimationParameters params) { - mExpandOffset = params != null ? params.getTopChange() : 0; - updateQsExpansion(); - if (params != null) { - boolean hideIcons = params.getProgress( - ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f; - if (hideIcons != mHideIconsDuringNotificationLaunch) { - mHideIconsDuringNotificationLaunch = hideIcons; - if (!hideIcons) { - mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); - } - } - } - } - - public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) { - mTrackingHeadsUpListeners.add(listener); - } - - public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) { - mTrackingHeadsUpListeners.remove(listener); - } - - public void addVerticalTranslationListener(Runnable verticalTranslationListener) { - mVerticalTranslationListener.add(verticalTranslationListener); - } - - public void removeVerticalTranslationListener(Runnable verticalTranslationListener) { - mVerticalTranslationListener.remove(verticalTranslationListener); - } - - public void setHeadsUpAppearanceController( - HeadsUpAppearanceController headsUpAppearanceController) { - mHeadsUpAppearanceController = headsUpAppearanceController; - } - - /** - * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the - * security view of the bouncer. - */ - public void onBouncerPreHideAnimation() { - setKeyguardStatusViewVisibility(mBarState, true /* keyguardFadingAway */, - false /* goingToFullShade */); - } - - /** - * Do not let the user drag the shade up and down for the current touch session. - * This is necessary to avoid shade expansion while/after the bouncer is dismissed. - */ - public void blockExpansionForCurrentTouch() { - mBlockingExpansionForCurrentTouch = mTracking; - } - - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - super.dump(fd, pw, args); - pw.println(" gestureExclusionRect: " + calculateGestureExclusionRect()); - if (mKeyguardStatusBar != null) { - mKeyguardStatusBar.dump(fd, pw, args); - } - if (mKeyguardStatusView != null) { - mKeyguardStatusView.dump(fd, pw, args); - } - } - - public boolean hasActiveClearableNotifications() { - return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL); - } - - @Override - public void onZenChanged(int zen) { - updateShowEmptyShadeView(); - } - - private void updateShowEmptyShadeView() { - boolean showEmptyShadeView = - mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications(); - showEmptyShadeView(showEmptyShadeView); - } - - public RemoteInputController.Delegate createRemoteInputDelegate() { - return mNotificationStackScroller.createDelegate(); - } - - public void updateNotificationViews() { - mNotificationStackScroller.updateSectionBoundaries(); - mNotificationStackScroller.updateSpeedBumpIndex(); - mNotificationStackScroller.updateFooter(); - updateShowEmptyShadeView(); - mNotificationStackScroller.updateIconAreaViews(); - } - - public void onUpdateRowStates() { - mNotificationStackScroller.onUpdateRowStates(); - } - - public boolean hasPulsingNotifications() { - return mNotificationStackScroller.hasPulsingNotifications(); - } - - public ActivatableNotificationView getActivatedChild() { - return mNotificationStackScroller.getActivatedChild(); - } - - public void setActivatedChild(ActivatableNotificationView o) { - mNotificationStackScroller.setActivatedChild(o); - } - - public void runAfterAnimationFinished(Runnable r) { - mNotificationStackScroller.runAfterAnimationFinished(r); - } - - public void setScrollingEnabled(boolean b) { - mNotificationStackScroller.setScrollingEnabled(b); - } - - public void initDependencies(StatusBar statusBar, NotificationGroupManager groupManager, - NotificationShelf notificationShelf, - HeadsUpManagerPhone headsUpManager, - NotificationIconAreaController notificationIconAreaController, - ScrimController scrimController) { - setStatusBar(statusBar); - setGroupManager(mGroupManager); - mNotificationStackScroller.setNotificationPanel(this); - mNotificationStackScroller.setIconAreaController(notificationIconAreaController); - mNotificationStackScroller.setStatusBar(statusBar); - mNotificationStackScroller.setGroupManager(groupManager); - mNotificationStackScroller.setShelf(notificationShelf); - mNotificationStackScroller.setScrimController(scrimController); - updateShowEmptyShadeView(); - } - - public void showTransientIndication(int id) { - mKeyguardIndicationController.showTransientIndication(id); - } - - @Override - public void onDynamicPrivacyChanged() { - // Do not request animation when pulsing or waking up, otherwise the clock wiill be out - // of sync with the notification panel. - if (mLinearDarkAmount != 0) { - return; - } - mAnimateNextPositionUpdate = true; - } - - public void setOnReinflationListener(Runnable onReinflationListener) { - mOnReinflationListener = onReinflationListener; + void setRtlChangeListener(RtlChangeListener listener) { + mRtlChangeListener = listener; } - public static boolean isQsSplitEnabled() { - return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.QS_SPLIT_ENABLED, false); + interface RtlChangeListener { + void onRtlPropertielsChanged(int layoutDirection); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java new file mode 100644 index 000000000000..90ec2a076e87 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -0,0 +1,3741 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; +import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.app.ActivityManager; +import android.app.Fragment; +import android.app.StatusBarManager; +import android.content.Context; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.Region; +import android.graphics.drawable.Drawable; +import android.hardware.biometrics.BiometricSourceType; +import android.os.PowerManager; +import android.os.SystemClock; +import android.provider.DeviceConfig; +import android.provider.Settings; +import android.util.Log; +import android.util.MathUtils; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; +import android.view.ViewTreeObserver; +import android.view.WindowInsets; +import android.view.accessibility.AccessibilityManager; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.util.LatencyTracker; +import com.android.keyguard.KeyguardClockSwitch; +import com.android.keyguard.KeyguardStatusView; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.DejankUtils; +import com.android.systemui.Interpolators; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.DisplayId; +import com.android.systemui.doze.DozeLog; +import com.android.systemui.fragments.FragmentHostManager; +import com.android.systemui.fragments.FragmentHostManager.FragmentListener; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.HomeControlsPlugin; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.plugins.qs.QS; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.qs.QSFragment; +import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.FlingAnimationUtils; +import com.android.systemui.statusbar.GestureRecorder; +import com.android.systemui.statusbar.KeyguardAffordanceView; +import com.android.systemui.statusbar.KeyguardIndicationController; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationShelf; +import com.android.systemui.statusbar.PulseExpansionHandler; +import com.android.systemui.statusbar.RemoteInputController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.VibratorHelper; +import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; +import com.android.systemui.statusbar.notification.AnimatableProperty; +import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.PropertyAnimator; +import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.ExpandableView; +import com.android.systemui.statusbar.notification.stack.AnimationProperties; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; +import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.InjectionInflationController; +import com.android.systemui.util.Utils; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +import javax.inject.Inject; + +@StatusBarComponent.StatusBarScope +public class NotificationPanelViewController extends PanelViewController { + + private static final boolean DEBUG = false; + + /** + * Fling expanding QS. + */ + private static final int FLING_EXPAND = 0; + + /** + * Fling collapsing QS, potentially stopping when QS becomes QQS. + */ + private static final int FLING_COLLAPSE = 1; + + /** + * Fling until QS is completely hidden. + */ + private static final int FLING_HIDE = 2; + private final DozeParameters mDozeParameters; + private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener(); + private final OnClickListener mOnClickListener = new OnClickListener(); + private final OnOverscrollTopChangedListener + mOnOverscrollTopChangedListener = + new OnOverscrollTopChangedListener(); + private final KeyguardAffordanceHelperCallback + mKeyguardAffordanceHelperCallback = + new KeyguardAffordanceHelperCallback(); + private final OnEmptySpaceClickListener + mOnEmptySpaceClickListener = + new OnEmptySpaceClickListener(); + private final MyOnHeadsUpChangedListener + mOnHeadsUpChangedListener = + new MyOnHeadsUpChangedListener(); + private final HeightListener mHeightListener = new HeightListener(); + private final ZenModeControllerCallback + mZenModeControllerCallback = + new ZenModeControllerCallback(); + private final ConfigurationListener mConfigurationListener = new ConfigurationListener(); + private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener(); + private final ExpansionCallback mExpansionCallback = new ExpansionCallback(); + private final NotificationPanelView mView; + private final MetricsLogger mMetricsLogger; + private final ActivityManager mActivityManager; + private final ZenModeController mZenModeController; + private final ConfigurationController mConfigurationController; + private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; + + private double mQqsSplitFraction; + + // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is + // changed. + private static final int CAP_HEIGHT = 1456; + private static final int FONT_HEIGHT = 2163; + + /** + * Maximum time before which we will expand the panel even for slow motions when getting a + * touch passed over from launcher. + */ + private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300; + + private static final String COUNTER_PANEL_OPEN = "panel_open"; + private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs"; + private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek"; + + private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1); + private static final Rect EMPTY_RECT = new Rect(); + + private static final AnimationProperties + CLOCK_ANIMATION_PROPERTIES = + new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); + private final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT = AnimatableProperty.from( + "KEYGUARD_HEADS_UP_SHOWING_AMOUNT", + (notificationPanelView, aFloat) -> setKeyguardHeadsUpShowingAmount(aFloat), + (Function<NotificationPanelView, Float>) notificationPanelView -> + getKeyguardHeadsUpShowingAmount(), + R.id.keyguard_hun_animator_tag, R.id.keyguard_hun_animator_end_tag, + R.id.keyguard_hun_animator_start_tag); + private static final AnimationProperties + KEYGUARD_HUN_PROPERTIES = + new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); + @VisibleForTesting + final KeyguardUpdateMonitorCallback + mKeyguardUpdateCallback = + new KeyguardUpdateMonitorCallback() { + + @Override + public void onBiometricAuthenticated(int userId, + BiometricSourceType biometricSourceType) { + if (mFirstBypassAttempt && mUpdateMonitor.isUnlockingWithBiometricAllowed()) { + mDelayShowingKeyguardStatusBar = true; + } + } + + @Override + public void onBiometricRunningStateChanged(boolean running, + BiometricSourceType biometricSourceType) { + boolean + keyguardOrShadeLocked = + mBarState == StatusBarState.KEYGUARD + || mBarState == StatusBarState.SHADE_LOCKED; + if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing + && !mDelayShowingKeyguardStatusBar) { + mFirstBypassAttempt = false; + animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); + } + } + + @Override + public void onFinishedGoingToSleep(int why) { + mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); + mDelayShowingKeyguardStatusBar = false; + } + }; + + private final InjectionInflationController mInjectionInflationController; + private final PowerManager mPowerManager; + private final AccessibilityManager mAccessibilityManager; + private final NotificationWakeUpCoordinator mWakeUpCoordinator; + private final PulseExpansionHandler mPulseExpansionHandler; + private final KeyguardBypassController mKeyguardBypassController; + private final KeyguardUpdateMonitor mUpdateMonitor; + + private KeyguardAffordanceHelper mAffordanceHelper; + private KeyguardUserSwitcher mKeyguardUserSwitcher; + private KeyguardStatusBarView mKeyguardStatusBar; + private ViewGroup mBigClockContainer; + private QS mQs; + private FrameLayout mQsFrame; + private KeyguardStatusView mKeyguardStatusView; + private View mQsNavbarScrim; + private NotificationsQuickSettingsContainer mNotificationContainerParent; + private NotificationStackScrollLayout mNotificationStackScroller; + private FrameLayout mHomeControlsLayout; + private boolean mAnimateNextPositionUpdate; + + private int mTrackingPointer; + private VelocityTracker mQsVelocityTracker; + private boolean mQsTracking; + + /** + * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and + * the expansion for quick settings. + */ + private boolean mConflictingQsExpansionGesture; + + private boolean mPanelExpanded; + private boolean mQsExpanded; + private boolean mQsExpandedWhenExpandingStarted; + private boolean mQsFullyExpanded; + private boolean mKeyguardShowing; + private boolean mDozing; + private boolean mDozingOnDown; + private int mBarState; + private float mInitialHeightOnTouch; + private float mInitialTouchX; + private float mInitialTouchY; + private float mQsExpansionHeight; + private int mQsMinExpansionHeight; + private int mQsMaxExpansionHeight; + private int mQsPeekHeight; + private boolean mStackScrollerOverscrolling; + private boolean mQsExpansionFromOverscroll; + private float mLastOverscroll; + private boolean mQsExpansionEnabled = true; + private ValueAnimator mQsExpansionAnimator; + private FlingAnimationUtils mFlingAnimationUtils; + private int mStatusBarMinHeight; + private int mNotificationsHeaderCollideDistance; + private float mEmptyDragAmount; + private float mDownX; + private float mDownY; + + private final KeyguardClockPositionAlgorithm + mClockPositionAlgorithm = + new KeyguardClockPositionAlgorithm(); + private final KeyguardClockPositionAlgorithm.Result + mClockPositionResult = + new KeyguardClockPositionAlgorithm.Result(); + private boolean mIsExpanding; + + private boolean mBlockTouches; + // Used for two finger gesture as well as accessibility shortcut to QS. + private boolean mQsExpandImmediate; + private boolean mTwoFingerQsExpandPossible; + + /** + * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still + * need to take this into account in our panel height calculation. + */ + private boolean mQsAnimatorExpand; + private boolean mIsLaunchTransitionFinished; + private boolean mIsLaunchTransitionRunning; + private Runnable mLaunchAnimationEndRunnable; + private boolean mOnlyAffordanceInThisMotion; + private boolean mKeyguardStatusViewAnimating; + private ValueAnimator mQsSizeChangeAnimator; + + private boolean mShowEmptyShadeView; + + private boolean mQsScrimEnabled = true; + private boolean mQsTouchAboveFalsingThreshold; + private int mQsFalsingThreshold; + + private float mKeyguardStatusBarAnimateAlpha = 1f; + private HeadsUpTouchHelper mHeadsUpTouchHelper; + private boolean mListenForHeadsUp; + private int mNavigationBarBottomHeight; + private boolean mExpandingFromHeadsUp; + private boolean mCollapsedOnDown; + private int mPositionMinSideMargin; + private int mLastOrientation = -1; + private boolean mClosingWithAlphaFadeOut; + private boolean mHeadsUpAnimatingAway; + private boolean mLaunchingAffordance; + private boolean mAffordanceHasPreview; + private FalsingManager mFalsingManager; + private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; + + private Runnable mHeadsUpExistenceChangedRunnable = () -> { + setHeadsUpAnimatingAway(false); + notifyBarPanelExpansionChanged(); + }; + private NotificationGroupManager mGroupManager; + private boolean mShowIconsWhenExpanded; + private int mIndicationBottomPadding; + private int mAmbientIndicationBottomPadding; + private boolean mIsFullWidth; + private boolean mBlockingExpansionForCurrentTouch; + + /** + * Following variables maintain state of events when input focus transfer may occur. + */ + private boolean mExpectingSynthesizedDown; // expecting to see synthesized DOWN event + private boolean mLastEventSynthesizedDown; // last event was synthesized DOWN event + + /** + * Current dark amount that follows regular interpolation curve of animation. + */ + private float mInterpolatedDarkAmount; + + /** + * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the + * interpolation curve is different. + */ + private float mLinearDarkAmount; + + private boolean mPulsing; + private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); + private boolean mUserSetupComplete; + private int mQsNotificationTopPadding; + private float mExpandOffset; + private boolean mHideIconsDuringNotificationLaunch = true; + private int mStackScrollerMeasuringPass; + private ArrayList<Consumer<ExpandableNotificationRow>> + mTrackingHeadsUpListeners = + new ArrayList<>(); + private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>(); + private HeadsUpAppearanceController mHeadsUpAppearanceController; + + private int mPanelAlpha; + private Runnable mPanelAlphaEndAction; + private float mBottomAreaShadeAlpha; + private final ValueAnimator mBottomAreaShadeAlphaAnimator; + private AnimatorListenerAdapter mAnimatorListenerAdapter = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (mPanelAlphaEndAction != null) { + mPanelAlphaEndAction.run(); + } + } + }; + private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha", + NotificationPanelView::setPanelAlphaInternal, + NotificationPanelView::getCurrentPanelAlpha, + R.id.panel_alpha_animator_tag, R.id.panel_alpha_animator_start_tag, + R.id.panel_alpha_animator_end_tag); + private final AnimationProperties mPanelAlphaOutPropertiesAnimator = + new AnimationProperties().setDuration(150).setCustomInterpolator( + mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT); + private final AnimationProperties mPanelAlphaInPropertiesAnimator = + new AnimationProperties().setDuration(200).setAnimationFinishListener( + mAnimatorListenerAdapter).setCustomInterpolator( + mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN); + private final NotificationEntryManager mEntryManager; + + private final CommandQueue mCommandQueue; + private final NotificationLockscreenUserManager mLockscreenUserManager; + private final ShadeController mShadeController; + private int mDisplayId; + + /** + * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged. + * + * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary + * work, check the current id with the cached id. + */ + private int mThemeResId; + private KeyguardIndicationController mKeyguardIndicationController; + private Consumer<Boolean> mAffordanceLaunchListener; + private int mShelfHeight; + private Runnable mOnReinflationListener; + private int mDarkIconSize; + private int mHeadsUpInset; + private boolean mHeadsUpPinnedMode; + private float mKeyguardHeadsUpShowingAmount = 0.0f; + private boolean mShowingKeyguardHeadsUp; + private boolean mAllowExpandForSmallExpansion; + private Runnable mExpandAfterLayoutRunnable; + + /** + * If face auth with bypass is running for the first time after you turn on the screen. + * (From aod or screen off) + */ + private boolean mFirstBypassAttempt; + /** + * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until + * the keyguard is dismissed to show the status bar. + */ + private boolean mDelayShowingKeyguardStatusBar; + + private PluginManager mPluginManager; + private FrameLayout mPluginFrame; + private NPVPluginManager mNPVPluginManager; + private int mOldLayoutDirection; + + @Inject + public NotificationPanelViewController(NotificationPanelView view, + InjectionInflationController injectionInflationController, + NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, + DynamicPrivacyController dynamicPrivacyController, + KeyguardBypassController bypassController, FalsingManager falsingManager, + PluginManager pluginManager, ShadeController shadeController, + NotificationLockscreenUserManager notificationLockscreenUserManager, + NotificationEntryManager notificationEntryManager, + KeyguardStateController keyguardStateController, + StatusBarStateController statusBarStateController, DozeLog dozeLog, + DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, + LatencyTracker latencyTracker, PowerManager powerManager, + AccessibilityManager accessibilityManager, @DisplayId int displayId, + KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, + ActivityManager activityManager, ZenModeController zenModeController, + ConfigurationController configurationController, + FlingAnimationUtils.Builder flingAnimationUtilsBuilder) { + super(view, falsingManager, dozeLog, keyguardStateController, + (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, + latencyTracker, flingAnimationUtilsBuilder); + mView = view; + mMetricsLogger = metricsLogger; + mActivityManager = activityManager; + mZenModeController = zenModeController; + mConfigurationController = configurationController; + mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder; + mView.setWillNotDraw(!DEBUG); + mInjectionInflationController = injectionInflationController; + mFalsingManager = falsingManager; + mPowerManager = powerManager; + mWakeUpCoordinator = coordinator; + mAccessibilityManager = accessibilityManager; + mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); + setPanelAlpha(255, false /* animate */); + mCommandQueue = commandQueue; + mDisplayId = displayId; + mPulseExpansionHandler = pulseExpansionHandler; + mDozeParameters = dozeParameters; + pulseExpansionHandler.setPulseExpandAbortListener(() -> { + if (mQs != null) { + mQs.animateHeaderSlidingOut(); + } + }); + mThemeResId = mView.getContext().getThemeResId(); + mKeyguardBypassController = bypassController; + mUpdateMonitor = keyguardUpdateMonitor; + mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); + KeyguardStateController.Callback + keyguardMonitorCallback = + new KeyguardStateController.Callback() { + @Override + public void onKeyguardFadingAwayChanged() { + if (!mKeyguardStateController.isKeyguardFadingAway()) { + mFirstBypassAttempt = false; + mDelayShowingKeyguardStatusBar = false; + } + } + }; + mKeyguardStateController.addCallback(keyguardMonitorCallback); + DynamicPrivacyControlListener + dynamicPrivacyControlListener = + new DynamicPrivacyControlListener(); + dynamicPrivacyController.addListener(dynamicPrivacyControlListener); + + mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0); + mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> { + mBottomAreaShadeAlpha = (float) animation.getAnimatedValue(); + updateKeyguardBottomAreaAlpha(); + }); + mBottomAreaShadeAlphaAnimator.setDuration(160); + mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT); + mPluginManager = pluginManager; + mShadeController = shadeController; + mLockscreenUserManager = notificationLockscreenUserManager; + mEntryManager = notificationEntryManager; + + mView.setBackgroundColor(Color.TRANSPARENT); + OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener(); + mView.addOnAttachStateChangeListener(onAttachStateChangeListener); + if (mView.isAttachedToWindow()) { + onAttachStateChangeListener.onViewAttachedToWindow(mView); + } + + mView.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener()); + + if (DEBUG) { + mView.getOverlay().add(new DebugDrawable()); + } + + onFinishInflate(); + } + + private void onFinishInflate() { + loadDimens(); + mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header); + mKeyguardStatusView = mView.findViewById(R.id.keyguard_status_view); + + KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container); + mBigClockContainer = mView.findViewById(R.id.big_clock_container); + keyguardClockSwitch.setBigClockContainer(mBigClockContainer); + + mHomeControlsLayout = mView.findViewById(R.id.home_controls_layout); + mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent); + mNotificationStackScroller = mView.findViewById(R.id.notification_stack_scroller); + mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener); + mNotificationStackScroller.setOverscrollTopChangedListener(mOnOverscrollTopChangedListener); + mNotificationStackScroller.setOnEmptySpaceClickListener(mOnEmptySpaceClickListener); + addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp); + mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area); + mQsNavbarScrim = mView.findViewById(R.id.qs_navbar_scrim); + mLastOrientation = mResources.getConfiguration().orientation; + mPluginFrame = mView.findViewById(R.id.plugin_frame); + if (Settings.System.getInt(mView.getContext().getContentResolver(), "npv_plugin_flag", 0) + == 1) { + mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager); + } + + + initBottomArea(); + + mWakeUpCoordinator.setStackScroller(mNotificationStackScroller); + mQsFrame = mView.findViewById(R.id.qs_frame); + mPulseExpansionHandler.setUp( + mNotificationStackScroller, mExpansionCallback, mShadeController); + mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() { + @Override + public void onFullyHiddenChanged(boolean isFullyHidden) { + updateKeyguardStatusBarForHeadsUp(); + } + + @Override + public void onPulseExpansionChanged(boolean expandingChanged) { + if (mKeyguardBypassController.getBypassEnabled()) { + // Position the notifications while dragging down while pulsing + requestScrollerTopPaddingUpdate(false /* animate */); + updateQSPulseExpansion(); + } + } + }); + + mPluginManager.addPluginListener(new PluginListener<HomeControlsPlugin>() { + + @Override + public void onPluginConnected(HomeControlsPlugin plugin, Context pluginContext) { + plugin.sendParentGroup(mHomeControlsLayout); + } + + @Override + public void onPluginDisconnected(HomeControlsPlugin plugin) { + + } + }, HomeControlsPlugin.class, false); + + mView.setRtlChangeListener(layoutDirection -> { + if (layoutDirection != mOldLayoutDirection) { + mAffordanceHelper.onRtlPropertiesChanged(); + mOldLayoutDirection = layoutDirection; + } + }); + } + + @Override + protected void loadDimens() { + super.loadDimens(); + mFlingAnimationUtils = mFlingAnimationUtilsBuilder.reset() + .setMaxLengthSeconds(0.4f).build(); + mStatusBarMinHeight = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height); + mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height); + mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize( + R.dimen.header_notifications_collide_distance); + mClockPositionAlgorithm.loadDimens(mResources); + mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold); + mPositionMinSideMargin = mResources.getDimensionPixelSize( + R.dimen.notification_panel_min_side_margin); + mIndicationBottomPadding = mResources.getDimensionPixelSize( + R.dimen.keyguard_indication_bottom_padding); + mQsNotificationTopPadding = mResources.getDimensionPixelSize( + R.dimen.qs_notification_padding); + mShelfHeight = mResources.getDimensionPixelSize(R.dimen.notification_shelf_height); + mDarkIconSize = mResources.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark); + int statusbarHeight = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height); + mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize( + R.dimen.heads_up_status_bar_padding); + mQqsSplitFraction = ((float) mResources.getInteger(R.integer.qqs_split_fraction)) / ( + mResources.getInteger(R.integer.qqs_split_fraction) + mResources.getInteger( + R.integer.qs_split_fraction)); + } + + /** + * Returns if there's a custom clock being presented. + */ + public boolean hasCustomClock() { + return mKeyguardStatusView.hasCustomClock(); + } + + private void setStatusBar(StatusBar bar) { + // TODO: this can be injected. + mStatusBar = bar; + mKeyguardBottomArea.setStatusBar(mStatusBar); + } + /** + * @see #launchCamera(boolean, int) + * @see #setLaunchingAffordance(boolean) + */ + public void setLaunchAffordanceListener(Consumer<Boolean> listener) { + mAffordanceLaunchListener = listener; + } + + public void updateResources() { + int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width); + int panelGravity = mResources.getInteger(R.integer.notification_panel_layout_gravity); + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mQsFrame.getLayoutParams(); + if (lp.width != qsWidth || lp.gravity != panelGravity) { + lp.width = qsWidth; + lp.gravity = panelGravity; + mQsFrame.setLayoutParams(lp); + } + + int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width); + lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams(); + if (lp.width != panelWidth || lp.gravity != panelGravity) { + lp.width = panelWidth; + lp.gravity = panelGravity; + mNotificationStackScroller.setLayoutParams(lp); + } + int sideMargin = mResources.getDimensionPixelOffset(R.dimen.notification_side_paddings); + int topMargin = sideMargin; + lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams(); + if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin + || lp.rightMargin != sideMargin || lp.topMargin != topMargin) { + lp.width = qsWidth; + lp.gravity = panelGravity; + lp.leftMargin = sideMargin; + lp.rightMargin = sideMargin; + lp.topMargin = topMargin; + mPluginFrame.setLayoutParams(lp); + } + } + + private void reInflateViews() { + updateShowEmptyShadeView(); + + // Re-inflate the status view group. + int index = mView.indexOfChild(mKeyguardStatusView); + mView.removeView(mKeyguardStatusView); + mKeyguardStatusView = (KeyguardStatusView) mInjectionInflationController.injectable( + LayoutInflater.from(mView.getContext())).inflate( + R.layout.keyguard_status_view, mView, false); + mView.addView(mKeyguardStatusView, index); + + // Re-associate the clock container with the keyguard clock switch. + mBigClockContainer.removeAllViews(); + KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container); + keyguardClockSwitch.setBigClockContainer(mBigClockContainer); + + // Update keyguard bottom area + index = mView.indexOfChild(mKeyguardBottomArea); + mView.removeView(mKeyguardBottomArea); + KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea; + mKeyguardBottomArea = (KeyguardBottomAreaView) mInjectionInflationController.injectable( + LayoutInflater.from(mView.getContext())).inflate( + R.layout.keyguard_bottom_area, mView, false); + mKeyguardBottomArea.initFrom(oldBottomArea); + mView.addView(mKeyguardBottomArea, index); + initBottomArea(); + mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); + mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(), + mStatusBarStateController.getInterpolatedDozeAmount()); + + if (mKeyguardStatusBar != null) { + mKeyguardStatusBar.onThemeChanged(); + } + + setKeyguardStatusViewVisibility(mBarState, false, false); + setKeyguardBottomAreaVisibility(mBarState, false); + if (mOnReinflationListener != null) { + mOnReinflationListener.run(); + } + reinflatePluginContainer(); + } + + private void reinflatePluginContainer() { + int index = mView.indexOfChild(mPluginFrame); + mView.removeView(mPluginFrame); + mPluginFrame = (FrameLayout) mInjectionInflationController.injectable( + LayoutInflater.from(mView.getContext())).inflate( + R.layout.status_bar_expanded_plugin_frame, mView, false); + mView.addView(mPluginFrame, index); + + Resources res = mView.getResources(); + int qsWidth = res.getDimensionPixelSize(R.dimen.qs_panel_width); + int panelGravity = mView.getResources().getInteger( + R.integer.notification_panel_layout_gravity); + FrameLayout.LayoutParams lp; + int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings); + int topMargin = res.getDimensionPixelOffset( + com.android.internal.R.dimen.quick_qs_total_height); + if (Utils.useQsMediaPlayer(mView.getContext())) { + topMargin = res.getDimensionPixelOffset( + com.android.internal.R.dimen.quick_qs_total_height_with_media); + } + lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams(); + if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin + || lp.rightMargin != sideMargin || lp.topMargin != topMargin) { + lp.width = qsWidth; + lp.gravity = panelGravity; + lp.leftMargin = sideMargin; + lp.rightMargin = sideMargin; + lp.topMargin = topMargin; + mPluginFrame.setLayoutParams(lp); + } + + if (mNPVPluginManager != null) mNPVPluginManager.replaceFrameLayout(mPluginFrame); + } + + private void initBottomArea() { + mAffordanceHelper = new KeyguardAffordanceHelper( + mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager); + mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper); + mKeyguardBottomArea.setStatusBar(mStatusBar); + mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete); + } + + public void setKeyguardIndicationController(KeyguardIndicationController indicationController) { + mKeyguardIndicationController = indicationController; + mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); + } + + private void updateGestureExclusionRect() { + Rect exclusionRect = calculateGestureExclusionRect(); + mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.EMPTY_LIST + : Collections.singletonList(exclusionRect)); + } + + private Rect calculateGestureExclusionRect() { + Rect exclusionRect = null; + Region touchableRegion = mHeadsUpManager.calculateTouchableRegion(); + if (isFullyCollapsed() && touchableRegion != null) { + // Note: The heads up manager also calculates the non-pinned touchable region + exclusionRect = touchableRegion.getBounds(); + } + return exclusionRect != null ? exclusionRect : EMPTY_RECT; + } + + private void setIsFullWidth(boolean isFullWidth) { + mIsFullWidth = isFullWidth; + mNotificationStackScroller.setIsFullWidth(isFullWidth); + } + + private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) { + if (mQsSizeChangeAnimator != null) { + oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); + mQsSizeChangeAnimator.cancel(); + } + mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight); + mQsSizeChangeAnimator.setDuration(300); + mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + requestScrollerTopPaddingUpdate(false /* animate */); + requestPanelHeightUpdate(); + int height = (int) mQsSizeChangeAnimator.getAnimatedValue(); + mQs.setHeightOverride(height); + } + }); + mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mQsSizeChangeAnimator = null; + } + }); + mQsSizeChangeAnimator.start(); + } + + /** + * Positions the clock and notifications dynamically depending on how many notifications are + * showing. + */ + private void positionClockAndNotifications() { + boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending(); + boolean animateClock = animate || mAnimateNextPositionUpdate; + int stackScrollerPadding; + if (mBarState != StatusBarState.KEYGUARD) { + stackScrollerPadding = getUnlockedStackScrollerPadding(); + } else { + int totalHeight = mView.getHeight(); + int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); + int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight); + boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); + final boolean + hasVisibleNotifications = + !bypassEnabled && mNotificationStackScroller.getVisibleNotificationCount() != 0; + mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications); + mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding, + mNotificationStackScroller.getIntrinsicContentHeight(), getExpandedFraction(), + totalHeight, (int) (mKeyguardStatusView.getHeight() - mShelfHeight / 2.0f + - mDarkIconSize / 2.0f), clockPreferredY, hasCustomClock(), + hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount, + bypassEnabled, getUnlockedStackScrollerPadding()); + mClockPositionAlgorithm.run(mClockPositionResult); + PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X, + mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock); + PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y, + mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock); + updateNotificationTranslucency(); + updateClock(); + stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded; + } + mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding); + mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX); + + mStackScrollerMeasuringPass++; + requestScrollerTopPaddingUpdate(animate); + mStackScrollerMeasuringPass = 0; + mAnimateNextPositionUpdate = false; + } + + /** + * @return the padding of the stackscroller when unlocked + */ + private int getUnlockedStackScrollerPadding() { + return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight + + mQsNotificationTopPadding; + } + + /** + * @param maximum the maximum to return at most + * @return the maximum keyguard notifications that can fit on the screen + */ + public int computeMaxKeyguardNotifications(int maximum) { + float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(); + int notificationPadding = Math.max( + 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height)); + NotificationShelf shelf = mNotificationStackScroller.getNotificationShelf(); + float + shelfSize = + shelf.getVisibility() == View.GONE ? 0 + : shelf.getIntrinsicHeight() + notificationPadding; + float + availableSpace = + mNotificationStackScroller.getHeight() - minPadding - shelfSize - Math.max( + mIndicationBottomPadding, mAmbientIndicationBottomPadding) + - mKeyguardStatusView.getLogoutButtonHeight(); + int count = 0; + for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) { + ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i); + if (!(child instanceof ExpandableNotificationRow)) { + continue; + } + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + boolean + suppressedSummary = + mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup( + row.getEntry().getSbn()); + if (suppressedSummary) { + continue; + } + if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) { + continue; + } + if (row.isRemoved()) { + continue; + } + availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */) + + notificationPadding; + if (availableSpace >= 0 && count < maximum) { + count++; + } else if (availableSpace > -shelfSize) { + // if we are exactly the last view, then we can show us still! + for (int j = i + 1; j < mNotificationStackScroller.getChildCount(); j++) { + if (mNotificationStackScroller.getChildAt( + j) instanceof ExpandableNotificationRow) { + return count; + } + } + count++; + return count; + } else { + return count; + } + } + return count; + } + + private void updateClock() { + if (!mKeyguardStatusViewAnimating) { + mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha); + } + } + + public void animateToFullShade(long delay) { + mNotificationStackScroller.goToFullShade(delay); + mView.requestLayout(); + mAnimateNextPositionUpdate = true; + } + + public void setQsExpansionEnabled(boolean qsExpansionEnabled) { + mQsExpansionEnabled = qsExpansionEnabled; + if (mQs == null) return; + mQs.setHeaderClickable(qsExpansionEnabled); + } + + @Override + public void resetViews(boolean animate) { + mIsLaunchTransitionFinished = false; + mBlockTouches = false; + if (!mLaunchingAffordance) { + mAffordanceHelper.reset(false); + mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; + } + mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */, + true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */); + if (animate) { + animateCloseQs(true /* animateAway */); + } else { + closeQs(); + } + mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, animate, + !animate /* cancelAnimators */); + mNotificationStackScroller.resetScrollPosition(); + } + + @Override + public void collapse(boolean delayed, float speedUpFactor) { + if (!canPanelBeCollapsed()) { + return; + } + + if (mQsExpanded) { + mQsExpandImmediate = true; + mNotificationStackScroller.setShouldShowShelfOnly(true); + } + super.collapse(delayed, speedUpFactor); + } + + public void closeQs() { + cancelQsAnimation(); + setQsExpansion(mQsMinExpansionHeight); + } + + public void cancelAnimation() { + mView.animate().cancel(); + } + + + /** + * Animate QS closing by flinging it. + * If QS is expanded, it will collapse into QQS and stop. + * + * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore. + */ + public void animateCloseQs(boolean animateAway) { + if (mQsExpansionAnimator != null) { + if (!mQsAnimatorExpand) { + return; + } + float height = mQsExpansionHeight; + mQsExpansionAnimator.cancel(); + setQsExpansion(height); + } + flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE); + } + + public void expandWithQs() { + if (mQsExpansionEnabled) { + mQsExpandImmediate = true; + mNotificationStackScroller.setShouldShowShelfOnly(true); + } + if (isFullyCollapsed()) { + expand(true /* animate */); + } else { + flingSettings(0 /* velocity */, FLING_EXPAND); + } + } + + public void expandWithoutQs() { + if (isQsExpanded()) { + flingSettings(0 /* velocity */, FLING_COLLAPSE); + } else { + expand(true /* animate */); + } + } + + @Override + public void fling(float vel, boolean expand) { + GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder(); + if (gr != null) { + gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel); + } + super.fling(vel, expand); + } + + @Override + protected void flingToHeight(float vel, boolean expand, float target, + float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) { + mHeadsUpTouchHelper.notifyFling(!expand); + setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f); + super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing); + } + + + private boolean onQsIntercept(MotionEvent event) { + int pointerIndex = event.findPointerIndex(mTrackingPointer); + if (pointerIndex < 0) { + pointerIndex = 0; + mTrackingPointer = event.getPointerId(pointerIndex); + } + final float x = event.getX(pointerIndex); + final float y = event.getY(pointerIndex); + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mInitialTouchY = y; + mInitialTouchX = x; + initVelocityTracker(); + trackMovement(event); + if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) { + mView.getParent().requestDisallowInterceptTouchEvent(true); + } + if (mQsExpansionAnimator != null) { + onQsExpansionStarted(); + mInitialHeightOnTouch = mQsExpansionHeight; + mQsTracking = true; + mNotificationStackScroller.cancelLongPress(); + } + break; + case MotionEvent.ACTION_POINTER_UP: + final int upPointer = event.getPointerId(event.getActionIndex()); + if (mTrackingPointer == upPointer) { + // gesture is ongoing, find a new pointer to track + final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; + mTrackingPointer = event.getPointerId(newIndex); + mInitialTouchX = event.getX(newIndex); + mInitialTouchY = event.getY(newIndex); + } + break; + + case MotionEvent.ACTION_MOVE: + final float h = y - mInitialTouchY; + trackMovement(event); + if (mQsTracking) { + + // Already tracking because onOverscrolled was called. We need to update here + // so we don't stop for a frame until the next touch event gets handled in + // onTouchEvent. + setQsExpansion(h + mInitialHeightOnTouch); + trackMovement(event); + return true; + } + if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX) + && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) { + mQsTracking = true; + onQsExpansionStarted(); + notifyExpandingFinished(); + mInitialHeightOnTouch = mQsExpansionHeight; + mInitialTouchY = y; + mInitialTouchX = x; + mNotificationStackScroller.cancelLongPress(); + return true; + } + break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + trackMovement(event); + if (mQsTracking) { + flingQsWithCurrentVelocity(y, + event.getActionMasked() == MotionEvent.ACTION_CANCEL); + mQsTracking = false; + } + break; + } + return false; + } + + @Override + protected boolean isInContentBounds(float x, float y) { + float stackScrollerX = mNotificationStackScroller.getX(); + return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y) + && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth(); + } + + private void initDownStates(MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + mOnlyAffordanceInThisMotion = false; + mQsTouchAboveFalsingThreshold = mQsFullyExpanded; + mDozingOnDown = isDozing(); + mDownX = event.getX(); + mDownY = event.getY(); + mCollapsedOnDown = isFullyCollapsed(); + mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp(); + mAllowExpandForSmallExpansion = mExpectingSynthesizedDown; + mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown; + if (mExpectingSynthesizedDown) { + mLastEventSynthesizedDown = true; + } else { + // down but not synthesized motion event. + mLastEventSynthesizedDown = false; + } + } else { + // not down event at all. + mLastEventSynthesizedDown = false; + } + } + + private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) { + float vel = getCurrentQSVelocity(); + final boolean expandsQs = flingExpandsQs(vel); + if (expandsQs) { + logQsSwipeDown(y); + } + flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE); + } + + private void logQsSwipeDown(float y) { + float vel = getCurrentQSVelocity(); + final int + gesture = + mBarState == StatusBarState.KEYGUARD ? MetricsEvent.ACTION_LS_QS + : MetricsEvent.ACTION_SHADE_QS_PULL; + mLockscreenGestureLogger.write(gesture, + (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()), + (int) (vel / mStatusBar.getDisplayDensity())); + } + + private boolean flingExpandsQs(float vel) { + if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) { + return false; + } + if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { + return getQsExpansionFraction() > 0.5f; + } else { + return vel > 0; + } + } + + private boolean isFalseTouch() { + if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) { + return false; + } + if (mFalsingManager.isClassifierEnabled()) { + return mFalsingManager.isFalseTouch(); + } + return !mQsTouchAboveFalsingThreshold; + } + + private float getQsExpansionFraction() { + return Math.min( + 1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight + - mQsMinExpansionHeight)); + } + + @Override + protected boolean shouldExpandWhenNotFlinging() { + if (super.shouldExpandWhenNotFlinging()) { + return true; + } + if (mAllowExpandForSmallExpansion) { + // When we get a touch that came over from launcher, the velocity isn't always correct + // Let's err on expanding if the gesture has been reasonably slow + long timeSinceDown = SystemClock.uptimeMillis() - mDownTime; + return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER; + } + return false; + } + + @Override + protected float getOpeningHeight() { + return mNotificationStackScroller.getOpeningHeight(); + } + + + private boolean handleQsTouch(MotionEvent event) { + final int action = event.getActionMasked(); + if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f + && mBarState != StatusBarState.KEYGUARD && !mQsExpanded && mQsExpansionEnabled) { + + // Down in the empty area while fully expanded - go to QS. + mQsTracking = true; + mConflictingQsExpansionGesture = true; + onQsExpansionStarted(); + mInitialHeightOnTouch = mQsExpansionHeight; + mInitialTouchY = event.getX(); + mInitialTouchX = event.getY(); + } + if (!isFullyCollapsed()) { + handleQsDown(event); + } + if (!mQsExpandImmediate && mQsTracking) { + onQsTouch(event); + if (!mConflictingQsExpansionGesture) { + return true; + } + } + if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { + mConflictingQsExpansionGesture = false; + } + if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() && mQsExpansionEnabled) { + mTwoFingerQsExpandPossible = true; + } + if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) && event.getY(event.getActionIndex()) + < mStatusBarMinHeight) { + mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1); + mQsExpandImmediate = true; + mNotificationStackScroller.setShouldShowShelfOnly(true); + requestPanelHeightUpdate(); + + // Normally, we start listening when the panel is expanded, but here we need to start + // earlier so the state is already up to date when dragging down. + setListening(true); + } + if (isQsSplitEnabled() && !mKeyguardShowing) { + if (mQsExpandImmediate) { + mNotificationStackScroller.setVisibility(View.GONE); + mQsFrame.setVisibility(View.VISIBLE); + mHomeControlsLayout.setVisibility(View.VISIBLE); + } else { + mNotificationStackScroller.setVisibility(View.VISIBLE); + mQsFrame.setVisibility(View.GONE); + mHomeControlsLayout.setVisibility(View.GONE); + } + } + return false; + } + + private boolean isInQsArea(float x, float y) { + return (x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()) && ( + y <= mNotificationStackScroller.getBottomMostNotificationBottom() + || y <= mQs.getView().getY() + mQs.getView().getHeight()); + } + + private boolean isOnQsEndArea(float x) { + if (!isQsSplitEnabled()) return false; + if (mView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR) { + return x >= mQsFrame.getX() + mQqsSplitFraction * mQsFrame.getWidth() + && x <= mQsFrame.getX() + mQsFrame.getWidth(); + } else { + return x >= mQsFrame.getX() + && x <= mQsFrame.getX() + (1 - mQqsSplitFraction) * mQsFrame.getWidth(); + } + } + + private boolean isOpenQsEvent(MotionEvent event) { + final int pointerCount = event.getPointerCount(); + final int action = event.getActionMasked(); + + final boolean + twoFingerDrag = + action == MotionEvent.ACTION_POINTER_DOWN && pointerCount == 2; + + final boolean + stylusButtonClickDrag = + action == MotionEvent.ACTION_DOWN && (event.isButtonPressed( + MotionEvent.BUTTON_STYLUS_PRIMARY) || event.isButtonPressed( + MotionEvent.BUTTON_STYLUS_SECONDARY)); + + final boolean + mouseButtonClickDrag = + action == MotionEvent.ACTION_DOWN && (event.isButtonPressed( + MotionEvent.BUTTON_SECONDARY) || event.isButtonPressed( + MotionEvent.BUTTON_TERTIARY)); + + final boolean onHeaderRight = isOnQsEndArea(event.getX()); + + return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag || onHeaderRight; + } + + private void handleQsDown(MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept( + event.getX(), event.getY(), -1)) { + mFalsingManager.onQsDown(); + mQsTracking = true; + onQsExpansionStarted(); + mInitialHeightOnTouch = mQsExpansionHeight; + mInitialTouchY = event.getX(); + mInitialTouchX = event.getY(); + + // If we interrupt an expansion gesture here, make sure to update the state correctly. + notifyExpandingFinished(); + } + } + + /** + * Input focus transfer is about to happen. + */ + public void startWaitingForOpenPanelGesture() { + if (!isFullyCollapsed()) { + return; + } + mExpectingSynthesizedDown = true; + onTrackingStarted(); + updatePanelExpanded(); + } + + /** + * Called when this view is no longer waiting for input focus transfer. + * + * There are two scenarios behind this function call. First, input focus transfer + * has successfully happened and this view already received synthetic DOWN event. + * (mExpectingSynthesizedDown == false). Do nothing. + * + * Second, before input focus transfer finished, user may have lifted finger + * in previous window and this window never received synthetic DOWN event. + * (mExpectingSynthesizedDown == true). + * In this case, we use the velocity to trigger fling event. + * + * @param velocity unit is in px / millis + */ + public void stopWaitingForOpenPanelGesture(final float velocity) { + if (mExpectingSynthesizedDown) { + mExpectingSynthesizedDown = false; + maybeVibrateOnOpening(); + Runnable runnable = () -> fling(velocity > 1f ? 1000f * velocity : 0, + true /* expand */); + if (mStatusBar.getStatusBarWindow().getHeight() != mStatusBar.getStatusBarHeight()) { + // The panel is already expanded to its full size, let's expand directly + runnable.run(); + } else { + mExpandAfterLayoutRunnable = runnable; + } + onTrackingStopped(false); + } + } + + @Override + protected boolean flingExpands(float vel, float vectorVel, float x, float y) { + boolean expands = super.flingExpands(vel, vectorVel, x, y); + + // If we are already running a QS expansion, make sure that we keep the panel open. + if (mQsExpansionAnimator != null) { + expands = true; + } + return expands; + } + + @Override + protected boolean shouldGestureWaitForTouchSlop() { + if (mExpectingSynthesizedDown) { + mExpectingSynthesizedDown = false; + return false; + } + return isFullyCollapsed() || mBarState != StatusBarState.SHADE; + } + + @Override + protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) { + return !mAffordanceHelper.isOnAffordanceIcon(x, y); + } + + private void onQsTouch(MotionEvent event) { + int pointerIndex = event.findPointerIndex(mTrackingPointer); + if (pointerIndex < 0) { + pointerIndex = 0; + mTrackingPointer = event.getPointerId(pointerIndex); + } + final float y = event.getY(pointerIndex); + final float x = event.getX(pointerIndex); + final float h = y - mInitialTouchY; + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mQsTracking = true; + mInitialTouchY = y; + mInitialTouchX = x; + onQsExpansionStarted(); + mInitialHeightOnTouch = mQsExpansionHeight; + initVelocityTracker(); + trackMovement(event); + break; + + case MotionEvent.ACTION_POINTER_UP: + final int upPointer = event.getPointerId(event.getActionIndex()); + if (mTrackingPointer == upPointer) { + // gesture is ongoing, find a new pointer to track + final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; + final float newY = event.getY(newIndex); + final float newX = event.getX(newIndex); + mTrackingPointer = event.getPointerId(newIndex); + mInitialHeightOnTouch = mQsExpansionHeight; + mInitialTouchY = newY; + mInitialTouchX = newX; + } + break; + + case MotionEvent.ACTION_MOVE: + setQsExpansion(h + mInitialHeightOnTouch); + if (h >= getFalsingThreshold()) { + mQsTouchAboveFalsingThreshold = true; + } + trackMovement(event); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mQsTracking = false; + mTrackingPointer = -1; + trackMovement(event); + float fraction = getQsExpansionFraction(); + if (fraction != 0f || y >= mInitialTouchY) { + flingQsWithCurrentVelocity(y, + event.getActionMasked() == MotionEvent.ACTION_CANCEL); + } + if (mQsVelocityTracker != null) { + mQsVelocityTracker.recycle(); + mQsVelocityTracker = null; + } + break; + } + } + + private int getFalsingThreshold() { + float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; + return (int) (mQsFalsingThreshold * factor); + } + + private void setOverScrolling(boolean overscrolling) { + mStackScrollerOverscrolling = overscrolling; + if (mQs == null) return; + mQs.setOverscrolling(overscrolling); + } + + private void onQsExpansionStarted() { + onQsExpansionStarted(0); + } + + protected void onQsExpansionStarted(int overscrollAmount) { + cancelQsAnimation(); + cancelHeightAnimator(); + + // Reset scroll position and apply that position to the expanded height. + float height = mQsExpansionHeight - overscrollAmount; + setQsExpansion(height); + requestPanelHeightUpdate(); + mNotificationStackScroller.checkSnoozeLeavebehind(); + + // When expanding QS, let's authenticate the user if possible, + // this will speed up notification actions. + if (height == 0) { + mStatusBar.requestFaceAuth(); + } + } + + private void setQsExpanded(boolean expanded) { + boolean changed = mQsExpanded != expanded; + if (changed) { + mQsExpanded = expanded; + updateQsState(); + requestPanelHeightUpdate(); + mFalsingManager.setQsExpanded(expanded); + mStatusBar.setQsExpanded(expanded); + mNotificationContainerParent.setQsExpanded(expanded); + mPulseExpansionHandler.setQsExpanded(expanded); + mKeyguardBypassController.setQSExpanded(expanded); + } + } + + private void maybeAnimateBottomAreaAlpha() { + mBottomAreaShadeAlphaAnimator.cancel(); + if (mBarState == StatusBarState.SHADE_LOCKED) { + mBottomAreaShadeAlphaAnimator.start(); + } else { + mBottomAreaShadeAlpha = 1f; + } + } + + private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() { + @Override + public void run() { + mKeyguardStatusViewAnimating = false; + mKeyguardStatusView.setVisibility(View.INVISIBLE); + } + }; + + private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() { + @Override + public void run() { + mKeyguardStatusViewAnimating = false; + mKeyguardStatusView.setVisibility(View.GONE); + } + }; + + private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = new Runnable() { + @Override + public void run() { + mKeyguardStatusViewAnimating = false; + } + }; + + private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() { + @Override + public void run() { + mKeyguardStatusBar.setVisibility(View.INVISIBLE); + mKeyguardStatusBar.setAlpha(1f); + mKeyguardStatusBarAnimateAlpha = 1f; + } + }; + + private void animateKeyguardStatusBarOut() { + ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f); + anim.addUpdateListener(mStatusBarAnimateAlphaListener); + anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway() + ? mKeyguardStateController.getKeyguardFadingAwayDelay() : 0); + + long duration; + if (mKeyguardStateController.isKeyguardFadingAway()) { + duration = mKeyguardStateController.getShortenedFadingAwayDuration(); + } else { + duration = StackStateAnimator.ANIMATION_DURATION_STANDARD; + } + anim.setDuration(duration); + + anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mAnimateKeyguardStatusBarInvisibleEndRunnable.run(); + } + }); + anim.start(); + } + + private final ValueAnimator.AnimatorUpdateListener + mStatusBarAnimateAlphaListener = + new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue(); + updateHeaderKeyguardAlpha(); + } + }; + + private void animateKeyguardStatusBarIn(long duration) { + mKeyguardStatusBar.setVisibility(View.VISIBLE); + mKeyguardStatusBar.setAlpha(0f); + ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); + anim.addUpdateListener(mStatusBarAnimateAlphaListener); + anim.setDuration(duration); + anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + anim.start(); + } + + private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() { + @Override + public void run() { + mKeyguardBottomArea.setVisibility(View.GONE); + } + }; + + private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) { + mKeyguardBottomArea.animate().cancel(); + if (goingToFullShade) { + mKeyguardBottomArea.animate().alpha(0f).setStartDelay( + mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration( + mKeyguardStateController.getShortenedFadingAwayDuration()).setInterpolator( + Interpolators.ALPHA_OUT).withEndAction( + mAnimateKeyguardBottomAreaInvisibleEndRunnable).start(); + } else if (statusBarState == StatusBarState.KEYGUARD + || statusBarState == StatusBarState.SHADE_LOCKED) { + mKeyguardBottomArea.setVisibility(View.VISIBLE); + mKeyguardBottomArea.setAlpha(1f); + } else { + mKeyguardBottomArea.setVisibility(View.GONE); + } + } + + private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway, + boolean goingToFullShade) { + mKeyguardStatusView.animate().cancel(); + mKeyguardStatusViewAnimating = false; + if ((!keyguardFadingAway && mBarState == StatusBarState.KEYGUARD + && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) { + mKeyguardStatusViewAnimating = true; + mKeyguardStatusView.animate().alpha(0f).setStartDelay(0).setDuration( + 160).setInterpolator(Interpolators.ALPHA_OUT).withEndAction( + mAnimateKeyguardStatusViewGoneEndRunnable); + if (keyguardFadingAway) { + mKeyguardStatusView.animate().setStartDelay( + mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration( + mKeyguardStateController.getShortenedFadingAwayDuration()).start(); + } + } else if (mBarState == StatusBarState.SHADE_LOCKED + && statusBarState == StatusBarState.KEYGUARD) { + mKeyguardStatusView.setVisibility(View.VISIBLE); + mKeyguardStatusViewAnimating = true; + mKeyguardStatusView.setAlpha(0f); + mKeyguardStatusView.animate().alpha(1f).setStartDelay(0).setDuration( + 320).setInterpolator(Interpolators.ALPHA_IN).withEndAction( + mAnimateKeyguardStatusViewVisibleEndRunnable); + } else if (statusBarState == StatusBarState.KEYGUARD) { + if (keyguardFadingAway) { + mKeyguardStatusViewAnimating = true; + mKeyguardStatusView.animate().alpha(0).translationYBy( + -getHeight() * 0.05f).setInterpolator( + Interpolators.FAST_OUT_LINEAR_IN).setDuration(125).setStartDelay( + 0).withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable).start(); + } else { + mKeyguardStatusView.setVisibility(View.VISIBLE); + mKeyguardStatusView.setAlpha(1f); + } + } else { + mKeyguardStatusView.setVisibility(View.GONE); + mKeyguardStatusView.setAlpha(1f); + } + } + + private void updateQsState() { + mNotificationStackScroller.setQsExpanded(mQsExpanded); + mNotificationStackScroller.setScrollingEnabled( + mBarState != StatusBarState.KEYGUARD && (!mQsExpanded + || mQsExpansionFromOverscroll)); + updateEmptyShadeView(); + if (mNPVPluginManager != null) { + mNPVPluginManager.changeVisibility( + (mBarState != StatusBarState.KEYGUARD) ? View.VISIBLE : View.INVISIBLE); + } + mQsNavbarScrim.setVisibility( + mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling + && mQsScrimEnabled ? View.VISIBLE : View.INVISIBLE); + if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) { + mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */); + } + if (mQs == null) return; + mQs.setExpanded(mQsExpanded); + } + + private void setQsExpansion(float height) { + height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight); + mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0; + if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling + && !mDozing) { + setQsExpanded(true); + } else if (height <= mQsMinExpansionHeight && mQsExpanded) { + setQsExpanded(false); + } + mQsExpansionHeight = height; + updateQsExpansion(); + requestScrollerTopPaddingUpdate(false /* animate */); + updateHeaderKeyguardAlpha(); + if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == StatusBarState.KEYGUARD) { + updateKeyguardBottomAreaAlpha(); + updateBigClockAlpha(); + } + if (mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling + && mQsScrimEnabled) { + mQsNavbarScrim.setAlpha(getQsExpansionFraction()); + } + + if (mAccessibilityManager.isEnabled()) { + mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); + } + + if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded + && mFalsingManager.shouldEnforceBouncer()) { + mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */, + false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */); + } + for (int i = 0; i < mExpansionListeners.size(); i++) { + mExpansionListeners.get(i).onQsExpansionChanged( + mQsMaxExpansionHeight != 0 ? mQsExpansionHeight / mQsMaxExpansionHeight : 0); + } + if (DEBUG) { + mView.invalidate(); + } + } + + protected void updateQsExpansion() { + if (mQs == null) return; + float qsExpansionFraction = getQsExpansionFraction(); + mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation()); + int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight(); + if (mNPVPluginManager != null) { + mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff); + } + mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction); + } + + private String determineAccessibilityPaneTitle() { + if (mQs != null && mQs.isCustomizing()) { + return mResources.getString(R.string.accessibility_desc_quick_settings_edit); + } else if (mQsExpansionHeight != 0.0f && mQsFullyExpanded) { + // Upon initialisation when we are not layouted yet we don't want to announce that we + // are fully expanded, hence the != 0.0f check. + return mResources.getString(R.string.accessibility_desc_quick_settings); + } else if (mBarState == StatusBarState.KEYGUARD) { + return mResources.getString(R.string.accessibility_desc_lock_screen); + } else { + return mResources.getString(R.string.accessibility_desc_notification_shade); + } + } + + private float calculateQsTopPadding() { + if (mKeyguardShowing && (mQsExpandImmediate + || mIsExpanding && mQsExpandedWhenExpandingStarted)) { + + // Either QS pushes the notifications down when fully expanded, or QS is fully above the + // notifications (mostly on tablets). maxNotificationPadding denotes the normal top + // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings + // panel. We need to take the maximum and linearly interpolate with the panel expansion + // for a nice motion. + int maxNotificationPadding = getKeyguardNotificationStaticPadding(); + int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding; + int max = mBarState == StatusBarState.KEYGUARD ? Math.max( + maxNotificationPadding, maxQsPadding) : maxQsPadding; + return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max, + getExpandedFraction()); + } else if (mQsSizeChangeAnimator != null) { + return Math.max( + (int) mQsSizeChangeAnimator.getAnimatedValue(), + getKeyguardNotificationStaticPadding()); + } else if (mKeyguardShowing) { + // We can only do the smoother transition on Keyguard when we also are not collapsing + // from a scrolled quick settings. + return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(), + (float) (mQsMaxExpansionHeight + mQsNotificationTopPadding), + getQsExpansionFraction()); + } else { + return mQsExpansionHeight + mQsNotificationTopPadding; + } + } + + /** + * @return the topPadding of notifications when on keyguard not respecting quick settings + * expansion + */ + private int getKeyguardNotificationStaticPadding() { + if (!mKeyguardShowing) { + return 0; + } + if (!mKeyguardBypassController.getBypassEnabled()) { + return mClockPositionResult.stackScrollerPadding; + } + int collapsedPosition = mHeadsUpInset; + if (!mNotificationStackScroller.isPulseExpanding()) { + return collapsedPosition; + } else { + int expandedPosition = mClockPositionResult.stackScrollerPadding; + return (int) MathUtils.lerp(collapsedPosition, expandedPosition, + mNotificationStackScroller.calculateAppearFractionBypass()); + } + } + + + protected void requestScrollerTopPaddingUpdate(boolean animate) { + mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate); + if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) { + // update the position of the header + updateQsExpansion(); + } + } + + + private void updateQSPulseExpansion() { + if (mQs != null) { + mQs.setShowCollapsedOnKeyguard( + mKeyguardShowing && mKeyguardBypassController.getBypassEnabled() + && mNotificationStackScroller.isPulseExpanding()); + } + } + + private void trackMovement(MotionEvent event) { + if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event); + } + + private void initVelocityTracker() { + if (mQsVelocityTracker != null) { + mQsVelocityTracker.recycle(); + } + mQsVelocityTracker = VelocityTracker.obtain(); + } + + private float getCurrentQSVelocity() { + if (mQsVelocityTracker == null) { + return 0; + } + mQsVelocityTracker.computeCurrentVelocity(1000); + return mQsVelocityTracker.getYVelocity(); + } + + private void cancelQsAnimation() { + if (mQsExpansionAnimator != null) { + mQsExpansionAnimator.cancel(); + } + } + + /** + * @see #flingSettings(float, int, Runnable, boolean) + */ + public void flingSettings(float vel, int type) { + flingSettings(vel, type, null, false /* isClick */); + } + + /** + * Animates QS or QQS as if the user had swiped up or down. + * + * @param vel Finger velocity or 0 when not initiated by touch events. + * @param type Either {@link #FLING_EXPAND}, {@link #FLING_COLLAPSE} or {@link + * #FLING_HIDE}. + * @param onFinishRunnable Runnable to be executed at the end of animation. + * @param isClick If originated by click (different interpolator and duration.) + */ + protected void flingSettings(float vel, int type, final Runnable onFinishRunnable, + boolean isClick) { + float target; + switch (type) { + case FLING_EXPAND: + target = mQsMaxExpansionHeight; + break; + case FLING_COLLAPSE: + target = mQsMinExpansionHeight; + break; + case FLING_HIDE: + default: + target = 0; + } + if (target == mQsExpansionHeight) { + if (onFinishRunnable != null) { + onFinishRunnable.run(); + } + return; + } + + // If we move in the opposite direction, reset velocity and use a different duration. + boolean oppositeDirection = false; + boolean expanding = type == FLING_EXPAND; + if (vel > 0 && !expanding || vel < 0 && expanding) { + vel = 0; + oppositeDirection = true; + } + ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target); + if (isClick) { + animator.setInterpolator(Interpolators.TOUCH_RESPONSE); + animator.setDuration(368); + } else { + mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel); + } + if (oppositeDirection) { + animator.setDuration(350); + } + animator.addUpdateListener(animation -> { + setQsExpansion((Float) animation.getAnimatedValue()); + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mNotificationStackScroller.resetCheckSnoozeLeavebehind(); + mQsExpansionAnimator = null; + if (onFinishRunnable != null) { + onFinishRunnable.run(); + } + } + }); + animator.start(); + mQsExpansionAnimator = animator; + mQsAnimatorExpand = expanding; + } + + /** + * @return Whether we should intercept a gesture to open Quick Settings. + */ + private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) { + if (!mQsExpansionEnabled || mCollapsedOnDown || (mKeyguardShowing + && mKeyguardBypassController.getBypassEnabled())) { + return false; + } + View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader(); + final boolean + onHeader = + x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth() + && y >= header.getTop() && y <= header.getBottom(); + if (mQsExpanded) { + return onHeader || (yDiff < 0 && isInQsArea(x, y)); + } else { + return onHeader; + } + } + + @Override + protected boolean isScrolledToBottom() { + if (!isInSettings()) { + return mBarState == StatusBarState.KEYGUARD + || mNotificationStackScroller.isScrolledToBottom(); + } else { + return true; + } + } + + @Override + protected int getMaxPanelHeight() { + if (mKeyguardBypassController.getBypassEnabled() && mBarState == StatusBarState.KEYGUARD) { + return getMaxPanelHeightBypass(); + } else { + return getMaxPanelHeightNonBypass(); + } + } + + private int getMaxPanelHeightNonBypass() { + int min = mStatusBarMinHeight; + if (!(mBarState == StatusBarState.KEYGUARD) + && mNotificationStackScroller.getNotGoneChildCount() == 0) { + int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount()); + min = Math.max(min, minHeight); + } + int maxHeight; + if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted + || mPulsing) { + maxHeight = calculatePanelHeightQsExpanded(); + } else { + maxHeight = calculatePanelHeightShade(); + } + maxHeight = Math.max(maxHeight, min); + return maxHeight; + } + + private int getMaxPanelHeightBypass() { + int position = + mClockPositionAlgorithm.getExpandedClockPosition() + + mKeyguardStatusView.getHeight(); + if (mNotificationStackScroller.getVisibleNotificationCount() != 0) { + position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f; + } + return position; + } + + public boolean isInSettings() { + return mQsExpanded; + } + + public boolean isExpanding() { + return mIsExpanding; + } + + @Override + protected void onHeightUpdated(float expandedHeight) { + if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) { + // Updating the clock position will set the top padding which might + // trigger a new panel height and re-position the clock. + // This is a circular dependency and should be avoided, otherwise we'll have + // a stack overflow. + if (mStackScrollerMeasuringPass > 2) { + if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting."); + } else { + positionClockAndNotifications(); + } + } + if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null + && !mQsExpansionFromOverscroll) { + float t; + if (mKeyguardShowing) { + + // On Keyguard, interpolate the QS expansion linearly to the panel expansion + t = expandedHeight / (getMaxPanelHeight()); + } else { + // In Shade, interpolate linearly such that QS is closed whenever panel height is + // minimum QS expansion + minStackHeight + float + panelHeightQsCollapsed = + mNotificationStackScroller.getIntrinsicPadding() + + mNotificationStackScroller.getLayoutMinHeight(); + float panelHeightQsExpanded = calculatePanelHeightQsExpanded(); + t = + (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded + - panelHeightQsCollapsed); + } + float + targetHeight = + mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight); + setQsExpansion(targetHeight); + mHomeControlsLayout.setTranslationY(targetHeight); + } + updateExpandedHeight(expandedHeight); + updateHeader(); + updateNotificationTranslucency(); + updatePanelExpanded(); + updateGestureExclusionRect(); + if (DEBUG) { + mView.invalidate(); + } + } + + private void updatePanelExpanded() { + boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown; + if (mPanelExpanded != isExpanded) { + mHeadsUpManager.setIsPanelExpanded(isExpanded); + mStatusBar.setPanelExpanded(isExpanded); + mPanelExpanded = isExpanded; + } + } + + private int calculatePanelHeightShade() { + int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin(); + int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin; + maxHeight += mNotificationStackScroller.getTopPaddingOverflow(); + + if (mBarState == StatusBarState.KEYGUARD) { + int + minKeyguardPanelBottom = + mClockPositionAlgorithm.getExpandedClockPosition() + + mKeyguardStatusView.getHeight() + + mNotificationStackScroller.getIntrinsicContentHeight(); + return Math.max(maxHeight, minKeyguardPanelBottom); + } else { + return maxHeight; + } + } + + private int calculatePanelHeightQsExpanded() { + float + notificationHeight = + mNotificationStackScroller.getHeight() + - mNotificationStackScroller.getEmptyBottomMargin() + - mNotificationStackScroller.getTopPadding(); + + // When only empty shade view is visible in QS collapsed state, simulate that we would have + // it in expanded QS state as well so we don't run into troubles when fading the view in/out + // and expanding/collapsing the whole panel from/to quick settings. + if (mNotificationStackScroller.getNotGoneChildCount() == 0 && mShowEmptyShadeView) { + notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight(); + } + int maxQsHeight = mQsMaxExpansionHeight; + + if (mKeyguardShowing) { + maxQsHeight += mQsNotificationTopPadding; + } + + // If an animation is changing the size of the QS panel, take the animated value. + if (mQsSizeChangeAnimator != null) { + maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); + } + float totalHeight = Math.max(maxQsHeight, + mBarState == StatusBarState.KEYGUARD ? mClockPositionResult.stackScrollerPadding + : 0) + notificationHeight + + mNotificationStackScroller.getTopPaddingOverflow(); + if (totalHeight > mNotificationStackScroller.getHeight()) { + float + fullyCollapsedHeight = + maxQsHeight + mNotificationStackScroller.getLayoutMinHeight(); + totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight()); + } + return (int) totalHeight; + } + + private void updateNotificationTranslucency() { + float alpha = 1f; + if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp + && !mHeadsUpManager.hasPinnedHeadsUp()) { + alpha = getFadeoutAlpha(); + } + if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning + && !mKeyguardBypassController.getBypassEnabled()) { + alpha *= mClockPositionResult.clockAlpha; + } + mNotificationStackScroller.setAlpha(alpha); + } + + private float getFadeoutAlpha() { + float alpha; + if (mQsMinExpansionHeight == 0) { + return 1.0f; + } + alpha = getExpandedHeight() / mQsMinExpansionHeight; + alpha = Math.max(0, Math.min(alpha, 1)); + alpha = (float) Math.pow(alpha, 0.75); + return alpha; + } + + @Override + protected float getOverExpansionAmount() { + return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */); + } + + @Override + protected float getOverExpansionPixels() { + return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */); + } + + /** + * Hides the header when notifications are colliding with it. + */ + private void updateHeader() { + if (mBarState == StatusBarState.KEYGUARD) { + updateHeaderKeyguardAlpha(); + } + updateQsExpansion(); + } + + protected float getHeaderTranslation() { + if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) { + return -mQs.getQsMinExpansionHeight(); + } + float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight); + float startHeight = -mQsExpansionHeight; + if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard() + && mNotificationStackScroller.isPulseExpanding()) { + if (!mPulseExpansionHandler.isExpanding() + && !mPulseExpansionHandler.getLeavingLockscreen()) { + // If we aborted the expansion we need to make sure the header doesn't reappear + // again after the header has animated away + appearAmount = 0; + } else { + appearAmount = mNotificationStackScroller.calculateAppearFractionBypass(); + } + startHeight = -mQs.getQsMinExpansionHeight(); + if (mNPVPluginManager != null) startHeight -= mNPVPluginManager.getHeight(); + } + float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount)) + + mExpandOffset; + return Math.min(0, translation); + } + + /** + * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area) + * during swiping up + */ + private float getKeyguardContentsAlpha() { + float alpha; + if (mBarState == StatusBarState.KEYGUARD) { + + // When on Keyguard, we hide the header as soon as we expanded close enough to the + // header + alpha = + getExpandedHeight() / (mKeyguardStatusBar.getHeight() + + mNotificationsHeaderCollideDistance); + } else { + + // In SHADE_LOCKED, the top card is already really close to the header. Hide it as + // soon as we start translating the stack. + alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight(); + } + alpha = MathUtils.saturate(alpha); + alpha = (float) Math.pow(alpha, 0.75); + return alpha; + } + + private void updateHeaderKeyguardAlpha() { + if (!mKeyguardShowing) { + return; + } + float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2); + float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion) + * mKeyguardStatusBarAnimateAlpha; + newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount; + mKeyguardStatusBar.setAlpha(newAlpha); + boolean + hideForBypass = + mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace() + || mDelayShowingKeyguardStatusBar; + mKeyguardStatusBar.setVisibility( + newAlpha != 0f && !mDozing && !hideForBypass ? View.VISIBLE : View.INVISIBLE); + } + + private void updateKeyguardBottomAreaAlpha() { + // There are two possible panel expansion behaviors: + // • User dragging up to unlock: we want to fade out as quick as possible + // (ALPHA_EXPANSION_THRESHOLD) to avoid seeing the bouncer over the bottom area. + // • User tapping on lock screen: bouncer won't be visible but panel expansion will + // change due to "unlock hint animation." In this case, fading out the bottom area + // would also hide the message that says "swipe to unlock," we don't want to do that. + float expansionAlpha = MathUtils.map( + isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, + getExpandedFraction()); + float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction()); + alpha *= mBottomAreaShadeAlpha; + mKeyguardBottomArea.setAffordanceAlpha(alpha); + mKeyguardBottomArea.setImportantForAccessibility( + alpha == 0f ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS + : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); + View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer(); + if (ambientIndicationContainer != null) { + ambientIndicationContainer.setAlpha(alpha); + } + } + + /** + * Custom clock fades away when user drags up to unlock or pulls down quick settings. + * + * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See + * {@link #updateKeyguardBottomAreaAlpha}. + */ + private void updateBigClockAlpha() { + float expansionAlpha = MathUtils.map( + isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, + getExpandedFraction()); + float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction()); + mBigClockContainer.setAlpha(alpha); + } + + @Override + protected void onExpandingStarted() { + super.onExpandingStarted(); + mNotificationStackScroller.onExpansionStarted(); + mIsExpanding = true; + mQsExpandedWhenExpandingStarted = mQsFullyExpanded; + if (mQsExpanded) { + onQsExpansionStarted(); + } + // Since there are QS tiles in the header now, we need to make sure we start listening + // immediately so they can be up to date. + if (mQs == null) return; + mQs.setHeaderListening(true); + } + + @Override + protected void onExpandingFinished() { + super.onExpandingFinished(); + mNotificationStackScroller.onExpansionStopped(); + mHeadsUpManager.onExpandingFinished(); + mIsExpanding = false; + if (isFullyCollapsed()) { + DejankUtils.postAfterTraversal(new Runnable() { + @Override + public void run() { + setListening(false); + } + }); + + // Workaround b/22639032: Make sure we invalidate something because else RenderThread + // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go + // ahead with rendering and we jank. + mView.postOnAnimation(new Runnable() { + @Override + public void run() { + mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT); + } + }); + } else { + setListening(true); + } + mQsExpandImmediate = false; + mNotificationStackScroller.setShouldShowShelfOnly(false); + mTwoFingerQsExpandPossible = false; + notifyListenersTrackingHeadsUp(null); + mExpandingFromHeadsUp = false; + setPanelScrimMinFraction(0.0f); + } + + private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) { + for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) { + Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i); + listener.accept(pickedChild); + } + } + + private void setListening(boolean listening) { + mKeyguardStatusBar.setListening(listening); + if (mQs == null) return; + mQs.setListening(listening); + if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening); + } + + @Override + public void expand(boolean animate) { + super.expand(animate); + setListening(true); + } + + @Override + protected void setOverExpansion(float overExpansion, boolean isPixels) { + if (mConflictingQsExpansionGesture || mQsExpandImmediate) { + return; + } + if (mBarState != StatusBarState.KEYGUARD) { + mNotificationStackScroller.setOnHeightChangedListener(null); + if (isPixels) { + mNotificationStackScroller.setOverScrolledPixels(overExpansion, true /* onTop */, + false /* animate */); + } else { + mNotificationStackScroller.setOverScrollAmount(overExpansion, true /* onTop */, + false /* animate */); + } + mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener); + } + } + + @Override + protected void onTrackingStarted() { + mFalsingManager.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen()); + super.onTrackingStarted(); + if (mQsFullyExpanded) { + mQsExpandImmediate = true; + mNotificationStackScroller.setShouldShowShelfOnly(true); + } + if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) { + mAffordanceHelper.animateHideLeftRightIcon(); + } + mNotificationStackScroller.onPanelTrackingStarted(); + } + + @Override + protected void onTrackingStopped(boolean expand) { + mFalsingManager.onTrackingStopped(); + super.onTrackingStopped(expand); + if (expand) { + mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */, + true /* animate */); + } + mNotificationStackScroller.onPanelTrackingStopped(); + if (expand && (mBarState == StatusBarState.KEYGUARD + || mBarState == StatusBarState.SHADE_LOCKED)) { + if (!mHintAnimationRunning) { + mAffordanceHelper.reset(true); + } + } + } + + private void updateMaxHeadsUpTranslation() { + mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight); + } + + @Override + protected void startUnlockHintAnimation() { + if (mPowerManager.isPowerSaveMode()) { + onUnlockHintStarted(); + onUnlockHintFinished(); + return; + } + super.startUnlockHintAnimation(); + } + + @Override + protected void onUnlockHintFinished() { + super.onUnlockHintFinished(); + mNotificationStackScroller.setUnlockHintRunning(false); + } + + @Override + protected void onUnlockHintStarted() { + super.onUnlockHintStarted(); + mNotificationStackScroller.setUnlockHintRunning(true); + } + + @Override + protected float getPeekHeight() { + if (mNotificationStackScroller.getNotGoneChildCount() > 0) { + return mNotificationStackScroller.getPeekHeight(); + } else { + return mQsMinExpansionHeight; + } + } + + @Override + protected boolean shouldUseDismissingAnimation() { + return mBarState != StatusBarState.SHADE && (mKeyguardStateController.canDismissLockScreen() + || !isTracking()); + } + + @Override + protected boolean fullyExpandedClearAllVisible() { + return mNotificationStackScroller.isFooterViewNotGone() + && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate; + } + + @Override + protected boolean isClearAllVisible() { + return mNotificationStackScroller.isFooterViewContentVisible(); + } + + @Override + protected int getClearAllHeight() { + return mNotificationStackScroller.getFooterViewHeight(); + } + + @Override + protected boolean isTrackingBlocked() { + return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch; + } + + public boolean isQsExpanded() { + return mQsExpanded; + } + + public boolean isQsDetailShowing() { + return mQs.isShowingDetail(); + } + + public void closeQsDetail() { + mQs.closeDetail(); + } + + public boolean isLaunchTransitionFinished() { + return mIsLaunchTransitionFinished; + } + + public boolean isLaunchTransitionRunning() { + return mIsLaunchTransitionRunning; + } + + public void setLaunchTransitionEndRunnable(Runnable r) { + mLaunchAnimationEndRunnable = r; + } + + private void updateDozingVisibilities(boolean animate) { + mKeyguardBottomArea.setDozing(mDozing, animate); + if (!mDozing && animate) { + animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); + } + } + + @Override + public boolean isDozing() { + return mDozing; + } + + public void showEmptyShadeView(boolean emptyShadeViewVisible) { + mShowEmptyShadeView = emptyShadeViewVisible; + updateEmptyShadeView(); + } + + private void updateEmptyShadeView() { + // Hide "No notifications" in QS. + mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded); + } + + public void setQsScrimEnabled(boolean qsScrimEnabled) { + boolean changed = mQsScrimEnabled != qsScrimEnabled; + mQsScrimEnabled = qsScrimEnabled; + if (changed) { + updateQsState(); + } + } + + public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) { + mKeyguardUserSwitcher = keyguardUserSwitcher; + } + + public void onScreenTurningOn() { + mKeyguardStatusView.dozeTimeTick(); + } + + @Override + protected boolean onMiddleClicked() { + switch (mBarState) { + case StatusBarState.KEYGUARD: + if (!mDozingOnDown) { + if (mKeyguardBypassController.getBypassEnabled()) { + mUpdateMonitor.requestFaceAuth(); + } else { + mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT, + 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); + startUnlockHintAnimation(); + } + } + return true; + case StatusBarState.SHADE_LOCKED: + if (!mQsExpanded) { + mStatusBarStateController.setState(StatusBarState.KEYGUARD); + } + return true; + case StatusBarState.SHADE: + + // This gets called in the middle of the touch handling, where the state is still + // that we are tracking the panel. Collapse the panel after this is done. + mView.post(mPostCollapseRunnable); + return false; + default: + return true; + } + } + + public void setPanelAlpha(int alpha, boolean animate) { + if (mPanelAlpha != alpha) { + mPanelAlpha = alpha; + PropertyAnimator.setProperty(mView, mPanelAlphaAnimator, alpha, alpha == 255 + ? mPanelAlphaInPropertiesAnimator : mPanelAlphaOutPropertiesAnimator, + animate); + } + } + + public void setPanelAlphaEndAction(Runnable r) { + mPanelAlphaEndAction = r; + } + + private void updateKeyguardStatusBarForHeadsUp() { + boolean + showingKeyguardHeadsUp = + mKeyguardShowing && mHeadsUpAppearanceController.shouldBeVisible(); + if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) { + mShowingKeyguardHeadsUp = showingKeyguardHeadsUp; + if (mKeyguardShowing) { + PropertyAnimator.setProperty(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, + showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES, + true /* animate */); + } else { + PropertyAnimator.applyImmediately(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f); + } + } + } + + private void setKeyguardHeadsUpShowingAmount(float amount) { + mKeyguardHeadsUpShowingAmount = amount; + updateHeaderKeyguardAlpha(); + } + + private float getKeyguardHeadsUpShowingAmount() { + return mKeyguardHeadsUpShowingAmount; + } + + public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { + mHeadsUpAnimatingAway = headsUpAnimatingAway; + mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway); + updateHeadsUpVisibility(); + } + + private void updateHeadsUpVisibility() { + ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode); + } + + @Override + public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { + super.setHeadsUpManager(headsUpManager); + mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, + mNotificationStackScroller.getHeadsUpCallback(), + NotificationPanelViewController.this); + } + + public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) { + if (pickedChild != null) { + notifyListenersTrackingHeadsUp(pickedChild); + mExpandingFromHeadsUp = true; + } + // otherwise we update the state when the expansion is finished + } + + @Override + protected void onClosingFinished() { + super.onClosingFinished(); + resetHorizontalPanelPosition(); + setClosingWithAlphaFadeout(false); + } + + private void setClosingWithAlphaFadeout(boolean closing) { + mClosingWithAlphaFadeOut = closing; + mNotificationStackScroller.forceNoOverlappingRendering(closing); + } + + /** + * Updates the vertical position of the panel so it is positioned closer to the touch + * responsible for opening the panel. + * + * @param x the x-coordinate the touch event + */ + protected void updateVerticalPanelPosition(float x) { + if (mNotificationStackScroller.getWidth() * 1.75f > mView.getWidth()) { + resetHorizontalPanelPosition(); + return; + } + float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2; + float + rightMost = + mView.getWidth() - mPositionMinSideMargin + - mNotificationStackScroller.getWidth() / 2; + if (Math.abs(x - mView.getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) { + x = mView.getWidth() / 2; + } + x = Math.min(rightMost, Math.max(leftMost, x)); + float + center = + mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2; + setHorizontalPanelTranslation(x - center); + } + + private void resetHorizontalPanelPosition() { + setHorizontalPanelTranslation(0f); + } + + protected void setHorizontalPanelTranslation(float translation) { + mNotificationStackScroller.setTranslationX(translation); + mQsFrame.setTranslationX(translation); + int size = mVerticalTranslationListener.size(); + for (int i = 0; i < size; i++) { + mVerticalTranslationListener.get(i).run(); + } + } + + protected void updateExpandedHeight(float expandedHeight) { + if (mTracking) { + mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity()); + } + if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) { + // The expandedHeight is always the full panel Height when bypassing + expandedHeight = getMaxPanelHeightNonBypass(); + } + mNotificationStackScroller.setExpandedHeight(expandedHeight); + updateKeyguardBottomAreaAlpha(); + updateBigClockAlpha(); + updateStatusBarIcons(); + } + + /** + * @return whether the notifications are displayed full width and don't have any margins on + * the side. + */ + public boolean isFullWidth() { + return mIsFullWidth; + } + + private void updateStatusBarIcons() { + boolean + showIconsWhenExpanded = + (isPanelVisibleBecauseOfHeadsUp() || isFullWidth()) + && getExpandedHeight() < getOpeningHeight(); + boolean noVisibleNotifications = true; + if (showIconsWhenExpanded && noVisibleNotifications && isOnKeyguard()) { + showIconsWhenExpanded = false; + } + if (showIconsWhenExpanded != mShowIconsWhenExpanded) { + mShowIconsWhenExpanded = showIconsWhenExpanded; + mCommandQueue.recomputeDisableFlags(mDisplayId, false); + } + } + + private boolean isOnKeyguard() { + return mBarState == StatusBarState.KEYGUARD; + } + + public void setPanelScrimMinFraction(float minFraction) { + mBar.panelScrimMinFractionChanged(minFraction); + } + + public void clearNotificationEffects() { + mStatusBar.clearNotificationEffects(); + } + + @Override + protected boolean isPanelVisibleBecauseOfHeadsUp() { + return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway) + && mBarState == StatusBarState.SHADE; + } + + public void launchCamera(boolean animate, int source) { + if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) { + mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP; + } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) { + mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE; + } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) { + mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER; + } else { + + // Default. + mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; + } + + // If we are launching it when we are occluded already we don't want it to animate, + // nor setting these flags, since the occluded state doesn't change anymore, hence it's + // never reset. + if (!isFullyCollapsed()) { + setLaunchingAffordance(true); + } else { + animate = false; + } + mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null; + mAffordanceHelper.launchAffordance( + animate, mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); + } + + public void onAffordanceLaunchEnded() { + setLaunchingAffordance(false); + } + + /** + * Set whether we are currently launching an affordance. This is currently only set when + * launched via a camera gesture. + */ + private void setLaunchingAffordance(boolean launchingAffordance) { + mLaunchingAffordance = launchingAffordance; + mKeyguardAffordanceHelperCallback.getLeftIcon().setLaunchingAffordance(launchingAffordance); + mKeyguardAffordanceHelperCallback.getRightIcon().setLaunchingAffordance( + launchingAffordance); + mKeyguardBypassController.setLaunchingAffordance(launchingAffordance); + if (mAffordanceLaunchListener != null) { + mAffordanceLaunchListener.accept(launchingAffordance); + } + } + + /** + * Return true when a bottom affordance is launching an occluded activity with a splash screen. + */ + public boolean isLaunchingAffordanceWithPreview() { + return mLaunchingAffordance && mAffordanceHasPreview; + } + + /** + * Whether the camera application can be launched for the camera launch gesture. + */ + public boolean canCameraGestureBeLaunched() { + if (!mStatusBar.isCameraAllowedByAdmin()) { + return false; + } + + ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent(); + String + packageToLaunch = + (resolveInfo == null || resolveInfo.activityInfo == null) ? null + : resolveInfo.activityInfo.packageName; + return packageToLaunch != null && (mBarState != StatusBarState.SHADE || !isForegroundApp( + packageToLaunch)) && !mAffordanceHelper.isSwipingInProgress(); + } + + /** + * Return true if the applications with the package name is running in foreground. + * + * @param pkgName application package name. + */ + private boolean isForegroundApp(String pkgName) { + List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1); + return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); + } + + private void setGroupManager(NotificationGroupManager groupManager) { + mGroupManager = groupManager; + } + + public boolean hideStatusBarIconsWhenExpanded() { + if (mLaunchingNotification) { + return mHideIconsDuringNotificationLaunch; + } + if (mHeadsUpAppearanceController != null + && mHeadsUpAppearanceController.shouldBeVisible()) { + return false; + } + return !isFullWidth() || !mShowIconsWhenExpanded; + } + + private final FragmentListener mFragmentListener = new FragmentListener() { + @Override + public void onFragmentViewCreated(String tag, Fragment fragment) { + mQs = (QS) fragment; + mQs.setPanelView(mHeightListener); + mQs.setExpandClickListener(mOnClickListener); + mQs.setHeaderClickable(mQsExpansionEnabled); + updateQSPulseExpansion(); + mQs.setOverscrolling(mStackScrollerOverscrolling); + + // recompute internal state when qspanel height changes + mQs.getView().addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { + final int height = bottom - top; + final int oldHeight = oldBottom - oldTop; + if (height != oldHeight) { + mHeightListener.onQsHeightChanged(); + } + }); + mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView()); + if (mQs instanceof QSFragment) { + mKeyguardStatusBar.setQSPanel(((QSFragment) mQs).getQsPanel()); + } + updateQsExpansion(); + } + + @Override + public void onFragmentViewDestroyed(String tag, Fragment fragment) { + // Manual handling of fragment lifecycle is only required because this bridges + // non-fragment and fragment code. Once we are using a fragment for the notification + // panel, mQs will not need to be null cause it will be tied to the same lifecycle. + if (fragment == mQs) { + mQs = null; + } + } + }; + + @Override + public void setTouchAndAnimationDisabled(boolean disabled) { + super.setTouchAndAnimationDisabled(disabled); + if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) { + mAffordanceHelper.reset(false /* animate */); + } + mNotificationStackScroller.setAnimationsEnabled(!disabled); + } + + /** + * Sets the dozing state. + * + * @param dozing {@code true} when dozing. + * @param animate if transition should be animated. + * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor. + */ + public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) { + if (dozing == mDozing) return; + mView.setDozing(dozing); + mDozing = dozing; + mNotificationStackScroller.setDozing(mDozing, animate, wakeUpTouchLocation); + mKeyguardBottomArea.setDozing(mDozing, animate); + + if (dozing) { + mBottomAreaShadeAlphaAnimator.cancel(); + } + + if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) { + updateDozingVisibilities(animate); + } + + final float dozeAmount = dozing ? 1 : 0; + mStatusBarStateController.setDozeAmount(dozeAmount, animate); + } + + public void setPulsing(boolean pulsing) { + mPulsing = pulsing; + final boolean + animatePulse = + !mDozeParameters.getDisplayNeedsBlanking() && mDozeParameters.getAlwaysOn(); + if (animatePulse) { + mAnimateNextPositionUpdate = true; + } + // Do not animate the clock when waking up from a pulse. + // The height callback will take care of pushing the clock to the right position. + if (!mPulsing && !mDozing) { + mAnimateNextPositionUpdate = false; + } + mNotificationStackScroller.setPulsing(pulsing, animatePulse); + mKeyguardStatusView.setPulsing(pulsing); + } + + public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) { + if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) { + mAmbientIndicationBottomPadding = ambientIndicationBottomPadding; + mStatusBar.updateKeyguardMaxNotifications(); + } + } + + public void dozeTimeTick() { + mKeyguardBottomArea.dozeTimeTick(); + mKeyguardStatusView.dozeTimeTick(); + if (mInterpolatedDarkAmount > 0) { + positionClockAndNotifications(); + } + } + + public void setStatusAccessibilityImportance(int mode) { + mKeyguardStatusView.setImportantForAccessibility(mode); + } + + /** + * TODO: this should be removed. + * It's not correct to pass this view forward because other classes will end up adding + * children to it. Theme will be out of sync. + * + * @return bottom area view + */ + public KeyguardBottomAreaView getKeyguardBottomAreaView() { + return mKeyguardBottomArea; + } + + public void setUserSetupComplete(boolean userSetupComplete) { + mUserSetupComplete = userSetupComplete; + mKeyguardBottomArea.setUserSetupComplete(userSetupComplete); + } + + public void applyExpandAnimationParams(ExpandAnimationParameters params) { + mExpandOffset = params != null ? params.getTopChange() : 0; + updateQsExpansion(); + if (params != null) { + boolean hideIcons = params.getProgress( + ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f; + if (hideIcons != mHideIconsDuringNotificationLaunch) { + mHideIconsDuringNotificationLaunch = hideIcons; + if (!hideIcons) { + mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); + } + } + } + } + + public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) { + mTrackingHeadsUpListeners.add(listener); + } + + public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) { + mTrackingHeadsUpListeners.remove(listener); + } + + public void addVerticalTranslationListener(Runnable verticalTranslationListener) { + mVerticalTranslationListener.add(verticalTranslationListener); + } + + public void removeVerticalTranslationListener(Runnable verticalTranslationListener) { + mVerticalTranslationListener.remove(verticalTranslationListener); + } + + public void setHeadsUpAppearanceController( + HeadsUpAppearanceController headsUpAppearanceController) { + mHeadsUpAppearanceController = headsUpAppearanceController; + } + + /** + * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the + * security view of the bouncer. + */ + public void onBouncerPreHideAnimation() { + setKeyguardStatusViewVisibility(mBarState, true /* keyguardFadingAway */, + false /* goingToFullShade */); + } + + /** + * Do not let the user drag the shade up and down for the current touch session. + * This is necessary to avoid shade expansion while/after the bouncer is dismissed. + */ + public void blockExpansionForCurrentTouch() { + mBlockingExpansionForCurrentTouch = mTracking; + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + super.dump(fd, pw, args); + pw.println(" gestureExclusionRect: " + calculateGestureExclusionRect()); + if (mKeyguardStatusBar != null) { + mKeyguardStatusBar.dump(fd, pw, args); + } + if (mKeyguardStatusView != null) { + mKeyguardStatusView.dump(fd, pw, args); + } + } + + public boolean hasActiveClearableNotifications() { + return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL); + } + + private void updateShowEmptyShadeView() { + boolean + showEmptyShadeView = + mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications(); + showEmptyShadeView(showEmptyShadeView); + } + + public RemoteInputController.Delegate createRemoteInputDelegate() { + return mNotificationStackScroller.createDelegate(); + } + + public void updateNotificationViews() { + mNotificationStackScroller.updateSectionBoundaries(); + mNotificationStackScroller.updateSpeedBumpIndex(); + mNotificationStackScroller.updateFooter(); + updateShowEmptyShadeView(); + mNotificationStackScroller.updateIconAreaViews(); + } + + public void onUpdateRowStates() { + mNotificationStackScroller.onUpdateRowStates(); + } + + public boolean hasPulsingNotifications() { + return mNotificationStackScroller.hasPulsingNotifications(); + } + + public ActivatableNotificationView getActivatedChild() { + return mNotificationStackScroller.getActivatedChild(); + } + + public void setActivatedChild(ActivatableNotificationView o) { + mNotificationStackScroller.setActivatedChild(o); + } + + public void runAfterAnimationFinished(Runnable r) { + mNotificationStackScroller.runAfterAnimationFinished(r); + } + + public void setScrollingEnabled(boolean b) { + mNotificationStackScroller.setScrollingEnabled(b); + } + + public void initDependencies(StatusBar statusBar, NotificationGroupManager groupManager, + NotificationShelf notificationShelf, + NotificationIconAreaController notificationIconAreaController, + ScrimController scrimController) { + setStatusBar(statusBar); + setGroupManager(mGroupManager); + mNotificationStackScroller.setNotificationPanelController(this); + mNotificationStackScroller.setIconAreaController(notificationIconAreaController); + mNotificationStackScroller.setStatusBar(statusBar); + mNotificationStackScroller.setGroupManager(groupManager); + mNotificationStackScroller.setShelf(notificationShelf); + mNotificationStackScroller.setScrimController(scrimController); + updateShowEmptyShadeView(); + } + + public void showTransientIndication(int id) { + mKeyguardIndicationController.showTransientIndication(id); + } + + public void setOnReinflationListener(Runnable onReinflationListener) { + mOnReinflationListener = onReinflationListener; + } + + public static boolean isQsSplitEnabled() { + return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.QS_SPLIT_ENABLED, false); + } + + public void setAlpha(float alpha) { + mView.setAlpha(alpha); + } + + public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) { + return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration( + durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction( + endAction); + } + + public void resetViewGroupFade() { + ViewGroupFadeHelper.reset(mView); + } + + public void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) { + mView.getViewTreeObserver().addOnGlobalLayoutListener(listener); + } + + public void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) { + mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener); + } + + public MyOnHeadsUpChangedListener getOnHeadsUpChangedListener() { + return mOnHeadsUpChangedListener; + } + + public int getHeight() { + return mView.getHeight(); + } + + public TextView getHeaderDebugInfo() { + return mView.findViewById(R.id.header_debug_info); + } + + public void onThemeChanged() { + mConfigurationListener.onThemeChanged(); + } + + @Override + public OnLayoutChangeListener createLayoutChangeListener() { + return new OnLayoutChangeListener(); + } + + public void setEmptyDragAmount(float amount) { + mExpansionCallback.setEmptyDragAmount(amount); + } + + @Override + protected TouchHandler createTouchHandler() { + return new TouchHandler() { + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + if (mBlockTouches || mQsFullyExpanded && mQs.onInterceptTouchEvent(event)) { + return false; + } + initDownStates(event); + // Do not let touches go to shade or QS if the bouncer is visible, + // but still let user swipe down to expand the panel, dismissing the bouncer. + if (mStatusBar.isBouncerShowing()) { + return true; + } + if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { + mMetricsLogger.count(COUNTER_PANEL_OPEN, 1); + mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1); + return true; + } + if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0) + && mPulseExpansionHandler.onInterceptTouchEvent(event)) { + return true; + } + + if (!isFullyCollapsed() && onQsIntercept(event)) { + return true; + } + return super.onInterceptTouchEvent(event); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (mBlockTouches || (mQs != null && mQs.isCustomizing())) { + return false; + } + + // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able + // to pull down QS or expand the shade. + if (mStatusBar.isBouncerShowingScrimmed()) { + return false; + } + + // Make sure the next touch won't the blocked after the current ends. + if (event.getAction() == MotionEvent.ACTION_UP + || event.getAction() == MotionEvent.ACTION_CANCEL) { + mBlockingExpansionForCurrentTouch = false; + } + // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately + // without any ACTION_MOVE event. + // In such case, simply expand the panel instead of being stuck at the bottom bar. + if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) { + expand(true /* animate */); + } + initDownStates(event); + if (!mIsExpanding && !shouldQuickSettingsIntercept(mDownX, mDownY, 0) + && mPulseExpansionHandler.onTouchEvent(event)) { + // We're expanding all the other ones shouldn't get this anymore + return true; + } + if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp() + && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { + mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1); + } + boolean handled = false; + if ((!mIsExpanding || mHintAnimationRunning) && !mQsExpanded + && mBarState != StatusBarState.SHADE && !mDozing) { + handled |= mAffordanceHelper.onTouchEvent(event); + } + if (mOnlyAffordanceInThisMotion) { + return true; + } + handled |= mHeadsUpTouchHelper.onTouchEvent(event); + + if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) { + return true; + } + if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) { + mMetricsLogger.count(COUNTER_PANEL_OPEN, 1); + updateVerticalPanelPosition(event.getX()); + handled = true; + } + handled |= super.onTouch(v, event); + return !mDozing || mPulsing || handled; + } + }; + } + + @Override + protected PanelViewController.OnConfigurationChangedListener + createOnConfigurationChangedListener() { + return new OnConfigurationChangedListener(); + } + + private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener { + @Override + public void onHeightChanged(ExpandableView view, boolean needsAnimation) { + + // Block update if we are in quick settings and just the top padding changed + // (i.e. view == null). + if (view == null && mQsExpanded) { + return; + } + if (needsAnimation && mInterpolatedDarkAmount == 0) { + mAnimateNextPositionUpdate = true; + } + ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone(); + ExpandableNotificationRow + firstRow = + firstChildNotGone instanceof ExpandableNotificationRow + ? (ExpandableNotificationRow) firstChildNotGone : null; + if (firstRow != null && (view == firstRow || (firstRow.getNotificationParent() + == firstRow))) { + requestScrollerTopPaddingUpdate(false /* animate */); + } + requestPanelHeightUpdate(); + } + + @Override + public void onReset(ExpandableView view) { + } + } + + private class OnClickListener implements View.OnClickListener { + @Override + public void onClick(View v) { + onQsExpansionStarted(); + if (mQsExpanded) { + flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */, + true /* isClick */); + } else if (mQsExpansionEnabled) { + mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0); + flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */, + true /* isClick */); + } + } + } + + private class OnOverscrollTopChangedListener implements + NotificationStackScrollLayout.OnOverscrollTopChangedListener { + @Override + public void onOverscrollTopChanged(float amount, boolean isRubberbanded) { + cancelQsAnimation(); + if (!mQsExpansionEnabled) { + amount = 0f; + } + float rounded = amount >= 1f ? amount : 0f; + setOverScrolling(rounded != 0f && isRubberbanded); + mQsExpansionFromOverscroll = rounded != 0f; + mLastOverscroll = rounded; + updateQsState(); + setQsExpansion(mQsMinExpansionHeight + rounded); + } + + @Override + public void flingTopOverscroll(float velocity, boolean open) { + mLastOverscroll = 0f; + mQsExpansionFromOverscroll = false; + setQsExpansion(mQsExpansionHeight); + flingSettings(!mQsExpansionEnabled && open ? 0f : velocity, + open && mQsExpansionEnabled ? FLING_EXPAND : FLING_COLLAPSE, () -> { + mStackScrollerOverscrolling = false; + setOverScrolling(false); + updateQsState(); + }, false /* isClick */); + } + } + + private class DynamicPrivacyControlListener implements DynamicPrivacyController.Listener { + @Override + public void onDynamicPrivacyChanged() { + // Do not request animation when pulsing or waking up, otherwise the clock wiill be out + // of sync with the notification panel. + if (mLinearDarkAmount != 0) { + return; + } + mAnimateNextPositionUpdate = true; + } + } + + private class KeyguardAffordanceHelperCallback implements KeyguardAffordanceHelper.Callback { + @Override + public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) { + boolean + start = + mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? rightPage + : !rightPage; + mIsLaunchTransitionRunning = true; + mLaunchAnimationEndRunnable = null; + float displayDensity = mStatusBar.getDisplayDensity(); + int lengthDp = Math.abs((int) (translation / displayDensity)); + int velocityDp = Math.abs((int) (vel / displayDensity)); + if (start) { + mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp); + + mFalsingManager.onLeftAffordanceOn(); + if (mFalsingManager.shouldEnforceBouncer()) { + mStatusBar.executeRunnableDismissingKeyguard( + () -> mKeyguardBottomArea.launchLeftAffordance(), null, + true /* dismissShade */, false /* afterKeyguardGone */, + true /* deferred */); + } else { + mKeyguardBottomArea.launchLeftAffordance(); + } + } else { + if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals( + mLastCameraLaunchSource)) { + mLockscreenGestureLogger.write( + MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp); + } + mFalsingManager.onCameraOn(); + if (mFalsingManager.shouldEnforceBouncer()) { + mStatusBar.executeRunnableDismissingKeyguard( + () -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null, + true /* dismissShade */, false /* afterKeyguardGone */, + true /* deferred */); + } else { + mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource); + } + } + mStatusBar.startLaunchTransitionTimeout(); + mBlockTouches = true; + } + + @Override + public void onAnimationToSideEnded() { + mIsLaunchTransitionRunning = false; + mIsLaunchTransitionFinished = true; + if (mLaunchAnimationEndRunnable != null) { + mLaunchAnimationEndRunnable.run(); + mLaunchAnimationEndRunnable = null; + } + mStatusBar.readyForKeyguardDone(); + } + + @Override + public float getMaxTranslationDistance() { + return (float) Math.hypot(mView.getWidth(), getHeight()); + } + + @Override + public void onSwipingStarted(boolean rightIcon) { + mFalsingManager.onAffordanceSwipingStarted(rightIcon); + boolean + camera = + mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon + : rightIcon; + if (camera) { + mKeyguardBottomArea.bindCameraPrewarmService(); + } + mView.requestDisallowInterceptTouchEvent(true); + mOnlyAffordanceInThisMotion = true; + mQsTracking = false; + } + + @Override + public void onSwipingAborted() { + mFalsingManager.onAffordanceSwipingAborted(); + mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */); + } + + @Override + public void onIconClicked(boolean rightIcon) { + if (mHintAnimationRunning) { + return; + } + mHintAnimationRunning = true; + mAffordanceHelper.startHintAnimation(rightIcon, () -> { + mHintAnimationRunning = false; + mStatusBar.onHintFinished(); + }); + rightIcon = + mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon + : rightIcon; + if (rightIcon) { + mStatusBar.onCameraHintStarted(); + } else { + if (mKeyguardBottomArea.isLeftVoiceAssist()) { + mStatusBar.onVoiceAssistHintStarted(); + } else { + mStatusBar.onPhoneHintStarted(); + } + } + } + + @Override + public KeyguardAffordanceView getLeftIcon() { + return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL + ? mKeyguardBottomArea.getRightView() : mKeyguardBottomArea.getLeftView(); + } + + @Override + public KeyguardAffordanceView getRightIcon() { + return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL + ? mKeyguardBottomArea.getLeftView() : mKeyguardBottomArea.getRightView(); + } + + @Override + public View getLeftPreview() { + return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL + ? mKeyguardBottomArea.getRightPreview() : mKeyguardBottomArea.getLeftPreview(); + } + + @Override + public View getRightPreview() { + return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL + ? mKeyguardBottomArea.getLeftPreview() : mKeyguardBottomArea.getRightPreview(); + } + + @Override + public float getAffordanceFalsingFactor() { + return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; + } + + @Override + public boolean needsAntiFalsing() { + return mBarState == StatusBarState.KEYGUARD; + } + } + + private class OnEmptySpaceClickListener implements + NotificationStackScrollLayout.OnEmptySpaceClickListener { + @Override + public void onEmptySpaceClicked(float x, float y) { + onEmptySpaceClick(x); + } + } + + private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener { + @Override + public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) { + mNotificationStackScroller.setInHeadsUpPinnedMode(inPinnedMode); + if (inPinnedMode) { + mHeadsUpExistenceChangedRunnable.run(); + updateNotificationTranslucency(); + } else { + setHeadsUpAnimatingAway(true); + mNotificationStackScroller.runAfterAnimationFinished( + mHeadsUpExistenceChangedRunnable); + } + updateGestureExclusionRect(); + mHeadsUpPinnedMode = inPinnedMode; + updateHeadsUpVisibility(); + updateKeyguardStatusBarForHeadsUp(); + } + + @Override + public void onHeadsUpPinned(NotificationEntry entry) { + if (!isOnKeyguard()) { + mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), + true); + } + } + + @Override + public void onHeadsUpUnPinned(NotificationEntry entry) { + + // When we're unpinning the notification via active edge they remain heads-upped, + // we need to make sure that an animation happens in this case, otherwise the + // notification + // will stick to the top without any interaction. + if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) { + mNotificationStackScroller.generateHeadsUpAnimation( + entry.getHeadsUpAnimationView(), false); + entry.setHeadsUpIsVisible(); + } + } + + @Override + public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { + mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp); + } + } + + private class HeightListener implements QS.HeightListener { + public void onQsHeightChanged() { + mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0; + if (mQsExpanded && mQsFullyExpanded) { + mQsExpansionHeight = mQsMaxExpansionHeight; + requestScrollerTopPaddingUpdate(false /* animate */); + requestPanelHeightUpdate(); + } + if (mAccessibilityManager.isEnabled()) { + mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); + } + mNotificationStackScroller.setMaxTopPadding( + mQsMaxExpansionHeight + mQsNotificationTopPadding); + } + } + + private class ZenModeControllerCallback implements ZenModeController.Callback { + @Override + public void onZenChanged(int zen) { + updateShowEmptyShadeView(); + } + } + + private class ConfigurationListener implements ConfigurationController.ConfigurationListener { + @Override + public void onDensityOrFontScaleChanged() { + updateShowEmptyShadeView(); + } + + @Override + public void onThemeChanged() { + final int themeResId = mView.getContext().getThemeResId(); + if (mThemeResId == themeResId) { + return; + } + mThemeResId = themeResId; + + reInflateViews(); + } + + @Override + public void onOverlayChanged() { + reInflateViews(); + } + + @Override + public void onUiModeChanged() { + reinflatePluginContainer(); + } + } + + private class StatusBarStateListener implements StateListener { + @Override + public void onStateChanged(int statusBarState) { + boolean goingToFullShade = mStatusBarStateController.goingToFullShade(); + boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway(); + int oldState = mBarState; + boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD; + setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade); + setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade); + + mBarState = statusBarState; + mKeyguardShowing = keyguardShowing; + if (mKeyguardShowing && isQsSplitEnabled()) { + mNotificationStackScroller.setVisibility(View.VISIBLE); + mQsFrame.setVisibility(View.VISIBLE); + mHomeControlsLayout.setVisibility(View.GONE); + } + + if (oldState == StatusBarState.KEYGUARD && (goingToFullShade + || statusBarState == StatusBarState.SHADE_LOCKED)) { + animateKeyguardStatusBarOut(); + long + delay = + mBarState == StatusBarState.SHADE_LOCKED ? 0 + : mKeyguardStateController.calculateGoingToFullShadeDelay(); + mQs.animateHeaderSlidingIn(delay); + } else if (oldState == StatusBarState.SHADE_LOCKED + && statusBarState == StatusBarState.KEYGUARD) { + animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); + mNotificationStackScroller.resetScrollPosition(); + // Only animate header if the header is visible. If not, it will partially + // animate out + // the top of QS + if (!mQsExpanded) { + mQs.animateHeaderSlidingOut(); + } + } else { + mKeyguardStatusBar.setAlpha(1f); + mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE); + ((PhoneStatusBarView) mBar).maybeShowDivider(keyguardShowing); + if (keyguardShowing && oldState != mBarState) { + if (mQs != null) { + mQs.hideImmediately(); + } + } + } + updateKeyguardStatusBarForHeadsUp(); + if (keyguardShowing) { + updateDozingVisibilities(false /* animate */); + } + // THe update needs to happen after the headerSlide in above, otherwise the translation + // would reset + updateQSPulseExpansion(); + maybeAnimateBottomAreaAlpha(); + resetHorizontalPanelPosition(); + updateQsState(); + } + + @Override + public void onDozeAmountChanged(float linearAmount, float amount) { + mInterpolatedDarkAmount = amount; + mLinearDarkAmount = linearAmount; + mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount); + mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount); + positionClockAndNotifications(); + } + } + + private class ExpansionCallback implements PulseExpansionHandler.ExpansionCallback { + public void setEmptyDragAmount(float amount) { + mEmptyDragAmount = amount * 0.2f; + positionClockAndNotifications(); + } + } + + private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener { + @Override + public void onViewAttachedToWindow(View v) { + FragmentHostManager.get(mView).addTagListener(QS.TAG, mFragmentListener); + mStatusBarStateController.addCallback(mStatusBarStateListener); + mZenModeController.addCallback(mZenModeControllerCallback); + mConfigurationController.addCallback(mConfigurationListener); + mUpdateMonitor.registerCallback(mKeyguardUpdateCallback); + // Theme might have changed between inflating this view and attaching it to the + // window, so + // force a call to onThemeChanged + mConfigurationListener.onThemeChanged(); + } + + @Override + public void onViewDetachedFromWindow(View v) { + FragmentHostManager.get(mView).removeTagListener(QS.TAG, mFragmentListener); + mStatusBarStateController.removeCallback(mStatusBarStateListener); + mZenModeController.removeCallback(mZenModeControllerCallback); + mConfigurationController.removeCallback(mConfigurationListener); + mUpdateMonitor.removeCallback(mKeyguardUpdateCallback); + } + } + + private class OnLayoutChangeListener extends PanelViewController.OnLayoutChangeListener { + + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, + int oldTop, int oldRight, int oldBottom) { + DejankUtils.startDetectingBlockingIpcs("NVP#onLayout"); + super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom); + setIsFullWidth(mNotificationStackScroller.getWidth() == mView.getWidth()); + + // Update Clock Pivot + mKeyguardStatusView.setPivotX(mView.getWidth() / 2); + mKeyguardStatusView.setPivotY( + (FONT_HEIGHT - CAP_HEIGHT) / 2048f * mKeyguardStatusView.getClockTextSize()); + + // Calculate quick setting heights. + int oldMaxHeight = mQsMaxExpansionHeight; + if (mQs != null) { + mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight(); + if (mNPVPluginManager != null) { + mNPVPluginManager.setYOffset(mQsMinExpansionHeight); + mQsMinExpansionHeight += mNPVPluginManager.getHeight(); + } + mQsMaxExpansionHeight = mQs.getDesiredHeight(); + mNotificationStackScroller.setMaxTopPadding( + mQsMaxExpansionHeight + mQsNotificationTopPadding); + } + positionClockAndNotifications(); + if (mQsExpanded && mQsFullyExpanded) { + mQsExpansionHeight = mQsMaxExpansionHeight; + requestScrollerTopPaddingUpdate(false /* animate */); + requestPanelHeightUpdate(); + + // Size has changed, start an animation. + if (mQsMaxExpansionHeight != oldMaxHeight) { + startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight); + } + } else if (!mQsExpanded) { + setQsExpansion(mQsMinExpansionHeight + mLastOverscroll); + } + updateExpandedHeight(getExpandedHeight()); + updateHeader(); + + // If we are running a size change animation, the animation takes care of the height of + // the container. However, if we are not animating, we always need to make the QS + // container + // the desired height so when closing the QS detail, it stays smaller after the size + // change + // animation is finished but the detail view is still being animated away (this + // animation + // takes longer than the size change animation). + if (mQsSizeChangeAnimator == null && mQs != null) { + mQs.setHeightOverride(mQs.getDesiredHeight()); + } + updateMaxHeadsUpTranslation(); + updateGestureExclusionRect(); + if (mExpandAfterLayoutRunnable != null) { + mExpandAfterLayoutRunnable.run(); + mExpandAfterLayoutRunnable = null; + } + DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout"); + } + } + + private class DebugDrawable extends Drawable { + + @Override + public void draw(Canvas canvas) { + Paint p = new Paint(); + p.setColor(Color.RED); + p.setStrokeWidth(2); + p.setStyle(Paint.Style.STROKE); + canvas.drawLine(0, getMaxPanelHeight(), mView.getWidth(), getMaxPanelHeight(), p); + p.setColor(Color.BLUE); + canvas.drawLine(0, getExpandedHeight(), mView.getWidth(), getExpandedHeight(), p); + p.setColor(Color.GREEN); + canvas.drawLine(0, calculatePanelHeightQsExpanded(), mView.getWidth(), + calculatePanelHeightQsExpanded(), p); + p.setColor(Color.YELLOW); + canvas.drawLine(0, calculatePanelHeightShade(), mView.getWidth(), + calculatePanelHeightShade(), p); + p.setColor(Color.MAGENTA); + canvas.drawLine( + 0, calculateQsTopPadding(), mView.getWidth(), calculateQsTopPadding(), p); + p.setColor(Color.CYAN); + canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(), + mNotificationStackScroller.getTopPadding(), p); + p.setColor(Color.GRAY); + canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(), + mClockPositionResult.clockY, p); + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + + } + + @Override + public int getOpacity() { + return 0; + } + } + + private class OnConfigurationChangedListener extends + PanelViewController.OnConfigurationChangedListener { + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mAffordanceHelper.onConfigurationChanged(); + if (newConfig.orientation != mLastOrientation) { + resetHorizontalPanelPosition(); + } + mLastOrientation = newConfig.orientation; + } + } + + private class OnApplyWindowInsetsListener implements View.OnApplyWindowInsetsListener { + public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { + mNavigationBarBottomHeight = insets.getStableInsetBottom(); + updateMaxHeadsUpTranslation(); + return insets; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java index 063d00b806c2..8d8c8da62b4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java @@ -43,7 +43,7 @@ public abstract class PanelBar extends FrameLayout { public static final int STATE_OPENING = 1; public static final int STATE_OPEN = 2; - PanelView mPanel; + PanelViewController mPanel; private int mState = STATE_CLOSED; private boolean mTracking; @@ -83,7 +83,8 @@ public abstract class PanelBar extends FrameLayout { super.onFinishInflate(); } - public void setPanel(PanelView pv) { + /** Set the PanelViewController */ + public void setPanel(PanelViewController pv) { mPanel = pv; pv.setBar(this); } @@ -96,7 +97,7 @@ public abstract class PanelBar extends FrameLayout { setImportantForAccessibility(important); updateVisibility(); - if (mPanel != null) mPanel.setImportantForAccessibility(important); + if (mPanel != null) mPanel.getView().setImportantForAccessibility(important); } public float getExpansionFraction() { @@ -108,7 +109,7 @@ public abstract class PanelBar extends FrameLayout { } protected void updateVisibility() { - mPanel.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE); + mPanel.getView().setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE); } protected boolean shouldPanelBeVisible() { @@ -131,7 +132,7 @@ public abstract class PanelBar extends FrameLayout { } if (event.getAction() == MotionEvent.ACTION_DOWN) { - final PanelView panel = mPanel; + final PanelViewController panel = mPanel; if (panel == null) { // panel is not there, so we'll eat the gesture Log.v(TAG, String.format("onTouch: no panel for touch at (%d,%d)", @@ -149,7 +150,7 @@ public abstract class PanelBar extends FrameLayout { return true; } } - return mPanel == null || mPanel.onTouchEvent(event); + return mPanel == null || mPanel.getView().dispatchTouchEvent(event); } public abstract void panelScrimMinFractionChanged(float minFraction); @@ -163,7 +164,7 @@ public abstract class PanelBar extends FrameLayout { boolean fullyClosed = true; boolean fullyOpened = false; if (SPEW) LOG("panelExpansionChanged: start state=%d", mState); - PanelView pv = mPanel; + PanelViewController pv = mPanel; mExpanded = expanded; mPanelFraction = frac; updateVisibility(); @@ -192,7 +193,7 @@ public abstract class PanelBar extends FrameLayout { public void collapsePanel(boolean animate, boolean delayed, float speedUpFactor) { boolean waiting = false; - PanelView pv = mPanel; + PanelViewController pv = mPanel; if (animate && !pv.isFullyCollapsed()) { pv.collapse(delayed, speedUpFactor); waiting = true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index cd56d0651275..2719a3278d24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -16,1255 +16,62 @@ package com.android.systemui.statusbar.phone; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Configuration; -import android.content.res.Resources; -import android.os.SystemClock; -import android.os.VibrationEffect; import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.InputDevice; import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewTreeObserver; -import android.view.animation.Interpolator; import android.widget.FrameLayout; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.util.LatencyTracker; -import com.android.systemui.DejankUtils; -import com.android.systemui.Dependency; -import com.android.systemui.Interpolators; -import com.android.systemui.R; -import com.android.systemui.doze.DozeLog; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.statusbar.FlingAnimationUtils; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.policy.KeyguardStateController; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; - public abstract class PanelView extends FrameLayout { public static final boolean DEBUG = PanelBar.DEBUG; public static final String TAG = PanelView.class.getSimpleName(); - private static final int INITIAL_OPENING_PEEK_DURATION = 200; - private static final int PEEK_ANIMATION_DURATION = 360; - private static final int NO_FIXED_DURATION = -1; - protected long mDownTime; - protected boolean mTouchSlopExceededBeforeDown; - private float mMinExpandHeight; - private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); - private boolean mPanelUpdateWhenAnimatorEnds; - private boolean mVibrateOnOpening; - protected boolean mLaunchingNotification; - private int mFixedDuration = NO_FIXED_DURATION; - protected ArrayList<PanelExpansionListener> mExpansionListeners = new ArrayList<>(); - - private final void logf(String fmt, Object... args) { - Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args)); - } + private PanelViewController.TouchHandler mTouchHandler; protected StatusBar mStatusBar; protected HeadsUpManagerPhone mHeadsUpManager; - private float mPeekHeight; - private float mHintDistance; - private float mInitialOffsetOnTouch; - private boolean mCollapsedAndHeadsUpOnDown; - private float mExpandedFraction = 0; - protected float mExpandedHeight = 0; - private boolean mPanelClosedOnDown; - private boolean mHasLayoutedSinceDown; - private float mUpdateFlingVelocity; - private boolean mUpdateFlingOnLayout; - private boolean mPeekTouching; - private boolean mJustPeeked; - private boolean mClosing; - protected boolean mTracking; - private boolean mTouchSlopExceeded; - private int mTrackingPointer; protected int mTouchSlop; - protected boolean mHintAnimationRunning; - private boolean mOverExpandedBeforeFling; - private boolean mTouchAboveFalsingThreshold; - private int mUnlockFalsingThreshold; - private boolean mTouchStartedInEmptyArea; - private boolean mMotionAborted; - private boolean mUpwardsWhenTresholdReached; - private boolean mAnimatingOnDown; - - private ValueAnimator mHeightAnimator; - private ObjectAnimator mPeekAnimator; - private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); - private FlingAnimationUtils mFlingAnimationUtils; - private FlingAnimationUtils mFlingAnimationUtilsClosing; - private FlingAnimationUtils mFlingAnimationUtilsDismissing; - private final FalsingManager mFalsingManager; - private final DozeLog mDozeLog; - private final VibratorHelper mVibratorHelper; - - /** - * Whether an instant expand request is currently pending and we are just waiting for layout. - */ - private boolean mInstantExpanding; - private boolean mAnimateAfterExpanding; - - PanelBar mBar; - private String mViewName; - private float mInitialTouchY; - private float mInitialTouchX; - private boolean mTouchDisabled; - - /** - * Whether or not the PanelView can be expanded or collapsed with a drag. - */ - private boolean mNotificationsDragEnabled; - - private Interpolator mBounceInterpolator; protected KeyguardBottomAreaView mKeyguardBottomArea; + private OnConfigurationChangedListener mOnConfigurationChangedListener; - /** - * Speed-up factor to be used when {@link #mFlingCollapseRunnable} runs the next time. - */ - private float mNextCollapseSpeedUpFactor = 1.0f; - - protected boolean mExpanding; - private boolean mGestureWaitForTouchSlop; - private boolean mIgnoreXTouchSlop; - private boolean mExpandLatencyTracking; - protected final KeyguardStateController mKeyguardStateController; - protected final SysuiStatusBarStateController mStatusBarStateController; - - protected void onExpandingFinished() { - mBar.onExpandingFinished(); - } - - protected void onExpandingStarted() { + public PanelView(Context context) { + super(context); } - private void notifyExpandingStarted() { - if (!mExpanding) { - mExpanding = true; - onExpandingStarted(); - } - } - - protected final void notifyExpandingFinished() { - endClosing(); - if (mExpanding) { - mExpanding = false; - onExpandingFinished(); - } - } - - private void runPeekAnimation(long duration, float peekHeight, boolean collapseWhenFinished) { - mPeekHeight = peekHeight; - if (DEBUG) logf("peek to height=%.1f", mPeekHeight); - if (mHeightAnimator != null) { - return; - } - if (mPeekAnimator != null) { - mPeekAnimator.cancel(); - } - mPeekAnimator = ObjectAnimator.ofFloat(this, "expandedHeight", mPeekHeight) - .setDuration(duration); - mPeekAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); - mPeekAnimator.addListener(new AnimatorListenerAdapter() { - private boolean mCancelled; - - @Override - public void onAnimationCancel(Animator animation) { - mCancelled = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - mPeekAnimator = null; - if (!mCancelled && collapseWhenFinished) { - postOnAnimation(mPostCollapseRunnable); - } - - } - }); - notifyExpandingStarted(); - mPeekAnimator.start(); - mJustPeeked = true; - } - - public PanelView(Context context, AttributeSet attrs, FalsingManager falsingManager, - DozeLog dozeLog, KeyguardStateController keyguardStateController, - SysuiStatusBarStateController statusBarStateController) { + public PanelView(Context context, AttributeSet attrs) { super(context, attrs); - mKeyguardStateController = keyguardStateController; - mStatusBarStateController = statusBarStateController; - DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); - mFlingAnimationUtils = new FlingAnimationUtils(displayMetrics, - 0.6f /* maxLengthSeconds */, 0.6f /* speedUpFactor */); - mFlingAnimationUtilsClosing = new FlingAnimationUtils(displayMetrics, - 0.5f /* maxLengthSeconds */, 0.6f /* speedUpFactor */); - mFlingAnimationUtilsDismissing = new FlingAnimationUtils(displayMetrics, - 0.5f /* maxLengthSeconds */, 0.2f /* speedUpFactor */, 0.6f /* x2 */, - 0.84f /* y2 */); - mBounceInterpolator = new BounceInterpolator(); - mFalsingManager = falsingManager; - mDozeLog = dozeLog; - mNotificationsDragEnabled = - getResources().getBoolean(R.bool.config_enableNotificationShadeDrag); - mVibratorHelper = Dependency.get(VibratorHelper.class); - mVibrateOnOpening = mContext.getResources().getBoolean( - R.bool.config_vibrateOnIconAnimation); - } - - protected void loadDimens() { - final Resources res = getContext().getResources(); - final ViewConfiguration configuration = ViewConfiguration.get(getContext()); - mTouchSlop = configuration.getScaledTouchSlop(); - mHintDistance = res.getDimension(R.dimen.hint_move_distance); - mUnlockFalsingThreshold = res.getDimensionPixelSize(R.dimen.unlock_falsing_threshold); - } - - private void addMovement(MotionEvent event) { - // Add movement to velocity tracker using raw screen X and Y coordinates instead - // of window coordinates because the window frame may be moving at the same time. - float deltaX = event.getRawX() - event.getX(); - float deltaY = event.getRawY() - event.getY(); - event.offsetLocation(deltaX, deltaY); - mVelocityTracker.addMovement(event); - event.offsetLocation(-deltaX, -deltaY); - } - - public void setTouchAndAnimationDisabled(boolean disabled) { - mTouchDisabled = disabled; - if (mTouchDisabled) { - cancelHeightAnimator(); - if (mTracking) { - onTrackingStopped(true /* expanded */); - } - notifyExpandingFinished(); - } - } - - public void startExpandLatencyTracking() { - if (LatencyTracker.isEnabled(mContext)) { - LatencyTracker.getInstance(mContext).onActionStart( - LatencyTracker.ACTION_EXPAND_PANEL); - mExpandLatencyTracking = true; - } - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (mInstantExpanding - || (mTouchDisabled && event.getActionMasked() != MotionEvent.ACTION_CANCEL) - || (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN)) { - return false; - } - - // If dragging should not expand the notifications shade, then return false. - if (!mNotificationsDragEnabled) { - if (mTracking) { - // Turn off tracking if it's on or the shade can get stuck in the down position. - onTrackingStopped(true /* expand */); - } - return false; - } - - // On expanding, single mouse click expands the panel instead of dragging. - if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) { - if (event.getAction() == MotionEvent.ACTION_UP) { - expand(true); - } - return true; - } - - /* - * We capture touch events here and update the expand height here in case according to - * the users fingers. This also handles multi-touch. - * - * If the user just clicks shortly, we show a quick peek of the shade. - * - * Flinging is also enabled in order to open or close the shade. - */ - - int pointerIndex = event.findPointerIndex(mTrackingPointer); - if (pointerIndex < 0) { - pointerIndex = 0; - mTrackingPointer = event.getPointerId(pointerIndex); - } - final float x = event.getX(pointerIndex); - final float y = event.getY(pointerIndex); - - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop(); - mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y); - } - - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - startExpandMotion(x, y, false /* startTracking */, mExpandedHeight); - mJustPeeked = false; - mMinExpandHeight = 0.0f; - mPanelClosedOnDown = isFullyCollapsed(); - mHasLayoutedSinceDown = false; - mUpdateFlingOnLayout = false; - mMotionAborted = false; - mPeekTouching = mPanelClosedOnDown; - mDownTime = SystemClock.uptimeMillis(); - mTouchAboveFalsingThreshold = false; - mCollapsedAndHeadsUpOnDown = isFullyCollapsed() - && mHeadsUpManager.hasPinnedHeadsUp(); - addMovement(event); - if (!mGestureWaitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning) - || mPeekAnimator != null) { - mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning) - || mPeekAnimator != null || mTouchSlopExceededBeforeDown; - cancelHeightAnimator(); - cancelPeek(); - onTrackingStarted(); - } - if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp() - && !mStatusBar.isBouncerShowing()) { - startOpening(event); - } - break; - - case MotionEvent.ACTION_POINTER_UP: - final int upPointer = event.getPointerId(event.getActionIndex()); - if (mTrackingPointer == upPointer) { - // gesture is ongoing, find a new pointer to track - final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; - final float newY = event.getY(newIndex); - final float newX = event.getX(newIndex); - mTrackingPointer = event.getPointerId(newIndex); - startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight); - } - break; - case MotionEvent.ACTION_POINTER_DOWN: - if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { - mMotionAborted = true; - endMotionEvent(event, x, y, true /* forceCancel */); - return false; - } - break; - case MotionEvent.ACTION_MOVE: - addMovement(event); - float h = y - mInitialTouchY; - - // If the panel was collapsed when touching, we only need to check for the - // y-component of the gesture, as we have no conflicting horizontal gesture. - if (Math.abs(h) > mTouchSlop - && (Math.abs(h) > Math.abs(x - mInitialTouchX) - || mIgnoreXTouchSlop)) { - mTouchSlopExceeded = true; - if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) { - if (!mJustPeeked && mInitialOffsetOnTouch != 0f) { - startExpandMotion(x, y, false /* startTracking */, mExpandedHeight); - h = 0; - } - cancelHeightAnimator(); - onTrackingStarted(); - } - } - float newHeight = Math.max(0, h + mInitialOffsetOnTouch); - if (newHeight > mPeekHeight) { - if (mPeekAnimator != null) { - mPeekAnimator.cancel(); - } - mJustPeeked = false; - } else if (mPeekAnimator == null && mJustPeeked) { - // The initial peek has finished, but we haven't dragged as far yet, lets - // speed it up by starting at the peek height. - mInitialOffsetOnTouch = mExpandedHeight; - mInitialTouchY = y; - mMinExpandHeight = mExpandedHeight; - mJustPeeked = false; - } - newHeight = Math.max(newHeight, mMinExpandHeight); - if (-h >= getFalsingThreshold()) { - mTouchAboveFalsingThreshold = true; - mUpwardsWhenTresholdReached = isDirectionUpwards(x, y); - } - if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking) && - !isTrackingBlocked()) { - setExpandedHeightInternal(newHeight); - } - break; - - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - addMovement(event); - endMotionEvent(event, x, y, false /* forceCancel */); - break; - } - return !mGestureWaitForTouchSlop || mTracking; - } - - private void startOpening(MotionEvent event) { - runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(), - false /* collapseWhenFinished */); - notifyBarPanelExpansionChanged(); - maybeVibrateOnOpening(); - - //TODO: keyguard opens QS a different way; log that too? - - // Log the position of the swipe that opened the panel - float width = mStatusBar.getDisplayWidth(); - float height = mStatusBar.getDisplayHeight(); - int rot = mStatusBar.getRotation(); - - mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND, - (int) (event.getX() / width * 100), - (int) (event.getY() / height * 100), - rot); - } - - protected void maybeVibrateOnOpening() { - if (mVibrateOnOpening) { - mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); - } - } - - protected abstract float getOpeningHeight(); - - /** - * @return whether the swiping direction is upwards and above a 45 degree angle compared to the - * horizontal direction - */ - private boolean isDirectionUpwards(float x, float y) { - float xDiff = x - mInitialTouchX; - float yDiff = y - mInitialTouchY; - if (yDiff >= 0) { - return false; - } - return Math.abs(yDiff) >= Math.abs(xDiff); } - protected void startExpandingFromPeek() { - mStatusBar.handlePeekToExpandTransistion(); + public PanelView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); } - protected void startExpandMotion(float newX, float newY, boolean startTracking, - float expandedHeight) { - mInitialOffsetOnTouch = expandedHeight; - mInitialTouchY = newY; - mInitialTouchX = newX; - if (startTracking) { - mTouchSlopExceeded = true; - setExpandedHeight(mInitialOffsetOnTouch); - onTrackingStarted(); - } + public PanelView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); } - private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) { - mTrackingPointer = -1; - if ((mTracking && mTouchSlopExceeded) - || Math.abs(x - mInitialTouchX) > mTouchSlop - || Math.abs(y - mInitialTouchY) > mTouchSlop - || event.getActionMasked() == MotionEvent.ACTION_CANCEL - || forceCancel) { - mVelocityTracker.computeCurrentVelocity(1000); - float vel = mVelocityTracker.getYVelocity(); - float vectorVel = (float) Math.hypot( - mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); - - boolean expand = flingExpands(vel, vectorVel, x, y) - || event.getActionMasked() == MotionEvent.ACTION_CANCEL - || forceCancel; - mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold, - mStatusBar.isFalsingThresholdNeeded(), - mStatusBar.isWakeUpComingFromTouch()); - // Log collapse gesture if on lock screen. - if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { - float displayDensity = mStatusBar.getDisplayDensity(); - int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity); - int velocityDp = (int) Math.abs(vel / displayDensity); - mLockscreenGestureLogger.write( - MetricsEvent.ACTION_LS_UNLOCK, - heightDp, velocityDp); - } - fling(vel, expand, isFalseTouch(x, y)); - onTrackingStopped(expand); - mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown; - if (mUpdateFlingOnLayout) { - mUpdateFlingVelocity = vel; - } - } else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking - && !mStatusBar.isBouncerShowing() - && !mKeyguardStateController.isKeyguardFadingAway()) { - long timePassed = SystemClock.uptimeMillis() - mDownTime; - if (timePassed < ViewConfiguration.getLongPressTimeout()) { - // Lets show the user that he can actually expand the panel - runPeekAnimation(PEEK_ANIMATION_DURATION, getPeekHeight(), true /* collapseWhenFinished */); - } else { - // We need to collapse the panel since we peeked to the small height. - postOnAnimation(mPostCollapseRunnable); - } - } else if (!mStatusBar.isBouncerShowing()) { - boolean expands = onEmptySpaceClick(mInitialTouchX); - onTrackingStopped(expands); - } - - mVelocityTracker.clear(); - mPeekTouching = false; + public void setOnTouchListener(PanelViewController.TouchHandler touchHandler) { + super.setOnTouchListener(touchHandler); + mTouchHandler = touchHandler; } - protected float getCurrentExpandVelocity() { - mVelocityTracker.computeCurrentVelocity(1000); - return mVelocityTracker.getYVelocity(); - } - - private int getFalsingThreshold() { - float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; - return (int) (mUnlockFalsingThreshold * factor); - } - - protected abstract boolean shouldGestureWaitForTouchSlop(); - - protected abstract boolean shouldGestureIgnoreXTouchSlop(float x, float y); - - protected void onTrackingStopped(boolean expand) { - mTracking = false; - mBar.onTrackingStopped(expand); - notifyBarPanelExpansionChanged(); - } - - protected void onTrackingStarted() { - endClosing(); - mTracking = true; - mBar.onTrackingStarted(); - notifyExpandingStarted(); - notifyBarPanelExpansionChanged(); + public void setOnConfigurationChangedListener(OnConfigurationChangedListener listener) { + mOnConfigurationChangedListener = listener; } @Override public boolean onInterceptTouchEvent(MotionEvent event) { - if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled - || (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN)) { - return false; - } - - /* - * If the user drags anywhere inside the panel we intercept it if the movement is - * upwards. This allows closing the shade from anywhere inside the panel. - * - * We only do this if the current content is scrolled to the bottom, - * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling gesture - * possible. - */ - int pointerIndex = event.findPointerIndex(mTrackingPointer); - if (pointerIndex < 0) { - pointerIndex = 0; - mTrackingPointer = event.getPointerId(pointerIndex); - } - final float x = event.getX(pointerIndex); - final float y = event.getY(pointerIndex); - boolean scrolledToBottom = isScrolledToBottom(); - - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - mStatusBar.userActivity(); - mAnimatingOnDown = mHeightAnimator != null; - mMinExpandHeight = 0.0f; - mDownTime = SystemClock.uptimeMillis(); - if (mAnimatingOnDown && mClosing && !mHintAnimationRunning - || mPeekAnimator != null) { - cancelHeightAnimator(); - cancelPeek(); - mTouchSlopExceeded = true; - return true; - } - mInitialTouchY = y; - mInitialTouchX = x; - mTouchStartedInEmptyArea = !isInContentBounds(x, y); - mTouchSlopExceeded = mTouchSlopExceededBeforeDown; - mJustPeeked = false; - mMotionAborted = false; - mPanelClosedOnDown = isFullyCollapsed(); - mCollapsedAndHeadsUpOnDown = false; - mHasLayoutedSinceDown = false; - mUpdateFlingOnLayout = false; - mTouchAboveFalsingThreshold = false; - addMovement(event); - break; - case MotionEvent.ACTION_POINTER_UP: - final int upPointer = event.getPointerId(event.getActionIndex()); - if (mTrackingPointer == upPointer) { - // gesture is ongoing, find a new pointer to track - final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; - mTrackingPointer = event.getPointerId(newIndex); - mInitialTouchX = event.getX(newIndex); - mInitialTouchY = event.getY(newIndex); - } - break; - case MotionEvent.ACTION_POINTER_DOWN: - if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { - mMotionAborted = true; - mVelocityTracker.clear(); - } - break; - case MotionEvent.ACTION_MOVE: - final float h = y - mInitialTouchY; - addMovement(event); - if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) { - float hAbs = Math.abs(h); - if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop)) - && hAbs > Math.abs(x - mInitialTouchX)) { - cancelHeightAnimator(); - startExpandMotion(x, y, true /* startTracking */, mExpandedHeight); - return true; - } - } - break; - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - mVelocityTracker.clear(); - break; - } - return false; - } - - /** - * @return Whether a pair of coordinates are inside the visible view content bounds. - */ - protected abstract boolean isInContentBounds(float x, float y); - - protected void cancelHeightAnimator() { - if (mHeightAnimator != null) { - if (mHeightAnimator.isRunning()) { - mPanelUpdateWhenAnimatorEnds = false; - } - mHeightAnimator.cancel(); - } - endClosing(); - } - - private void endClosing() { - if (mClosing) { - mClosing = false; - onClosingFinished(); - } - } - - protected boolean isScrolledToBottom() { - return true; - } - - protected float getContentHeight() { - return mExpandedHeight; - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - loadDimens(); + return mTouchHandler.onInterceptTouchEvent(event); } @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - loadDimens(); - } - - /** - * @param vel the current vertical velocity of the motion - * @param vectorVel the length of the vectorial velocity - * @return whether a fling should expands the panel; contracts otherwise - */ - protected boolean flingExpands(float vel, float vectorVel, float x, float y) { - if (mFalsingManager.isUnlockingDisabled()) { - return true; - } - - if (isFalseTouch(x, y)) { - return true; - } - if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { - return shouldExpandWhenNotFlinging(); - } else { - return vel > 0; - } - } - - protected boolean shouldExpandWhenNotFlinging() { - return getExpandedFraction() > 0.5f; - } - - /** - * @param x the final x-coordinate when the finger was lifted - * @param y the final y-coordinate when the finger was lifted - * @return whether this motion should be regarded as a false touch - */ - private boolean isFalseTouch(float x, float y) { - if (!mStatusBar.isFalsingThresholdNeeded()) { - return false; - } - if (mFalsingManager.isClassifierEnabled()) { - return mFalsingManager.isFalseTouch(); - } - if (!mTouchAboveFalsingThreshold) { - return true; - } - if (mUpwardsWhenTresholdReached) { - return false; - } - return !isDirectionUpwards(x, y); - } - - protected void fling(float vel, boolean expand) { - fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, false); - } - - protected void fling(float vel, boolean expand, boolean expandBecauseOfFalsing) { - fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, expandBecauseOfFalsing); - } - - protected void fling(float vel, boolean expand, float collapseSpeedUpFactor, - boolean expandBecauseOfFalsing) { - cancelPeek(); - float target = expand ? getMaxPanelHeight() : 0; - if (!expand) { - mClosing = true; - } - flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing); - } - - protected void flingToHeight(float vel, boolean expand, float target, - float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) { - // Hack to make the expand transition look nice when clear all button is visible - we make - // the animation only to the last notification, and then jump to the maximum panel height so - // clear all just fades in and the decelerating motion is towards the last notification. - final boolean clearAllExpandHack = expand && fullyExpandedClearAllVisible() - && mExpandedHeight < getMaxPanelHeight() - getClearAllHeight() - && !isClearAllVisible(); - if (clearAllExpandHack) { - target = getMaxPanelHeight() - getClearAllHeight(); - } - if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) { - notifyExpandingFinished(); - return; - } - mOverExpandedBeforeFling = getOverExpansionAmount() > 0f; - ValueAnimator animator = createHeightAnimator(target); - if (expand) { - if (expandBecauseOfFalsing && vel < 0) { - vel = 0; - } - mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight()); - if (vel == 0) { - animator.setDuration(350); - } - } else { - if (shouldUseDismissingAnimation()) { - if (vel == 0) { - animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED); - long duration = (long) (200 + mExpandedHeight / getHeight() * 100); - animator.setDuration(duration); - } else { - mFlingAnimationUtilsDismissing.apply(animator, mExpandedHeight, target, vel, - getHeight()); - } - } else { - mFlingAnimationUtilsClosing - .apply(animator, mExpandedHeight, target, vel, getHeight()); - } - - // Make it shorter if we run a canned animation - if (vel == 0) { - animator.setDuration((long) (animator.getDuration() / collapseSpeedUpFactor)); - } - if (mFixedDuration != NO_FIXED_DURATION) { - animator.setDuration(mFixedDuration); - } - } - animator.addListener(new AnimatorListenerAdapter() { - private boolean mCancelled; - - @Override - public void onAnimationCancel(Animator animation) { - mCancelled = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - if (clearAllExpandHack && !mCancelled) { - setExpandedHeightInternal(getMaxPanelHeight()); - } - setAnimator(null); - if (!mCancelled) { - notifyExpandingFinished(); - } - notifyBarPanelExpansionChanged(); - } - }); - setAnimator(animator); - animator.start(); - } - - protected abstract boolean shouldUseDismissingAnimation(); - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mViewName = getResources().getResourceName(getId()); - } - - public String getName() { - return mViewName; - } - - public void setExpandedHeight(float height) { - if (DEBUG) logf("setExpandedHeight(%.1f)", height); - setExpandedHeightInternal(height + getOverExpansionPixels()); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - mStatusBar.onPanelLaidOut(); - requestPanelHeightUpdate(); - mHasLayoutedSinceDown = true; - if (mUpdateFlingOnLayout) { - abortAnimations(); - fling(mUpdateFlingVelocity, true /* expands */); - mUpdateFlingOnLayout = false; - } - } - - protected void requestPanelHeightUpdate() { - float currentMaxPanelHeight = getMaxPanelHeight(); - - if (isFullyCollapsed()) { - return; - } - - if (currentMaxPanelHeight == mExpandedHeight) { - return; - } - - if (mPeekAnimator != null || mPeekTouching) { - return; - } - - if (mTracking && !isTrackingBlocked()) { - return; - } - - if (mHeightAnimator != null) { - mPanelUpdateWhenAnimatorEnds = true; - return; - } - - setExpandedHeight(currentMaxPanelHeight); - } - - public void setExpandedHeightInternal(float h) { - if (mExpandLatencyTracking && h != 0f) { - DejankUtils.postAfterTraversal(() -> LatencyTracker.getInstance(mContext).onActionEnd( - LatencyTracker.ACTION_EXPAND_PANEL)); - mExpandLatencyTracking = false; - } - float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount(); - if (mHeightAnimator == null) { - float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion); - if (getOverExpansionPixels() != overExpansionPixels && mTracking) { - setOverExpansion(overExpansionPixels, true /* isPixels */); - } - mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount(); - } else { - mExpandedHeight = h; - if (mOverExpandedBeforeFling) { - setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */); - } - } - - // If we are closing the panel and we are almost there due to a slow decelerating - // interpolator, abort the animation. - if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) { - mExpandedHeight = 0f; - if (mHeightAnimator != null) { - mHeightAnimator.end(); - } - } - mExpandedFraction = Math.min(1f, - fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion); - onHeightUpdated(mExpandedHeight); - notifyBarPanelExpansionChanged(); - } - - /** - * @return true if the panel tracking should be temporarily blocked; this is used when a - * conflicting gesture (opening QS) is happening - */ - protected abstract boolean isTrackingBlocked(); - - protected abstract void setOverExpansion(float overExpansion, boolean isPixels); - - protected abstract void onHeightUpdated(float expandedHeight); - - protected abstract float getOverExpansionAmount(); - - protected abstract float getOverExpansionPixels(); - - /** - * This returns the maximum height of the panel. Children should override this if their - * desired height is not the full height. - * - * @return the default implementation simply returns the maximum height. - */ - protected abstract int getMaxPanelHeight(); - - public void setExpandedFraction(float frac) { - setExpandedHeight(getMaxPanelHeight() * frac); - } - - public float getExpandedHeight() { - return mExpandedHeight; - } - - public float getExpandedFraction() { - return mExpandedFraction; - } - - public boolean isFullyExpanded() { - return mExpandedHeight >= getMaxPanelHeight(); - } - - public boolean isFullyCollapsed() { - return mExpandedFraction <= 0.0f; - } - - public boolean isCollapsing() { - return mClosing || mLaunchingNotification; - } - - public boolean isTracking() { - return mTracking; - } - - public void setBar(PanelBar panelBar) { - mBar = panelBar; - } - - public void collapse(boolean delayed, float speedUpFactor) { - if (DEBUG) logf("collapse: " + this); - if (canPanelBeCollapsed()) { - cancelHeightAnimator(); - notifyExpandingStarted(); - - // Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state. - mClosing = true; - if (delayed) { - mNextCollapseSpeedUpFactor = speedUpFactor; - postDelayed(mFlingCollapseRunnable, 120); - } else { - fling(0, false /* expand */, speedUpFactor, false /* expandBecauseOfFalsing */); - } - } - } - - public boolean canPanelBeCollapsed() { - return !isFullyCollapsed() && !mTracking && !mClosing; - } - - private final Runnable mFlingCollapseRunnable = new Runnable() { - @Override - public void run() { - fling(0, false /* expand */, mNextCollapseSpeedUpFactor, - false /* expandBecauseOfFalsing */); - } - }; - - public void cancelPeek() { - boolean cancelled = false; - if (mPeekAnimator != null) { - cancelled = true; - mPeekAnimator.cancel(); - } - - if (cancelled) { - // When peeking, we already tell mBar that we expanded ourselves. Make sure that we also - // notify mBar that we might have closed ourselves. - notifyBarPanelExpansionChanged(); - } - } - - public void expand(final boolean animate) { - if (!isFullyCollapsed() && !isCollapsing()) { - return; - } - - mInstantExpanding = true; - mAnimateAfterExpanding = animate; - mUpdateFlingOnLayout = false; - abortAnimations(); - cancelPeek(); - if (mTracking) { - onTrackingStopped(true /* expands */); // The panel is expanded after this call. - } - if (mExpanding) { - notifyExpandingFinished(); - } - notifyBarPanelExpansionChanged(); - - // Wait for window manager to pickup the change, so we know the maximum height of the panel - // then. - getViewTreeObserver().addOnGlobalLayoutListener( - new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - if (!mInstantExpanding) { - getViewTreeObserver().removeOnGlobalLayoutListener(this); - return; - } - if (mStatusBar.getStatusBarWindow().getHeight() - != mStatusBar.getStatusBarHeight()) { - getViewTreeObserver().removeOnGlobalLayoutListener(this); - if (mAnimateAfterExpanding) { - notifyExpandingStarted(); - fling(0, true /* expand */); - } else { - setExpandedFraction(1f); - } - mInstantExpanding = false; - } - } - }); - - // Make sure a layout really happens. - requestLayout(); - } - - public void instantCollapse() { - abortAnimations(); - setExpandedFraction(0f); - if (mExpanding) { - notifyExpandingFinished(); - } - if (mInstantExpanding) { - mInstantExpanding = false; - notifyBarPanelExpansionChanged(); - } - } - - private void abortAnimations() { - cancelPeek(); - cancelHeightAnimator(); - removeCallbacks(mPostCollapseRunnable); - removeCallbacks(mFlingCollapseRunnable); - } - - protected void onClosingFinished() { - mBar.onClosingFinished(); - } - - - protected void startUnlockHintAnimation() { - - // We don't need to hint the user if an animation is already running or the user is changing - // the expansion. - if (mHeightAnimator != null || mTracking) { - return; - } - cancelPeek(); - notifyExpandingStarted(); - startUnlockHintAnimationPhase1(() -> { - notifyExpandingFinished(); - onUnlockHintFinished(); - mHintAnimationRunning = false; - }); - onUnlockHintStarted(); - mHintAnimationRunning = true; - } - - protected void onUnlockHintFinished() { - mStatusBar.onHintFinished(); - } - - protected void onUnlockHintStarted() { - mStatusBar.onUnlockHintStarted(); - } - - public boolean isUnlockHintRunning() { - return mHintAnimationRunning; - } - - /** - * Phase 1: Move everything upwards. - */ - private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) { - float target = Math.max(0, getMaxPanelHeight() - mHintDistance); - ValueAnimator animator = createHeightAnimator(target); - animator.setDuration(250); - animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - animator.addListener(new AnimatorListenerAdapter() { - private boolean mCancelled; - - @Override - public void onAnimationCancel(Animator animation) { - mCancelled = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - if (mCancelled) { - setAnimator(null); - onAnimationFinished.run(); - } else { - startUnlockHintAnimationPhase2(onAnimationFinished); - } - } - }); - animator.start(); - setAnimator(animator); - - View[] viewsToAnimate = { - mKeyguardBottomArea.getIndicationArea(), - mStatusBar.getAmbientIndicationContainer()}; - for (View v : viewsToAnimate) { - if (v == null) { - continue; - } - v.animate() - .translationY(-mHintDistance) - .setDuration(250) - .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) - .withEndAction(() -> v.animate() - .translationY(0) - .setDuration(450) - .setInterpolator(mBounceInterpolator) - .start()) - .start(); - } - } - - private void setAnimator(ValueAnimator animator) { - mHeightAnimator = animator; - if (animator == null && mPanelUpdateWhenAnimatorEnds) { - mPanelUpdateWhenAnimatorEnds = false; - requestPanelHeightUpdate(); - } - } - - /** - * Phase 2: Bounce down. - */ - private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) { - ValueAnimator animator = createHeightAnimator(getMaxPanelHeight()); - animator.setDuration(450); - animator.setInterpolator(mBounceInterpolator); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - setAnimator(null); - onAnimationFinished.run(); - notifyBarPanelExpansionChanged(); - } - }); - animator.start(); - setAnimator(animator); - } - - private ValueAnimator createHeightAnimator(float targetHeight) { - ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight); - animator.addUpdateListener( - animation -> setExpandedHeightInternal((float) animation.getAnimatedValue())); - return animator; - } - - protected void notifyBarPanelExpansionChanged() { - if (mBar != null) { - mBar.panelExpansionChanged(mExpandedFraction, mExpandedFraction > 0f - || mPeekAnimator != null || mInstantExpanding - || isPanelVisibleBecauseOfHeadsUp() || mTracking || mHeightAnimator != null); - } - for (int i = 0; i < mExpansionListeners.size(); i++) { - mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking); - } - } - - public void addExpansionListener(PanelExpansionListener panelExpansionListener) { - mExpansionListeners.add(panelExpansionListener); - } - - protected abstract boolean isPanelVisibleBecauseOfHeadsUp(); - - /** - * Gets called when the user performs a click anywhere in the empty area of the panel. - * - * @return whether the panel will be expanded after the action performed by this method - */ - protected boolean onEmptySpaceClick(float x) { - if (mHintAnimationRunning) { - return true; - } - return onMiddleClicked(); - } - - protected final Runnable mPostCollapseRunnable = new Runnable() { - @Override - public void run() { - collapse(false /* delayed */, 1.0f /* speedUpFactor */); - } - }; - - protected abstract boolean onMiddleClicked(); - - protected abstract boolean isDozing(); - - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s" - + " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s touchDisabled=%s" - + "]", - this.getClass().getSimpleName(), - getExpandedHeight(), - getMaxPanelHeight(), - mClosing?"T":"f", - mTracking?"T":"f", - mJustPeeked?"T":"f", - mPeekAnimator, ((mPeekAnimator!=null && mPeekAnimator.isStarted())?" (started)":""), - mHeightAnimator, ((mHeightAnimator !=null && mHeightAnimator.isStarted())?" (started)":""), - mTouchDisabled?"T":"f" - )); - } - - public abstract void resetViews(boolean animate); - - protected abstract float getPeekHeight(); - /** - * @return whether "Clear all" button will be visible when the panel is fully expanded - */ - protected abstract boolean fullyExpandedClearAllVisible(); - - protected abstract boolean isClearAllVisible(); - - /** - * @return the height of the clear all button, in pixels - */ - protected abstract int getClearAllHeight(); - - public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { - mHeadsUpManager = headsUpManager; - } - - public void setLaunchingNotification(boolean launchingNotification) { - mLaunchingNotification = launchingNotification; + public void dispatchConfigurationChanged(Configuration newConfig) { + super.dispatchConfigurationChanged(newConfig); + mOnConfigurationChangedListener.onConfigurationChanged(newConfig); } - public void collapseWithDuration(int animationDuration) { - mFixedDuration = animationDuration; - collapse(false /* delayed */, 1.0f /* speedUpFactor */); - mFixedDuration = NO_FIXED_DURATION; + interface OnConfigurationChangedListener { + void onConfigurationChanged(Configuration newConfig); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java new file mode 100644 index 000000000000..3d8e09afea4d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -0,0 +1,1297 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.SystemClock; +import android.os.VibrationEffect; +import android.util.Log; +import android.view.InputDevice; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.animation.Interpolator; + +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.util.LatencyTracker; +import com.android.systemui.DejankUtils; +import com.android.systemui.Interpolators; +import com.android.systemui.R; +import com.android.systemui.doze.DozeLog; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.statusbar.FlingAnimationUtils; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.VibratorHelper; +import com.android.systemui.statusbar.policy.KeyguardStateController; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; + +public abstract class PanelViewController { + public static final boolean DEBUG = PanelBar.DEBUG; + public static final String TAG = PanelView.class.getSimpleName(); + private static final int INITIAL_OPENING_PEEK_DURATION = 200; + private static final int PEEK_ANIMATION_DURATION = 360; + private static final int NO_FIXED_DURATION = -1; + protected long mDownTime; + protected boolean mTouchSlopExceededBeforeDown; + private float mMinExpandHeight; + private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); + private boolean mPanelUpdateWhenAnimatorEnds; + private boolean mVibrateOnOpening; + protected boolean mLaunchingNotification; + private int mFixedDuration = NO_FIXED_DURATION; + protected ArrayList<PanelExpansionListener> mExpansionListeners = new ArrayList<>(); + + private void logf(String fmt, Object... args) { + Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args)); + } + + protected StatusBar mStatusBar; + protected HeadsUpManagerPhone mHeadsUpManager; + + private float mPeekHeight; + private float mHintDistance; + private float mInitialOffsetOnTouch; + private boolean mCollapsedAndHeadsUpOnDown; + private float mExpandedFraction = 0; + protected float mExpandedHeight = 0; + private boolean mPanelClosedOnDown; + private boolean mHasLayoutedSinceDown; + private float mUpdateFlingVelocity; + private boolean mUpdateFlingOnLayout; + private boolean mPeekTouching; + private boolean mJustPeeked; + private boolean mClosing; + protected boolean mTracking; + private boolean mTouchSlopExceeded; + private int mTrackingPointer; + protected int mTouchSlop; + protected boolean mHintAnimationRunning; + private boolean mOverExpandedBeforeFling; + private boolean mTouchAboveFalsingThreshold; + private int mUnlockFalsingThreshold; + private boolean mTouchStartedInEmptyArea; + private boolean mMotionAborted; + private boolean mUpwardsWhenThresholdReached; + private boolean mAnimatingOnDown; + + private ValueAnimator mHeightAnimator; + private ObjectAnimator mPeekAnimator; + private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); + private FlingAnimationUtils mFlingAnimationUtils; + private FlingAnimationUtils mFlingAnimationUtilsClosing; + private FlingAnimationUtils mFlingAnimationUtilsDismissing; + private final LatencyTracker mLatencyTracker; + private final FalsingManager mFalsingManager; + private final DozeLog mDozeLog; + private final VibratorHelper mVibratorHelper; + + /** + * Whether an instant expand request is currently pending and we are just waiting for layout. + */ + private boolean mInstantExpanding; + private boolean mAnimateAfterExpanding; + + PanelBar mBar; + + private String mViewName; + private float mInitialTouchY; + private float mInitialTouchX; + private boolean mTouchDisabled; + + /** + * Whether or not the PanelView can be expanded or collapsed with a drag. + */ + private boolean mNotificationsDragEnabled; + + private Interpolator mBounceInterpolator; + protected KeyguardBottomAreaView mKeyguardBottomArea; + + /** + * Speed-up factor to be used when {@link #mFlingCollapseRunnable} runs the next time. + */ + private float mNextCollapseSpeedUpFactor = 1.0f; + + protected boolean mExpanding; + private boolean mGestureWaitForTouchSlop; + private boolean mIgnoreXTouchSlop; + private boolean mExpandLatencyTracking; + private final PanelView mView; + protected final Resources mResources; + protected final KeyguardStateController mKeyguardStateController; + protected final SysuiStatusBarStateController mStatusBarStateController; + + protected void onExpandingFinished() { + mBar.onExpandingFinished(); + } + + protected void onExpandingStarted() { + } + + private void notifyExpandingStarted() { + if (!mExpanding) { + mExpanding = true; + onExpandingStarted(); + } + } + + protected final void notifyExpandingFinished() { + endClosing(); + if (mExpanding) { + mExpanding = false; + onExpandingFinished(); + } + } + + private void runPeekAnimation(long duration, float peekHeight, boolean collapseWhenFinished) { + mPeekHeight = peekHeight; + if (DEBUG) logf("peek to height=%.1f", mPeekHeight); + if (mHeightAnimator != null) { + return; + } + if (mPeekAnimator != null) { + mPeekAnimator.cancel(); + } + mPeekAnimator = ObjectAnimator.ofFloat(this, "expandedHeight", mPeekHeight).setDuration( + duration); + mPeekAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + mPeekAnimator.addListener(new AnimatorListenerAdapter() { + private boolean mCancelled; + + @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + mPeekAnimator = null; + if (!mCancelled && collapseWhenFinished) { + mView.postOnAnimation(mPostCollapseRunnable); + } + + } + }); + notifyExpandingStarted(); + mPeekAnimator.start(); + mJustPeeked = true; + } + + public PanelViewController(PanelView view, + FalsingManager falsingManager, DozeLog dozeLog, + KeyguardStateController keyguardStateController, + SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper, + LatencyTracker latencyTracker, FlingAnimationUtils.Builder flingAnimationUtilsBuilder) { + mView = view; + mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + mViewName = mResources.getResourceName(mView.getId()); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }); + + mView.addOnLayoutChangeListener(createLayoutChangeListener()); + mView.setOnTouchListener(createTouchHandler()); + mView.setOnConfigurationChangedListener(createOnConfigurationChangedListener()); + + mResources = mView.getResources(); + mKeyguardStateController = keyguardStateController; + mStatusBarStateController = statusBarStateController; + mFlingAnimationUtils = flingAnimationUtilsBuilder + .reset() + .setMaxLengthSeconds(0.6f) + .setSpeedUpFactor(0.6f) + .build(); + mFlingAnimationUtilsClosing = flingAnimationUtilsBuilder + .reset() + .setMaxLengthSeconds(0.5f) + .setSpeedUpFactor(0.6f) + .build(); + mFlingAnimationUtilsDismissing = flingAnimationUtilsBuilder + .reset() + .setMaxLengthSeconds(0.5f) + .setSpeedUpFactor(0.6f) + .setX2(0.6f) + .setY2(0.84f) + .build(); + mLatencyTracker = latencyTracker; + mBounceInterpolator = new BounceInterpolator(); + mFalsingManager = falsingManager; + mDozeLog = dozeLog; + mNotificationsDragEnabled = mResources.getBoolean( + R.bool.config_enableNotificationShadeDrag); + mVibratorHelper = vibratorHelper; + mVibrateOnOpening = mResources.getBoolean(R.bool.config_vibrateOnIconAnimation); + } + + protected void loadDimens() { + final ViewConfiguration configuration = ViewConfiguration.get(mView.getContext()); + mTouchSlop = configuration.getScaledTouchSlop(); + mHintDistance = mResources.getDimension(R.dimen.hint_move_distance); + mUnlockFalsingThreshold = mResources.getDimensionPixelSize( + R.dimen.unlock_falsing_threshold); + } + + private void addMovement(MotionEvent event) { + // Add movement to velocity tracker using raw screen X and Y coordinates instead + // of window coordinates because the window frame may be moving at the same time. + float deltaX = event.getRawX() - event.getX(); + float deltaY = event.getRawY() - event.getY(); + event.offsetLocation(deltaX, deltaY); + mVelocityTracker.addMovement(event); + event.offsetLocation(-deltaX, -deltaY); + } + + public void setTouchAndAnimationDisabled(boolean disabled) { + mTouchDisabled = disabled; + if (mTouchDisabled) { + cancelHeightAnimator(); + if (mTracking) { + onTrackingStopped(true /* expanded */); + } + notifyExpandingFinished(); + } + } + + public void startExpandLatencyTracking() { + if (mLatencyTracker.isEnabled()) { + mLatencyTracker.onActionStart(LatencyTracker.ACTION_EXPAND_PANEL); + mExpandLatencyTracking = true; + } + } + + private void startOpening(MotionEvent event) { + runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(), + false /* collapseWhenFinished */); + notifyBarPanelExpansionChanged(); + maybeVibrateOnOpening(); + + //TODO: keyguard opens QS a different way; log that too? + + // Log the position of the swipe that opened the panel + float width = mStatusBar.getDisplayWidth(); + float height = mStatusBar.getDisplayHeight(); + int rot = mStatusBar.getRotation(); + + mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND, + (int) (event.getX() / width * 100), (int) (event.getY() / height * 100), rot); + } + + protected void maybeVibrateOnOpening() { + if (mVibrateOnOpening) { + mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); + } + } + + protected abstract float getOpeningHeight(); + + /** + * @return whether the swiping direction is upwards and above a 45 degree angle compared to the + * horizontal direction + */ + private boolean isDirectionUpwards(float x, float y) { + float xDiff = x - mInitialTouchX; + float yDiff = y - mInitialTouchY; + if (yDiff >= 0) { + return false; + } + return Math.abs(yDiff) >= Math.abs(xDiff); + } + + protected void startExpandingFromPeek() { + mStatusBar.handlePeekToExpandTransistion(); + } + + protected void startExpandMotion(float newX, float newY, boolean startTracking, + float expandedHeight) { + mInitialOffsetOnTouch = expandedHeight; + mInitialTouchY = newY; + mInitialTouchX = newX; + if (startTracking) { + mTouchSlopExceeded = true; + setExpandedHeight(mInitialOffsetOnTouch); + onTrackingStarted(); + } + } + + private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) { + mTrackingPointer = -1; + if ((mTracking && mTouchSlopExceeded) || Math.abs(x - mInitialTouchX) > mTouchSlop + || Math.abs(y - mInitialTouchY) > mTouchSlop + || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) { + mVelocityTracker.computeCurrentVelocity(1000); + float vel = mVelocityTracker.getYVelocity(); + float vectorVel = (float) Math.hypot( + mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); + + boolean expand = flingExpands(vel, vectorVel, x, y) + || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel; + mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold, + mStatusBar.isFalsingThresholdNeeded(), mStatusBar.isWakeUpComingFromTouch()); + // Log collapse gesture if on lock screen. + if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { + float displayDensity = mStatusBar.getDisplayDensity(); + int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity); + int velocityDp = (int) Math.abs(vel / displayDensity); + mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp); + } + fling(vel, expand, isFalseTouch(x, y)); + onTrackingStopped(expand); + mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown; + if (mUpdateFlingOnLayout) { + mUpdateFlingVelocity = vel; + } + } else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking + && !mStatusBar.isBouncerShowing() + && !mKeyguardStateController.isKeyguardFadingAway()) { + long timePassed = SystemClock.uptimeMillis() - mDownTime; + if (timePassed < ViewConfiguration.getLongPressTimeout()) { + // Lets show the user that he can actually expand the panel + runPeekAnimation( + PEEK_ANIMATION_DURATION, getPeekHeight(), true /* collapseWhenFinished */); + } else { + // We need to collapse the panel since we peeked to the small height. + mView.postOnAnimation(mPostCollapseRunnable); + } + } else if (!mStatusBar.isBouncerShowing()) { + boolean expands = onEmptySpaceClick(mInitialTouchX); + onTrackingStopped(expands); + } + + mVelocityTracker.clear(); + mPeekTouching = false; + } + + protected float getCurrentExpandVelocity() { + mVelocityTracker.computeCurrentVelocity(1000); + return mVelocityTracker.getYVelocity(); + } + + private int getFalsingThreshold() { + float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; + return (int) (mUnlockFalsingThreshold * factor); + } + + protected abstract boolean shouldGestureWaitForTouchSlop(); + + protected abstract boolean shouldGestureIgnoreXTouchSlop(float x, float y); + + protected void onTrackingStopped(boolean expand) { + mTracking = false; + mBar.onTrackingStopped(expand); + notifyBarPanelExpansionChanged(); + } + + protected void onTrackingStarted() { + endClosing(); + mTracking = true; + mBar.onTrackingStarted(); + notifyExpandingStarted(); + notifyBarPanelExpansionChanged(); + } + + /** + * @return Whether a pair of coordinates are inside the visible view content bounds. + */ + protected abstract boolean isInContentBounds(float x, float y); + + protected void cancelHeightAnimator() { + if (mHeightAnimator != null) { + if (mHeightAnimator.isRunning()) { + mPanelUpdateWhenAnimatorEnds = false; + } + mHeightAnimator.cancel(); + } + endClosing(); + } + + private void endClosing() { + if (mClosing) { + mClosing = false; + onClosingFinished(); + } + } + + protected boolean isScrolledToBottom() { + return true; + } + + protected float getContentHeight() { + return mExpandedHeight; + } + + /** + * @param vel the current vertical velocity of the motion + * @param vectorVel the length of the vectorial velocity + * @return whether a fling should expands the panel; contracts otherwise + */ + protected boolean flingExpands(float vel, float vectorVel, float x, float y) { + if (mFalsingManager.isUnlockingDisabled()) { + return true; + } + + if (isFalseTouch(x, y)) { + return true; + } + if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { + return shouldExpandWhenNotFlinging(); + } else { + return vel > 0; + } + } + + protected boolean shouldExpandWhenNotFlinging() { + return getExpandedFraction() > 0.5f; + } + + /** + * @param x the final x-coordinate when the finger was lifted + * @param y the final y-coordinate when the finger was lifted + * @return whether this motion should be regarded as a false touch + */ + private boolean isFalseTouch(float x, float y) { + if (!mStatusBar.isFalsingThresholdNeeded()) { + return false; + } + if (mFalsingManager.isClassifierEnabled()) { + return mFalsingManager.isFalseTouch(); + } + if (!mTouchAboveFalsingThreshold) { + return true; + } + if (mUpwardsWhenThresholdReached) { + return false; + } + return !isDirectionUpwards(x, y); + } + + protected void fling(float vel, boolean expand) { + fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, false); + } + + protected void fling(float vel, boolean expand, boolean expandBecauseOfFalsing) { + fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, expandBecauseOfFalsing); + } + + protected void fling(float vel, boolean expand, float collapseSpeedUpFactor, + boolean expandBecauseOfFalsing) { + cancelPeek(); + float target = expand ? getMaxPanelHeight() : 0; + if (!expand) { + mClosing = true; + } + flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing); + } + + protected void flingToHeight(float vel, boolean expand, float target, + float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) { + // Hack to make the expand transition look nice when clear all button is visible - we make + // the animation only to the last notification, and then jump to the maximum panel height so + // clear all just fades in and the decelerating motion is towards the last notification. + final boolean + clearAllExpandHack = + expand && fullyExpandedClearAllVisible() + && mExpandedHeight < getMaxPanelHeight() - getClearAllHeight() + && !isClearAllVisible(); + if (clearAllExpandHack) { + target = getMaxPanelHeight() - getClearAllHeight(); + } + if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) { + notifyExpandingFinished(); + return; + } + mOverExpandedBeforeFling = getOverExpansionAmount() > 0f; + ValueAnimator animator = createHeightAnimator(target); + if (expand) { + if (expandBecauseOfFalsing && vel < 0) { + vel = 0; + } + mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, mView.getHeight()); + if (vel == 0) { + animator.setDuration(350); + } + } else { + if (shouldUseDismissingAnimation()) { + if (vel == 0) { + animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED); + long duration = (long) (200 + mExpandedHeight / mView.getHeight() * 100); + animator.setDuration(duration); + } else { + mFlingAnimationUtilsDismissing.apply(animator, mExpandedHeight, target, vel, + mView.getHeight()); + } + } else { + mFlingAnimationUtilsClosing.apply( + animator, mExpandedHeight, target, vel, mView.getHeight()); + } + + // Make it shorter if we run a canned animation + if (vel == 0) { + animator.setDuration((long) (animator.getDuration() / collapseSpeedUpFactor)); + } + if (mFixedDuration != NO_FIXED_DURATION) { + animator.setDuration(mFixedDuration); + } + } + animator.addListener(new AnimatorListenerAdapter() { + private boolean mCancelled; + + @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (clearAllExpandHack && !mCancelled) { + setExpandedHeightInternal(getMaxPanelHeight()); + } + setAnimator(null); + if (!mCancelled) { + notifyExpandingFinished(); + } + notifyBarPanelExpansionChanged(); + } + }); + setAnimator(animator); + animator.start(); + } + + protected abstract boolean shouldUseDismissingAnimation(); + + public String getName() { + return mViewName; + } + + public void setExpandedHeight(float height) { + if (DEBUG) logf("setExpandedHeight(%.1f)", height); + setExpandedHeightInternal(height + getOverExpansionPixels()); + } + + protected void requestPanelHeightUpdate() { + float currentMaxPanelHeight = getMaxPanelHeight(); + + if (isFullyCollapsed()) { + return; + } + + if (currentMaxPanelHeight == mExpandedHeight) { + return; + } + + if (mPeekAnimator != null || mPeekTouching) { + return; + } + + if (mTracking && !isTrackingBlocked()) { + return; + } + + if (mHeightAnimator != null) { + mPanelUpdateWhenAnimatorEnds = true; + return; + } + + setExpandedHeight(currentMaxPanelHeight); + } + + public void setExpandedHeightInternal(float h) { + if (mExpandLatencyTracking && h != 0f) { + DejankUtils.postAfterTraversal( + () -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL)); + mExpandLatencyTracking = false; + } + float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount(); + if (mHeightAnimator == null) { + float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion); + if (getOverExpansionPixels() != overExpansionPixels && mTracking) { + setOverExpansion(overExpansionPixels, true /* isPixels */); + } + mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount(); + } else { + mExpandedHeight = h; + if (mOverExpandedBeforeFling) { + setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */); + } + } + + // If we are closing the panel and we are almost there due to a slow decelerating + // interpolator, abort the animation. + if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) { + mExpandedHeight = 0f; + if (mHeightAnimator != null) { + mHeightAnimator.end(); + } + } + mExpandedFraction = Math.min(1f, + fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion); + onHeightUpdated(mExpandedHeight); + notifyBarPanelExpansionChanged(); + } + + /** + * @return true if the panel tracking should be temporarily blocked; this is used when a + * conflicting gesture (opening QS) is happening + */ + protected abstract boolean isTrackingBlocked(); + + protected abstract void setOverExpansion(float overExpansion, boolean isPixels); + + protected abstract void onHeightUpdated(float expandedHeight); + + protected abstract float getOverExpansionAmount(); + + protected abstract float getOverExpansionPixels(); + + /** + * This returns the maximum height of the panel. Children should override this if their + * desired height is not the full height. + * + * @return the default implementation simply returns the maximum height. + */ + protected abstract int getMaxPanelHeight(); + + public void setExpandedFraction(float frac) { + setExpandedHeight(getMaxPanelHeight() * frac); + } + + public float getExpandedHeight() { + return mExpandedHeight; + } + + public float getExpandedFraction() { + return mExpandedFraction; + } + + public boolean isFullyExpanded() { + return mExpandedHeight >= getMaxPanelHeight(); + } + + public boolean isFullyCollapsed() { + return mExpandedFraction <= 0.0f; + } + + public boolean isCollapsing() { + return mClosing || mLaunchingNotification; + } + + public boolean isTracking() { + return mTracking; + } + + public void setBar(PanelBar panelBar) { + mBar = panelBar; + } + + public void collapse(boolean delayed, float speedUpFactor) { + if (DEBUG) logf("collapse: " + this); + if (canPanelBeCollapsed()) { + cancelHeightAnimator(); + notifyExpandingStarted(); + + // Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state. + mClosing = true; + if (delayed) { + mNextCollapseSpeedUpFactor = speedUpFactor; + mView.postDelayed(mFlingCollapseRunnable, 120); + } else { + fling(0, false /* expand */, speedUpFactor, false /* expandBecauseOfFalsing */); + } + } + } + + public boolean canPanelBeCollapsed() { + return !isFullyCollapsed() && !mTracking && !mClosing; + } + + private final Runnable mFlingCollapseRunnable = new Runnable() { + @Override + public void run() { + fling(0, false /* expand */, mNextCollapseSpeedUpFactor, + false /* expandBecauseOfFalsing */); + } + }; + + public void cancelPeek() { + boolean cancelled = false; + if (mPeekAnimator != null) { + cancelled = true; + mPeekAnimator.cancel(); + } + + if (cancelled) { + // When peeking, we already tell mBar that we expanded ourselves. Make sure that we also + // notify mBar that we might have closed ourselves. + notifyBarPanelExpansionChanged(); + } + } + + public void expand(final boolean animate) { + if (!isFullyCollapsed() && !isCollapsing()) { + return; + } + + mInstantExpanding = true; + mAnimateAfterExpanding = animate; + mUpdateFlingOnLayout = false; + abortAnimations(); + cancelPeek(); + if (mTracking) { + onTrackingStopped(true /* expands */); // The panel is expanded after this call. + } + if (mExpanding) { + notifyExpandingFinished(); + } + notifyBarPanelExpansionChanged(); + + // Wait for window manager to pickup the change, so we know the maximum height of the panel + // then. + mView.getViewTreeObserver().addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (!mInstantExpanding) { + mView.getViewTreeObserver().removeOnGlobalLayoutListener(this); + return; + } + if (mStatusBar.getStatusBarWindow().getHeight() + != mStatusBar.getStatusBarHeight()) { + mView.getViewTreeObserver().removeOnGlobalLayoutListener(this); + if (mAnimateAfterExpanding) { + notifyExpandingStarted(); + fling(0, true /* expand */); + } else { + setExpandedFraction(1f); + } + mInstantExpanding = false; + } + } + }); + + // Make sure a layout really happens. + mView.requestLayout(); + } + + public void instantCollapse() { + abortAnimations(); + setExpandedFraction(0f); + if (mExpanding) { + notifyExpandingFinished(); + } + if (mInstantExpanding) { + mInstantExpanding = false; + notifyBarPanelExpansionChanged(); + } + } + + private void abortAnimations() { + cancelPeek(); + cancelHeightAnimator(); + mView.removeCallbacks(mPostCollapseRunnable); + mView.removeCallbacks(mFlingCollapseRunnable); + } + + protected void onClosingFinished() { + mBar.onClosingFinished(); + } + + + protected void startUnlockHintAnimation() { + + // We don't need to hint the user if an animation is already running or the user is changing + // the expansion. + if (mHeightAnimator != null || mTracking) { + return; + } + cancelPeek(); + notifyExpandingStarted(); + startUnlockHintAnimationPhase1(() -> { + notifyExpandingFinished(); + onUnlockHintFinished(); + mHintAnimationRunning = false; + }); + onUnlockHintStarted(); + mHintAnimationRunning = true; + } + + protected void onUnlockHintFinished() { + mStatusBar.onHintFinished(); + } + + protected void onUnlockHintStarted() { + mStatusBar.onUnlockHintStarted(); + } + + public boolean isUnlockHintRunning() { + return mHintAnimationRunning; + } + + /** + * Phase 1: Move everything upwards. + */ + private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) { + float target = Math.max(0, getMaxPanelHeight() - mHintDistance); + ValueAnimator animator = createHeightAnimator(target); + animator.setDuration(250); + animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + animator.addListener(new AnimatorListenerAdapter() { + private boolean mCancelled; + + @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (mCancelled) { + setAnimator(null); + onAnimationFinished.run(); + } else { + startUnlockHintAnimationPhase2(onAnimationFinished); + } + } + }); + animator.start(); + setAnimator(animator); + + View[] viewsToAnimate = { + mKeyguardBottomArea.getIndicationArea(), + mStatusBar.getAmbientIndicationContainer()}; + for (View v : viewsToAnimate) { + if (v == null) { + continue; + } + v.animate().translationY(-mHintDistance).setDuration(250).setInterpolator( + Interpolators.FAST_OUT_SLOW_IN).withEndAction(() -> v.animate().translationY( + 0).setDuration(450).setInterpolator(mBounceInterpolator).start()).start(); + } + } + + private void setAnimator(ValueAnimator animator) { + mHeightAnimator = animator; + if (animator == null && mPanelUpdateWhenAnimatorEnds) { + mPanelUpdateWhenAnimatorEnds = false; + requestPanelHeightUpdate(); + } + } + + /** + * Phase 2: Bounce down. + */ + private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) { + ValueAnimator animator = createHeightAnimator(getMaxPanelHeight()); + animator.setDuration(450); + animator.setInterpolator(mBounceInterpolator); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + setAnimator(null); + onAnimationFinished.run(); + notifyBarPanelExpansionChanged(); + } + }); + animator.start(); + setAnimator(animator); + } + + private ValueAnimator createHeightAnimator(float targetHeight) { + ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight); + animator.addUpdateListener( + animation -> setExpandedHeightInternal((float) animation.getAnimatedValue())); + return animator; + } + + protected void notifyBarPanelExpansionChanged() { + if (mBar != null) { + mBar.panelExpansionChanged( + mExpandedFraction, + mExpandedFraction > 0f || mPeekAnimator != null || mInstantExpanding + || isPanelVisibleBecauseOfHeadsUp() || mTracking + || mHeightAnimator != null); + } + for (int i = 0; i < mExpansionListeners.size(); i++) { + mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking); + } + } + + public void addExpansionListener(PanelExpansionListener panelExpansionListener) { + mExpansionListeners.add(panelExpansionListener); + } + + protected abstract boolean isPanelVisibleBecauseOfHeadsUp(); + + /** + * Gets called when the user performs a click anywhere in the empty area of the panel. + * + * @return whether the panel will be expanded after the action performed by this method + */ + protected boolean onEmptySpaceClick(float x) { + if (mHintAnimationRunning) { + return true; + } + return onMiddleClicked(); + } + + protected final Runnable mPostCollapseRunnable = new Runnable() { + @Override + public void run() { + collapse(false /* delayed */, 1.0f /* speedUpFactor */); + } + }; + + protected abstract boolean onMiddleClicked(); + + protected abstract boolean isDozing(); + + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s" + + " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s " + + "touchDisabled=%s" + "]", + this.getClass().getSimpleName(), getExpandedHeight(), getMaxPanelHeight(), + mClosing ? "T" : "f", mTracking ? "T" : "f", mJustPeeked ? "T" : "f", mPeekAnimator, + ((mPeekAnimator != null && mPeekAnimator.isStarted()) ? " (started)" : ""), + mHeightAnimator, + ((mHeightAnimator != null && mHeightAnimator.isStarted()) ? " (started)" : ""), + mTouchDisabled ? "T" : "f")); + } + + public abstract void resetViews(boolean animate); + + protected abstract float getPeekHeight(); + + /** + * @return whether "Clear all" button will be visible when the panel is fully expanded + */ + protected abstract boolean fullyExpandedClearAllVisible(); + + protected abstract boolean isClearAllVisible(); + + /** + * @return the height of the clear all button, in pixels + */ + protected abstract int getClearAllHeight(); + + public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { + mHeadsUpManager = headsUpManager; + } + + public void setLaunchingNotification(boolean launchingNotification) { + mLaunchingNotification = launchingNotification; + } + + public void collapseWithDuration(int animationDuration) { + mFixedDuration = animationDuration; + collapse(false /* delayed */, 1.0f /* speedUpFactor */); + mFixedDuration = NO_FIXED_DURATION; + } + + public ViewGroup getView() { + // TODO: remove this method, or at least reduce references to it. + return mView; + } + + public boolean isEnabled() { + return mView.isEnabled(); + } + + public OnLayoutChangeListener createLayoutChangeListener() { + return new OnLayoutChangeListener(); + } + + protected TouchHandler createTouchHandler() { + return new TouchHandler(); + } + + protected OnConfigurationChangedListener createOnConfigurationChangedListener() { + return new OnConfigurationChangedListener(); + } + + public class TouchHandler implements View.OnTouchListener { + public boolean onInterceptTouchEvent(MotionEvent event) { + if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled || (mMotionAborted + && event.getActionMasked() != MotionEvent.ACTION_DOWN)) { + return false; + } + + /* + * If the user drags anywhere inside the panel we intercept it if the movement is + * upwards. This allows closing the shade from anywhere inside the panel. + * + * We only do this if the current content is scrolled to the bottom, + * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling + * gesture + * possible. + */ + int pointerIndex = event.findPointerIndex(mTrackingPointer); + if (pointerIndex < 0) { + pointerIndex = 0; + mTrackingPointer = event.getPointerId(pointerIndex); + } + final float x = event.getX(pointerIndex); + final float y = event.getY(pointerIndex); + boolean scrolledToBottom = isScrolledToBottom(); + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mStatusBar.userActivity(); + mAnimatingOnDown = mHeightAnimator != null; + mMinExpandHeight = 0.0f; + mDownTime = SystemClock.uptimeMillis(); + if (mAnimatingOnDown && mClosing && !mHintAnimationRunning + || mPeekAnimator != null) { + cancelHeightAnimator(); + cancelPeek(); + mTouchSlopExceeded = true; + return true; + } + mInitialTouchY = y; + mInitialTouchX = x; + mTouchStartedInEmptyArea = !isInContentBounds(x, y); + mTouchSlopExceeded = mTouchSlopExceededBeforeDown; + mJustPeeked = false; + mMotionAborted = false; + mPanelClosedOnDown = isFullyCollapsed(); + mCollapsedAndHeadsUpOnDown = false; + mHasLayoutedSinceDown = false; + mUpdateFlingOnLayout = false; + mTouchAboveFalsingThreshold = false; + addMovement(event); + break; + case MotionEvent.ACTION_POINTER_UP: + final int upPointer = event.getPointerId(event.getActionIndex()); + if (mTrackingPointer == upPointer) { + // gesture is ongoing, find a new pointer to track + final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; + mTrackingPointer = event.getPointerId(newIndex); + mInitialTouchX = event.getX(newIndex); + mInitialTouchY = event.getY(newIndex); + } + break; + case MotionEvent.ACTION_POINTER_DOWN: + if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { + mMotionAborted = true; + mVelocityTracker.clear(); + } + break; + case MotionEvent.ACTION_MOVE: + final float h = y - mInitialTouchY; + addMovement(event); + if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) { + float hAbs = Math.abs(h); + if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop)) + && hAbs > Math.abs(x - mInitialTouchX)) { + cancelHeightAnimator(); + startExpandMotion(x, y, true /* startTracking */, mExpandedHeight); + return true; + } + } + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + mVelocityTracker.clear(); + break; + } + return false; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (mInstantExpanding || (mTouchDisabled + && event.getActionMasked() != MotionEvent.ACTION_CANCEL) || (mMotionAborted + && event.getActionMasked() != MotionEvent.ACTION_DOWN)) { + return false; + } + + // If dragging should not expand the notifications shade, then return false. + if (!mNotificationsDragEnabled) { + if (mTracking) { + // Turn off tracking if it's on or the shade can get stuck in the down position. + onTrackingStopped(true /* expand */); + } + return false; + } + + // On expanding, single mouse click expands the panel instead of dragging. + if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) { + if (event.getAction() == MotionEvent.ACTION_UP) { + expand(true); + } + return true; + } + + /* + * We capture touch events here and update the expand height here in case according to + * the users fingers. This also handles multi-touch. + * + * If the user just clicks shortly, we show a quick peek of the shade. + * + * Flinging is also enabled in order to open or close the shade. + */ + + int pointerIndex = event.findPointerIndex(mTrackingPointer); + if (pointerIndex < 0) { + pointerIndex = 0; + mTrackingPointer = event.getPointerId(pointerIndex); + } + final float x = event.getX(pointerIndex); + final float y = event.getY(pointerIndex); + + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop(); + mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y); + } + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + startExpandMotion(x, y, false /* startTracking */, mExpandedHeight); + mJustPeeked = false; + mMinExpandHeight = 0.0f; + mPanelClosedOnDown = isFullyCollapsed(); + mHasLayoutedSinceDown = false; + mUpdateFlingOnLayout = false; + mMotionAborted = false; + mPeekTouching = mPanelClosedOnDown; + mDownTime = SystemClock.uptimeMillis(); + mTouchAboveFalsingThreshold = false; + mCollapsedAndHeadsUpOnDown = + isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp(); + addMovement(event); + if (!mGestureWaitForTouchSlop || (mHeightAnimator != null + && !mHintAnimationRunning) || mPeekAnimator != null) { + mTouchSlopExceeded = + (mHeightAnimator != null && !mHintAnimationRunning) + || mPeekAnimator != null || mTouchSlopExceededBeforeDown; + cancelHeightAnimator(); + cancelPeek(); + onTrackingStarted(); + } + if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp() + && !mStatusBar.isBouncerShowing()) { + startOpening(event); + } + break; + + case MotionEvent.ACTION_POINTER_UP: + final int upPointer = event.getPointerId(event.getActionIndex()); + if (mTrackingPointer == upPointer) { + // gesture is ongoing, find a new pointer to track + final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; + final float newY = event.getY(newIndex); + final float newX = event.getX(newIndex); + mTrackingPointer = event.getPointerId(newIndex); + startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight); + } + break; + case MotionEvent.ACTION_POINTER_DOWN: + if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { + mMotionAborted = true; + endMotionEvent(event, x, y, true /* forceCancel */); + return false; + } + break; + case MotionEvent.ACTION_MOVE: + addMovement(event); + float h = y - mInitialTouchY; + + // If the panel was collapsed when touching, we only need to check for the + // y-component of the gesture, as we have no conflicting horizontal gesture. + if (Math.abs(h) > mTouchSlop && (Math.abs(h) > Math.abs(x - mInitialTouchX) + || mIgnoreXTouchSlop)) { + mTouchSlopExceeded = true; + if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) { + if (!mJustPeeked && mInitialOffsetOnTouch != 0f) { + startExpandMotion(x, y, false /* startTracking */, mExpandedHeight); + h = 0; + } + cancelHeightAnimator(); + onTrackingStarted(); + } + } + float newHeight = Math.max(0, h + mInitialOffsetOnTouch); + if (newHeight > mPeekHeight) { + if (mPeekAnimator != null) { + mPeekAnimator.cancel(); + } + mJustPeeked = false; + } else if (mPeekAnimator == null && mJustPeeked) { + // The initial peek has finished, but we haven't dragged as far yet, lets + // speed it up by starting at the peek height. + mInitialOffsetOnTouch = mExpandedHeight; + mInitialTouchY = y; + mMinExpandHeight = mExpandedHeight; + mJustPeeked = false; + } + newHeight = Math.max(newHeight, mMinExpandHeight); + if (-h >= getFalsingThreshold()) { + mTouchAboveFalsingThreshold = true; + mUpwardsWhenThresholdReached = isDirectionUpwards(x, y); + } + if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking) + && !isTrackingBlocked()) { + setExpandedHeightInternal(newHeight); + } + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + addMovement(event); + endMotionEvent(event, x, y, false /* forceCancel */); + break; + } + return !mGestureWaitForTouchSlop || mTracking; + } + } + + public class OnLayoutChangeListener implements View.OnLayoutChangeListener { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, + int oldTop, int oldRight, int oldBottom) { + mStatusBar.onPanelLaidOut(); + requestPanelHeightUpdate(); + mHasLayoutedSinceDown = true; + if (mUpdateFlingOnLayout) { + abortAnimations(); + fling(mUpdateFlingVelocity, true /* expands */); + mUpdateFlingOnLayout = false; + } + } + } + + public class OnConfigurationChangedListener implements + PanelView.OnConfigurationChangedListener { + @Override + public void onConfigurationChanged(Configuration newConfig) { + loadDimens(); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 312ca26c1bc4..45f3bf986141 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -236,7 +236,7 @@ public class PhoneStatusBarView extends PanelBar { public void onPanelFullyOpened() { super.onPanelFullyOpened(); if (!mIsFullyOpenedPanel) { - mPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + mPanel.getView().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); } mIsFullyOpenedPanel = true; maybeShowDivider(!mBar.mPanelExpanded); @@ -420,7 +420,8 @@ public class PhoneStatusBarView extends PanelBar { void maybeShowDivider(boolean showDivider) { int state = - showDivider && NotificationPanelView.isQsSplitEnabled() ? View.VISIBLE : View.GONE; + showDivider && NotificationPanelViewController.isQsSplitEnabled() + ? View.VISIBLE : View.GONE; mDividerContainer.setVisibility(state); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java index 57e70141bfcf..866dc2d51241 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java @@ -79,7 +79,7 @@ public class ShadeControllerImpl implements ShadeController { public void instantExpandNotificationsPanel() { // Make our window larger and the panel expanded. getStatusBar().makeExpandedVisible(true /* force */); - getNotificationPanelView().expand(false /* animate */); + getNotificationPanelViewController().expand(false /* animate */); mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */); } @@ -123,8 +123,9 @@ public class ShadeControllerImpl implements ShadeController { // TODO(b/62444020): remove when this bug is fixed Log.v(TAG, "mStatusBarWindow: " + getStatusBarWindowView() + " canPanelBeCollapsed(): " - + getNotificationPanelView().canPanelBeCollapsed()); - if (getStatusBarWindowView() != null && getNotificationPanelView().canPanelBeCollapsed()) { + + getNotificationPanelViewController().canPanelBeCollapsed()); + if (getStatusBarWindowView() != null + && getNotificationPanelViewController().canPanelBeCollapsed()) { // release focus immediately to kick off focus change transition mStatusBarWindowController.setStatusBarFocusable(false); @@ -138,7 +139,7 @@ public class ShadeControllerImpl implements ShadeController { @Override public boolean closeShadeIfOpen() { - if (!getNotificationPanelView().isFullyCollapsed()) { + if (!getNotificationPanelViewController().isFullyCollapsed()) { mCommandQueue.animateCollapsePanels( CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); getStatusBar().visibilityChanged(false); @@ -149,15 +150,14 @@ public class ShadeControllerImpl implements ShadeController { @Override public void postOnShadeExpanded(Runnable executable) { - getNotificationPanelView().getViewTreeObserver().addOnGlobalLayoutListener( + getNotificationPanelViewController().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (getStatusBar().getStatusBarWindow().getHeight() != getStatusBar().getStatusBarHeight()) { - getNotificationPanelView().getViewTreeObserver() - .removeOnGlobalLayoutListener(this); - getNotificationPanelView().post(executable); + getNotificationPanelViewController().removeOnGlobalLayoutListener(this); + getNotificationPanelViewController().getView().post(executable); } } }); @@ -187,7 +187,7 @@ public class ShadeControllerImpl implements ShadeController { @Override public boolean collapsePanel() { - if (!getNotificationPanelView().isFullyCollapsed()) { + if (!getNotificationPanelViewController().isFullyCollapsed()) { // close the shade if it was open animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */, true /* delayed */); @@ -230,7 +230,7 @@ public class ShadeControllerImpl implements ShadeController { return (PhoneStatusBarView) getStatusBar().getStatusBarView(); } - private NotificationPanelView getNotificationPanelView() { - return getStatusBar().getPanel(); + private NotificationPanelViewController getNotificationPanelViewController() { + return getStatusBar().getPanelController(); } } 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 c5d7da4c618e..ccc86b1f8c5f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -131,7 +131,6 @@ import com.android.systemui.DemoMode; import com.android.systemui.Dumpable; import com.android.systemui.EventLogTags; import com.android.systemui.InitController; -import com.android.systemui.Interpolators; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.SystemUI; @@ -199,7 +198,6 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationListController; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; @@ -208,6 +206,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; import com.android.systemui.statusbar.policy.BrightnessMirrorController; @@ -392,7 +391,8 @@ public class StatusBar extends SystemUI implements DemoMode, private final DismissCallbackRegistry mDismissCallbackRegistry; // expanded notifications - protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window + // the sliding/resizing panel within the notification window + protected NotificationPanelViewController mNotificationPanelViewController; // settings private QSPanel mQSPanel; @@ -461,8 +461,8 @@ public class StatusBar extends SystemUI implements DemoMode, mUserSetup = userSetup; if (!mUserSetup && mStatusBarView != null) animateCollapseQuickSettings(); - if (mNotificationPanel != null) { - mNotificationPanel.setUserSetupComplete(mUserSetup); + if (mNotificationPanelViewController != null) { + mNotificationPanelViewController.setUserSetupComplete(mUserSetup); } updateQsExpansionEnabled(); } @@ -913,9 +913,8 @@ public class StatusBar extends SystemUI implements DemoMode, mKeyguardUpdateMonitor.registerCallback(mUpdateCallback); mDozeServiceHost.initialize(this, mNotificationIconAreaController, - mStatusBarKeyguardViewManager, - mStatusBarWindowViewController, - mNotificationPanel, mAmbientIndicationContainer); + mStatusBarKeyguardViewManager, mStatusBarWindowViewController, + mNotificationPanelViewController, mAmbientIndicationContainer); mConfigurationController.addCallback(this); @@ -985,8 +984,6 @@ public class StatusBar extends SystemUI implements DemoMode, // TODO: Deal with the ugliness that comes from having some of the statusbar broken out // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot. - mNotificationPanel = mSuperStatusBarViewFactory.getNotificationPanelView(); - mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller); NotificationListContainer notifListContainer = (NotificationListContainer) mStackScroller; mNotificationLogger.setUpWithContainer(notifListContainer); @@ -1000,8 +997,9 @@ public class StatusBar extends SystemUI implements DemoMode, mWakeUpCoordinator.setIconAreaController(mNotificationIconAreaController); inflateShelf(); mNotificationIconAreaController.setupShelf(mNotificationShelf); - mNotificationPanel.setOnReinflationListener(mNotificationIconAreaController::initAodIcons); - mNotificationPanel.addExpansionListener(mWakeUpCoordinator); + mNotificationPanelViewController.setOnReinflationListener( + mNotificationIconAreaController::initAodIcons); + mNotificationPanelViewController.addExpansionListener(mWakeUpCoordinator); mDarkIconDispatcher.addDarkReceiver(mNotificationIconAreaController); // Allow plugins to reference DarkIconDispatcher and StatusBarStateController @@ -1015,7 +1013,7 @@ public class StatusBar extends SystemUI implements DemoMode, PhoneStatusBarView oldStatusBarView = mStatusBarView; mStatusBarView = (PhoneStatusBarView) fragment.getView(); mStatusBarView.setBar(this); - mStatusBarView.setPanel(mNotificationPanel); + mStatusBarView.setPanel(mNotificationPanelViewController); mStatusBarView.setScrimController(mScrimController); // CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of @@ -1026,7 +1024,7 @@ public class StatusBar extends SystemUI implements DemoMode, // it needs to notify PhoneStatusBarView's new instance to update the correct // status by calling mNotificationPanel.notifyBarPanelExpansionChanged(). if (mHeadsUpManager.hasPinnedHeadsUp()) { - mNotificationPanel.notifyBarPanelExpansionChanged(); + mNotificationPanelViewController.notifyBarPanelExpansionChanged(); } mStatusBarView.setBouncerShowing(mBouncerShowing); if (oldStatusBarView != null) { @@ -1040,10 +1038,12 @@ public class StatusBar extends SystemUI implements DemoMode, // This view is being recreated, let's destroy the old one mHeadsUpAppearanceController.destroy(); } + // TODO: this should probably be scoped to the StatusBarComponent mHeadsUpAppearanceController = new HeadsUpAppearanceController( mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow, mStatusBarStateController, mKeyguardBypassController, - mKeyguardStateController, mWakeUpCoordinator, mCommandQueue); + mKeyguardStateController, mWakeUpCoordinator, mCommandQueue, + mNotificationPanelViewController); mHeadsUpAppearanceController.readFrom(oldController); mLightsOutNotifController.setLightsOutNotifView( @@ -1059,11 +1059,11 @@ public class StatusBar extends SystemUI implements DemoMode, mHeadsUpManager.setUp(mStatusBarWindow, mGroupManager, this, mVisualStabilityManager); mConfigurationController.addCallback(mHeadsUpManager); mHeadsUpManager.addListener(this); - mHeadsUpManager.addListener(mNotificationPanel); + mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener()); mHeadsUpManager.addListener(mGroupManager); mHeadsUpManager.addListener(mGroupAlertTransferHelper); mHeadsUpManager.addListener(mVisualStabilityManager); - mNotificationPanel.setHeadsUpManager(mHeadsUpManager); + mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); mGroupManager.setHeadsUpManager(mHeadsUpManager); mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager); mNotificationLogger.setHeadsUpManager(mHeadsUpManager); @@ -1078,7 +1078,8 @@ public class StatusBar extends SystemUI implements DemoMode, SystemUIFactory.getInstance().createKeyguardIndicationController(mContext, mStatusBarWindow.findViewById(R.id.keyguard_indication_area), mStatusBarWindow.findViewById(R.id.lock_icon)); - mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController); + mNotificationPanelViewController.setKeyguardIndicationController( + mKeyguardIndicationController); mAmbientIndicationContainer = mStatusBarWindow.findViewById( R.id.ambient_indication_container); @@ -1113,19 +1114,19 @@ public class StatusBar extends SystemUI implements DemoMode, }); mScrimController.attachViews(scrimBehind, scrimInFront, scrimForBubble); - mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf, - mHeadsUpManager, mNotificationIconAreaController, mScrimController); + mNotificationPanelViewController.initDependencies(this, mGroupManager, mNotificationShelf, + mNotificationIconAreaController, mScrimController); BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop); mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front), backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper); - mNotificationPanel.setUserSetupComplete(mUserSetup); + mNotificationPanelViewController.setUserSetupComplete(mUserSetup); if (UserManager.get(mContext).isUserSwitcherEnabled()) { createUserSwitcher(); } - mNotificationPanel.setLaunchAffordanceListener( + mNotificationPanelViewController.setLaunchAffordanceListener( mLockscreenLockIconController::onShowingLaunchAffordanceChanged); // Set up the quick settings tile panel @@ -1139,6 +1140,7 @@ public class StatusBar extends SystemUI implements DemoMode, .withDefault(this::createDefaultQSFragment) .build()); mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow, + mNotificationPanelViewController, (visible) -> { mBrightnessMirrorVisible = visible; updateScrimController(); @@ -1232,7 +1234,7 @@ public class StatusBar extends SystemUI implements DemoMode, private void setUpPresenter() { // Set up the initial notification state. mActivityLaunchAnimator = new ActivityLaunchAnimator( - mStatusBarWindowViewController, this, mNotificationPanel, + mStatusBarWindowViewController, this, mNotificationPanelViewController, (NotificationListContainer) mStackScroller); final NotificationRowBinderImpl rowBinder = @@ -1244,7 +1246,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationLogger); // TODO: inject this. - mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel, + mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController, mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController, mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController, mNotificationAlertingManager, rowBinder, mKeyguardStateController, @@ -1265,6 +1267,7 @@ public class StatusBar extends SystemUI implements DemoMode, .setStatusBar(this) .setActivityLaunchAnimator(mActivityLaunchAnimator) .setNotificationPresenter(mPresenter) + .setNotificationPanelViewController(mNotificationPanelViewController) .build(); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); @@ -1378,7 +1381,7 @@ public class StatusBar extends SystemUI implements DemoMode, } // We need the new R.id.keyguard_indication_area before recreating // mKeyguardIndicationController - mNotificationPanel.onThemeChanged(); + mNotificationPanelViewController.onThemeChanged(); onThemeChanged(); } @@ -1393,7 +1396,7 @@ public class StatusBar extends SystemUI implements DemoMode, mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext, mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mStatusBarWindow.findViewById(R.id.keyguard_header), - mNotificationPanel); + mNotificationPanelViewController); } private void inflateStatusBarWindow() { @@ -1402,16 +1405,17 @@ public class StatusBar extends SystemUI implements DemoMode, .statusBarWindowView(mStatusBarWindow).build(); mStatusBarWindowViewController = statusBarComponent.getStatusBarWindowViewController(); mStatusBarWindowViewController.setupExpandedStatusBar(); + mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController(); } protected void startKeyguard() { Trace.beginSection("StatusBar#startKeyguard"); mBiometricUnlockController = mBiometricUnlockControllerLazy.get(); mStatusBarKeyguardViewManager.registerStatusBar( - /* statusBar= */ this, getBouncerContainer(), mNotificationPanel, - mBiometricUnlockController, mDismissCallbackRegistry, - mStatusBarWindow.findViewById(R.id.lock_icon_container), mStackScroller, - mKeyguardBypassController, mFalsingManager); + /* statusBar= */ this, getBouncerContainer(), + mNotificationPanelViewController, mBiometricUnlockController, + mDismissCallbackRegistry, mStatusBarWindow.findViewById(R.id.lock_icon_container), + mStackScroller, mKeyguardBypassController, mFalsingManager); mKeyguardIndicationController .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); @@ -1488,7 +1492,7 @@ public class StatusBar extends SystemUI implements DemoMode, && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0) && !mDozing && !ONLY_CORE_APPS; - mNotificationPanel.setQsExpansionEnabled(expandEnabled); + mNotificationPanelViewController.setQsExpansionEnabled(expandEnabled); Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled); } @@ -1648,7 +1652,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void setQsExpanded(boolean expanded) { mStatusBarWindowController.setQsExpanded(expanded); - mNotificationPanel.setStatusAccessibilityImportance(expanded + mNotificationPanelViewController.setStatusAccessibilityImportance(expanded ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); if (getNavigationBarView() != null) { @@ -1682,22 +1686,22 @@ public class StatusBar extends SystemUI implements DemoMode, if (inPinnedMode) { mStatusBarWindowController.setHeadsUpShowing(true); mStatusBarWindowController.setForceStatusBarVisible(true); - if (mNotificationPanel.isFullyCollapsed()) { + if (mNotificationPanelViewController.isFullyCollapsed()) { // We need to ensure that the touchable region is updated before the window will be // resized, in order to not catch any touches. A layout will ensure that // onComputeInternalInsets will be called and after that we can resize the layout. Let's // make sure that the window stays small for one frame until the touchableRegion is set. - mNotificationPanel.requestLayout(); + mNotificationPanelViewController.getView().requestLayout(); mStatusBarWindowController.setForceWindowCollapsed(true); - mNotificationPanel.post(() -> { + mNotificationPanelViewController.getView().post(() -> { mStatusBarWindowController.setForceWindowCollapsed(false); }); } } else { boolean bypassKeyguard = mKeyguardBypassController.getBypassEnabled() && mState == StatusBarState.KEYGUARD; - if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking() - || bypassKeyguard) { + if (!mNotificationPanelViewController.isFullyCollapsed() + || mNotificationPanelViewController.isTracking() || bypassKeyguard) { // We are currently tracking or is open and the shade doesn't need to be kept // open artificially. mStatusBarWindowController.setHeadsUpShowing(false); @@ -1708,7 +1712,7 @@ public class StatusBar extends SystemUI implements DemoMode, // we need to keep the panel open artificially, let's wait until the animation // is finished. mHeadsUpManager.setHeadsUpGoingAway(true); - mNotificationPanel.runAfterAnimationFinished(() -> { + mNotificationPanelViewController.runAfterAnimationFinished(() -> { if (!mHeadsUpManager.hasPinnedHeadsUp()) { mStatusBarWindowController.setHeadsUpShowing(false); mHeadsUpManager.setHeadsUpGoingAway(false); @@ -1761,7 +1765,7 @@ public class StatusBar extends SystemUI implements DemoMode, } public boolean hideStatusBarIconsWhenExpanded() { - return mNotificationPanel.hideStatusBarIconsWhenExpanded(); + return mNotificationPanelViewController.hideStatusBarIconsWhenExpanded(); } @Override @@ -1951,19 +1955,21 @@ public class StatusBar extends SystemUI implements DemoMode, if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) { mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP); - mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */); + mNotificationPanelViewController.collapse( + false /* delayed */, 1.0f /* speedUpFactor */); } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) { mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN); - if (mNotificationPanel.isFullyCollapsed()) { + if (mNotificationPanelViewController.isFullyCollapsed()) { if (mVibrateOnOpening) { mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); } - mNotificationPanel.expand(true /* animate */); + mNotificationPanelViewController.expand(true /* animate */); ((NotificationListContainer) mStackScroller).setWillExpand(true); mHeadsUpManager.unpinAll(true /* userUnpinned */); mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1); - } else if (!mNotificationPanel.isInSettings() && !mNotificationPanel.isExpanding()){ - mNotificationPanel.flingSettings(0 /* velocity */, + } else if (!mNotificationPanelViewController.isInSettings() + && !mNotificationPanelViewController.isExpanding()) { + mNotificationPanelViewController.flingSettings(0 /* velocity */, NotificationPanelView.FLING_EXPAND); mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN_QS, 1); } @@ -2058,9 +2064,9 @@ public class StatusBar extends SystemUI implements DemoMode, } if (start) { - mNotificationPanel.startWaitingForOpenPanelGesture(); + mNotificationPanelViewController.startWaitingForOpenPanelGesture(); } else { - mNotificationPanel.stopWaitingForOpenPanelGesture(velocity); + mNotificationPanelViewController.stopWaitingForOpenPanelGesture(velocity); } } @@ -2071,7 +2077,7 @@ public class StatusBar extends SystemUI implements DemoMode, return ; } - mNotificationPanel.expandWithoutQs(); + mNotificationPanelViewController.expandWithoutQs(); if (false) postStartTracing(); } @@ -2089,7 +2095,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (subPanel != null) { mQSPanel.openDetails(subPanel); } - mNotificationPanel.expandWithQs(); + mNotificationPanelViewController.expandWithQs(); if (false) postStartTracing(); } @@ -2112,7 +2118,7 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/, 1.0f /* speedUpFactor */); - mNotificationPanel.closeQs(); + mNotificationPanelViewController.closeQs(); mExpandedVisible = false; visibilityChanged(false); @@ -2133,7 +2139,8 @@ public class StatusBar extends SystemUI implements DemoMode, Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen"); } mCommandQueue.recomputeDisableFlags( - mDisplayId, mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */); + mDisplayId, + mNotificationPanelViewController.hideStatusBarIconsWhenExpanded() /* animate */); // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in // the bouncer appear animation. @@ -2314,12 +2321,12 @@ public class StatusBar extends SystemUI implements DemoMode, batteryLevel, new WirelessChargingAnimation.Callback() { @Override public void onAnimationStarting() { - CrossFadeHelper.fadeOut(mNotificationPanel, 1); + CrossFadeHelper.fadeOut(mNotificationPanelViewController.getView(), 1); } @Override public void onAnimationEnded() { - CrossFadeHelper.fadeIn(mNotificationPanel); + CrossFadeHelper.fadeIn(mNotificationPanelViewController.getView()); } }, mDozing).show(); } else { @@ -2348,7 +2355,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Called by NavigationBarFragment void setQsScrimEnabled(boolean scrimEnabled) { - mNotificationPanel.setQsScrimEnabled(scrimEnabled); + mNotificationPanelViewController.setQsScrimEnabled(scrimEnabled); } void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState, @@ -2440,11 +2447,12 @@ public class StatusBar extends SystemUI implements DemoMode, } pw.println(" Panels: "); - if (mNotificationPanel != null) { - pw.println(" mNotificationPanel=" + - mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug("")); + if (mNotificationPanelViewController != null) { + pw.println(" mNotificationPanel=" + + mNotificationPanelViewController.getView() + " params=" + + mNotificationPanelViewController.getView().getLayoutParams().debug("")); pw.print (" "); - mNotificationPanel.dump(fd, pw, args); + mNotificationPanelViewController.dump(fd, pw, args); } pw.println(" mStackScroller: "); if (mStackScroller instanceof Dumpable) { @@ -2657,7 +2665,8 @@ public class StatusBar extends SystemUI implements DemoMode, // Do it after DismissAction has been processed to conserve the needed ordering. mHandler.post(mShadeController::runPostCollapseRunnables); } - } else if (isInLaunchTransition() && mNotificationPanel.isLaunchTransitionFinished()) { + } else if (isInLaunchTransition() + && mNotificationPanelViewController.isLaunchTransitionFinished()) { // We are not dismissing the shade, but the launch transition is already finished, // so nobody will call readyForKeyguardDone anymore. Post it such that @@ -2814,8 +2823,8 @@ public class StatusBar extends SystemUI implements DemoMode, if (mStatusBarView != null) { mStatusBarView.updateResources(); } - if (mNotificationPanel != null) { - mNotificationPanel.updateResources(); + if (mNotificationPanelViewController != null) { + mNotificationPanelViewController.updateResources(); } if (mBrightnessMirrorController != null) { mBrightnessMirrorController.updateResources(); @@ -3107,7 +3116,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void showKeyguardImpl() { mIsKeyguard = true; if (mKeyguardStateController.isLaunchTransitionFadingAway()) { - mNotificationPanel.animate().cancel(); + mNotificationPanelViewController.cancelAnimation(); onLaunchTransitionFadingEnded(); } mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); @@ -3134,8 +3143,8 @@ public class StatusBar extends SystemUI implements DemoMode, } private void onLaunchTransitionFadingEnded() { - mNotificationPanel.setAlpha(1.0f); - mNotificationPanel.onAffordanceLaunchEnded(); + mNotificationPanelViewController.setAlpha(1.0f); + mNotificationPanelViewController.onAffordanceLaunchEnded(); releaseGestureWakeLock(); runLaunchTransitionEndRunnable(); mKeyguardStateController.setLaunchTransitionFadingAway(false); @@ -3143,8 +3152,8 @@ public class StatusBar extends SystemUI implements DemoMode, } public boolean isInLaunchTransition() { - return mNotificationPanel.isLaunchTransitionRunning() - || mNotificationPanel.isLaunchTransitionFinished(); + return mNotificationPanelViewController.isLaunchTransitionRunning() + || mNotificationPanelViewController.isLaunchTransitionFinished(); } /** @@ -3165,18 +3174,15 @@ public class StatusBar extends SystemUI implements DemoMode, } updateScrimController(); mPresenter.updateMediaMetaData(false, true); - mNotificationPanel.setAlpha(1); - mNotificationPanel.animate() - .alpha(0) - .setStartDelay(FADE_KEYGUARD_START_DELAY) - .setDuration(FADE_KEYGUARD_DURATION) - .withLayer() - .withEndAction(this::onLaunchTransitionFadingEnded); + mNotificationPanelViewController.setAlpha(1); + mNotificationPanelViewController.fadeOut( + FADE_KEYGUARD_START_DELAY, FADE_KEYGUARD_DURATION, + this::onLaunchTransitionFadingEnded); mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(), LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); }; - if (mNotificationPanel.isLaunchTransitionRunning()) { - mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable); + if (mNotificationPanelViewController.isLaunchTransitionRunning()) { + mNotificationPanelViewController.setLaunchTransitionEndRunnable(hideRunnable); } else { hideRunnable.run(); } @@ -3187,22 +3193,18 @@ public class StatusBar extends SystemUI implements DemoMode, * fading. */ public void fadeKeyguardWhilePulsing() { - mNotificationPanel.animate() - .alpha(0f) - .setStartDelay(0) - .setDuration(FADE_KEYGUARD_DURATION_PULSING) - .setInterpolator(Interpolators.ALPHA_OUT) - .withEndAction(()-> { - hideKeyguard(); - mStatusBarKeyguardViewManager.onKeyguardFadedAway(); - }).start(); + mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING, + ()-> { + hideKeyguard(); + mStatusBarKeyguardViewManager.onKeyguardFadedAway(); + }).start(); } /** * Plays the animation when an activity that was occluding Keyguard goes away. */ public void animateKeyguardUnoccluding() { - mNotificationPanel.setExpandedFraction(0f); + mNotificationPanelViewController.setExpandedFraction(0f); animateExpandNotificationsPanel(); } @@ -3218,9 +3220,9 @@ public class StatusBar extends SystemUI implements DemoMode, private void onLaunchTransitionTimeout() { Log.w(TAG, "Launch transition: Timeout!"); - mNotificationPanel.onAffordanceLaunchEnded(); + mNotificationPanelViewController.onAffordanceLaunchEnded(); releaseGestureWakeLock(); - mNotificationPanel.resetViews(false /* animate */); + mNotificationPanelViewController.resetViews(false /* animate */); } private void runLaunchTransitionEndRunnable() { @@ -3253,7 +3255,7 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); } long delay = mKeyguardStateController.calculateGoingToFullShadeDelay(); - mNotificationPanel.animateToFullShade(delay); + mNotificationPanelViewController.animateToFullShade(delay); if (mDraggedDownEntry != null) { mDraggedDownEntry.setUserLocked(false); mDraggedDownEntry = null; @@ -3262,7 +3264,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Disable layout transitions in navbar for this transition because the load is just // too heavy for the CPU and GPU on any device. mNavigationBarController.disableAnimationsDuringHide(mDisplayId, delay); - } else if (!mNotificationPanel.isCollapsing()) { + } else if (!mNotificationPanelViewController.isCollapsing()) { instantCollapseNotificationPanel(); } @@ -3273,10 +3275,10 @@ public class StatusBar extends SystemUI implements DemoMode, } mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); releaseGestureWakeLock(); - mNotificationPanel.onAffordanceLaunchEnded(); - mNotificationPanel.animate().cancel(); - mNotificationPanel.setAlpha(1f); - ViewGroupFadeHelper.reset(mNotificationPanel); + mNotificationPanelViewController.onAffordanceLaunchEnded(); + mNotificationPanelViewController.cancelAnimation(); + mNotificationPanelViewController.setAlpha(1f); + mNotificationPanelViewController.resetViewGroupFade(); updateScrimController(); Trace.endSection(); return staying; @@ -3351,7 +3353,7 @@ public class StatusBar extends SystemUI implements DemoMode, boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup() && !wakeAndUnlock) || (mDozing && mDozeServiceHost.shouldAnimateScreenOff() && sleepingFromKeyguard); - mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation); + mNotificationPanelViewController.setDozing(mDozing, animate, mWakeUpTouchLocation); updateQsExpansionEnabled(); Trace.endSection(); } @@ -3383,27 +3385,27 @@ public class StatusBar extends SystemUI implements DemoMode, public void endAffordanceLaunch() { releaseGestureWakeLock(); - mNotificationPanel.onAffordanceLaunchEnded(); + mNotificationPanelViewController.onAffordanceLaunchEnded(); } public boolean onBackPressed() { boolean isScrimmedBouncer = mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED; if (mStatusBarKeyguardViewManager.onBackPressed(isScrimmedBouncer /* hideImmediately */)) { if (!isScrimmedBouncer) { - mNotificationPanel.expandWithoutQs(); + mNotificationPanelViewController.expandWithoutQs(); } return true; } - if (mNotificationPanel.isQsExpanded()) { - if (mNotificationPanel.isQsDetailShowing()) { - mNotificationPanel.closeQsDetail(); + if (mNotificationPanelViewController.isQsExpanded()) { + if (mNotificationPanelViewController.isQsDetailShowing()) { + mNotificationPanelViewController.closeQsDetail(); } else { - mNotificationPanel.animateCloseQs(false /* animateAway */); + mNotificationPanelViewController.animateCloseQs(false /* animateAway */); } return true; } if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) { - if (mNotificationPanel.canPanelBeCollapsed()) { + if (mNotificationPanelViewController.canPanelBeCollapsed()) { mShadeController.animateCollapsePanels(); } else { mBubbleController.performBackPressIfNeeded(); @@ -3433,7 +3435,7 @@ public class StatusBar extends SystemUI implements DemoMode, } void instantCollapseNotificationPanel() { - mNotificationPanel.instantCollapse(); + mNotificationPanelViewController.instantCollapse(); mShadeController.runPostCollapseRunnables(); } @@ -3499,7 +3501,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Collapse the notification panel if open boolean dozingAnimated = mDozeServiceHost.getDozingRequested() && mDozeParameters.shouldControlScreenOff(); - mNotificationPanel.resetViews(dozingAnimated); + mNotificationPanelViewController.resetViews(dozingAnimated); updateQsExpansionEnabled(); mKeyguardViewMediator.setDozing(mDozing); @@ -3573,7 +3575,7 @@ public class StatusBar extends SystemUI implements DemoMode, * @return bottom area view */ public KeyguardBottomAreaView getKeyguardBottomAreaView() { - return mNotificationPanel.getKeyguardBottomAreaView(); + return mNotificationPanelViewController.getKeyguardBottomAreaView(); } /** @@ -3612,7 +3614,7 @@ public class StatusBar extends SystemUI implements DemoMode, mDraggedDownEntry = entry; mPendingRemoteInputView = null; } else { - mNotificationPanel.animateToFullShade(0 /* delay */); + mNotificationPanelViewController.animateToFullShade(0 /* delay */); mStatusBarStateController.setState(StatusBarState.SHADE_LOCKED); } } @@ -3638,7 +3640,7 @@ public class StatusBar extends SystemUI implements DemoMode, * Collapses the notification shade if it is tracking or expanded. */ public void collapseShade() { - if (mNotificationPanel.isTracking()) { + if (mNotificationPanelViewController.isTracking()) { mStatusBarWindowViewController.cancelCurrentTouch(); } if (mPanelExpanded && mState == StatusBarState.SHADE) { @@ -3650,7 +3652,7 @@ public class StatusBar extends SystemUI implements DemoMode, final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() { @Override public void onFinishedGoingToSleep() { - mNotificationPanel.onAffordanceLaunchEnded(); + mNotificationPanelViewController.onAffordanceLaunchEnded(); releaseGestureWakeLock(); mLaunchCameraWhenFinishedWaking = false; mDeviceInteractive = false; @@ -3711,7 +3713,8 @@ public class StatusBar extends SystemUI implements DemoMode, mBypassHeadsUpNotifier.setFullyAwake(true); mWakeUpCoordinator.setWakingUp(false); if (mLaunchCameraWhenFinishedWaking) { - mNotificationPanel.launchCamera(false /* animate */, mLastCameraLaunchSource); + mNotificationPanelViewController.launchCamera( + false /* animate */, mLastCameraLaunchSource); mLaunchCameraWhenFinishedWaking = false; } updateScrimController(); @@ -3728,7 +3731,7 @@ public class StatusBar extends SystemUI implements DemoMode, && !mDozeParameters.shouldControlScreenOff(); boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing()) || goingToSleepWithoutAnimation; - mNotificationPanel.setTouchAndAnimationDisabled(disabled); + mNotificationPanelViewController.setTouchAndAnimationDisabled(disabled); mNotificationIconAreaController.setAnimationsEnabled(!disabled); } @@ -3736,7 +3739,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onScreenTurningOn() { mFalsingManager.onScreenTurningOn(); - mNotificationPanel.onScreenTurningOn(); + mNotificationPanelViewController.onScreenTurningOn(); } @Override @@ -3805,7 +3808,7 @@ public class StatusBar extends SystemUI implements DemoMode, mLaunchCameraOnFinishedGoingToSleep = true; return; } - if (!mNotificationPanel.canCameraGestureBeLaunched()) { + if (!mNotificationPanelViewController.canCameraGestureBeLaunched()) { if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Can't launch camera right now"); return; } @@ -3835,7 +3838,8 @@ public class StatusBar extends SystemUI implements DemoMode, if (mStatusBarKeyguardViewManager.isBouncerShowing()) { mStatusBarKeyguardViewManager.reset(true /* hide */); } - mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source); + mNotificationPanelViewController.launchCamera( + mDeviceInteractive /* animate */, source); updateScrimController(); } else { // We need to defer the camera launch until the screen comes on, since otherwise @@ -3894,7 +3898,7 @@ public class StatusBar extends SystemUI implements DemoMode, !mBiometricUnlockController.isBiometricUnlock()); boolean launchingAffordanceWithPreview = - mNotificationPanel.isLaunchingAffordanceWithPreview(); + mNotificationPanelViewController.isLaunchingAffordanceWithPreview(); mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview); if (mBouncerShowing) { @@ -4243,7 +4247,7 @@ public class StatusBar extends SystemUI implements DemoMode, * When {@link KeyguardBouncer} starts to be dismissed, playing its animation. */ public void onBouncerPreHideAnimation() { - mNotificationPanel.onBouncerPreHideAnimation(); + mNotificationPanelViewController.onBouncerPreHideAnimation(); mLockscreenLockIconController.onBouncerPreHideAnimation(); } @@ -4286,8 +4290,8 @@ public class StatusBar extends SystemUI implements DemoMode, mAssistManagerLazy.get().showDisclosure(); } - public NotificationPanelView getPanel() { - return mNotificationPanel; + public NotificationPanelViewController getPanelController() { + return mNotificationPanelViewController; } @Override 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 f51174b55b74..407d25691798 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -133,7 +133,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb protected LockPatternUtils mLockPatternUtils; protected ViewMediatorCallback mViewMediatorCallback; protected StatusBar mStatusBar; - private NotificationPanelView mNotificationPanelView; + private NotificationPanelViewController mNotificationPanelViewController; private BiometricUnlockController mBiometricUnlockController; private ViewGroup mContainer; @@ -224,7 +224,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void registerStatusBar(StatusBar statusBar, ViewGroup container, - NotificationPanelView notificationPanelView, + NotificationPanelViewController notificationPanelViewController, BiometricUnlockController biometricUnlockController, DismissCallbackRegistry dismissCallbackRegistry, ViewGroup lockIconContainer, View notificationContainer, @@ -239,8 +239,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry, mExpansionCallback, mKeyguardStateController, falsingManager, bypassController); - mNotificationPanelView = notificationPanelView; - notificationPanelView.addExpansionListener(this); + mNotificationPanelViewController = notificationPanelViewController; + notificationPanelViewController.addExpansionListener(this); mBypassController = bypassController; mNotificationContainer = notificationContainer; } @@ -253,7 +253,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // • The user quickly taps on the display and we show "swipe up to unlock." // • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY // • Full-screen user switcher is displayed. - if (mNotificationPanelView.isUnlockHintRunning()) { + if (mNotificationPanelViewController.isUnlockHintRunning()) { mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); } else if (bouncerNeedsScrimming()) { mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); @@ -284,7 +284,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return; } boolean keyguardWithoutQs = mStatusBarStateController.getState() == StatusBarState.KEYGUARD - && !mNotificationPanelView.isQsExpanded(); + && !mNotificationPanelViewController.isQsExpanded(); boolean lockVisible = (mBouncer.isShowing() || keyguardWithoutQs) && !mBouncer.isAnimatingAway() && !mKeyguardStateController.isKeyguardFadingAway(); @@ -555,7 +555,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } else if (finishRunnable != null) { finishRunnable.run(); } - mNotificationPanelView.blockExpansionForCurrentTouch(); + mNotificationPanelViewController.blockExpansionForCurrentTouch(); updateLockIcon(); } @@ -609,7 +609,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb hideBouncer(true /* destroyView */); if (wakeUnlockPulsing) { if (needsFading) { - ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView, + ViewGroupFadeHelper.fadeOutAllChildrenExcept( + mNotificationPanelViewController.getView(), mNotificationContainer, fadeoutDuration, () -> { @@ -625,7 +626,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (!staying) { mStatusBarWindowController.setKeyguardFadingAway(true); if (needsFading) { - ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView, + ViewGroupFadeHelper.fadeOutAllChildrenExcept( + mNotificationPanelViewController.getView(), mNotificationContainer, fadeoutDuration, () -> { @@ -684,7 +686,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void onKeyguardFadedAway() { mContainer.postDelayed(() -> mStatusBarWindowController.setKeyguardFadingAway(false), 100); - ViewGroupFadeHelper.reset(mNotificationPanelView); + ViewGroupFadeHelper.reset(mNotificationPanelViewController.getView()); mStatusBar.finishKeyguardFadingAway(); mBiometricUnlockController.finishKeyguardFadingAway(); WindowManagerGlobal.getInstance().trimMemory( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java index 12033de10cb8..153ca22933a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java @@ -69,6 +69,7 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 661a7b1319a5..0f3b5db2d281 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -61,7 +61,6 @@ import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryListener; @@ -102,7 +101,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private final MetricsLogger mMetricsLogger; private final Context mContext; - private final NotificationPanelView mNotificationPanel; + private final NotificationPanelViewController mNotificationPanel; private final NotificationPresenter mPresenter; private final LockPatternUtils mLockPatternUtils; private final HeadsUpManagerPhone mHeadsUpManager; @@ -121,7 +120,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private boolean mIsCollapsingToShowActivityOverLockscreen; private StatusBarNotificationActivityStarter(Context context, CommandQueue commandQueue, - Lazy<AssistManager> assistManagerLazy, NotificationPanelView panel, + Lazy<AssistManager> assistManagerLazy, NotificationPanelViewController panel, NotificationPresenter presenter, NotificationEntryManager entryManager, HeadsUpManagerPhone headsUpManager, ActivityStarter activityStarter, ActivityLaunchAnimator activityLaunchAnimator, IStatusBarService statusBarService, @@ -519,7 +518,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final NotificationGroupManager mGroupManager; private final NotificationLockscreenUserManager mLockscreenUserManager; private final KeyguardStateController mKeyguardStateController; - private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private final MetricsLogger mMetricsLogger; private final LockPatternUtils mLockPatternUtils; private final Handler mMainThreadHandler; @@ -527,7 +525,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final Executor mUiBgExecutor; private final ActivityIntentHelper mActivityIntentHelper; private final BubbleController mBubbleController; - private final SuperStatusBarViewFactory mSuperStatusBarViewFactory; + private NotificationPanelViewController mNotificationPanelViewController; + private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private final ShadeController mShadeController; private NotificationPresenter mNotificationPresenter; private ActivityLaunchAnimator mActivityLaunchAnimator; @@ -558,8 +557,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit @UiBackground Executor uiBgExecutor, ActivityIntentHelper activityIntentHelper, BubbleController bubbleController, - ShadeController shadeController, - SuperStatusBarViewFactory superStatusBarViewFactory) { + ShadeController shadeController) { mContext = context; mCommandQueue = commandQueue; mAssistManagerLazy = assistManagerLazy; @@ -585,7 +583,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mActivityIntentHelper = activityIntentHelper; mBubbleController = bubbleController; mShadeController = shadeController; - mSuperStatusBarViewFactory = superStatusBarViewFactory; } /** Sets the status bar to use as {@link StatusBar}. */ @@ -604,10 +601,19 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit return this; } + /** Set the NotificationPanelViewController */ + public Builder setNotificationPanelViewController( + NotificationPanelViewController notificationPanelViewController) { + mNotificationPanelViewController = notificationPanelViewController; + return this; + } + + + public StatusBarNotificationActivityStarter build() { return new StatusBarNotificationActivityStarter(mContext, mCommandQueue, mAssistManagerLazy, - mSuperStatusBarViewFactory.getNotificationPanelView(), + mNotificationPanelViewController, mNotificationPresenter, mEntryManager, mHeadsUpManager, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index ab9d540c8458..12a65169e1df 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -107,7 +107,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, private final NotificationGutsManager mGutsManager = Dependency.get(NotificationGutsManager.class); - private final NotificationPanelView mNotificationPanel; + private final NotificationPanelViewController mNotificationPanel; private final HeadsUpManagerPhone mHeadsUpManager; private final AboveShelfObserver mAboveShelfObserver; private final DozeScrimController mDozeScrimController; @@ -132,7 +132,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, private int mMaxKeyguardNotifications; public StatusBarNotificationPresenter(Context context, - NotificationPanelView panel, + NotificationPanelViewController panel, HeadsUpManagerPhone headsUp, StatusBarWindowView statusBarWindow, ViewGroup stackScroller, @@ -172,7 +172,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, ServiceManager.getService(Context.STATUS_BAR_SERVICE)); if (MULTIUSER_DEBUG) { - mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info); + mNotificationPanelDebugText = mNotificationPanel.getHeaderDebugInfo(); mNotificationPanelDebugText.setVisibility(View.VISIBLE); } 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 6b513919e912..1e3c5d608bf8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -75,6 +75,10 @@ public class StatusBarWindowView extends FrameLayout { setMotionEventSplittingEnabled(false); } + public NotificationPanelView getNotificationPanelView() { + return findViewById(R.id.notification_panel); + } + @Override public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) { final Insets insets = windowInsets.getMaxInsets(WindowInsets.Type.systemBars()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java index eb86bcc8a52f..4935f0e8dd83 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java @@ -26,11 +26,9 @@ import android.provider.Settings; import android.view.GestureDetector; import android.view.InputDevice; import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewStub; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.ExpandHelper; @@ -93,6 +91,7 @@ public class StatusBarWindowViewController { private boolean mSingleTapEnabled; private boolean mExpandingBelowNotch; private final DockManager mDockManager; + private final NotificationPanelViewController mNotificationPanelViewController; @Inject public StatusBarWindowViewController( @@ -113,7 +112,8 @@ public class StatusBarWindowViewController { CommandQueue commandQueue, ShadeController shadeController, DockManager dockManager, - StatusBarWindowView statusBarWindowView) { + StatusBarWindowView statusBarWindowView, + NotificationPanelViewController notificationPanelViewController) { mInjectionInflationController = injectionInflationController; mCoordinator = coordinator; mPulseExpansionHandler = pulseExpansionHandler; @@ -132,6 +132,7 @@ public class StatusBarWindowViewController { mView = statusBarWindowView; mShadeController = shadeController; mDockManager = dockManager; + mNotificationPanelViewController = notificationPanelViewController; // This view is not part of the newly inflated expanded status bar. mBrightnessMirror = mView.findViewById(R.id.brightness_mirror); @@ -139,39 +140,6 @@ public class StatusBarWindowViewController { /** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */ public void setupExpandedStatusBar() { - // TODO: create controller for NotificationPanelView - NotificationPanelView notificationPanelView = new NotificationPanelView( - mView.getContext(), - null, - mInjectionInflationController, - mCoordinator, - mPulseExpansionHandler, - mDynamicPrivacyController, - mBypassController, - mFalsingManager, - mPluginManager, - mShadeController, - mNotificationLockscreenUserManager, - mNotificationEntryManager, - mKeyguardStateController, - mStatusBarStateController, - mDozeLog, - mDozeParameters, - mCommandQueue); - ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - notificationPanelView.setVisibility(View.INVISIBLE); - notificationPanelView.setId(R.id.notification_panel); - LayoutInflater li = mInjectionInflationController.injectable( - LayoutInflater.from(mView.getContext())); - - li.inflate(R.layout.status_bar_expanded, notificationPanelView); - notificationPanelView.onChildrenAttached(); - - ViewStub statusBarExpanded = mView.findViewById(R.id.status_bar_expanded); - mView.addView(notificationPanelView, mView.indexOfChild(statusBarExpanded), lp); - mView.removeView(statusBarExpanded); - mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller); TunerService.Tunable tunable = (key, newValue) -> { @@ -233,8 +201,8 @@ public class StatusBarWindowViewController { if (!isCancel && mService.shouldIgnoreTouch()) { return false; } - if (isDown && notificationPanelView.isFullyCollapsed()) { - notificationPanelView.startExpandLatencyTracking(); + if (isDown && mNotificationPanelViewController.isFullyCollapsed()) { + mNotificationPanelViewController.startExpandLatencyTracking(); } if (isDown) { setTouchActive(true); @@ -287,7 +255,7 @@ public class StatusBarWindowViewController { return true; } boolean intercept = false; - if (notificationPanelView.isFullyExpanded() + if (mNotificationPanelViewController.isFullyExpanded() && mDragDownHelper.isDragDownEnabled() && !mService.isBouncerShowing() && !mStatusBarStateController.isDozing()) { @@ -303,7 +271,7 @@ public class StatusBarWindowViewController { MotionEvent cancellation = MotionEvent.obtain(ev); cancellation.setAction(MotionEvent.ACTION_CANCEL); mStackScrollLayout.onInterceptTouchEvent(cancellation); - notificationPanelView.onInterceptTouchEvent(cancellation); + mNotificationPanelViewController.getView().onInterceptTouchEvent(cancellation); cancellation.recycle(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java index f3c843c6d62d..21d0bb8c2daf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java @@ -14,10 +14,14 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.statusbar.phone.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.android.systemui.statusbar.phone.NotificationPanelViewController; +import com.android.systemui.statusbar.phone.StatusBarWindowView; +import com.android.systemui.statusbar.phone.StatusBarWindowViewController; + import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -29,7 +33,8 @@ import dagger.Subcomponent; /** * Dagger subcomponent tied to the lifecycle of StatusBar views. */ -@Subcomponent +@Subcomponent(modules = {StatusBarViewModule.class}) +@StatusBarComponent.StatusBarScope public interface StatusBarComponent { /** * Builder for {@link StatusBarComponent}. @@ -54,4 +59,10 @@ public interface StatusBarComponent { @StatusBarScope StatusBarWindowViewController getStatusBarWindowViewController(); + /** + * Creates a NotificationPanelViewController. + */ + @StatusBarScope + NotificationPanelViewController getNotificationPanelViewController(); + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java new file mode 100644 index 000000000000..20bd51d27050 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone.dagger; + +import com.android.systemui.statusbar.phone.NotificationPanelView; +import com.android.systemui.statusbar.phone.StatusBarWindowView; + +import dagger.Module; +import dagger.Provides; + +@Module +public abstract class StatusBarViewModule { + /** */ + @Provides + @StatusBarComponent.StatusBarScope + public static NotificationPanelView getNotificationPanelView( + StatusBarWindowView statusBarWindowView) { + return statusBarWindowView.getNotificationPanelView(); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java index 88edf8e40972..625d88481c5a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java @@ -24,7 +24,7 @@ import android.view.View; import android.widget.FrameLayout; import com.android.systemui.R; -import com.android.systemui.statusbar.phone.NotificationPanelView; +import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.StatusBarWindowView; import java.util.Objects; @@ -38,16 +38,17 @@ public class BrightnessMirrorController private final StatusBarWindowView mStatusBarWindow; private final Consumer<Boolean> mVisibilityCallback; - private final NotificationPanelView mNotificationPanel; + private final NotificationPanelViewController mNotificationPanel; private final ArraySet<BrightnessMirrorListener> mBrightnessMirrorListeners = new ArraySet<>(); private final int[] mInt2Cache = new int[2]; private View mBrightnessMirror; public BrightnessMirrorController(StatusBarWindowView statusBarWindow, + NotificationPanelViewController notificationPanelViewController, @NonNull Consumer<Boolean> visibilityCallback) { mStatusBarWindow = statusBarWindow; mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror); - mNotificationPanel = statusBarWindow.findViewById(R.id.notification_panel); + mNotificationPanel = notificationPanelViewController; mNotificationPanel.setPanelAlphaEndAction(() -> { mBrightnessMirror.setVisibility(View.INVISIBLE); }); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java index 4b283fedfe09..d28a66994e1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java @@ -35,7 +35,7 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.qs.tiles.UserDetailItemView; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; -import com.android.systemui.statusbar.phone.NotificationPanelView; +import com.android.systemui.statusbar.phone.NotificationPanelViewController; /** * Manages the user switcher on the Keyguard. @@ -57,7 +57,8 @@ public class KeyguardUserSwitcher { private boolean mAnimating; public KeyguardUserSwitcher(Context context, ViewStub userSwitcher, - KeyguardStatusBarView statusBarView, NotificationPanelView panelView) { + KeyguardStatusBarView statusBarView, + NotificationPanelViewController panelViewController) { boolean keyguardUserSwitcherEnabled = context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON; UserSwitcherController userSwitcherController = Dependency.get(UserSwitcherController.class); @@ -67,7 +68,7 @@ public class KeyguardUserSwitcher { reinflateViews(); mStatusBarView = statusBarView; mStatusBarView.setKeyguardUserSwitcher(this); - panelView.setKeyguardUserSwitcher(this); + panelViewController.setKeyguardUserSwitcher(this); mAdapter = new Adapter(context, userSwitcherController, this); mAdapter.registerDataSetObserver(mDataSetObserver); mUserSwitcherController = userSwitcherController; diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index 6976649ec497..56aae17f451e 100644 --- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java +++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java @@ -35,7 +35,6 @@ import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.phone.LockIcon; -import com.android.systemui.statusbar.phone.NotificationPanelView; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -129,11 +128,6 @@ public class InjectionInflationController { NotificationStackScrollLayout createNotificationStackScrollLayout(); /** - * Creates the NotificationPanelView. - */ - NotificationPanelView createPanelView(); - - /** * Creates the Shelf. */ NotificationShelf creatNotificationShelf(); diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java new file mode 100644 index 000000000000..d413308d4573 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wm; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.res.Configuration; +import android.os.Handler; +import android.os.RemoteException; +import android.util.Slog; +import android.util.SparseArray; +import android.view.IDisplayWindowInsetsController; +import android.view.InsetsSource; +import android.view.InsetsSourceControl; +import android.view.InsetsState; +import android.view.Surface; +import android.view.SurfaceControl; +import android.view.WindowInsets; +import android.view.animation.Interpolator; +import android.view.animation.PathInterpolator; + +import com.android.systemui.dagger.qualifiers.Main; + +import java.util.ArrayList; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Manages IME control at the display-level. This occurs when IME comes up in multi-window mode. + */ +@Singleton +public class DisplayImeController implements DisplayWindowController.DisplayWindowListener { + private static final String TAG = "DisplayImeController"; + + static final int ANIMATION_DURATION_SHOW_MS = 275; + static final int ANIMATION_DURATION_HIDE_MS = 340; + static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f); + private static final int DIRECTION_NONE = 0; + private static final int DIRECTION_SHOW = 1; + private static final int DIRECTION_HIDE = 2; + + SystemWindows mSystemWindows; + final Handler mHandler; + + final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>(); + + final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>(); + + @Inject + DisplayImeController(SystemWindows syswin, DisplayWindowController displayController, + @Main Handler mainHandler) { + mHandler = mainHandler; + mSystemWindows = syswin; + displayController.addDisplayWindowListener(this); + } + + @Override + public void onDisplayAdded(int displayId) { + // Add's a system-ui window-manager specifically for ime. This type is special because + // WM will defer IME inset handling to it in multi-window scenarious. + PerDisplay pd = new PerDisplay(displayId, + mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()); + try { + mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to set insets controller on display " + displayId); + } + mImePerDisplay.put(displayId, pd); + } + + @Override + public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + PerDisplay pd = mImePerDisplay.get(displayId); + if (pd == null) { + return; + } + if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation() + != pd.mRotation && isImeShowing(displayId)) { + pd.startAnimation(true); + } + } + + @Override + public void onDisplayRemoved(int displayId) { + try { + mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to remove insets controller on display " + displayId); + } + mImePerDisplay.remove(displayId); + } + + private boolean isImeShowing(int displayId) { + PerDisplay pd = mImePerDisplay.get(displayId); + if (pd == null) { + return false; + } + final InsetsSource imeSource = pd.mInsetsState.getSource(InsetsState.ITYPE_IME); + return imeSource != null && pd.mImeSourceControl != null && imeSource.isVisible(); + } + + private void dispatchPositionChanged(int displayId, int imeTop, + SurfaceControl.Transaction t) { + synchronized (mPositionProcessors) { + for (ImePositionProcessor pp : mPositionProcessors) { + pp.onImePositionChanged(displayId, imeTop, t); + } + } + } + + private void dispatchStartPositioning(int displayId, int imeTop, int finalImeTop, + boolean show, SurfaceControl.Transaction t) { + synchronized (mPositionProcessors) { + for (ImePositionProcessor pp : mPositionProcessors) { + pp.onImeStartPositioning(displayId, imeTop, finalImeTop, show, t); + } + } + } + + private void dispatchEndPositioning(int displayId, int imeTop, boolean show, + SurfaceControl.Transaction t) { + synchronized (mPositionProcessors) { + for (ImePositionProcessor pp : mPositionProcessors) { + pp.onImeEndPositioning(displayId, imeTop, show, t); + } + } + } + + /** + * Adds an {@link ImePositionProcessor} to be called during ime position updates. + */ + public void addPositionProcessor(ImePositionProcessor processor) { + synchronized (mPositionProcessors) { + if (mPositionProcessors.contains(processor)) { + return; + } + mPositionProcessors.add(processor); + } + } + + /** + * Removes an {@link ImePositionProcessor} to be called during ime position updates. + */ + public void removePositionProcessor(ImePositionProcessor processor) { + synchronized (mPositionProcessors) { + mPositionProcessors.remove(processor); + } + } + + class PerDisplay extends IDisplayWindowInsetsController.Stub { + final int mDisplayId; + final InsetsState mInsetsState = new InsetsState(); + InsetsSourceControl mImeSourceControl = null; + int mAnimationDirection = DIRECTION_NONE; + ValueAnimator mAnimation = null; + int mRotation = Surface.ROTATION_0; + + PerDisplay(int displayId, int initialRotation) { + mDisplayId = displayId; + mRotation = initialRotation; + } + + @Override + public void insetsChanged(InsetsState insetsState) { + if (mInsetsState.equals(insetsState)) { + return; + } + mInsetsState.set(insetsState, true /* copySources */); + } + + @Override + public void insetsControlChanged(InsetsState insetsState, + InsetsSourceControl[] activeControls) { + insetsChanged(insetsState); + if (activeControls != null) { + for (InsetsSourceControl activeControl : activeControls) { + if (activeControl == null) { + continue; + } + if (activeControl.getType() == InsetsState.ITYPE_IME) { + mImeSourceControl = activeControl; + } + } + } + } + + @Override + public void showInsets(int types, boolean fromIme) { + if ((types & WindowInsets.Type.ime()) == 0) { + return; + } + startAnimation(true /* show */); + } + + @Override + public void hideInsets(int types, boolean fromIme) { + if ((types & WindowInsets.Type.ime()) == 0) { + return; + } + startAnimation(false /* show */); + } + + /** + * Sends the local visibility state back to window manager. Needed for legacy adjustForIme. + */ + private void setVisibleDirectly(boolean visible) { + mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible); + try { + mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState); + } catch (RemoteException e) { + } + } + + private int imeTop(InsetsSource imeSource, float surfaceOffset) { + return imeSource.getFrame().top + (int) surfaceOffset; + } + + private void startAnimation(final boolean show) { + final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME); + if (imeSource == null || mImeSourceControl == null) { + return; + } + if ((mAnimationDirection == DIRECTION_SHOW && show) + || (mAnimationDirection == DIRECTION_HIDE && !show)) { + return; + } + if (mAnimationDirection != DIRECTION_NONE) { + mAnimation.end(); + mAnimationDirection = DIRECTION_NONE; + } + mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE; + mHandler.post(() -> { + final float defaultY = mImeSourceControl.getSurfacePosition().y; + final float x = mImeSourceControl.getSurfacePosition().x; + final float startY = show ? defaultY + imeSource.getFrame().height() : defaultY; + final float endY = show ? defaultY : defaultY + imeSource.getFrame().height(); + mAnimation = ValueAnimator.ofFloat(startY, endY); + mAnimation.setDuration( + show ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS); + + mAnimation.addUpdateListener(animation -> { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + float value = (float) animation.getAnimatedValue(); + t.setPosition(mImeSourceControl.getLeash(), x, value); + dispatchPositionChanged(mDisplayId, imeTop(imeSource, value), t); + t.apply(); + t.close(); + }); + mAnimation.setInterpolator(INTERPOLATOR); + mAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.setPosition(mImeSourceControl.getLeash(), x, startY); + dispatchStartPositioning(mDisplayId, imeTop(imeSource, startY), + imeTop(imeSource, endY), mAnimationDirection == DIRECTION_SHOW, + t); + if (mAnimationDirection == DIRECTION_SHOW) { + t.show(mImeSourceControl.getLeash()); + } + t.apply(); + t.close(); + } + @Override + public void onAnimationEnd(Animator animation) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.setPosition(mImeSourceControl.getLeash(), x, endY); + dispatchEndPositioning(mDisplayId, imeTop(imeSource, endY), + mAnimationDirection == DIRECTION_SHOW, t); + if (mAnimationDirection == DIRECTION_HIDE) { + t.hide(mImeSourceControl.getLeash()); + } + t.apply(); + t.close(); + + mAnimationDirection = DIRECTION_NONE; + mAnimation = null; + } + }); + if (!show) { + // When going away, queue up insets change first, otherwise any bounds changes + // can have a "flicker" of ime-provided insets. + setVisibleDirectly(false /* visible */); + } + mAnimation.start(); + if (show) { + // When showing away, queue up insets change last, otherwise any bounds changes + // can have a "flicker" of ime-provided insets. + setVisibleDirectly(true /* visible */); + } + }); + } + } + + /** + * Allows other things to synchronize with the ime position + */ + public interface ImePositionProcessor { + /** + * Called when the IME position is starting to animate. + */ + void onImeStartPositioning(int displayId, int imeTop, int finalImeTop, boolean showing, + SurfaceControl.Transaction t); + + /** + * Called when the ime position changed. This is expected to be a synchronous call on the + * animation thread. Operations can be added to the transaction to be applied in sync. + */ + void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t); + + /** + * Called when the IME position is done animating. + */ + void onImeEndPositioning(int displayId, int imeTop, boolean showing, + SurfaceControl.Transaction t); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java index 145a25caf95d..a54f7335ba67 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java @@ -33,7 +33,7 @@ import android.view.View; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.phone.NotificationPanelView; +import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.StatusBarWindowView; import com.android.systemui.statusbar.phone.StatusBarWindowViewController; @@ -64,7 +64,7 @@ public class ActivityLaunchAnimatorTest extends SysuiTestCase { mLaunchAnimator = new ActivityLaunchAnimator( mStatusBarWindowViewController, mCallback, - mock(NotificationPanelView.class), + mock(NotificationPanelViewController.class), mNotificationContainer); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java index 8decae33d45b..ae87eefd243c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java @@ -64,7 +64,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mMockNotificiationAreaController = mock(NotificationIconAreaController.class); mNotificationAreaInner = mock(View.class); mCenteredNotificationAreaView = mock(View.class); - when(statusBar.getPanel()).thenReturn(mock(NotificationPanelView.class)); + when(statusBar.getPanelController()).thenReturn( + mock(NotificationPanelViewController.class)); when(mNotificationAreaInner.animate()).thenReturn(mock(ViewPropertyAnimator.class)); when(mMockNotificiationAreaController.getNotificationInnerAreaView()).thenReturn( mNotificationAreaInner); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java index 46f6cfe39e3c..d31f17531889 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java @@ -86,7 +86,7 @@ public class DozeServiceHostTest extends SysuiTestCase { @Mock private NotificationIconAreaController mNotificationIconAreaController; @Mock private StatusBarWindowViewController mStatusBarWindowViewController; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - @Mock private NotificationPanelView mNotificationPanel; + @Mock private NotificationPanelViewController mNotificationPanel; @Mock private View mAmbientIndicationContainer; @Mock private BiometricUnlockController mBiometricUnlockController; @Mock private LockscreenLockIconController mLockscreenLockIconController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index 026026949467..7448dbd0d116 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -53,7 +53,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { private final NotificationStackScrollLayout mStackScroller = mock(NotificationStackScrollLayout.class); - private final NotificationPanelView mPanelView = mock(NotificationPanelView.class); + private final NotificationPanelViewController mPanelView = + mock(NotificationPanelViewController.class); private final DarkIconDispatcher mDarkIconDispatcher = mock(DarkIconDispatcher.class); private HeadsUpAppearanceController mHeadsUpAppearanceController; private ExpandableNotificationRow mFirst; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index c165e561b393..1f37ad8b2b1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -16,9 +16,13 @@ package com.android.systemui.statusbar.phone; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; + import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -26,37 +30,44 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.ActivityManager; import android.app.StatusBarManager; +import android.content.res.Configuration; +import android.content.res.Resources; import android.hardware.biometrics.BiometricSourceType; +import android.os.PowerManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityManager; import androidx.test.filters.SmallTest; -import com.android.keyguard.KeyguardStatusView; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.util.LatencyTracker; +import com.android.keyguard.KeyguardClockSwitch; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.SystemUIFactory; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.doze.DozeLog; import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; -import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -70,6 +81,7 @@ import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; import java.util.function.Consumer; @@ -85,8 +97,6 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; @Mock - private KeyguardStatusView mKeyguardStatusView; - @Mock private KeyguardBottomAreaView mKeyguardBottomArea; @Mock private KeyguardBottomAreaView mQsFrame; @@ -109,27 +119,89 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private PanelBar mPanelBar; @Mock - private KeyguardAffordanceHelper mAffordanceHelper; - @Mock private KeyguardUpdateMonitor mUpdateMonitor; @Mock private FalsingManager mFalsingManager; @Mock private KeyguardBypassController mKeyguardBypassController; - @Mock private DozeParameters mDozeParameters; - private NotificationPanelView mNotificationPanelView; + @Mock + private DozeParameters mDozeParameters; + @Mock + private NotificationPanelView mView; + @Mock + private InjectionInflationController mInjectionInflationController; + @Mock + private DynamicPrivacyController mDynamicPrivacyController; + @Mock + private PluginManager mPluginManager; + @Mock + private ShadeController mShadeController; + @Mock + private NotificationLockscreenUserManager mNotificationLockscreenUserManager; + @Mock + private NotificationEntryManager mNotificationEntryManager; + @Mock + private KeyguardStateController mKeyguardStateController; + @Mock + private DozeLog mDozeLog; + @Mock + private CommandQueue mCommandQueue; + @Mock + private VibratorHelper mVibratorHelper; + @Mock + private LatencyTracker mLatencyTracker; + @Mock + private PowerManager mPowerManager; + @Mock + private AccessibilityManager mAccessibilityManager; + @Mock + private MetricsLogger mMetricsLogger; + @Mock + private ActivityManager mActivityManager; + @Mock + private Resources mResources; + @Mock + private Configuration mConfiguration; + private DisplayMetrics mDisplayMetrics = new DisplayMetrics(); + @Mock + private KeyguardClockSwitch mKeyguardClockSwitch; + private PanelViewController.TouchHandler mTouchHandler; + @Mock + private ZenModeController mZenModeController; + @Mock + private ConfigurationController mConfigurationController; + private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; + + private NotificationPanelViewController mNotificationPanelViewController; @Before public void setup() { MockitoAnnotations.initMocks(this); + when(mHeadsUpCallback.getContext()).thenReturn(mContext); + when(mView.getResources()).thenReturn(mResources); + when(mResources.getConfiguration()).thenReturn(mConfiguration); + mConfiguration.orientation = ORIENTATION_PORTRAIT; + when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); + mDisplayMetrics.density = 100; + when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true); + when(mView.getContext()).thenReturn(getContext()); + when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch); + when(mView.findViewById(R.id.notification_stack_scroller)) + .thenReturn(mNotificationStackScrollLayout); when(mNotificationStackScrollLayout.getHeight()).thenReturn(1000); when(mNotificationStackScrollLayout.getHeadsUpCallback()).thenReturn(mHeadsUpCallback); - when(mHeadsUpCallback.getContext()).thenReturn(mContext); - mDependency.injectTestDependency(StatusBarStateController.class, - mStatusBarStateController); - mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mUpdateMonitor); - mDependency.injectMockDependency(ConfigurationController.class); - mDependency.injectMockDependency(ZenModeController.class); + when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea); + when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class)); + when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class)); + when(mView.findViewById(R.id.big_clock_container)).thenReturn(mBigClockContainer); + when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); + mFlingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(mDisplayMetrics); + + doAnswer((Answer<Void>) invocation -> { + mTouchHandler = invocation.getArgument(0); + return null; + }).when(mView).setOnTouchListener(any(PanelViewController.TouchHandler.class)); + NotificationWakeUpCoordinator coordinator = new NotificationWakeUpCoordinator( mock(HeadsUpManagerPhone.class), @@ -143,18 +215,26 @@ public class NotificationPanelViewTest extends SysuiTestCase { mock(NotificationRoundnessManager.class), mStatusBarStateController, new FalsingManagerFake()); - mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler, - mKeyguardBypassController, mStatusBarStateController); - mNotificationPanelView.setHeadsUpManager(mHeadsUpManager); - mNotificationPanelView.setBar(mPanelBar); - - when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class)); - when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class)); + mNotificationPanelViewController = new NotificationPanelViewController(mView, + mInjectionInflationController, + coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, + mFalsingManager, mPluginManager, mShadeController, + mNotificationLockscreenUserManager, mNotificationEntryManager, + mKeyguardStateController, mStatusBarStateController, mDozeLog, + mDozeParameters, mCommandQueue, mVibratorHelper, + mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, + mMetricsLogger, mActivityManager, mZenModeController, mConfigurationController, + mFlingAnimationUtilsBuilder); + mNotificationPanelViewController.initDependencies(mStatusBar, mGroupManager, + mNotificationShelf, mNotificationAreaController, mScrimController); + mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); + mNotificationPanelViewController.setBar(mPanelBar); } @Test public void testSetDozing_notifiesNsslAndStateController() { - mNotificationPanelView.setDozing(true /* dozing */, true /* animate */, null /* touch */); + mNotificationPanelViewController.setDozing(true /* dozing */, true /* animate */, + null /* touch */); InOrder inOrder = inOrder(mNotificationStackScrollLayout, mStatusBarStateController); inOrder.verify(mNotificationStackScrollLayout).setDozing(eq(true), eq(true), eq(null)); inOrder.verify(mStatusBarStateController).setDozeAmount(eq(1f), eq(true)); @@ -162,103 +242,63 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Test public void testSetExpandedHeight() { - mNotificationPanelView.setExpandedHeight(200); - assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200); + mNotificationPanelViewController.setExpandedHeight(200); + assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200); } @Test public void testAffordanceLaunchingListener() { Consumer<Boolean> listener = spy((showing) -> { }); - mNotificationPanelView.setExpandedFraction(1f); - mNotificationPanelView.setLaunchAffordanceListener(listener); - mNotificationPanelView.launchCamera(false /* animate */, + mNotificationPanelViewController.setExpandedFraction(1f); + mNotificationPanelViewController.setLaunchAffordanceListener(listener); + mNotificationPanelViewController.launchCamera(false /* animate */, StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP); verify(listener).accept(eq(true)); - mNotificationPanelView.onAffordanceLaunchEnded(); + mNotificationPanelViewController.onAffordanceLaunchEnded(); verify(listener).accept(eq(false)); } @Test public void testOnTouchEvent_expansionCanBeBlocked() { - mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */, + onTouchEvent(MotionEvent.obtain(0L /* downTime */, 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */, 0 /* metaState */)); - mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */, + onTouchEvent(MotionEvent.obtain(0L /* downTime */, 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */, 0 /* metaState */)); - assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200); - assertThat(mNotificationPanelView.isTrackingBlocked()).isFalse(); + assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200); + assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse(); - mNotificationPanelView.blockExpansionForCurrentTouch(); - mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */, + mNotificationPanelViewController.blockExpansionForCurrentTouch(); + onTouchEvent(MotionEvent.obtain(0L /* downTime */, 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 300f /* y */, 0 /* metaState */)); // Expansion should not have changed because it was blocked - assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200); - assertThat(mNotificationPanelView.isTrackingBlocked()).isTrue(); + assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200); + assertThat(mNotificationPanelViewController.isTrackingBlocked()).isTrue(); - mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */, + onTouchEvent(MotionEvent.obtain(0L /* downTime */, 0L /* eventTime */, MotionEvent.ACTION_UP, 0f /* x */, 300f /* y */, 0 /* metaState */)); - assertThat(mNotificationPanelView.isTrackingBlocked()).isFalse(); + assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse(); } @Test public void testKeyguardStatusBarVisibility_hiddenForBypass() { when(mUpdateMonitor.shouldListenForFace()).thenReturn(true); - mNotificationPanelView.mKeyguardUpdateCallback.onBiometricRunningStateChanged(true, - BiometricSourceType.FACE); + mNotificationPanelViewController.mKeyguardUpdateCallback.onBiometricRunningStateChanged( + true, BiometricSourceType.FACE); verify(mKeyguardStatusBar, never()).setVisibility(View.VISIBLE); when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true); - mNotificationPanelView.mKeyguardUpdateCallback.onFinishedGoingToSleep(0); - mNotificationPanelView.mKeyguardUpdateCallback.onBiometricRunningStateChanged(true, - BiometricSourceType.FACE); + mNotificationPanelViewController.mKeyguardUpdateCallback.onFinishedGoingToSleep(0); + mNotificationPanelViewController.mKeyguardUpdateCallback.onBiometricRunningStateChanged( + true, BiometricSourceType.FACE); verify(mKeyguardStatusBar, never()).setVisibility(View.VISIBLE); } - private class TestableNotificationPanelView extends NotificationPanelView { - TestableNotificationPanelView(NotificationWakeUpCoordinator coordinator, - PulseExpansionHandler expansionHandler, - KeyguardBypassController bypassController, - SysuiStatusBarStateController statusBarStateController) { - super( - NotificationPanelViewTest.this.mContext, - null, - new InjectionInflationController( - SystemUIFactory.getInstance().getRootComponent()), - coordinator, - expansionHandler, - mock(DynamicPrivacyController.class), - bypassController, - mFalsingManager, - mock(PluginManager.class), - mock(ShadeController.class), - mock(NotificationLockscreenUserManager.class), - new NotificationEntryManager( - mock(NotifLog.class), - mock(NotificationGroupManager.class), - mock(NotificationRankingManager.class), - mock(NotificationEntryManager.KeyguardEnvironment.class)), - mock(KeyguardStateController.class), - statusBarStateController, - mock(DozeLog.class), - mDozeParameters, - new CommandQueue(NotificationPanelViewTest.this.mContext)); - mNotificationStackScroller = mNotificationStackScrollLayout; - mKeyguardStatusView = NotificationPanelViewTest.this.mKeyguardStatusView; - mKeyguardStatusBar = NotificationPanelViewTest.this.mKeyguardStatusBar; - mKeyguardBottomArea = NotificationPanelViewTest.this.mKeyguardBottomArea; - mBigClockContainer = NotificationPanelViewTest.this.mBigClockContainer; - mQsFrame = NotificationPanelViewTest.this.mQsFrame; - mAffordanceHelper = NotificationPanelViewTest.this.mAffordanceHelper; - initDependencies(NotificationPanelViewTest.this.mStatusBar, - NotificationPanelViewTest.this.mGroupManager, - NotificationPanelViewTest.this.mNotificationShelf, - NotificationPanelViewTest.this.mHeadsUpManager, - NotificationPanelViewTest.this.mNotificationAreaController, - NotificationPanelViewTest.this.mScrimController); - } + private void onTouchEvent(MotionEvent ev) { + mTouchHandler.onTouch(mView, ev); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index b27e84a37e3f..5b5eaadee552 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -74,7 +74,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private ViewGroup mContainer; @Mock - private NotificationPanelView mNotificationPanelView; + private NotificationPanelViewController mNotificationPanelView; @Mock private BiometricUnlockController mBiometrucUnlockController; @Mock @@ -281,12 +281,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Override public void registerStatusBar(StatusBar statusBar, ViewGroup container, - NotificationPanelView notificationPanelView, + NotificationPanelViewController notificationPanelViewController, BiometricUnlockController fingerprintUnlockController, DismissCallbackRegistry dismissCallbackRegistry, ViewGroup lockIconContainer, View notificationContainer, KeyguardBypassController bypassController, FalsingManager falsingManager) { - super.registerStatusBar(statusBar, container, notificationPanelView, + super.registerStatusBar(statusBar, container, notificationPanelViewController, fingerprintUnlockController, dismissCallbackRegistry, lockIconContainer, notificationContainer, bypassController, falsingManager); mBouncer = StatusBarKeyguardViewManagerTest.this.mBouncer; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 86b2a44a1acb..fea4b8bbbb04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -64,7 +64,6 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -124,10 +123,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private Intent mContentIntentInner; @Mock private NotificationActivityStarter mNotificationActivityStarter; - @Mock - private SuperStatusBarViewFactory mSuperStatusBarViewFactory; - @Mock - private NotificationPanelView mNotificationPanelView; private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); private NotificationTestHelper mNotificationTestHelper; @@ -167,8 +162,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mActiveNotifications.add(mBubbleNotificationRow.getEntry()); when(mEntryManager.getVisibleNotifications()).thenReturn(mActiveNotifications); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); - when(mSuperStatusBarViewFactory.getNotificationPanelView()) - .thenReturn(mNotificationPanelView); mNotificationActivityStarter = (new StatusBarNotificationActivityStarter.Builder( getContext(), mock(CommandQueue.class), () -> mAssistManager, @@ -182,9 +175,9 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mKeyguardStateController, mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class), mock(LockPatternUtils.class), mHandler, mHandler, mUiBgExecutor, - mActivityIntentHelper, mBubbleController, mShadeController, - mSuperStatusBarViewFactory)) + mActivityIntentHelper, mBubbleController, mShadeController)) .setStatusBar(mStatusBar) + .setNotificationPanelViewController(mock(NotificationPanelViewController.class)) .setNotificationPresenter(mock(NotificationPresenter.class)) .setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class)) .build(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 1296a973b50f..5ac7bfbfd296 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -108,7 +108,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class); when(statusBarWindowView.getResources()).thenReturn(mContext.getResources()); mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext, - mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class), + mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class), statusBarWindowView, mock(NotificationListContainerViewGroup.class), mock(DozeScrimController.class), mock(ScrimController.class), mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 1cdba472a085..7e485f45e5e6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -124,6 +124,7 @@ import com.android.systemui.statusbar.notification.collection.init.NewNotifPipel import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -172,6 +173,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private KeyguardIndicationController mKeyguardIndicationController; @Mock private NotificationStackScrollLayout mStackScroller; @Mock private HeadsUpManagerPhone mHeadsUpManager; + @Mock private NotificationPanelViewController mNotificationPanelViewController; @Mock private NotificationPanelView mNotificationPanelView; @Mock private IStatusBarService mBarService; @Mock private IDreamManager mDreamManager; @@ -285,6 +287,7 @@ public class StatusBarTest extends SysuiTestCase { mContext.setTheme(R.style.Theme_SystemUI_Light); when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0)); + when(mNotificationPanelViewController.getView()).thenReturn(mNotificationPanelView); when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0)); when(powerManagerService.isInteractive()).thenReturn(true); when(mStackScroller.getActivatedChild()).thenReturn(null); @@ -416,7 +419,7 @@ public class StatusBarTest extends SysuiTestCase { mLockIconContainer); when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class), - any(NotificationPanelView.class), any(BiometricUnlockController.class), + any(NotificationPanelViewController.class), any(BiometricUnlockController.class), any(ViewGroup.class), any(ViewGroup.class), any(KeyguardBypassController.class))) .thenReturn(mStatusBarKeyguardViewManager); @@ -426,7 +429,7 @@ public class StatusBarTest extends SysuiTestCase { // TODO: we should be able to call mStatusBar.start() and have all the below values // initialized automatically. mStatusBar.mStatusBarWindow = mStatusBarWindowView; - mStatusBar.mNotificationPanel = mNotificationPanelView; + mStatusBar.mNotificationPanelViewController = mNotificationPanelViewController; mStatusBar.mDozeScrimController = mDozeScrimController; mStatusBar.mNotificationIconAreaController = mNotificationIconAreaController; mStatusBar.mPresenter = mNotificationPresenter; @@ -731,20 +734,20 @@ public class StatusBarTest extends SysuiTestCase { when(mCommandQueue.panelsEnabled()).thenReturn(false); mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false); - verify(mNotificationPanelView).setQsExpansionEnabled(false); + verify(mNotificationPanelViewController).setQsExpansionEnabled(false); mStatusBar.animateExpandNotificationsPanel(); - verify(mNotificationPanelView, never()).expand(anyBoolean()); + verify(mNotificationPanelViewController, never()).expand(anyBoolean()); mStatusBar.animateExpandSettingsPanel(null); - verify(mNotificationPanelView, never()).expand(anyBoolean()); + verify(mNotificationPanelViewController, never()).expand(anyBoolean()); when(mCommandQueue.panelsEnabled()).thenReturn(true); mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NONE, false); - verify(mNotificationPanelView).setQsExpansionEnabled(true); + verify(mNotificationPanelViewController).setQsExpansionEnabled(true); mStatusBar.animateExpandNotificationsPanel(); - verify(mNotificationPanelView).expandWithoutQs(); + verify(mNotificationPanelViewController).expandWithoutQs(); mStatusBar.animateExpandSettingsPanel(null); - verify(mNotificationPanelView).expandWithQs(); + verify(mNotificationPanelViewController).expandWithQs(); } @Test @@ -834,12 +837,12 @@ public class StatusBarTest extends SysuiTestCase { when(mDozeServiceHost.getDozingRequested()).thenReturn(true); mStatusBar.updateIsKeyguard(); // TODO: mNotificationPanelView.expand(false) gets called twice. Should be once. - verify(mNotificationPanelView, times(2)).expand(eq(false)); - clearInvocations(mNotificationPanelView); + verify(mNotificationPanelViewController, times(2)).expand(eq(false)); + clearInvocations(mNotificationPanelViewController); mStatusBar.mWakefulnessObserver.onStartedWakingUp(); verify(mDozeServiceHost).stopDozing(); - verify(mNotificationPanelView).expand(eq(false)); + verify(mNotificationPanelViewController).expand(eq(false)); } @Test @@ -848,11 +851,11 @@ public class StatusBarTest extends SysuiTestCase { when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true); when(mDozeServiceHost.getDozingRequested()).thenReturn(true); mStatusBar.updateIsKeyguard(); - clearInvocations(mNotificationPanelView); + clearInvocations(mNotificationPanelViewController); mStatusBar.setBouncerShowing(true); mStatusBar.mWakefulnessObserver.onStartedWakingUp(); - verify(mNotificationPanelView, never()).expand(anyBoolean()); + verify(mNotificationPanelViewController, never()).expand(anyBoolean()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java index 9f899ee117e9..f9848f3c0719 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -26,6 +27,7 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; +import com.android.systemui.R; import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; @@ -40,6 +42,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.InjectionInflationController; @@ -74,12 +77,16 @@ public class StatusBarWindowViewTest extends SysuiTestCase { @Mock private DozeLog mDozeLog; @Mock private DozeParameters mDozeParameters; @Mock private DockManager mDockManager; + @Mock private NotificationPanelViewController mNotificationPanelViewController; + @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mView = new StatusBarWindowView(getContext(), null); + mView = spy(new StatusBarWindowView(getContext(), null)); + when(mView.findViewById(R.id.notification_stack_scroller)) + .thenReturn(mNotificationStackScrollLayout); when(mStatusBarStateController.isDozing()).thenReturn(false); mDependency.injectTestDependency(ShadeController.class, mShadeController); @@ -104,7 +111,8 @@ public class StatusBarWindowViewTest extends SysuiTestCase { new CommandQueue(mContext), mShadeController, mDockManager, - mView); + mView, + mNotificationPanelViewController); mController.setupExpandedStatusBar(); mController.setService(mStatusBar); mController.setDragDownHelper(mDragDownHelper); diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java index f4c27771c846..5b79d514fcc6 100644 --- a/rs/java/android/renderscript/RenderScript.java +++ b/rs/java/android/renderscript/RenderScript.java @@ -447,44 +447,33 @@ public class RenderScript { validate(); return rsnAllocationCreateTyped(mContext, type, mip, usage, pointer); } - native long rsnAllocationCreateFromBitmap(long con, long type, int mip, long bitmapHandle, + + native long rsnAllocationCreateFromBitmap(long con, long type, int mip, Bitmap bmp, int usage); synchronized long nAllocationCreateFromBitmap(long type, int mip, Bitmap bmp, int usage) { validate(); - return rsnAllocationCreateFromBitmap(mContext, type, mip, bmp.getNativeInstance(), usage); + return rsnAllocationCreateFromBitmap(mContext, type, mip, bmp, usage); } - native long rsnAllocationCreateBitmapBackedAllocation(long con, long type, int mip, long bitmapHandle, + native long rsnAllocationCreateBitmapBackedAllocation(long con, long type, int mip, Bitmap bmp, int usage); synchronized long nAllocationCreateBitmapBackedAllocation(long type, int mip, Bitmap bmp, int usage) { validate(); - return rsnAllocationCreateBitmapBackedAllocation(mContext, type, mip, bmp.getNativeInstance(), - usage); + return rsnAllocationCreateBitmapBackedAllocation(mContext, type, mip, bmp, usage); } - native long rsnAllocationCubeCreateFromBitmap(long con, long type, int mip, long bitmapHandle, + native long rsnAllocationCubeCreateFromBitmap(long con, long type, int mip, Bitmap bmp, int usage); synchronized long nAllocationCubeCreateFromBitmap(long type, int mip, Bitmap bmp, int usage) { validate(); - return rsnAllocationCubeCreateFromBitmap(mContext, type, mip, bmp.getNativeInstance(), - usage); - } - native long rsnAllocationCreateBitmapRef(long con, long type, long bitmapHandle); - synchronized long nAllocationCreateBitmapRef(long type, Bitmap bmp) { - validate(); - return rsnAllocationCreateBitmapRef(mContext, type, bmp.getNativeInstance()); - } - native long rsnAllocationCreateFromAssetStream(long con, int mips, int assetStream, int usage); - synchronized long nAllocationCreateFromAssetStream(int mips, int assetStream, int usage) { - validate(); - return rsnAllocationCreateFromAssetStream(mContext, mips, assetStream, usage); + return rsnAllocationCubeCreateFromBitmap(mContext, type, mip, bmp, usage); } - native void rsnAllocationCopyToBitmap(long con, long alloc, long bitmapHandle); + native void rsnAllocationCopyToBitmap(long con, long alloc, Bitmap bmp); synchronized void nAllocationCopyToBitmap(long alloc, Bitmap bmp) { validate(); - rsnAllocationCopyToBitmap(mContext, alloc, bmp.getNativeInstance()); + rsnAllocationCopyToBitmap(mContext, alloc, bmp); } native void rsnAllocationSyncAll(long con, long alloc, int src); @@ -537,10 +526,10 @@ public class RenderScript { validate(); rsnAllocationGenerateMipmaps(mContext, alloc); } - native void rsnAllocationCopyFromBitmap(long con, long alloc, long bitmapHandle); + native void rsnAllocationCopyFromBitmap(long con, long alloc, Bitmap bmp); synchronized void nAllocationCopyFromBitmap(long alloc, Bitmap bmp) { validate(); - rsnAllocationCopyFromBitmap(mContext, alloc, bmp.getNativeInstance()); + rsnAllocationCopyFromBitmap(mContext, alloc, bmp); } diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk index 0854b9582187..f9ef0b7b8e9d 100644 --- a/rs/jni/Android.mk +++ b/rs/jni/Android.mk @@ -12,7 +12,6 @@ LOCAL_SHARED_LIBRARIES := \ libRS \ libcutils \ liblog \ - libhwui \ libutils \ libui \ libgui \ diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index dfee96182a48..5ae895dbbce6 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -32,10 +32,10 @@ #include "jni.h" #include <nativehelper/JNIHelp.h> +#include <android/graphics/bitmap.h> #include "android_runtime/AndroidRuntime.h" #include "android_runtime/android_view_Surface.h" #include "android_runtime/android_util_AssetManager.h" -#include "android/graphics/GraphicsJNI.h" #include "android/native_window.h" #include "android/native_window_jni.h" @@ -1319,27 +1319,28 @@ nAllocationGenerateMipmaps(JNIEnv *_env, jobject _this, jlong con, jlong alloc) rsAllocationGenerateMipmaps((RsContext)con, (RsAllocation)alloc); } +static size_t computeByteSize(const android::graphics::Bitmap& bitmap) { + AndroidBitmapInfo info = bitmap.getInfo(); + return info.height * info.stride; +} + static jlong nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type, jint mip, - jlong bitmapPtr, jint usage) + jobject jbitmap, jint usage) { - SkBitmap bitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); - + android::graphics::Bitmap bitmap(_env, jbitmap); const void* ptr = bitmap.getPixels(); jlong id = (jlong)(uintptr_t)rsAllocationCreateFromBitmap((RsContext)con, (RsType)type, (RsAllocationMipmapControl)mip, - ptr, bitmap.computeByteSize(), usage); + ptr, computeByteSize(bitmap), usage); return id; } static jlong nAllocationCreateBitmapBackedAllocation(JNIEnv *_env, jobject _this, jlong con, jlong type, - jint mip, jlong bitmapPtr, jint usage) + jint mip, jobject jbitmap, jint usage) { - SkBitmap bitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); - + android::graphics::Bitmap bitmap(_env, jbitmap); const void* ptr = bitmap.getPixels(); jlong id = (jlong)(uintptr_t)rsAllocationCreateTyped((RsContext)con, (RsType)type, (RsAllocationMipmapControl)mip, @@ -1349,40 +1350,35 @@ nAllocationCreateBitmapBackedAllocation(JNIEnv *_env, jobject _this, jlong con, static jlong nAllocationCubeCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type, jint mip, - jlong bitmapPtr, jint usage) + jobject jbitmap, jint usage) { - SkBitmap bitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); - + android::graphics::Bitmap bitmap(_env, jbitmap); const void* ptr = bitmap.getPixels(); jlong id = (jlong)(uintptr_t)rsAllocationCubeCreateFromBitmap((RsContext)con, (RsType)type, (RsAllocationMipmapControl)mip, - ptr, bitmap.computeByteSize(), usage); + ptr, computeByteSize(bitmap), usage); return id; } static void -nAllocationCopyFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jlong bitmapPtr) +nAllocationCopyFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject jbitmap) { - SkBitmap bitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); - int w = bitmap.width(); - int h = bitmap.height(); + android::graphics::Bitmap bitmap(_env, jbitmap); + int w = bitmap.getInfo().width; + int h = bitmap.getInfo().height; const void* ptr = bitmap.getPixels(); rsAllocation2DData((RsContext)con, (RsAllocation)alloc, 0, 0, 0, RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, - w, h, ptr, bitmap.computeByteSize(), 0); + w, h, ptr, computeByteSize(bitmap), 0); } static void -nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jlong bitmapPtr) +nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject jbitmap) { - SkBitmap bitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); - + android::graphics::Bitmap bitmap(_env, jbitmap); void* ptr = bitmap.getPixels(); - rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, bitmap.computeByteSize()); + rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, computeByteSize(bitmap)); bitmap.notifyPixelsChanged(); } @@ -2867,12 +2863,12 @@ static const JNINativeMethod methods[] = { {"rsnTypeGetNativeData", "(JJ[J)V", (void*)nTypeGetNativeData }, {"rsnAllocationCreateTyped", "(JJIIJ)J", (void*)nAllocationCreateTyped }, -{"rsnAllocationCreateFromBitmap", "(JJIJI)J", (void*)nAllocationCreateFromBitmap }, -{"rsnAllocationCreateBitmapBackedAllocation", "(JJIJI)J", (void*)nAllocationCreateBitmapBackedAllocation }, -{"rsnAllocationCubeCreateFromBitmap","(JJIJI)J", (void*)nAllocationCubeCreateFromBitmap }, +{"rsnAllocationCreateFromBitmap", "(JJILandroid/graphics/Bitmap;I)J", (void*)nAllocationCreateFromBitmap }, +{"rsnAllocationCreateBitmapBackedAllocation", "(JJILandroid/graphics/Bitmap;I)J", (void*)nAllocationCreateBitmapBackedAllocation }, +{"rsnAllocationCubeCreateFromBitmap","(JJILandroid/graphics/Bitmap;I)J", (void*)nAllocationCubeCreateFromBitmap }, -{"rsnAllocationCopyFromBitmap", "(JJJ)V", (void*)nAllocationCopyFromBitmap }, -{"rsnAllocationCopyToBitmap", "(JJJ)V", (void*)nAllocationCopyToBitmap }, +{"rsnAllocationCopyFromBitmap", "(JJLandroid/graphics/Bitmap;)V", (void*)nAllocationCopyFromBitmap }, +{"rsnAllocationCopyToBitmap", "(JJLandroid/graphics/Bitmap;)V", (void*)nAllocationCopyToBitmap }, {"rsnAllocationSyncAll", "(JJI)V", (void*)nAllocationSyncAll }, {"rsnAllocationSetupBufferQueue", "(JJI)V", (void*)nAllocationSetupBufferQueue }, diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 472baf6700c6..ba9d757d979f 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -205,6 +205,7 @@ import android.view.Display; import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.Gravity; +import android.view.IDisplayWindowInsetsController; import android.view.ISystemGestureExclusionListener; import android.view.IWindow; import android.view.InputChannel; @@ -218,6 +219,7 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; import android.view.View; +import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManagerPolicyConstants.PointerEventListener; @@ -570,6 +572,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ WindowState mInputMethodTarget; + InsetsControlTarget mInputMethodControlTarget; + /** If true hold off on modifying the animation layer of mInputMethodTarget */ boolean mInputMethodTargetWaitingAnim; @@ -598,6 +602,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final float mWindowCornerRadius; private final SparseArray<ShellRoot> mShellRoots = new SparseArray<>(); + RemoteInsetsControlTarget mRemoteInsetsControlTarget = null; + private final IBinder.DeathRecipient mRemoteInsetsDeath = + () -> { + synchronized (mWmService.mGlobalLock) { + mRemoteInsetsControlTarget = null; + } + }; private RootWindowContainer mRootWindowContainer; @@ -1156,6 +1167,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mShellRoots.remove(windowType); } + void setRemoteInsetsController(IDisplayWindowInsetsController controller) { + if (mRemoteInsetsControlTarget != null) { + mRemoteInsetsControlTarget.mRemoteInsetsController.asBinder().unlinkToDeath( + mRemoteInsetsDeath, 0); + mRemoteInsetsControlTarget = null; + } + if (controller != null) { + try { + controller.asBinder().linkToDeath(mRemoteInsetsDeath, 0); + mRemoteInsetsControlTarget = new RemoteInsetsControlTarget(controller); + } catch (RemoteException e) { + return; + } + } + } + /** Changes the display the input window token is housed on to this one. */ void reParentWindowToken(WindowToken token) { final DisplayContent prevDc = token.getDisplayContent(); @@ -3383,6 +3410,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } + boolean isImeAttachedToApp() { + return (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null + && mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN + // An activity with override bounds should be letterboxed inside its parent bounds, + // so it doesn't fill the screen. + && mInputMethodTarget.mActivityRecord.matchParentBounds()); + } + private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) { if (target == mInputMethodTarget && mInputMethodTargetWaitingAnim == targetWaitingAnim) { return; @@ -3391,7 +3426,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mInputMethodTarget = target; mInputMethodTargetWaitingAnim = targetWaitingAnim; assignWindowLayers(false /* setLayoutNeeded */); - mInsetsStateController.onImeTargetChanged(target); + mInputMethodControlTarget = computeImeControlTarget(); + mInsetsStateController.onImeTargetChanged(mInputMethodControlTarget); updateImeParent(); } @@ -3416,11 +3452,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Attach it to app if the target is part of an app and such app is covering the entire // screen. If it's not covering the entire screen the IME might extend beyond the apps // bounds. - if (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null - && mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN - // An activity with override bounds should be letterboxed inside its parent bounds, - // so it doesn't fill the screen. - && mInputMethodTarget.mActivityRecord.matchParentBounds()) { + if (isImeAttachedToApp()) { return mInputMethodTarget.mActivityRecord.getSurfaceControl(); } @@ -3428,6 +3460,19 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mWindowContainers.getSurfaceControl(); } + /** + * Computes which control-target the IME should be attached to. + */ + @VisibleForTesting + InsetsControlTarget computeImeControlTarget() { + if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) { + return mRemoteInsetsControlTarget; + } + + // Otherwise, we just use the ime target + return mInputMethodTarget; + } + void setLayoutNeeded() { if (DEBUG_LAYOUT) Slog.w(TAG_WM, "setLayoutNeeded: callers=" + Debug.getCallers(3)); mLayoutNeeded = true; @@ -6688,4 +6733,50 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo Context getDisplayUiContext() { return mDisplayPolicy.getSystemUiContext(); } + + class RemoteInsetsControlTarget implements InsetsControlTarget { + private final IDisplayWindowInsetsController mRemoteInsetsController; + + RemoteInsetsControlTarget(IDisplayWindowInsetsController controller) { + mRemoteInsetsController = controller; + } + + void notifyInsetsChanged() { + try { + mRemoteInsetsController.insetsChanged( + getInsetsStateController().getRawInsetsState()); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to deliver inset state change", e); + } + } + + @Override + public void notifyInsetsControlChanged() { + final InsetsStateController stateController = getInsetsStateController(); + try { + mRemoteInsetsController.insetsControlChanged(stateController.getRawInsetsState(), + stateController.getControlsForDispatch(this)); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to deliver inset state change", e); + } + } + + @Override + public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) { + try { + mRemoteInsetsController.showInsets(types, fromIme); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to deliver showInsets", e); + } + } + + @Override + public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) { + try { + mRemoteInsetsController.hideInsets(types, fromIme); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to deliver showInsets", e); + } + } + } } diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 05ede21472f2..fb97ecf8bbc4 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -69,7 +69,7 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { mShowImeRunner = () -> { // Target should still be the same. if (isImeTargetFromDisplayContentAndImeSame()) { - mDisplayContent.mInputMethodTarget.showInsets( + mDisplayContent.mInputMethodControlTarget.showInsets( WindowInsets.Type.ime(), true /* fromIme */); } abortShowImePostLayout(); diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 184e7d61c355..7b40f609aaf1 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -210,7 +210,7 @@ class InsetsSourceProvider { new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top)); } - boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) { + boolean onInsetsModified(InsetsControlTarget caller, InsetsSource modifiedSource) { if (mControlTarget != caller || modifiedSource.isVisible() == mClientVisible) { return false; } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 152607470ed1..720493f4a466 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -91,6 +91,10 @@ class InsetsStateController { return state; } + InsetsState getRawInsetsState() { + return mState; + } + @Nullable InsetsSourceControl[] getControlsForDispatch(InsetsControlTarget target) { ArrayList<Integer> controlled = mControlTargetTypeMap.get(target); if (controlled == null) { @@ -144,7 +148,7 @@ class InsetsStateController { getImeSourceProvider().onPostInsetsDispatched(); } - void onInsetsModified(WindowState windowState, InsetsState state) { + void onInsetsModified(InsetsControlTarget windowState, InsetsState state) { boolean changed = false; for (int i = state.getSourcesCount() - 1; i >= 0; i--) { final InsetsSource source = state.sourceAt(i); @@ -296,6 +300,9 @@ class InsetsStateController { void notifyInsetsChanged() { mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */); + if (mDisplayContent.mRemoteInsetsControlTarget != null) { + mDisplayContent.mRemoteInsetsControlTarget.notifyInsetsChanged(); + } } void dump(String prefix, PrintWriter pw) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 223e9b9e3df2..fb1ec3300749 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -205,6 +205,7 @@ import android.view.DisplayInfo; import android.view.Gravity; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IDisplayFoldListener; +import android.view.IDisplayWindowInsetsController; import android.view.IDisplayWindowListener; import android.view.IDisplayWindowRotationController; import android.view.IDockedStackListener; @@ -3727,6 +3728,48 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void setDisplayWindowInsetsController( + int displayId, IDisplayWindowInsetsController insetsController) { + if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS); + } + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + return; + } + dc.setRemoteInsetsController(insetsController); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void modifyDisplayWindowInsets(int displayId, InsetsState state) { + if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS); + } + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null || dc.mRemoteInsetsControlTarget == null) { + return; + } + dc.getInsetsStateController().onInsetsModified( + dc.mRemoteInsetsControlTarget, state); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override public int watchRotation(IRotationWatcher watcher, int displayId) { final DisplayContent displayContent; synchronized (mGlobalLock) { @@ -7314,7 +7357,8 @@ public class WindowManagerService extends IWindowManager.Stub // If there was a pending IME show(), reset it as IME has been // requested to be hidden. dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout(); - dc.mInputMethodTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */); + dc.mInputMethodControlTarget.hideInsets(WindowInsets.Type.ime(), + true /* fromIme */); } } } diff --git a/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java index 2abcc76fdccc..2abcc76fdccc 100644 --- a/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java +++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java |