Merge "Phase 1 of status bar system event animations" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e438034aee
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2016 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- dot drawable for ongoing system privacy events -->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid
|
||||||
|
android:color="@color/privacy_circle"/>
|
||||||
|
<size
|
||||||
|
android:width="6dp"
|
||||||
|
android:height="6dp"
|
||||||
|
/>
|
||||||
|
</shape>
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
** See the License for the specific language governing permissions and
|
** See the License for the specific language governing permissions and
|
||||||
** limitations under the License.
|
** limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<!-- TODO: remove this in favor of requiring top and bottom layouts -->
|
||||||
<com.android.systemui.RegionInterceptingFrameLayout
|
<com.android.systemui.RegionInterceptingFrameLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/rounded_corners_default"
|
android:id="@+id/rounded_corners_default"
|
||||||
@@ -26,6 +28,25 @@
|
|||||||
android:layout_gravity="left|top"
|
android:layout_gravity="left|top"
|
||||||
android:tint="#ff000000"
|
android:tint="#ff000000"
|
||||||
android:src="@drawable/rounded"/>
|
android:src="@drawable/rounded"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/privacy_dot_left_container"
|
||||||
|
android:layout_height="@dimen/status_bar_height"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/status_bar_padding_top"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_gravity="left|top"
|
||||||
|
android:visibility="invisible" >
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/privacy_dot_left"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/system_animation_ongoing_dot"
|
||||||
|
android:visibility="visible" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/right"
|
android:id="@+id/right"
|
||||||
android:layout_width="12dp"
|
android:layout_width="12dp"
|
||||||
@@ -33,4 +54,22 @@
|
|||||||
android:tint="#ff000000"
|
android:tint="#ff000000"
|
||||||
android:layout_gravity="right|bottom"
|
android:layout_gravity="right|bottom"
|
||||||
android:src="@drawable/rounded"/>
|
android:src="@drawable/rounded"/>
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/privacy_dot_right_container"
|
||||||
|
android:layout_height="@dimen/status_bar_height"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/status_bar_padding_top"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:layout_gravity="right|top"
|
||||||
|
android:visibility="invisible" >
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/privacy_dot_right"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/system_animation_ongoing_dot"
|
||||||
|
android:visibility="visible" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
</com.android.systemui.RegionInterceptingFrameLayout>
|
</com.android.systemui.RegionInterceptingFrameLayout>
|
||||||
|
|||||||
@@ -26,6 +26,24 @@
|
|||||||
android:layout_gravity="left|bottom"
|
android:layout_gravity="left|bottom"
|
||||||
android:tint="#ff000000"
|
android:tint="#ff000000"
|
||||||
android:src="@drawable/rounded_corner_bottom"/>
|
android:src="@drawable/rounded_corner_bottom"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/privacy_dot_left_container"
|
||||||
|
android:layout_height="@dimen/status_bar_height"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/status_bar_padding_top"
|
||||||
|
android:layout_marginLeft="0dp"
|
||||||
|
android:layout_gravity="left|bottom"
|
||||||
|
android:visibility="invisible" >
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/privacy_dot"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical|right"
|
||||||
|
android:src="@drawable/system_animation_ongoing_dot"
|
||||||
|
android:visibility="visible" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/right"
|
android:id="@+id/right"
|
||||||
android:layout_width="12dp"
|
android:layout_width="12dp"
|
||||||
@@ -33,4 +51,21 @@
|
|||||||
android:tint="#ff000000"
|
android:tint="#ff000000"
|
||||||
android:layout_gravity="right|bottom"
|
android:layout_gravity="right|bottom"
|
||||||
android:src="@drawable/rounded_corner_bottom"/>
|
android:src="@drawable/rounded_corner_bottom"/>
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/privacy_dot_right_container"
|
||||||
|
android:layout_height="@dimen/status_bar_height"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/status_bar_padding_top"
|
||||||
|
android:layout_marginRight="0dp"
|
||||||
|
android:layout_gravity="right|bottom"
|
||||||
|
android:visibility="invisible" >
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/privacy_dot"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical|left"
|
||||||
|
android:src="@drawable/system_animation_ongoing_dot"
|
||||||
|
android:visibility="visible" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
</com.android.systemui.RegionInterceptingFrameLayout>
|
</com.android.systemui.RegionInterceptingFrameLayout>
|
||||||
|
|||||||
@@ -26,6 +26,24 @@
|
|||||||
android:layout_gravity="left|top"
|
android:layout_gravity="left|top"
|
||||||
android:tint="#ff000000"
|
android:tint="#ff000000"
|
||||||
android:src="@drawable/rounded_corner_top"/>
|
android:src="@drawable/rounded_corner_top"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/privacy_dot_left_container"
|
||||||
|
android:layout_height="@*android:dimen/status_bar_height_portrait"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/status_bar_padding_top"
|
||||||
|
android:layout_marginLeft="0dp"
|
||||||
|
android:layout_gravity="left|top"
|
||||||
|
android:visibility="invisible" >
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/privacy_dot"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical|right"
|
||||||
|
android:src="@drawable/system_animation_ongoing_dot"
|
||||||
|
android:visibility="visible" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/right"
|
android:id="@+id/right"
|
||||||
android:layout_width="12dp"
|
android:layout_width="12dp"
|
||||||
@@ -33,4 +51,24 @@
|
|||||||
android:tint="#ff000000"
|
android:tint="#ff000000"
|
||||||
android:layout_gravity="right|top"
|
android:layout_gravity="right|top"
|
||||||
android:src="@drawable/rounded_corner_top"/>
|
android:src="@drawable/rounded_corner_top"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/privacy_dot_right_container"
|
||||||
|
android:layout_height="@*android:dimen/status_bar_height_portrait"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/status_bar_padding_top"
|
||||||
|
android:layout_marginRight="0dp"
|
||||||
|
android:layout_gravity="right|top"
|
||||||
|
android:visibility="invisible" >
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/privacy_dot"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical|left"
|
||||||
|
android:src="@drawable/system_animation_ongoing_dot"
|
||||||
|
android:visibility="visible" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
||||||
</com.android.systemui.RegionInterceptingFrameLayout>
|
</com.android.systemui.RegionInterceptingFrameLayout>
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2021 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center_vertical|end"
|
||||||
|
android:paddingTop="@dimen/status_bar_padding_top"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/dot_view"
|
||||||
|
android:layout_width="10dp"
|
||||||
|
android:layout_height="10dp"
|
||||||
|
android:layout_gravity="center_vertical|end"
|
||||||
|
android:src="@drawable/system_animation_ongoing_dot"
|
||||||
|
android:visibility="invisible"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
@@ -79,6 +79,8 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
|
|||||||
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
|
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
|
||||||
import com.android.systemui.statusbar.SmartReplyController;
|
import com.android.systemui.statusbar.SmartReplyController;
|
||||||
import com.android.systemui.statusbar.VibratorHelper;
|
import com.android.systemui.statusbar.VibratorHelper;
|
||||||
|
import com.android.systemui.statusbar.events.PrivacyDotViewController;
|
||||||
|
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
|
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
|
||||||
import com.android.systemui.statusbar.notification.NotificationFilter;
|
import com.android.systemui.statusbar.notification.NotificationFilter;
|
||||||
@@ -352,6 +354,8 @@ public class Dependency {
|
|||||||
@Inject Lazy<DeviceConfigProxy> mDeviceConfigProxy;
|
@Inject Lazy<DeviceConfigProxy> mDeviceConfigProxy;
|
||||||
@Inject Lazy<NavigationBarOverlayController> mNavbarButtonsControllerLazy;
|
@Inject Lazy<NavigationBarOverlayController> mNavbarButtonsControllerLazy;
|
||||||
@Inject Lazy<TelephonyListenerManager> mTelephonyListenerManager;
|
@Inject Lazy<TelephonyListenerManager> mTelephonyListenerManager;
|
||||||
|
@Inject Lazy<SystemStatusAnimationScheduler> mSystemStatusAnimationSchedulerLazy;
|
||||||
|
@Inject Lazy<PrivacyDotViewController> mPrivacyDotViewControllerLazy;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Dependency() {
|
public Dependency() {
|
||||||
@@ -561,6 +565,10 @@ public class Dependency {
|
|||||||
|
|
||||||
mProviders.put(NavigationBarOverlayController.class, mNavbarButtonsControllerLazy::get);
|
mProviders.put(NavigationBarOverlayController.class, mNavbarButtonsControllerLazy::get);
|
||||||
|
|
||||||
|
mProviders.put(SystemStatusAnimationScheduler.class,
|
||||||
|
mSystemStatusAnimationSchedulerLazy::get);
|
||||||
|
mProviders.put(PrivacyDotViewController.class, mPrivacyDotViewControllerLazy::get);
|
||||||
|
|
||||||
Dependency.setInstance(this);
|
Dependency.setInstance(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ import com.android.systemui.dagger.SysUISingleton;
|
|||||||
import com.android.systemui.dagger.qualifiers.Main;
|
import com.android.systemui.dagger.qualifiers.Main;
|
||||||
import com.android.systemui.qs.SecureSetting;
|
import com.android.systemui.qs.SecureSetting;
|
||||||
import com.android.systemui.settings.UserTracker;
|
import com.android.systemui.settings.UserTracker;
|
||||||
|
import com.android.systemui.statusbar.events.PrivacyDotViewController;
|
||||||
import com.android.systemui.tuner.TunerService;
|
import com.android.systemui.tuner.TunerService;
|
||||||
import com.android.systemui.tuner.TunerService.Tunable;
|
import com.android.systemui.tuner.TunerService.Tunable;
|
||||||
import com.android.systemui.util.settings.SecureSettings;
|
import com.android.systemui.util.settings.SecureSettings;
|
||||||
@@ -126,6 +127,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
|||||||
private DisplayManager.DisplayListener mDisplayListener;
|
private DisplayManager.DisplayListener mDisplayListener;
|
||||||
private CameraAvailabilityListener mCameraListener;
|
private CameraAvailabilityListener mCameraListener;
|
||||||
private final UserTracker mUserTracker;
|
private final UserTracker mUserTracker;
|
||||||
|
private final PrivacyDotViewController mDotViewController;
|
||||||
|
|
||||||
//TODO: These are piecemeal being updated to Points for now to support non-square rounded
|
//TODO: These are piecemeal being updated to Points for now to support non-square rounded
|
||||||
// corners. for now it is only supposed when reading the intrinsic size from the drawables with
|
// corners. for now it is only supposed when reading the intrinsic size from the drawables with
|
||||||
@@ -140,6 +142,11 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
|||||||
protected View[] mOverlays;
|
protected View[] mOverlays;
|
||||||
@Nullable
|
@Nullable
|
||||||
private DisplayCutoutView[] mCutoutViews;
|
private DisplayCutoutView[] mCutoutViews;
|
||||||
|
//TODO:
|
||||||
|
View mTopLeftDot;
|
||||||
|
View mTopRightDot;
|
||||||
|
View mBottomLeftDot;
|
||||||
|
View mBottomRightDot;
|
||||||
private float mDensity;
|
private float mDensity;
|
||||||
private WindowManager mWindowManager;
|
private WindowManager mWindowManager;
|
||||||
private int mRotation;
|
private int mRotation;
|
||||||
@@ -147,6 +154,8 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
|||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
private boolean mPendingRotationChange;
|
private boolean mPendingRotationChange;
|
||||||
private boolean mIsRoundedCornerMultipleRadius;
|
private boolean mIsRoundedCornerMultipleRadius;
|
||||||
|
private int mStatusBarHeightPortrait;
|
||||||
|
private int mStatusBarHeightLandscape;
|
||||||
|
|
||||||
private CameraAvailabilityListener.CameraTransitionCallback mCameraTransitionCallback =
|
private CameraAvailabilityListener.CameraTransitionCallback mCameraTransitionCallback =
|
||||||
new CameraAvailabilityListener.CameraTransitionCallback() {
|
new CameraAvailabilityListener.CameraTransitionCallback() {
|
||||||
@@ -205,13 +214,15 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
|||||||
SecureSettings secureSettings,
|
SecureSettings secureSettings,
|
||||||
BroadcastDispatcher broadcastDispatcher,
|
BroadcastDispatcher broadcastDispatcher,
|
||||||
TunerService tunerService,
|
TunerService tunerService,
|
||||||
UserTracker userTracker) {
|
UserTracker userTracker,
|
||||||
|
PrivacyDotViewController dotViewController) {
|
||||||
super(context);
|
super(context);
|
||||||
mMainHandler = handler;
|
mMainHandler = handler;
|
||||||
mSecureSettings = secureSettings;
|
mSecureSettings = secureSettings;
|
||||||
mBroadcastDispatcher = broadcastDispatcher;
|
mBroadcastDispatcher = broadcastDispatcher;
|
||||||
mTunerService = tunerService;
|
mTunerService = tunerService;
|
||||||
mUserTracker = userTracker;
|
mUserTracker = userTracker;
|
||||||
|
mDotViewController = dotViewController;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -222,6 +233,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
|||||||
}
|
}
|
||||||
mHandler = startHandlerThread();
|
mHandler = startHandlerThread();
|
||||||
mHandler.post(this::startOnScreenDecorationsThread);
|
mHandler.post(this::startOnScreenDecorationsThread);
|
||||||
|
mDotViewController.setUiExecutor(mHandler::post);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -286,6 +298,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
|||||||
|
|
||||||
private void setupDecorations() {
|
private void setupDecorations() {
|
||||||
if (hasRoundedCorners() || shouldDrawCutout()) {
|
if (hasRoundedCorners() || shouldDrawCutout()) {
|
||||||
|
updateStatusBarHeight();
|
||||||
final DisplayCutout cutout = getCutout();
|
final DisplayCutout cutout = getCutout();
|
||||||
final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
|
final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
|
||||||
int rotatedPos;
|
int rotatedPos;
|
||||||
@@ -298,6 +311,10 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
|||||||
removeOverlay(i);
|
removeOverlay(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Overlays have been created, send the dots to the controller
|
||||||
|
//TODO: need a better way to do this
|
||||||
|
mDotViewController.initialize(
|
||||||
|
mTopLeftDot, mTopRightDot, mBottomLeftDot, mBottomRightDot);
|
||||||
} else {
|
} else {
|
||||||
removeAllOverlays();
|
removeAllOverlays();
|
||||||
}
|
}
|
||||||
@@ -431,14 +448,21 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
|||||||
private View overlayForPosition(@BoundsPosition int pos) {
|
private View overlayForPosition(@BoundsPosition int pos) {
|
||||||
switch (pos) {
|
switch (pos) {
|
||||||
case BOUNDS_POSITION_TOP:
|
case BOUNDS_POSITION_TOP:
|
||||||
return LayoutInflater.from(mContext)
|
case BOUNDS_POSITION_LEFT:
|
||||||
|
View top = LayoutInflater.from(mContext)
|
||||||
.inflate(R.layout.rounded_corners_top, null);
|
.inflate(R.layout.rounded_corners_top, null);
|
||||||
|
mTopLeftDot = top.findViewById(R.id.privacy_dot_left_container);
|
||||||
|
mTopRightDot = top.findViewById(R.id.privacy_dot_right_container);
|
||||||
|
return top;
|
||||||
case BOUNDS_POSITION_BOTTOM:
|
case BOUNDS_POSITION_BOTTOM:
|
||||||
return LayoutInflater.from(mContext)
|
case BOUNDS_POSITION_RIGHT:
|
||||||
|
View bottom = LayoutInflater.from(mContext)
|
||||||
.inflate(R.layout.rounded_corners_bottom, null);
|
.inflate(R.layout.rounded_corners_bottom, null);
|
||||||
|
mBottomLeftDot = bottom.findViewById(R.id.privacy_dot_left_container);
|
||||||
|
mBottomRightDot = bottom.findViewById(R.id.privacy_dot_right_container);
|
||||||
|
return bottom;
|
||||||
default:
|
default:
|
||||||
return LayoutInflater.from(mContext)
|
throw new IllegalArgumentException("Unknown bounds position");
|
||||||
.inflate(R.layout.rounded_corners, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -575,6 +599,11 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
|||||||
View child;
|
View child;
|
||||||
for (int j = 0; j < size; j++) {
|
for (int j = 0; j < size; j++) {
|
||||||
child = ((ViewGroup) mOverlays[i]).getChildAt(j);
|
child = ((ViewGroup) mOverlays[i]).getChildAt(j);
|
||||||
|
if (child.getId() == R.id.privacy_dot_left_container
|
||||||
|
|| child.getId() == R.id.privacy_dot_right_container) {
|
||||||
|
// Exclude privacy dot from color inversion (for now?)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (child instanceof ImageView) {
|
if (child instanceof ImageView) {
|
||||||
((ImageView) child).setImageTintList(tintList);
|
((ImageView) child).setImageTintList(tintList);
|
||||||
} else if (child instanceof DisplayCutoutView) {
|
} else if (child instanceof DisplayCutoutView) {
|
||||||
@@ -611,10 +640,15 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
|||||||
Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(),
|
Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(),
|
||||||
"must call on " + mHandler.getLooper().getThread()
|
"must call on " + mHandler.getLooper().getThread()
|
||||||
+ ", but was " + Thread.currentThread());
|
+ ", but was " + Thread.currentThread());
|
||||||
|
|
||||||
|
int newRotation = mContext.getDisplay().getRotation();
|
||||||
|
if (mRotation != newRotation) {
|
||||||
|
mDotViewController.updateRotation(newRotation);
|
||||||
|
}
|
||||||
|
|
||||||
if (mPendingRotationChange) {
|
if (mPendingRotationChange) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int newRotation = mContext.getDisplay().getRotation();
|
|
||||||
if (newRotation != mRotation) {
|
if (newRotation != mRotation) {
|
||||||
mRotation = newRotation;
|
mRotation = newRotation;
|
||||||
|
|
||||||
@@ -630,6 +664,14 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateStatusBarHeight() {
|
||||||
|
mStatusBarHeightLandscape = mContext.getResources().getDimensionPixelSize(
|
||||||
|
com.android.internal.R.dimen.status_bar_height_landscape);
|
||||||
|
mStatusBarHeightPortrait = mContext.getResources().getDimensionPixelSize(
|
||||||
|
com.android.internal.R.dimen.status_bar_height_portrait);
|
||||||
|
mDotViewController.setStatusBarHeights(mStatusBarHeightPortrait, mStatusBarHeightLandscape);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateRoundedCornerRadii() {
|
private void updateRoundedCornerRadii() {
|
||||||
// We should eventually move to just using the intrinsic size of the drawables since
|
// We should eventually move to just using the intrinsic size of the drawables since
|
||||||
// they should be sized to the exact pixels they want to cover. Therefore I'm purposely not
|
// they should be sized to the exact pixels they want to cover. Therefore I'm purposely not
|
||||||
|
|||||||
@@ -0,0 +1,352 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.events
|
||||||
|
|
||||||
|
import android.animation.Animator
|
||||||
|
import android.animation.AnimatorListenerAdapter
|
||||||
|
import android.animation.ObjectAnimator
|
||||||
|
import android.annotation.UiThread
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
|
||||||
|
import com.android.systemui.animation.Interpolators
|
||||||
|
import com.android.systemui.R
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
|
|
||||||
|
import java.lang.IllegalStateException
|
||||||
|
import java.util.concurrent.Executor
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Understands how to keep the persistent privacy dot in the corner of the screen in
|
||||||
|
* ScreenDecorations, which does not rotate with the device.
|
||||||
|
*
|
||||||
|
* The basic principle here is that each dot will sit in a box that is equal to the margins of the
|
||||||
|
* status bar (specifically the status_bar_contents view in PhoneStatusBarView). Each dot container
|
||||||
|
* will have its gravity set towards the corner (i.e., top-right corner gets top|right gravity), and
|
||||||
|
* the contained ImageView will be set to center_vertical and away from the corner horizontally. The
|
||||||
|
* Views will match the status bar top padding and status bar height so that the dot can appear to
|
||||||
|
* reside directly after the status bar system contents (basically to the right of the battery).
|
||||||
|
*
|
||||||
|
* NOTE: any operation that modifies views directly must run on the provided executor, because
|
||||||
|
* these views are owned by ScreenDecorations and it runs in its own thread
|
||||||
|
*/
|
||||||
|
|
||||||
|
@SysUISingleton
|
||||||
|
class PrivacyDotViewController @Inject constructor(
|
||||||
|
@Main val mainExecutor: Executor,
|
||||||
|
val animationScheduler: SystemStatusAnimationScheduler
|
||||||
|
) {
|
||||||
|
private var rotation = 0
|
||||||
|
private var leftSize = 0
|
||||||
|
private var rightSize = 0
|
||||||
|
|
||||||
|
private var sbHeightPortrait = 0
|
||||||
|
private var sbHeightLandscape = 0
|
||||||
|
|
||||||
|
private var hasMultipleHeights = false
|
||||||
|
private var needsHeightUpdate = false
|
||||||
|
private var needsRotationUpdate = false
|
||||||
|
private var needsMarginUpdate = false
|
||||||
|
|
||||||
|
private lateinit var tl: View
|
||||||
|
private lateinit var tr: View
|
||||||
|
private lateinit var bl: View
|
||||||
|
private lateinit var br: View
|
||||||
|
|
||||||
|
// Track which corner is active (based on orientation + RTL)
|
||||||
|
private var designatedCorner: View? = null
|
||||||
|
|
||||||
|
// Privacy dots are created in ScreenDecoration's UiThread, which is not the main thread
|
||||||
|
private var uiExecutor: Executor? = null
|
||||||
|
|
||||||
|
private val views: Sequence<View>
|
||||||
|
get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
|
||||||
|
|
||||||
|
fun setUiExecutor(e: Executor) {
|
||||||
|
uiExecutor = e
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
fun updateRotation(rot: Int) {
|
||||||
|
if (rot == rotation) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// A rotation has started, hide the views to avoid flicker
|
||||||
|
setCornerVisibilities(View.INVISIBLE)
|
||||||
|
|
||||||
|
if (hasMultipleHeights && (rotation % 2) != (rot % 2)) {
|
||||||
|
// we've changed from vertical to horizontal; update status bar height
|
||||||
|
needsHeightUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
rotation = rot
|
||||||
|
needsRotationUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private fun updateHeights(rot: Int) {
|
||||||
|
val height = when (rot) {
|
||||||
|
0, 2 -> sbHeightPortrait
|
||||||
|
1, 3 -> sbHeightLandscape
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
views.forEach { it.layoutParams.height = height }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the gravity and margins of the privacy views
|
||||||
|
@UiThread
|
||||||
|
private fun updateRotations() {
|
||||||
|
// To keep a view in the corner, its gravity is always the description of its current corner
|
||||||
|
// Therefore, just figure out which view is in which corner. This turns out to be something
|
||||||
|
// like (myCorner - rot) mod 4, where topLeft = 0, topRight = 1, etc. and portrait = 0, and
|
||||||
|
// rotating the device counter-clockwise increments rotation by 1
|
||||||
|
|
||||||
|
views.forEach { corner ->
|
||||||
|
val rotatedCorner = rotatedCorner(cornerForView(corner))
|
||||||
|
(corner.layoutParams as FrameLayout.LayoutParams).apply {
|
||||||
|
gravity = rotatedCorner.toGravity()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the dot's view gravity to hug the status bar
|
||||||
|
(corner.findViewById<View>(R.id.privacy_dot)
|
||||||
|
.layoutParams as FrameLayout.LayoutParams)
|
||||||
|
.gravity = rotatedCorner.innerGravity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private fun updateCornerSizes() {
|
||||||
|
views.forEach { corner ->
|
||||||
|
val rotatedCorner = rotatedCorner(cornerForView(corner))
|
||||||
|
val w = widthForCorner(rotatedCorner)
|
||||||
|
Log.d(TAG, "updateCornerSizes: setting (${cornerForView(corner)}) to $w")
|
||||||
|
(corner.layoutParams as FrameLayout.LayoutParams).width = w
|
||||||
|
corner.requestLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Designated view will be the one at statusbar's view.END
|
||||||
|
@UiThread
|
||||||
|
private fun selectDesignatedCorner(): View? {
|
||||||
|
if (!this::tl.isInitialized) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val isRtl = tl.isLayoutRtl
|
||||||
|
|
||||||
|
return when (rotation) {
|
||||||
|
0 -> if (isRtl) tl else tr
|
||||||
|
1 -> if (isRtl) tr else br
|
||||||
|
2 -> if (isRtl) br else bl
|
||||||
|
3 -> if (isRtl) bl else tl
|
||||||
|
else -> throw IllegalStateException("unknown rotation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track the current designated corner and maybe animate to a new rotation
|
||||||
|
@UiThread
|
||||||
|
private fun updateDesignatedCorner(newCorner: View) {
|
||||||
|
designatedCorner = newCorner
|
||||||
|
|
||||||
|
if (animationScheduler.hasPersistentDot) {
|
||||||
|
designatedCorner!!.visibility = View.VISIBLE
|
||||||
|
designatedCorner!!.alpha = 0f
|
||||||
|
designatedCorner!!.animate()
|
||||||
|
.alpha(1.0f)
|
||||||
|
.setDuration(300)
|
||||||
|
.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private fun setCornerVisibilities(vis: Int) {
|
||||||
|
views.forEach { corner ->
|
||||||
|
corner.visibility = vis
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cornerForView(v: View): Int {
|
||||||
|
return when (v) {
|
||||||
|
tl -> TOP_LEFT
|
||||||
|
tr -> TOP_RIGHT
|
||||||
|
bl -> BOTTOM_LEFT
|
||||||
|
br -> BOTTOM_RIGHT
|
||||||
|
else -> throw IllegalArgumentException("not a corner view")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rotatedCorner(corner: Int): Int {
|
||||||
|
var modded = corner - rotation
|
||||||
|
if (modded < 0) {
|
||||||
|
modded += 4
|
||||||
|
}
|
||||||
|
|
||||||
|
return modded
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun widthForCorner(corner: Int): Int {
|
||||||
|
return when (corner) {
|
||||||
|
TOP_LEFT, BOTTOM_LEFT -> leftSize
|
||||||
|
TOP_RIGHT, BOTTOM_RIGHT -> rightSize
|
||||||
|
else -> throw IllegalArgumentException("Unknown corner")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initialize(topLeft: View, topRight: View, bottomLeft: View, bottomRight: View) {
|
||||||
|
if (this::tl.isInitialized && this::tr.isInitialized &&
|
||||||
|
this::bl.isInitialized && this::br.isInitialized) {
|
||||||
|
if (tl == topLeft && tr == topRight && bl == bottomLeft && br == bottomRight) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tl = topLeft
|
||||||
|
tr = topRight
|
||||||
|
bl = bottomLeft
|
||||||
|
br = bottomRight
|
||||||
|
|
||||||
|
designatedCorner = selectDesignatedCorner()
|
||||||
|
mainExecutor.execute {
|
||||||
|
animationScheduler.addCallback(systemStatusAnimationCallback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the status bar height in portrait and landscape, in pixels. If they are the same you can
|
||||||
|
* pass the same value twice
|
||||||
|
*/
|
||||||
|
fun setStatusBarHeights(portrait: Int, landscape: Int) {
|
||||||
|
sbHeightPortrait = portrait
|
||||||
|
sbHeightLandscape = landscape
|
||||||
|
|
||||||
|
hasMultipleHeights = portrait != landscape
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The dot view containers will fill the margin in order to position the dots correctly
|
||||||
|
*
|
||||||
|
* @param left the space between the status bar contents and the left side of the screen
|
||||||
|
* @param right space between the status bar contents and the right side of the screen
|
||||||
|
*/
|
||||||
|
fun setStatusBarMargins(left: Int, right: Int) {
|
||||||
|
leftSize = left
|
||||||
|
rightSize = right
|
||||||
|
|
||||||
|
needsMarginUpdate = true
|
||||||
|
|
||||||
|
// Margins come after PhoneStatusBarView does a layout pass, and so will always happen
|
||||||
|
// after rotation changes. It is safe to execute the updates from here
|
||||||
|
uiExecutor?.execute {
|
||||||
|
doUpdates(needsRotationUpdate, needsHeightUpdate, needsMarginUpdate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doUpdates(rot: Boolean, height: Boolean, width: Boolean) {
|
||||||
|
var newDesignatedCorner: View? = null
|
||||||
|
|
||||||
|
if (rot) {
|
||||||
|
needsRotationUpdate = false
|
||||||
|
updateRotations()
|
||||||
|
newDesignatedCorner = selectDesignatedCorner()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (height) {
|
||||||
|
needsHeightUpdate = false
|
||||||
|
updateHeights(rotation)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width) {
|
||||||
|
needsMarginUpdate = false
|
||||||
|
updateCornerSizes()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newDesignatedCorner != null && newDesignatedCorner != designatedCorner) {
|
||||||
|
updateDesignatedCorner(newDesignatedCorner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val systemStatusAnimationCallback: SystemStatusAnimationCallback =
|
||||||
|
object : SystemStatusAnimationCallback {
|
||||||
|
override fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? {
|
||||||
|
if (designatedCorner == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val alpha = ObjectAnimator.ofFloat(
|
||||||
|
designatedCorner, "alpha", 0f, 1f)
|
||||||
|
alpha.duration = DURATION
|
||||||
|
alpha.interpolator = Interpolators.ALPHA_OUT
|
||||||
|
alpha.addListener(object : AnimatorListenerAdapter() {
|
||||||
|
override fun onAnimationStart(animator: Animator) {
|
||||||
|
uiExecutor?.execute { designatedCorner?.visibility = View.VISIBLE }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onHidePersistentDot(): Animator? {
|
||||||
|
if (designatedCorner == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val alpha = ObjectAnimator.ofFloat(
|
||||||
|
designatedCorner, "alpha", 1f, 0f)
|
||||||
|
alpha.duration = DURATION
|
||||||
|
alpha.interpolator = Interpolators.ALPHA_OUT
|
||||||
|
alpha.addListener(object : AnimatorListenerAdapter() {
|
||||||
|
override fun onAnimationEnd(animator: Animator) {
|
||||||
|
uiExecutor?.execute { designatedCorner?.visibility = View.INVISIBLE }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
alpha.start()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const val TOP_LEFT = 0
|
||||||
|
const val TOP_RIGHT = 1
|
||||||
|
const val BOTTOM_RIGHT = 2
|
||||||
|
const val BOTTOM_LEFT = 3
|
||||||
|
private const val DURATION = 160L
|
||||||
|
private const val TAG = "PrivacyDotViewController"
|
||||||
|
|
||||||
|
private fun Int.toGravity(): Int {
|
||||||
|
return when (this) {
|
||||||
|
TOP_LEFT -> Gravity.TOP or Gravity.LEFT
|
||||||
|
TOP_RIGHT -> Gravity.TOP or Gravity.RIGHT
|
||||||
|
BOTTOM_LEFT -> Gravity.BOTTOM or Gravity.LEFT
|
||||||
|
BOTTOM_RIGHT -> Gravity.BOTTOM or Gravity.RIGHT
|
||||||
|
else -> throw IllegalArgumentException("Not a corner")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Int.innerGravity(): Int {
|
||||||
|
return when (this) {
|
||||||
|
TOP_LEFT -> Gravity.CENTER_VERTICAL or Gravity.RIGHT
|
||||||
|
TOP_RIGHT -> Gravity.CENTER_VERTICAL or Gravity.LEFT
|
||||||
|
BOTTOM_LEFT -> Gravity.CENTER_VERTICAL or Gravity.RIGHT
|
||||||
|
BOTTOM_RIGHT -> Gravity.CENTER_VERTICAL or Gravity.LEFT
|
||||||
|
else -> throw IllegalArgumentException("Not a corner")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.events
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import com.android.settingslib.graph.ThemedBatteryDrawable
|
||||||
|
import com.android.systemui.R
|
||||||
|
import com.android.systemui.privacy.OngoingPrivacyChip
|
||||||
|
import com.android.systemui.privacy.PrivacyItem
|
||||||
|
|
||||||
|
interface StatusEvent {
|
||||||
|
val priority: Int
|
||||||
|
// Whether or not to force the status bar open and show a dot
|
||||||
|
val forceVisible: Boolean
|
||||||
|
val viewCreator: (context: Context) -> View
|
||||||
|
}
|
||||||
|
|
||||||
|
class BatteryEvent : StatusEvent {
|
||||||
|
override val priority = 50
|
||||||
|
override val forceVisible = false
|
||||||
|
|
||||||
|
override val viewCreator: (context: Context) -> View = { context ->
|
||||||
|
val iv = ImageView(context)
|
||||||
|
iv.setImageDrawable(ThemedBatteryDrawable(context, Color.WHITE))
|
||||||
|
iv.setBackgroundDrawable(ColorDrawable(Color.GREEN))
|
||||||
|
iv
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return javaClass.simpleName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class PrivacyEvent : StatusEvent {
|
||||||
|
override val priority = 100
|
||||||
|
override val forceVisible = true
|
||||||
|
var privacyItems: List<PrivacyItem> = listOf()
|
||||||
|
|
||||||
|
override val viewCreator: (context: Context) -> View = { context ->
|
||||||
|
val v = LayoutInflater.from(context)
|
||||||
|
.inflate(R.layout.ongoing_privacy_chip, null) as OngoingPrivacyChip
|
||||||
|
v.privacyList = privacyItems
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return javaClass.simpleName
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.events
|
||||||
|
|
||||||
|
import android.animation.ValueAnimator
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
|
||||||
|
import com.android.systemui.R
|
||||||
|
import com.android.systemui.statusbar.SuperStatusBarViewFactory
|
||||||
|
import com.android.systemui.statusbar.phone.StatusBarWindowController
|
||||||
|
import com.android.systemui.statusbar.phone.StatusBarWindowView
|
||||||
|
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* //TODO: this _probably_ doesn't control a window anymore
|
||||||
|
* Controls the window for system event animations.
|
||||||
|
*/
|
||||||
|
class SystemEventChipAnimationController @Inject constructor(
|
||||||
|
private val context: Context,
|
||||||
|
private val statusBarViewFactory: SuperStatusBarViewFactory,
|
||||||
|
private val statusBarWindowController: StatusBarWindowController
|
||||||
|
) : SystemStatusChipAnimationCallback {
|
||||||
|
var showPersistentDot = false
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
statusBarWindowController.setForceStatusBarVisible(value)
|
||||||
|
maybeUpdateShowDot()
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var animationWindowView: FrameLayout
|
||||||
|
private lateinit var animationDotView: View
|
||||||
|
private lateinit var statusBarWindowView: StatusBarWindowView
|
||||||
|
private var currentAnimatedView: View? = null
|
||||||
|
|
||||||
|
// TODO: move to dagger
|
||||||
|
private var initialized = false
|
||||||
|
|
||||||
|
override fun onChipAnimationStart(
|
||||||
|
viewCreator: (context: Context) -> View,
|
||||||
|
@SystemAnimationState state: Int
|
||||||
|
) {
|
||||||
|
if (!initialized) init()
|
||||||
|
|
||||||
|
if (state == ANIMATING_IN) {
|
||||||
|
currentAnimatedView = viewCreator(context)
|
||||||
|
animationWindowView.addView(currentAnimatedView, layoutParamsDefault)
|
||||||
|
|
||||||
|
// We are animating IN; chip comes in from View.END
|
||||||
|
currentAnimatedView?.apply {
|
||||||
|
translationX = width.toFloat()
|
||||||
|
alpha = 0f
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We are animating away
|
||||||
|
currentAnimatedView?.apply {
|
||||||
|
translationX = 0f
|
||||||
|
alpha = 1f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onChipAnimationEnd(@SystemAnimationState state: Int) {
|
||||||
|
if (state == ANIMATING_IN) {
|
||||||
|
// Finished animating in
|
||||||
|
currentAnimatedView?.apply {
|
||||||
|
translationX = 0f
|
||||||
|
alpha = 1f
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Finished animating away
|
||||||
|
currentAnimatedView?.apply {
|
||||||
|
visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
animationWindowView.removeView(currentAnimatedView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onChipAnimationUpdate(
|
||||||
|
animator: ValueAnimator,
|
||||||
|
@SystemAnimationState state: Int
|
||||||
|
) {
|
||||||
|
// Alpha is parameterized 0,1, and translation from (width, 0)
|
||||||
|
currentAnimatedView?.apply {
|
||||||
|
val amt = animator.animatedValue as Float
|
||||||
|
|
||||||
|
alpha = amt
|
||||||
|
|
||||||
|
val w = width
|
||||||
|
val translation = (1 - amt) * w
|
||||||
|
translationX = translation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun maybeUpdateShowDot() {
|
||||||
|
if (!initialized) return
|
||||||
|
if (!showPersistentDot && currentAnimatedView == null) {
|
||||||
|
animationDotView.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init() {
|
||||||
|
initialized = true
|
||||||
|
statusBarWindowView = statusBarViewFactory.statusBarWindowView
|
||||||
|
animationWindowView = LayoutInflater.from(context)
|
||||||
|
.inflate(R.layout.system_event_animation_window, null) as FrameLayout
|
||||||
|
animationDotView = animationWindowView.findViewById(R.id.dot_view)
|
||||||
|
val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
||||||
|
lp.gravity = Gravity.END or Gravity.CENTER_VERTICAL
|
||||||
|
statusBarWindowView.addView(animationWindowView, lp)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val layoutParamsDefault = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).also {
|
||||||
|
it.gravity = Gravity.END or Gravity.CENTER_VERTICAL
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.events
|
||||||
|
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.privacy.PrivacyItem
|
||||||
|
import com.android.systemui.privacy.PrivacyItemController
|
||||||
|
import com.android.systemui.statusbar.policy.BatteryController
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for system events (battery, privacy, connectivity) and allows listeners
|
||||||
|
* to show status bar animations when they happen
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class SystemEventCoordinator @Inject constructor(
|
||||||
|
private val batteryController: BatteryController,
|
||||||
|
private val privacyController: PrivacyItemController
|
||||||
|
) {
|
||||||
|
private lateinit var scheduler: SystemStatusAnimationScheduler
|
||||||
|
|
||||||
|
fun startObserving() {
|
||||||
|
/* currently unused
|
||||||
|
batteryController.addCallback(batteryStateListener)
|
||||||
|
*/
|
||||||
|
privacyController.addCallback(privacyStateListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopObserving() {
|
||||||
|
/* currently unused
|
||||||
|
batteryController.removeCallback(batteryStateListener)
|
||||||
|
*/
|
||||||
|
privacyController.removeCallback(privacyStateListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun attachScheduler(s: SystemStatusAnimationScheduler) {
|
||||||
|
this.scheduler = s
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyPluggedIn() {
|
||||||
|
scheduler.onStatusEvent(BatteryEvent())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyPrivacyItemsEmpty() {
|
||||||
|
scheduler.setShouldShowPersistentPrivacyIndicator(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyPrivacyItemsChanged() {
|
||||||
|
val event = PrivacyEvent()
|
||||||
|
event.privacyItems = privacyStateListener.currentPrivacyItems
|
||||||
|
scheduler.onStatusEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val batteryStateListener = object : BatteryController.BatteryStateChangeCallback {
|
||||||
|
var plugged = false
|
||||||
|
var stateKnown = false
|
||||||
|
override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
|
||||||
|
if (!stateKnown) {
|
||||||
|
stateKnown = true
|
||||||
|
plugged = pluggedIn
|
||||||
|
notifyListeners()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plugged != pluggedIn) {
|
||||||
|
plugged = pluggedIn
|
||||||
|
notifyListeners()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun notifyListeners() {
|
||||||
|
// We only care about the plugged in status
|
||||||
|
if (plugged) notifyPluggedIn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val privacyStateListener = object : PrivacyItemController.Callback {
|
||||||
|
var currentPrivacyItems = listOf<PrivacyItem>()
|
||||||
|
|
||||||
|
override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
|
||||||
|
if (privacyItems.isNotEmpty() && currentPrivacyItems.containsAll(privacyItems)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentPrivacyItems = privacyItems
|
||||||
|
notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun notifyListeners() {
|
||||||
|
if (currentPrivacyItems.isEmpty()) {
|
||||||
|
notifyPrivacyItemsEmpty()
|
||||||
|
} else {
|
||||||
|
notifyPrivacyItemsChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val TAG = "SystemEventCoordinator"
|
||||||
@@ -0,0 +1,333 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.events
|
||||||
|
|
||||||
|
import android.animation.Animator
|
||||||
|
import android.animation.AnimatorListenerAdapter
|
||||||
|
import android.animation.AnimatorSet
|
||||||
|
import android.animation.ValueAnimator
|
||||||
|
import android.annotation.IntDef
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Process
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
|
import com.android.systemui.statusbar.phone.StatusBarWindowController
|
||||||
|
import com.android.systemui.statusbar.policy.CallbackController
|
||||||
|
import com.android.systemui.util.Assert
|
||||||
|
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||||
|
import com.android.systemui.util.time.SystemClock
|
||||||
|
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dead-simple scheduler for system status events. Obeys the following principles (all values TBD):
|
||||||
|
* - Avoiding log spam by only allowing 12 events per minute (1event/5s)
|
||||||
|
* - Waits 100ms to schedule any event for debouncing/prioritization
|
||||||
|
* - Simple prioritization: Privacy > Battery > connectivity (encoded in StatusEvent)
|
||||||
|
* - Only schedules a single event, and throws away lowest priority events
|
||||||
|
*
|
||||||
|
* There are 4 basic stages of animation at play here:
|
||||||
|
* 1. System chrome animation OUT
|
||||||
|
* 2. Chip animation IN
|
||||||
|
* 3. Chip animation OUT; potentially into a dot
|
||||||
|
* 4. System chrome animation IN
|
||||||
|
*
|
||||||
|
* Thus we can keep all animations synchronized with two separate ValueAnimators, one for system
|
||||||
|
* chrome and the other for the chip. These can animate from 0,1 and listeners can parameterize
|
||||||
|
* their respective views based on the progress of the animator. Interpolation differences TBD
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class SystemStatusAnimationScheduler @Inject constructor(
|
||||||
|
private val coordinator: SystemEventCoordinator,
|
||||||
|
private val chipAnimationController: SystemEventChipAnimationController,
|
||||||
|
private val statusBarWindowController: StatusBarWindowController,
|
||||||
|
private val systemClock: SystemClock,
|
||||||
|
@Main private val executor: DelayableExecutor
|
||||||
|
) : CallbackController<SystemStatusAnimationCallback> {
|
||||||
|
|
||||||
|
/** True from the time a scheduled event starts until it's animation finishes */
|
||||||
|
var isActive = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
@SystemAnimationState var animationState: Int = IDLE
|
||||||
|
private set
|
||||||
|
|
||||||
|
/** True if the persistent privacy dot should be active */
|
||||||
|
var hasPersistentDot = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
private var scheduledEvent: StatusEvent? = null
|
||||||
|
private var cancelExecutionRunnable: Runnable? = null
|
||||||
|
private val listeners = mutableSetOf<SystemStatusAnimationCallback>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
coordinator.attachScheduler(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onStatusEvent(event: StatusEvent) {
|
||||||
|
// Ignore any updates until the system is up and running
|
||||||
|
if (isTooEarly()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't deal with threading for now (no need let's be honest)
|
||||||
|
Assert.isMainThread()
|
||||||
|
if (event.priority > scheduledEvent?.priority ?: -1) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "scheduling event $event")
|
||||||
|
}
|
||||||
|
scheduleEvent(event)
|
||||||
|
} else {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "ignoring event $event")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearDotIfVisible() {
|
||||||
|
notifyHidePersistentDot()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setShouldShowPersistentPrivacyIndicator(should: Boolean) {
|
||||||
|
if (hasPersistentDot == should) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPersistentDot = should
|
||||||
|
|
||||||
|
if (!hasPersistentDot) {
|
||||||
|
clearDotIfVisible()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isTooEarly(): Boolean {
|
||||||
|
Log.d(TAG, "time=> ${systemClock.uptimeMillis() - Process.getStartUptimeMillis()}")
|
||||||
|
return systemClock.uptimeMillis() - Process.getStartUptimeMillis() < MIN_UPTIME
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the scheduled event (if any) and schedule a new one
|
||||||
|
*/
|
||||||
|
private fun scheduleEvent(event: StatusEvent) {
|
||||||
|
scheduledEvent = event
|
||||||
|
if (scheduledEvent!!.forceVisible) {
|
||||||
|
hasPersistentDot = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedule the animation to start after a debounce period
|
||||||
|
cancelExecutionRunnable = executor.executeDelayed({
|
||||||
|
cancelExecutionRunnable = null
|
||||||
|
animationState = ANIMATING_IN
|
||||||
|
statusBarWindowController.setForceStatusBarVisible(true)
|
||||||
|
|
||||||
|
val entranceAnimator = ValueAnimator.ofFloat(1f, 0f)
|
||||||
|
entranceAnimator.duration = ENTRANCE_ANIM_LENGTH
|
||||||
|
entranceAnimator.addListener(systemAnimatorAdapter)
|
||||||
|
entranceAnimator.addUpdateListener(systemUpdateListener)
|
||||||
|
|
||||||
|
val chipAnimator = ValueAnimator.ofFloat(0f, 1f)
|
||||||
|
chipAnimator.duration = CHIP_ANIM_LENGTH
|
||||||
|
chipAnimator.addListener(
|
||||||
|
ChipAnimatorAdapter(RUNNING_CHIP_ANIM, scheduledEvent!!.viewCreator))
|
||||||
|
chipAnimator.addUpdateListener(chipUpdateListener)
|
||||||
|
|
||||||
|
val aSet2 = AnimatorSet()
|
||||||
|
aSet2.playSequentially(entranceAnimator, chipAnimator)
|
||||||
|
aSet2.start()
|
||||||
|
|
||||||
|
executor.executeDelayed({
|
||||||
|
animationState = ANIMATING_OUT
|
||||||
|
|
||||||
|
val systemAnimator = ValueAnimator.ofFloat(0f, 1f)
|
||||||
|
systemAnimator.duration = ENTRANCE_ANIM_LENGTH
|
||||||
|
systemAnimator.addListener(systemAnimatorAdapter)
|
||||||
|
systemAnimator.addUpdateListener(systemUpdateListener)
|
||||||
|
|
||||||
|
val chipAnimator = ValueAnimator.ofFloat(1f, 0f)
|
||||||
|
chipAnimator.duration = CHIP_ANIM_LENGTH
|
||||||
|
chipAnimator.addListener(ChipAnimatorAdapter(IDLE, scheduledEvent!!.viewCreator))
|
||||||
|
chipAnimator.addUpdateListener(chipUpdateListener)
|
||||||
|
|
||||||
|
val aSet2 = AnimatorSet()
|
||||||
|
|
||||||
|
aSet2.play(chipAnimator).before(systemAnimator)
|
||||||
|
if (hasPersistentDot) {
|
||||||
|
val dotAnim = notifyTransitionToPersistentDot()
|
||||||
|
if (dotAnim != null) aSet2.playTogether(systemAnimator, dotAnim)
|
||||||
|
}
|
||||||
|
|
||||||
|
aSet2.start()
|
||||||
|
|
||||||
|
statusBarWindowController.setForceStatusBarVisible(false)
|
||||||
|
scheduledEvent = null
|
||||||
|
}, DISPLAY_LENGTH)
|
||||||
|
}, DELAY)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun notifyTransitionToPersistentDot(): Animator? {
|
||||||
|
val anims: List<Animator> = listeners.mapNotNull {
|
||||||
|
it.onSystemStatusAnimationTransitionToPersistentDot()
|
||||||
|
}
|
||||||
|
if (anims.isNotEmpty()) {
|
||||||
|
val aSet = AnimatorSet()
|
||||||
|
aSet.playTogether(anims)
|
||||||
|
return aSet
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun notifyHidePersistentDot(): Animator? {
|
||||||
|
val anims: List<Animator> = listeners.mapNotNull {
|
||||||
|
it.onHidePersistentDot()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anims.isNotEmpty()) {
|
||||||
|
val aSet = AnimatorSet()
|
||||||
|
aSet.playTogether(anims)
|
||||||
|
return aSet
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun notifySystemStart() {
|
||||||
|
listeners.forEach { it.onSystemChromeAnimationStart() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun notifySystemFinish() {
|
||||||
|
listeners.forEach { it.onSystemChromeAnimationEnd() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun notifySystemAnimationUpdate(anim: ValueAnimator) {
|
||||||
|
listeners.forEach { it.onSystemChromeAnimationUpdate(anim) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addCallback(listener: SystemStatusAnimationCallback) {
|
||||||
|
Assert.isMainThread()
|
||||||
|
|
||||||
|
if (listeners.isEmpty()) {
|
||||||
|
coordinator.startObserving()
|
||||||
|
}
|
||||||
|
listeners.add(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeCallback(listener: SystemStatusAnimationCallback) {
|
||||||
|
Assert.isMainThread()
|
||||||
|
|
||||||
|
listeners.remove(listener)
|
||||||
|
if (listeners.isEmpty()) {
|
||||||
|
coordinator.stopObserving()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val systemUpdateListener = ValueAnimator.AnimatorUpdateListener {
|
||||||
|
anim -> notifySystemAnimationUpdate(anim)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val systemAnimatorAdapter = object : AnimatorListenerAdapter() {
|
||||||
|
override fun onAnimationEnd(p0: Animator?) {
|
||||||
|
notifySystemFinish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationStart(p0: Animator?) {
|
||||||
|
notifySystemStart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val chipUpdateListener = ValueAnimator.AnimatorUpdateListener {
|
||||||
|
anim -> chipAnimationController.onChipAnimationUpdate(anim, animationState)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ChipAnimatorAdapter(
|
||||||
|
@SystemAnimationState val endState: Int,
|
||||||
|
val viewCreator: (context: Context) -> View
|
||||||
|
) : AnimatorListenerAdapter() {
|
||||||
|
override fun onAnimationEnd(p0: Animator?) {
|
||||||
|
chipAnimationController.onChipAnimationEnd(animationState)
|
||||||
|
animationState = endState
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationStart(p0: Animator?) {
|
||||||
|
chipAnimationController.onChipAnimationStart(viewCreator, animationState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The general idea here is that this scheduler will run two value animators, and provide
|
||||||
|
* animator-like callbacks for each kind of animation. The SystemChrome animation is expected to
|
||||||
|
* create space for the chip animation to display. This means hiding the system elements in the
|
||||||
|
* status bar and keyguard.
|
||||||
|
*
|
||||||
|
* TODO: the chip animation really only has one client, we can probably remove it from this
|
||||||
|
* interface
|
||||||
|
*
|
||||||
|
* The value animators themselves are simple animators from 0.0 to 1.0. Listeners can apply any
|
||||||
|
* interpolation they choose but realistically these are most likely to be simple alpha transitions
|
||||||
|
*/
|
||||||
|
interface SystemStatusAnimationCallback {
|
||||||
|
@JvmDefault fun onSystemChromeAnimationUpdate(animator: ValueAnimator) {}
|
||||||
|
@JvmDefault fun onSystemChromeAnimationStart() {}
|
||||||
|
@JvmDefault fun onSystemChromeAnimationEnd() {}
|
||||||
|
|
||||||
|
// Best method name, change my mind
|
||||||
|
@JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? { return null }
|
||||||
|
@JvmDefault fun onHidePersistentDot(): Animator? { return null }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SystemStatusChipAnimationCallback {
|
||||||
|
fun onChipAnimationUpdate(animator: ValueAnimator, @SystemAnimationState state: Int) {}
|
||||||
|
|
||||||
|
fun onChipAnimationStart(
|
||||||
|
viewCreator: (context: Context) -> View,
|
||||||
|
@SystemAnimationState state: Int
|
||||||
|
) {}
|
||||||
|
|
||||||
|
fun onChipAnimationEnd(@SystemAnimationState state: Int) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
@IntDef(
|
||||||
|
value = [
|
||||||
|
IDLE, ANIMATING_IN, RUNNING_CHIP_ANIM, ANIMATING_OUT
|
||||||
|
]
|
||||||
|
)
|
||||||
|
annotation class SystemAnimationState
|
||||||
|
|
||||||
|
/** No animation is in progress */
|
||||||
|
const val IDLE = 0
|
||||||
|
/** System is animating out, and chip is animating in */
|
||||||
|
const val ANIMATING_IN = 1
|
||||||
|
/** Chip has animated in and is awaiting exit animation, and optionally playing its own animation */
|
||||||
|
const val RUNNING_CHIP_ANIM = 2
|
||||||
|
/** Chip is animating away and system is animating back */
|
||||||
|
const val ANIMATING_OUT = 3
|
||||||
|
|
||||||
|
private const val TAG = "SystemStatusAnimationScheduler"
|
||||||
|
private const val DELAY: Long = 100
|
||||||
|
private const val DISPLAY_LENGTH = 5000L
|
||||||
|
private const val ENTRANCE_ANIM_LENGTH = 500L
|
||||||
|
private const val CHIP_ANIM_LENGTH = 500L
|
||||||
|
private const val MIN_UPTIME: Long = 5 * 1000
|
||||||
|
|
||||||
|
private const val DEBUG = false
|
||||||
@@ -19,10 +19,16 @@ import static android.app.StatusBarManager.DISABLE_CLOCK;
|
|||||||
import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
|
import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
|
||||||
import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
|
import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
|
||||||
|
|
||||||
|
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
|
||||||
|
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
|
||||||
|
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
|
||||||
|
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -36,6 +42,9 @@ import com.android.systemui.animation.Interpolators;
|
|||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
import com.android.systemui.statusbar.CommandQueue;
|
import com.android.systemui.statusbar.CommandQueue;
|
||||||
import com.android.systemui.statusbar.StatusBarState;
|
import com.android.systemui.statusbar.StatusBarState;
|
||||||
|
import com.android.systemui.statusbar.events.PrivacyDotViewController;
|
||||||
|
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
|
||||||
|
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
|
||||||
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
|
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
|
||||||
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
|
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
|
||||||
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
|
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
|
||||||
@@ -44,6 +53,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
|
|||||||
import com.android.systemui.statusbar.policy.NetworkController;
|
import com.android.systemui.statusbar.policy.NetworkController;
|
||||||
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
|
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -55,7 +66,8 @@ import javax.inject.Inject;
|
|||||||
* updated by the StatusBarIconController and DarkIconManager while it is attached.
|
* updated by the StatusBarIconController and DarkIconManager while it is attached.
|
||||||
*/
|
*/
|
||||||
public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks,
|
public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks,
|
||||||
StatusBarStateController.StateListener {
|
StatusBarStateController.StateListener,
|
||||||
|
SystemStatusAnimationCallback {
|
||||||
|
|
||||||
public static final String TAG = "CollapsedStatusBarFragment";
|
public static final String TAG = "CollapsedStatusBarFragment";
|
||||||
private static final String EXTRA_PANEL_STATE = "panel_state";
|
private static final String EXTRA_PANEL_STATE = "panel_state";
|
||||||
@@ -78,6 +90,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
|||||||
private View mOperatorNameFrame;
|
private View mOperatorNameFrame;
|
||||||
private CommandQueue mCommandQueue;
|
private CommandQueue mCommandQueue;
|
||||||
private OngoingCallController mOngoingCallController;
|
private OngoingCallController mOngoingCallController;
|
||||||
|
private final SystemStatusAnimationScheduler mAnimationScheduler;
|
||||||
|
private final PrivacyDotViewController mDotViewController;
|
||||||
|
|
||||||
private List<String> mBlockedIcons = new ArrayList<>();
|
private List<String> mBlockedIcons = new ArrayList<>();
|
||||||
|
|
||||||
@@ -103,8 +117,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public CollapsedStatusBarFragment(OngoingCallController ongoingCallController) {
|
public CollapsedStatusBarFragment(
|
||||||
|
OngoingCallController ongoingCallController,
|
||||||
|
SystemStatusAnimationScheduler animationScheduler,
|
||||||
|
PrivacyDotViewController dotViewController
|
||||||
|
) {
|
||||||
mOngoingCallController = ongoingCallController;
|
mOngoingCallController = ongoingCallController;
|
||||||
|
mAnimationScheduler = animationScheduler;
|
||||||
|
mDotViewController = dotViewController;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -127,6 +147,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
|||||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
mStatusBar = (PhoneStatusBarView) view;
|
mStatusBar = (PhoneStatusBarView) view;
|
||||||
|
View contents = mStatusBar.findViewById(R.id.status_bar_contents);
|
||||||
|
contents.addOnLayoutChangeListener(mStatusBarLayoutListener);
|
||||||
|
updateStatusBarLocation(contents.getLeft(), contents.getRight());
|
||||||
if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
|
if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
|
||||||
mStatusBar.restoreHierarchyState(
|
mStatusBar.restoreHierarchyState(
|
||||||
savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
|
savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
|
||||||
@@ -143,6 +166,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
|||||||
showClock(false);
|
showClock(false);
|
||||||
initEmergencyCryptkeeperText();
|
initEmergencyCryptkeeperText();
|
||||||
initOperatorName();
|
initOperatorName();
|
||||||
|
mAnimationScheduler.addCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -208,6 +232,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
|||||||
if (displayId != getContext().getDisplayId()) {
|
if (displayId != getContext().getDisplayId()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Log.d(TAG, "disable: ");
|
||||||
state1 = adjustDisableFlags(state1);
|
state1 = adjustDisableFlags(state1);
|
||||||
final int old1 = mDisabled1;
|
final int old1 = mDisabled1;
|
||||||
final int diff1 = state1 ^ old1;
|
final int diff1 = state1 ^ old1;
|
||||||
@@ -292,19 +317,22 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hideSystemIconArea(boolean animate) {
|
private void hideSystemIconArea(boolean animate) {
|
||||||
animateHide(mSystemIconArea, animate);
|
animateHide(mSystemIconArea, animate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showSystemIconArea(boolean animate) {
|
private void showSystemIconArea(boolean animate) {
|
||||||
animateShow(mSystemIconArea, animate);
|
// Only show the system icon area if we are not currently animating
|
||||||
|
if (mAnimationScheduler.getAnimationState() == IDLE) {
|
||||||
|
animateShow(mSystemIconArea, animate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hideClock(boolean animate) {
|
private void hideClock(boolean animate) {
|
||||||
animateHiddenState(mClockView, clockHiddenMode(), animate);
|
animateHiddenState(mClockView, clockHiddenMode(), animate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showClock(boolean animate) {
|
private void showClock(boolean animate) {
|
||||||
animateShow(mClockView, animate);
|
animateShow(mClockView, animate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,12 +453,60 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStateChanged(int newState) {
|
public void onStateChanged(int newState) { }
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDozingChanged(boolean isDozing) {
|
public void onDozingChanged(boolean isDozing) {
|
||||||
disable(getContext().getDisplayId(), mDisabled1, mDisabled1, false /* animate */);
|
disable(getContext().getDisplayId(), mDisabled1, mDisabled1, false /* animate */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSystemChromeAnimationStart() {
|
||||||
|
if (mAnimationScheduler.getAnimationState() == ANIMATING_OUT
|
||||||
|
&& !isSystemIconAreaDisabled()) {
|
||||||
|
mSystemIconArea.setVisibility(View.VISIBLE);
|
||||||
|
mSystemIconArea.setAlpha(0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSystemChromeAnimationEnd() {
|
||||||
|
// Make sure the system icons are out of the way
|
||||||
|
if (mAnimationScheduler.getAnimationState() == ANIMATING_IN) {
|
||||||
|
mSystemIconArea.setVisibility(View.INVISIBLE);
|
||||||
|
mSystemIconArea.setAlpha(0f);
|
||||||
|
} else {
|
||||||
|
if (isSystemIconAreaDisabled()) {
|
||||||
|
// don't unhide
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mSystemIconArea.setAlpha(1f);
|
||||||
|
mSystemIconArea.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSystemChromeAnimationUpdate(@NotNull ValueAnimator animator) {
|
||||||
|
mSystemIconArea.setAlpha((float) animator.getAnimatedValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSystemIconAreaDisabled() {
|
||||||
|
return (mDisabled1 & DISABLE_SYSTEM_INFO) != 0 || (mDisabled2 & DISABLE2_SYSTEM_ICONS) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStatusBarLocation(int left, int right) {
|
||||||
|
int leftMargin = left - mStatusBar.getLeft();
|
||||||
|
int rightMargin = mStatusBar.getRight() - right;
|
||||||
|
|
||||||
|
mDotViewController.setStatusBarMargins(leftMargin, rightMargin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for view end changes of PhoneStatusBarView and publish that to the privacy dot
|
||||||
|
private View.OnLayoutChangeListener mStatusBarLayoutListener =
|
||||||
|
(view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
|
||||||
|
if (left != oldLeft || right != oldRight) {
|
||||||
|
updateStatusBarLocation(left, right);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ package com.android.systemui.statusbar.phone;
|
|||||||
|
|
||||||
import static com.android.systemui.DejankUtils.whitelistIpcs;
|
import static com.android.systemui.DejankUtils.whitelistIpcs;
|
||||||
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
|
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
|
||||||
|
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
|
||||||
|
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
|
||||||
|
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
import android.annotation.ColorInt;
|
import android.annotation.ColorInt;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
@@ -47,6 +50,8 @@ import com.android.systemui.Dependency;
|
|||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.animation.Interpolators;
|
import com.android.systemui.animation.Interpolators;
|
||||||
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
|
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
|
||||||
|
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
|
||||||
|
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
|
||||||
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
|
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
|
||||||
import com.android.systemui.statusbar.policy.BatteryController;
|
import com.android.systemui.statusbar.policy.BatteryController;
|
||||||
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
|
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
|
||||||
@@ -64,8 +69,11 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* The header group on Keyguard.
|
* The header group on Keyguard.
|
||||||
*/
|
*/
|
||||||
public class KeyguardStatusBarView extends RelativeLayout
|
public class KeyguardStatusBarView extends RelativeLayout implements
|
||||||
implements BatteryStateChangeCallback, OnUserInfoChangedListener, ConfigurationListener {
|
BatteryStateChangeCallback,
|
||||||
|
OnUserInfoChangedListener,
|
||||||
|
ConfigurationListener,
|
||||||
|
SystemStatusAnimationCallback {
|
||||||
|
|
||||||
private static final int LAYOUT_NONE = 0;
|
private static final int LAYOUT_NONE = 0;
|
||||||
private static final int LAYOUT_CUTOUT = 1;
|
private static final int LAYOUT_CUTOUT = 1;
|
||||||
@@ -96,6 +104,8 @@ public class KeyguardStatusBarView extends RelativeLayout
|
|||||||
private ViewGroup mStatusIconArea;
|
private ViewGroup mStatusIconArea;
|
||||||
private int mLayoutState = LAYOUT_NONE;
|
private int mLayoutState = LAYOUT_NONE;
|
||||||
|
|
||||||
|
private SystemStatusAnimationScheduler mAnimationScheduler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw this many pixels into the left/right side of the cutout to optimally use the space
|
* Draw this many pixels into the left/right side of the cutout to optimally use the space
|
||||||
*/
|
*/
|
||||||
@@ -125,6 +135,7 @@ public class KeyguardStatusBarView extends RelativeLayout
|
|||||||
loadDimens();
|
loadDimens();
|
||||||
loadBlockList();
|
loadBlockList();
|
||||||
mBatteryController = Dependency.get(BatteryController.class);
|
mBatteryController = Dependency.get(BatteryController.class);
|
||||||
|
mAnimationScheduler = Dependency.get(SystemStatusAnimationScheduler.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -349,6 +360,7 @@ public class KeyguardStatusBarView extends RelativeLayout
|
|||||||
mIconManager = new TintedIconManager(findViewById(R.id.statusIcons));
|
mIconManager = new TintedIconManager(findViewById(R.id.statusIcons));
|
||||||
mIconManager.setBlockList(mBlockedIcons);
|
mIconManager.setBlockList(mBlockedIcons);
|
||||||
Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
|
Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
|
||||||
|
mAnimationScheduler.addCallback(this);
|
||||||
onThemeChanged();
|
onThemeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,6 +370,7 @@ public class KeyguardStatusBarView extends RelativeLayout
|
|||||||
Dependency.get(UserInfoController.class).removeCallback(this);
|
Dependency.get(UserInfoController.class).removeCallback(this);
|
||||||
Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
|
Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
|
||||||
Dependency.get(ConfigurationController.class).removeCallback(this);
|
Dependency.get(ConfigurationController.class).removeCallback(this);
|
||||||
|
mAnimationScheduler.removeCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -509,4 +522,30 @@ public class KeyguardStatusBarView extends RelativeLayout
|
|||||||
mBatteryView.dump(fd, pw, args);
|
mBatteryView.dump(fd, pw, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** SystemStatusAnimationCallback */
|
||||||
|
@Override
|
||||||
|
public void onSystemChromeAnimationStart() {
|
||||||
|
if (mAnimationScheduler.getAnimationState() == ANIMATING_OUT) {
|
||||||
|
mSystemIconsContainer.setVisibility(View.VISIBLE);
|
||||||
|
mSystemIconsContainer.setAlpha(0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSystemChromeAnimationEnd() {
|
||||||
|
// Make sure the system icons are out of the way
|
||||||
|
if (mAnimationScheduler.getAnimationState() == ANIMATING_IN) {
|
||||||
|
mSystemIconsContainer.setVisibility(View.INVISIBLE);
|
||||||
|
mSystemIconsContainer.setAlpha(0f);
|
||||||
|
} else {
|
||||||
|
mSystemIconsContainer.setAlpha(1f);
|
||||||
|
mSystemIconsContainer.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSystemChromeAnimationUpdate(ValueAnimator anim) {
|
||||||
|
mSystemIconsContainer.setAlpha((float) anim.getAnimatedValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,6 +208,8 @@ import com.android.systemui.statusbar.SuperStatusBarViewFactory;
|
|||||||
import com.android.systemui.statusbar.SysuiStatusBarStateController;
|
import com.android.systemui.statusbar.SysuiStatusBarStateController;
|
||||||
import com.android.systemui.statusbar.VibratorHelper;
|
import com.android.systemui.statusbar.VibratorHelper;
|
||||||
import com.android.systemui.statusbar.charging.WiredChargingRippleController;
|
import com.android.systemui.statusbar.charging.WiredChargingRippleController;
|
||||||
|
import com.android.systemui.statusbar.events.PrivacyDotViewController;
|
||||||
|
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
|
||||||
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
||||||
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
||||||
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
|
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
|
||||||
@@ -426,6 +428,8 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
private final DemoModeController mDemoModeController;
|
private final DemoModeController mDemoModeController;
|
||||||
private NotificationsController mNotificationsController;
|
private NotificationsController mNotificationsController;
|
||||||
private final OngoingCallController mOngoingCallController;
|
private final OngoingCallController mOngoingCallController;
|
||||||
|
private final SystemStatusAnimationScheduler mAnimationScheduler;
|
||||||
|
private final PrivacyDotViewController mDotViewController;
|
||||||
|
|
||||||
// expanded notifications
|
// expanded notifications
|
||||||
// the sliding/resizing panel within the notification window
|
// the sliding/resizing panel within the notification window
|
||||||
@@ -794,6 +798,8 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
BrightnessSlider.Factory brightnessSliderFactory,
|
BrightnessSlider.Factory brightnessSliderFactory,
|
||||||
WiredChargingRippleController chargingRippleAnimationController,
|
WiredChargingRippleController chargingRippleAnimationController,
|
||||||
OngoingCallController ongoingCallController,
|
OngoingCallController ongoingCallController,
|
||||||
|
SystemStatusAnimationScheduler animationScheduler,
|
||||||
|
PrivacyDotViewController dotViewController,
|
||||||
TunerService tunerService,
|
TunerService tunerService,
|
||||||
FeatureFlags featureFlags) {
|
FeatureFlags featureFlags) {
|
||||||
super(context);
|
super(context);
|
||||||
@@ -875,6 +881,8 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
mBrightnessSliderFactory = brightnessSliderFactory;
|
mBrightnessSliderFactory = brightnessSliderFactory;
|
||||||
mChargingRippleAnimationController = chargingRippleAnimationController;
|
mChargingRippleAnimationController = chargingRippleAnimationController;
|
||||||
mOngoingCallController = ongoingCallController;
|
mOngoingCallController = ongoingCallController;
|
||||||
|
mAnimationScheduler = animationScheduler;
|
||||||
|
mDotViewController = dotViewController;
|
||||||
mFeatureFlags = featureFlags;
|
mFeatureFlags = featureFlags;
|
||||||
|
|
||||||
tunerService.addTunable(
|
tunerService.addTunable(
|
||||||
@@ -1171,7 +1179,10 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
}).getFragmentManager()
|
}).getFragmentManager()
|
||||||
.beginTransaction()
|
.beginTransaction()
|
||||||
.replace(R.id.status_bar_container,
|
.replace(R.id.status_bar_container,
|
||||||
new CollapsedStatusBarFragment(mOngoingCallController),
|
new CollapsedStatusBarFragment(
|
||||||
|
mOngoingCallController,
|
||||||
|
mAnimationScheduler,
|
||||||
|
mDotViewController),
|
||||||
CollapsedStatusBarFragment.TAG)
|
CollapsedStatusBarFragment.TAG)
|
||||||
.commit();
|
.commit();
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ import com.android.systemui.statusbar.SuperStatusBarViewFactory;
|
|||||||
import com.android.systemui.statusbar.SysuiStatusBarStateController;
|
import com.android.systemui.statusbar.SysuiStatusBarStateController;
|
||||||
import com.android.systemui.statusbar.VibratorHelper;
|
import com.android.systemui.statusbar.VibratorHelper;
|
||||||
import com.android.systemui.statusbar.charging.WiredChargingRippleController;
|
import com.android.systemui.statusbar.charging.WiredChargingRippleController;
|
||||||
|
import com.android.systemui.statusbar.events.PrivacyDotViewController;
|
||||||
|
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
|
||||||
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
||||||
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
|
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
|
||||||
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
|
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
|
||||||
@@ -209,6 +211,8 @@ public interface StatusBarPhoneModule {
|
|||||||
BrightnessSlider.Factory brightnessSliderFactory,
|
BrightnessSlider.Factory brightnessSliderFactory,
|
||||||
WiredChargingRippleController chargingRippleAnimationController,
|
WiredChargingRippleController chargingRippleAnimationController,
|
||||||
OngoingCallController ongoingCallController,
|
OngoingCallController ongoingCallController,
|
||||||
|
SystemStatusAnimationScheduler animationScheduler,
|
||||||
|
PrivacyDotViewController dotViewController,
|
||||||
TunerService tunerService,
|
TunerService tunerService,
|
||||||
FeatureFlags featureFlags) {
|
FeatureFlags featureFlags) {
|
||||||
return new StatusBar(
|
return new StatusBar(
|
||||||
@@ -293,6 +297,8 @@ public interface StatusBarPhoneModule {
|
|||||||
brightnessSliderFactory,
|
brightnessSliderFactory,
|
||||||
chargingRippleAnimationController,
|
chargingRippleAnimationController,
|
||||||
ongoingCallController,
|
ongoingCallController,
|
||||||
|
animationScheduler,
|
||||||
|
dotViewController,
|
||||||
tunerService,
|
tunerService,
|
||||||
featureFlags);
|
featureFlags);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import androidx.test.filters.SmallTest;
|
|||||||
import com.android.systemui.R.dimen;
|
import com.android.systemui.R.dimen;
|
||||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||||
import com.android.systemui.settings.UserTracker;
|
import com.android.systemui.settings.UserTracker;
|
||||||
|
import com.android.systemui.statusbar.events.PrivacyDotViewController;
|
||||||
import com.android.systemui.tuner.TunerService;
|
import com.android.systemui.tuner.TunerService;
|
||||||
import com.android.systemui.util.settings.FakeSettings;
|
import com.android.systemui.util.settings.FakeSettings;
|
||||||
import com.android.systemui.util.settings.SecureSettings;
|
import com.android.systemui.util.settings.SecureSettings;
|
||||||
@@ -94,6 +95,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
|||||||
private BroadcastDispatcher mBroadcastDispatcher;
|
private BroadcastDispatcher mBroadcastDispatcher;
|
||||||
@Mock
|
@Mock
|
||||||
private UserTracker mUserTracker;
|
private UserTracker mUserTracker;
|
||||||
|
@Mock
|
||||||
|
private PrivacyDotViewController mDotViewController;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
@@ -116,7 +119,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
|||||||
mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
|
mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
|
||||||
|
|
||||||
mScreenDecorations = spy(new ScreenDecorations(mContext, mMainHandler, mSecureSettings,
|
mScreenDecorations = spy(new ScreenDecorations(mContext, mMainHandler, mSecureSettings,
|
||||||
mBroadcastDispatcher, mTunerService, mUserTracker) {
|
mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController) {
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void start() {
|
||||||
super.start();
|
super.start();
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ import androidx.test.filters.SmallTest;
|
|||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.SysuiBaseFragmentTest;
|
import com.android.systemui.SysuiBaseFragmentTest;
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
|
import com.android.systemui.statusbar.events.PrivacyDotViewController;
|
||||||
|
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
|
||||||
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
|
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
|
||||||
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
|
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
|
||||||
|
|
||||||
@@ -58,6 +60,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
|
|||||||
private View mCenteredNotificationAreaView;
|
private View mCenteredNotificationAreaView;
|
||||||
private StatusBarStateController mStatusBarStateController;
|
private StatusBarStateController mStatusBarStateController;
|
||||||
private OngoingCallController mOngoingCallController;
|
private OngoingCallController mOngoingCallController;
|
||||||
|
private SystemStatusAnimationScheduler mAnimationScheduler;
|
||||||
|
private PrivacyDotViewController mDotViewController;
|
||||||
|
|
||||||
public CollapsedStatusBarFragmentTest() {
|
public CollapsedStatusBarFragmentTest() {
|
||||||
super(CollapsedStatusBarFragment.class);
|
super(CollapsedStatusBarFragment.class);
|
||||||
@@ -212,6 +216,11 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
|
|||||||
@Override
|
@Override
|
||||||
protected Fragment instantiate(Context context, String className, Bundle arguments) {
|
protected Fragment instantiate(Context context, String className, Bundle arguments) {
|
||||||
mOngoingCallController = mock(OngoingCallController.class);
|
mOngoingCallController = mock(OngoingCallController.class);
|
||||||
return new CollapsedStatusBarFragment(mOngoingCallController);
|
mAnimationScheduler = mock(SystemStatusAnimationScheduler.class);
|
||||||
|
mDotViewController = mock(PrivacyDotViewController.class);
|
||||||
|
return new CollapsedStatusBarFragment(
|
||||||
|
mOngoingCallController,
|
||||||
|
mAnimationScheduler,
|
||||||
|
mDotViewController);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,8 @@ import com.android.systemui.statusbar.StatusBarStateControllerImpl;
|
|||||||
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
|
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
|
||||||
import com.android.systemui.statusbar.VibratorHelper;
|
import com.android.systemui.statusbar.VibratorHelper;
|
||||||
import com.android.systemui.statusbar.charging.WiredChargingRippleController;
|
import com.android.systemui.statusbar.charging.WiredChargingRippleController;
|
||||||
|
import com.android.systemui.statusbar.events.PrivacyDotViewController;
|
||||||
|
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
|
||||||
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||||
@@ -263,6 +265,8 @@ public class StatusBarTest extends SysuiTestCase {
|
|||||||
@Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
|
@Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
|
||||||
@Mock private WiredChargingRippleController mWiredChargingRippleController;
|
@Mock private WiredChargingRippleController mWiredChargingRippleController;
|
||||||
@Mock private OngoingCallController mOngoingCallController;
|
@Mock private OngoingCallController mOngoingCallController;
|
||||||
|
@Mock private SystemStatusAnimationScheduler mAnimationScheduler;
|
||||||
|
@Mock private PrivacyDotViewController mDotViewController;
|
||||||
@Mock private TunerService mTunerService;
|
@Mock private TunerService mTunerService;
|
||||||
@Mock private FeatureFlags mFeatureFlags;
|
@Mock private FeatureFlags mFeatureFlags;
|
||||||
private ShadeController mShadeController;
|
private ShadeController mShadeController;
|
||||||
@@ -429,6 +433,8 @@ public class StatusBarTest extends SysuiTestCase {
|
|||||||
mBrightnessSliderFactory,
|
mBrightnessSliderFactory,
|
||||||
mWiredChargingRippleController,
|
mWiredChargingRippleController,
|
||||||
mOngoingCallController,
|
mOngoingCallController,
|
||||||
|
mAnimationScheduler,
|
||||||
|
mDotViewController,
|
||||||
mTunerService,
|
mTunerService,
|
||||||
mFeatureFlags);
|
mFeatureFlags);
|
||||||
when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
|
when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
|
||||||
|
|||||||
Reference in New Issue
Block a user