Merge "Move Privacy dots to new DecorProvider"
This commit is contained in:
committed by
Android (Google) Code Review
commit
b89f428f97
33
packages/SystemUI/res/layout/privacy_dot_bottom_left.xml
Normal file
33
packages/SystemUI/res/layout/privacy_dot_bottom_left.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
** Copyright 2022, 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/privacy_dot_bottom_left_container"
|
||||
android:layout_height="@dimen/status_bar_height"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="bottom|left"
|
||||
android:paddingTop="@dimen/status_bar_padding_top"
|
||||
android:visibility="invisible" >
|
||||
<ImageView
|
||||
android:id="@+id/privacy_dot"
|
||||
android:contentDescription="@null"
|
||||
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>
|
||||
33
packages/SystemUI/res/layout/privacy_dot_bottom_right.xml
Normal file
33
packages/SystemUI/res/layout/privacy_dot_bottom_right.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
** Copyright 2022, 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/privacy_dot_bottom_right_container"
|
||||
android:layout_height="@dimen/status_bar_height"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:paddingTop="@dimen/status_bar_padding_top"
|
||||
android:visibility="invisible" >
|
||||
<ImageView
|
||||
android:id="@+id/privacy_dot"
|
||||
android:contentDescription="@null"
|
||||
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>
|
||||
33
packages/SystemUI/res/layout/privacy_dot_top_left.xml
Normal file
33
packages/SystemUI/res/layout/privacy_dot_top_left.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
** Copyright 2022, 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/privacy_dot_top_left_container"
|
||||
android:layout_height="@dimen/status_bar_height"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="top|left"
|
||||
android:paddingTop="@dimen/status_bar_padding_top"
|
||||
android:visibility="invisible" >
|
||||
<ImageView
|
||||
android:id="@+id/privacy_dot"
|
||||
android:contentDescription="@null"
|
||||
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>
|
||||
33
packages/SystemUI/res/layout/privacy_dot_top_right.xml
Normal file
33
packages/SystemUI/res/layout/privacy_dot_top_right.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
** Copyright 2022, 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/privacy_dot_top_right_container"
|
||||
android:layout_height="@dimen/status_bar_height"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="top|right"
|
||||
android:paddingTop="@dimen/status_bar_padding_top"
|
||||
android:visibility="invisible" >
|
||||
<ImageView
|
||||
android:id="@+id/privacy_dot"
|
||||
android:contentDescription="@null"
|
||||
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>
|
||||
@@ -27,22 +27,6 @@
|
||||
android:tint="#ff000000"
|
||||
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:paddingTop="@dimen/status_bar_padding_top"
|
||||
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
|
||||
android:id="@+id/right"
|
||||
android:layout_width="12dp"
|
||||
@@ -51,20 +35,4 @@
|
||||
android:layout_gravity="right|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:paddingTop="@dimen/status_bar_padding_top"
|
||||
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>
|
||||
|
||||
@@ -27,22 +27,6 @@
|
||||
android:tint="#ff000000"
|
||||
android:src="@drawable/rounded_corner_top"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/privacy_dot_left_container"
|
||||
android:layout_height="@dimen/status_bar_height"
|
||||
android:layout_width="wrap_content"
|
||||
android:paddingTop="@dimen/status_bar_padding_top"
|
||||
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
|
||||
android:id="@+id/right"
|
||||
android:layout_width="12dp"
|
||||
@@ -51,20 +35,4 @@
|
||||
android:layout_gravity="right|top"
|
||||
android:src="@drawable/rounded_corner_top"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/privacy_dot_right_container"
|
||||
android:layout_height="@dimen/status_bar_height"
|
||||
android:layout_width="wrap_content"
|
||||
android:paddingTop="@dimen/status_bar_padding_top"
|
||||
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>
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.Dimension;
|
||||
import android.annotation.IdRes;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
@@ -87,6 +88,11 @@ import com.android.systemui.animation.Interpolators;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.decor.DecorProvider;
|
||||
import com.android.systemui.decor.DecorProviderFactory;
|
||||
import com.android.systemui.decor.DecorProviderKt;
|
||||
import com.android.systemui.decor.OverlayWindow;
|
||||
import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
|
||||
import com.android.systemui.qs.SettingObserver;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.statusbar.events.PrivacyDotViewController;
|
||||
@@ -104,6 +110,8 @@ import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import kotlin.Pair;
|
||||
|
||||
/**
|
||||
* An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
|
||||
* for antialiasing and emulation purposes.
|
||||
@@ -135,6 +143,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
private final UserTracker mUserTracker;
|
||||
private final PrivacyDotViewController mDotViewController;
|
||||
private final ThreadFactory mThreadFactory;
|
||||
private final DecorProviderFactory mDotFactory;
|
||||
|
||||
//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
|
||||
@@ -146,14 +155,9 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
@VisibleForTesting
|
||||
protected Point mRoundedDefaultBottom = new Point(0, 0);
|
||||
@VisibleForTesting
|
||||
protected View[] mOverlays;
|
||||
protected OverlayWindow[] mOverlays = null;
|
||||
@Nullable
|
||||
private DisplayCutoutView[] mCutoutViews;
|
||||
//TODO:
|
||||
View mTopLeftDot;
|
||||
View mTopRightDot;
|
||||
View mBottomLeftDot;
|
||||
View mBottomRightDot;
|
||||
private float mDensity;
|
||||
private WindowManager mWindowManager;
|
||||
private int mRotation;
|
||||
@@ -162,7 +166,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
private Handler mHandler;
|
||||
private boolean mPendingRotationChange;
|
||||
private boolean mIsRoundedCornerMultipleRadius;
|
||||
private boolean mIsPrivacyDotEnabled;
|
||||
private Drawable mRoundedCornerDrawable;
|
||||
private Drawable mRoundedCornerDrawableTop;
|
||||
private Drawable mRoundedCornerDrawableBottom;
|
||||
@@ -227,7 +230,8 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
TunerService tunerService,
|
||||
UserTracker userTracker,
|
||||
PrivacyDotViewController dotViewController,
|
||||
ThreadFactory threadFactory) {
|
||||
ThreadFactory threadFactory,
|
||||
PrivacyDotDecorProviderFactory dotFactory) {
|
||||
super(context);
|
||||
mMainExecutor = mainExecutor;
|
||||
mSecureSettings = secureSettings;
|
||||
@@ -236,6 +240,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
mUserTracker = userTracker;
|
||||
mDotViewController = dotViewController;
|
||||
mThreadFactory = threadFactory;
|
||||
mDotFactory = dotFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -250,11 +255,14 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
mDotViewController.setUiExecutor(mExecutor);
|
||||
}
|
||||
|
||||
private boolean isPrivacyDotEnabled() {
|
||||
return mDotFactory.getHasProviders();
|
||||
}
|
||||
|
||||
private void startOnScreenDecorationsThread() {
|
||||
mRotation = mContext.getDisplay().getRotation();
|
||||
mDisplayUniqueId = mContext.getDisplay().getUniqueId();
|
||||
mIsRoundedCornerMultipleRadius = isRoundedCornerMultipleRadius(mContext, mDisplayUniqueId);
|
||||
mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot);
|
||||
mWindowManager = mContext.getSystemService(WindowManager.class);
|
||||
mDisplayManager = mContext.getSystemService(DisplayManager.class);
|
||||
updateRoundedCornerDrawable();
|
||||
@@ -292,8 +300,9 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
|
||||
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
|
||||
if (mOverlays[i] != null) {
|
||||
mOverlays[i].getViewTreeObserver().addOnPreDrawListener(
|
||||
new RestartingPreDrawListener(mOverlays[i], i, newRotation));
|
||||
final ViewGroup overlayView = mOverlays[i].getRootView();
|
||||
overlayView.getViewTreeObserver().addOnPreDrawListener(
|
||||
new RestartingPreDrawListener(overlayView, i, newRotation));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,24 +322,64 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
updateOrientation();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private View getOverlayView(@IdRes int id) {
|
||||
if (mOverlays == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (final OverlayWindow overlay : mOverlays) {
|
||||
if (overlay == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final View view = overlay.getView(id);
|
||||
if (view != null) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void removeOverlayView(@IdRes int id) {
|
||||
if (mOverlays == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final OverlayWindow overlay : mOverlays) {
|
||||
if (overlay == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
overlay.removeView(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupDecorations() {
|
||||
if (hasRoundedCorners() || shouldDrawCutout() || mIsPrivacyDotEnabled) {
|
||||
List<DecorProvider> decorProviders = mDotFactory.getProviders();
|
||||
|
||||
if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) {
|
||||
final DisplayCutout cutout = getCutout();
|
||||
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
|
||||
if (shouldShowCutout(i, cutout) || shouldShowRoundedCorner(i, cutout)
|
||||
|| shouldShowPrivacyDot(i, cutout)) {
|
||||
createOverlay(i, cutout);
|
||||
Pair<List<DecorProvider>, List<DecorProvider>> pair =
|
||||
DecorProviderKt.partitionAlignedBound(decorProviders, i);
|
||||
decorProviders = pair.getSecond();
|
||||
createOverlay(i, cutout, pair.getFirst());
|
||||
} else {
|
||||
removeOverlay(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (mTopLeftDot != null && mTopRightDot != null && mBottomLeftDot != null
|
||||
&& mBottomRightDot != null) {
|
||||
final View tl, tr, bl, br;
|
||||
if ((tl = getOverlayView(R.id.privacy_dot_top_left_container)) != null
|
||||
&& (tr = getOverlayView(R.id.privacy_dot_top_right_container)) != null
|
||||
&& (bl = getOverlayView(R.id.privacy_dot_bottom_left_container)) != null
|
||||
&& (br = getOverlayView(R.id.privacy_dot_bottom_right_container)) != null) {
|
||||
// Overlays have been created, send the dots to the controller
|
||||
//TODO: need a better way to do this
|
||||
mDotViewController.initialize(
|
||||
mTopLeftDot, mTopRightDot, mBottomLeftDot, mBottomRightDot);
|
||||
mDotViewController.initialize(tl, tr, bl, br);
|
||||
}
|
||||
} else {
|
||||
removeAllOverlays();
|
||||
@@ -414,13 +463,16 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
if (mOverlays == null || mOverlays[pos] == null) {
|
||||
return;
|
||||
}
|
||||
mWindowManager.removeViewImmediate(mOverlays[pos]);
|
||||
mWindowManager.removeViewImmediate(mOverlays[pos].getRootView());
|
||||
mOverlays[pos] = null;
|
||||
}
|
||||
|
||||
private void createOverlay(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
|
||||
private void createOverlay(
|
||||
@BoundsPosition int pos,
|
||||
@Nullable DisplayCutout cutout,
|
||||
@NonNull List<DecorProvider> decorProviders) {
|
||||
if (mOverlays == null) {
|
||||
mOverlays = new View[BOUNDS_POSITION_LENGTH];
|
||||
mOverlays = new OverlayWindow[BOUNDS_POSITION_LENGTH];
|
||||
}
|
||||
|
||||
if (mCutoutViews == null) {
|
||||
@@ -430,78 +482,49 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
if (mOverlays[pos] != null) {
|
||||
return;
|
||||
}
|
||||
mOverlays[pos] = overlayForPosition(pos, cutout);
|
||||
|
||||
mOverlays[pos] = overlayForPosition(pos, decorProviders);
|
||||
|
||||
mCutoutViews[pos] = new DisplayCutoutView(mContext, pos, this);
|
||||
((ViewGroup) mOverlays[pos]).addView(mCutoutViews[pos]);
|
||||
mOverlays[pos].getRootView().addView(mCutoutViews[pos]);
|
||||
|
||||
mOverlays[pos].setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
mOverlays[pos].setAlpha(0);
|
||||
mOverlays[pos].setForceDarkAllowed(false);
|
||||
final ViewGroup overlayView = mOverlays[pos].getRootView();
|
||||
overlayView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
overlayView.setAlpha(0);
|
||||
overlayView.setForceDarkAllowed(false);
|
||||
|
||||
updateView(pos, cutout);
|
||||
|
||||
mWindowManager.addView(mOverlays[pos], getWindowLayoutParams(pos));
|
||||
mWindowManager.addView(overlayView, getWindowLayoutParams(pos));
|
||||
|
||||
mOverlays[pos].addOnLayoutChangeListener(new OnLayoutChangeListener() {
|
||||
overlayView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
|
||||
@Override
|
||||
public void onLayoutChange(View v, int left, int top, int right, int bottom,
|
||||
int oldLeft, int oldTop, int oldRight, int oldBottom) {
|
||||
mOverlays[pos].removeOnLayoutChangeListener(this);
|
||||
mOverlays[pos].animate()
|
||||
overlayView.removeOnLayoutChangeListener(this);
|
||||
overlayView.animate()
|
||||
.alpha(1)
|
||||
.setDuration(1000)
|
||||
.start();
|
||||
}
|
||||
});
|
||||
|
||||
mOverlays[pos].getViewTreeObserver().addOnPreDrawListener(
|
||||
new ValidatingPreDrawListener(mOverlays[pos]));
|
||||
mOverlays[pos].getRootView().getViewTreeObserver().addOnPreDrawListener(
|
||||
new ValidatingPreDrawListener(mOverlays[pos].getRootView()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow overrides for top/bottom positions
|
||||
*/
|
||||
private View overlayForPosition(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
|
||||
final int layoutId = (pos == BOUNDS_POSITION_LEFT || pos == BOUNDS_POSITION_TOP)
|
||||
? R.layout.rounded_corners_top : R.layout.rounded_corners_bottom;
|
||||
final ViewGroup vg = (ViewGroup) LayoutInflater.from(mContext).inflate(layoutId, null);
|
||||
initPrivacyDotView(vg, pos, cutout);
|
||||
return vg;
|
||||
}
|
||||
|
||||
private void initPrivacyDotView(@NonNull ViewGroup viewGroup, @BoundsPosition int pos,
|
||||
@Nullable DisplayCutout cutout) {
|
||||
final View left = viewGroup.findViewById(R.id.privacy_dot_left_container);
|
||||
final View right = viewGroup.findViewById(R.id.privacy_dot_right_container);
|
||||
if (!shouldShowPrivacyDot(pos, cutout)) {
|
||||
viewGroup.removeView(left);
|
||||
viewGroup.removeView(right);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pos) {
|
||||
case BOUNDS_POSITION_LEFT: {
|
||||
mTopLeftDot = left;
|
||||
mBottomLeftDot = right;
|
||||
break;
|
||||
}
|
||||
case BOUNDS_POSITION_TOP: {
|
||||
mTopLeftDot = left;
|
||||
mTopRightDot = right;
|
||||
break;
|
||||
}
|
||||
case BOUNDS_POSITION_RIGHT: {
|
||||
mTopRightDot = left;
|
||||
mBottomRightDot = right;
|
||||
break;
|
||||
}
|
||||
case BOUNDS_POSITION_BOTTOM: {
|
||||
mBottomLeftDot = left;
|
||||
mBottomRightDot = right;
|
||||
break;
|
||||
}
|
||||
}
|
||||
private OverlayWindow overlayForPosition(
|
||||
@BoundsPosition int pos,
|
||||
@NonNull List<DecorProvider> decorProviders) {
|
||||
final OverlayWindow currentOverlay = new OverlayWindow(LayoutInflater.from(mContext), pos);
|
||||
decorProviders.forEach(provider -> {
|
||||
removeOverlayView(provider.getViewId());
|
||||
currentOverlay.addDecorProvider(provider, mRotation);
|
||||
});
|
||||
return currentOverlay;
|
||||
}
|
||||
|
||||
private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
|
||||
@@ -638,12 +661,15 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
if (mOverlays[i] == null) {
|
||||
continue;
|
||||
}
|
||||
final int size = ((ViewGroup) mOverlays[i]).getChildCount();
|
||||
final ViewGroup overlayView = mOverlays[i].getRootView();
|
||||
final int size = overlayView.getChildCount();
|
||||
View child;
|
||||
for (int j = 0; j < size; j++) {
|
||||
child = ((ViewGroup) mOverlays[i]).getChildAt(j);
|
||||
if (child.getId() == R.id.privacy_dot_left_container
|
||||
|| child.getId() == R.id.privacy_dot_right_container) {
|
||||
child = overlayView.getChildAt(j);
|
||||
if (child.getId() == R.id.privacy_dot_top_left_container
|
||||
|| child.getId() == R.id.privacy_dot_top_right_container
|
||||
|| child.getId() == R.id.privacy_dot_bottom_left_container
|
||||
|| child.getId() == R.id.privacy_dot_bottom_right_container) {
|
||||
// Exclude privacy dot from color inversion (for now?)
|
||||
continue;
|
||||
}
|
||||
@@ -684,13 +710,18 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
pw.println("ScreenDecorations state:");
|
||||
pw.println(" DEBUG_DISABLE_SCREEN_DECORATIONS:" + DEBUG_DISABLE_SCREEN_DECORATIONS);
|
||||
pw.println(" mIsRoundedCornerMultipleRadius:" + mIsRoundedCornerMultipleRadius);
|
||||
pw.println(" mIsPrivacyDotEnabled:" + mIsPrivacyDotEnabled);
|
||||
pw.println(" mIsPrivacyDotEnabled:" + isPrivacyDotEnabled());
|
||||
pw.println(" mPendingRotationChange:" + mPendingRotationChange);
|
||||
pw.println(" mRoundedDefault(x,y)=(" + mRoundedDefault.x + "," + mRoundedDefault.y + ")");
|
||||
pw.println(" mRoundedDefaultTop(x,y)=(" + mRoundedDefaultTop.x + "," + mRoundedDefaultTop.y
|
||||
+ ")");
|
||||
pw.println(" mRoundedDefaultBottom(x,y)=(" + mRoundedDefaultBottom.x + ","
|
||||
+ mRoundedDefaultBottom.y + ")");
|
||||
pw.println(" mOverlays(left,top,right,bottom)=("
|
||||
+ (mOverlays != null && mOverlays[BOUNDS_POSITION_LEFT] != null) + ","
|
||||
+ (mOverlays != null && mOverlays[BOUNDS_POSITION_TOP] != null) + ","
|
||||
+ (mOverlays != null && mOverlays[BOUNDS_POSITION_RIGHT] != null) + ","
|
||||
+ (mOverlays != null && mOverlays[BOUNDS_POSITION_BOTTOM] != null) + ")");
|
||||
}
|
||||
|
||||
private void updateOrientation() {
|
||||
@@ -843,7 +874,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
|
||||
private void updateRoundedCornerView(@BoundsPosition int pos, int id,
|
||||
@Nullable DisplayCutout cutout) {
|
||||
final View rounded = mOverlays[pos].findViewById(id);
|
||||
final View rounded = mOverlays[pos].getRootView().findViewById(id);
|
||||
if (rounded == null) {
|
||||
return;
|
||||
}
|
||||
@@ -929,7 +960,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
}
|
||||
|
||||
private boolean shouldShowPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
|
||||
return mIsPrivacyDotEnabled && isDefaultShownOverlayPos(pos, cutout);
|
||||
return isPrivacyDotEnabled() && isDefaultShownOverlayPos(pos, cutout);
|
||||
}
|
||||
|
||||
private boolean shouldShowCutout(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
|
||||
@@ -955,7 +986,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
if (mOverlays[i] == null) {
|
||||
continue;
|
||||
}
|
||||
mWindowManager.updateViewLayout(mOverlays[i], getWindowLayoutParams(i));
|
||||
mWindowManager.updateViewLayout(mOverlays[i].getRootView(), getWindowLayoutParams(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1003,9 +1034,10 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
if (mOverlays[i] == null) {
|
||||
continue;
|
||||
}
|
||||
((ImageView) mOverlays[i].findViewById(R.id.left)).setImageDrawable(
|
||||
final ViewGroup overlayView = mOverlays[i].getRootView();
|
||||
((ImageView) overlayView.findViewById(R.id.left)).setImageDrawable(
|
||||
isTopRoundedCorner(i, R.id.left) ? top : bottom);
|
||||
((ImageView) mOverlays[i].findViewById(R.id.right)).setImageDrawable(
|
||||
((ImageView) overlayView.findViewById(R.id.right)).setImageDrawable(
|
||||
isTopRoundedCorner(i, R.id.right) ? top : bottom);
|
||||
}
|
||||
}
|
||||
@@ -1047,9 +1079,10 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
|
||||
if (mOverlays[i] == null) {
|
||||
continue;
|
||||
}
|
||||
setSize(mOverlays[i].findViewById(R.id.left),
|
||||
final ViewGroup overlayView = mOverlays[i].getRootView();
|
||||
setSize(overlayView.findViewById(R.id.left),
|
||||
isTopRoundedCorner(i, R.id.left) ? sizeTop : sizeBottom);
|
||||
setSize(mOverlays[i].findViewById(R.id.right),
|
||||
setSize(overlayView.findViewById(R.id.right),
|
||||
isTopRoundedCorner(i, R.id.right) ? sizeTop : sizeBottom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.decor
|
||||
import android.view.DisplayCutout
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Surface
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
||||
/**
|
||||
* An interface for providing view with a specific functionality. Take an example, if privacy dot
|
||||
* is enabled, there are 4 DecorProviders which are used to provide privacy dot views on top-left,
|
||||
* top-right, bottom-left, bottom-right.
|
||||
*/
|
||||
abstract class DecorProvider {
|
||||
|
||||
/** Id for the view which is created through inflateView() */
|
||||
abstract val viewId: Int
|
||||
|
||||
/** The number of total aligned bounds */
|
||||
val numOfAlignedEdge: Int
|
||||
get() = alignedBounds.size
|
||||
|
||||
/** The aligned bounds for the view which is created through inflateView() */
|
||||
abstract val alignedBounds: List<Int>
|
||||
|
||||
/** Inflate view into parent as current rotation */
|
||||
abstract fun inflateView(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup,
|
||||
@Surface.Rotation rotation: Int
|
||||
): View
|
||||
}
|
||||
|
||||
/**
|
||||
* Split list to 2 list, and return it back as Pair<>. The providers on the first list contains this
|
||||
* alignedBound element. The providers on the second list do not contain this alignedBound element
|
||||
*/
|
||||
fun List<DecorProvider>.partitionAlignedBound(
|
||||
@DisplayCutout.BoundsPosition alignedBound: Int
|
||||
): Pair<List<DecorProvider>, List<DecorProvider>> {
|
||||
return partition { it.alignedBounds.contains(alignedBound) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A provider for view shown on corner.
|
||||
*/
|
||||
abstract class CornerDecorProvider : DecorProvider() {
|
||||
/** The first bound which a corner view is aligned based on rotation 0 */
|
||||
@DisplayCutout.BoundsPosition protected abstract val alignedBound1: Int
|
||||
/** The second bound which a corner view is aligned based on rotation 0 */
|
||||
@DisplayCutout.BoundsPosition protected abstract val alignedBound2: Int
|
||||
|
||||
override val alignedBounds: List<Int> by lazy {
|
||||
listOf(alignedBound1, alignedBound2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.decor
|
||||
|
||||
abstract class DecorProviderFactory {
|
||||
abstract val providers: List<DecorProvider>
|
||||
abstract val hasProviders: Boolean
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.decor
|
||||
|
||||
import android.annotation.IdRes
|
||||
import android.view.DisplayCutout
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Surface
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.android.systemui.R
|
||||
import java.util.HashMap
|
||||
|
||||
class OverlayWindow(private val layoutInflater: LayoutInflater, private val pos: Int) {
|
||||
|
||||
private val layoutId: Int
|
||||
get() {
|
||||
return if (pos == DisplayCutout.BOUNDS_POSITION_LEFT ||
|
||||
pos == DisplayCutout.BOUNDS_POSITION_TOP) {
|
||||
R.layout.rounded_corners_top
|
||||
} else {
|
||||
R.layout.rounded_corners_bottom
|
||||
}
|
||||
}
|
||||
|
||||
val rootView = layoutInflater.inflate(layoutId, null) as ViewGroup
|
||||
private val viewProviderMap: MutableMap<Int, Pair<View, DecorProvider>> = HashMap()
|
||||
|
||||
fun addDecorProvider(decorProvider: DecorProvider, @Surface.Rotation rotation: Int) {
|
||||
val view = decorProvider.inflateView(layoutInflater, rootView, rotation)
|
||||
viewProviderMap[decorProvider.viewId] = Pair(view, decorProvider)
|
||||
}
|
||||
|
||||
fun getView(@IdRes id: Int): View? {
|
||||
val pair = viewProviderMap[id]
|
||||
return pair?.first
|
||||
}
|
||||
|
||||
fun removeView(@IdRes id: Int) {
|
||||
val view = getView(id)
|
||||
if (view != null) {
|
||||
rootView.removeView(view)
|
||||
viewProviderMap.remove(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.decor
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.view.DisplayCutout
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Surface
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import javax.inject.Inject
|
||||
|
||||
@SysUISingleton
|
||||
class PrivacyDotDecorProviderFactory @Inject constructor(
|
||||
@Main private val res: Resources
|
||||
) : DecorProviderFactory() {
|
||||
|
||||
private val isPrivacyDotEnabled: Boolean
|
||||
get() = res.getBoolean(R.bool.config_enablePrivacyDot)
|
||||
|
||||
override val hasProviders: Boolean
|
||||
get() = isPrivacyDotEnabled
|
||||
|
||||
override val providers: List<DecorProvider>
|
||||
get() {
|
||||
return if (hasProviders) {
|
||||
listOf(
|
||||
PrivacyDotCornerDecorProviderImpl(
|
||||
viewId = R.id.privacy_dot_top_left_container,
|
||||
alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
|
||||
alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
|
||||
layoutId = R.layout.privacy_dot_top_left),
|
||||
PrivacyDotCornerDecorProviderImpl(
|
||||
viewId = R.id.privacy_dot_top_right_container,
|
||||
alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
|
||||
alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
|
||||
layoutId = R.layout.privacy_dot_top_right),
|
||||
PrivacyDotCornerDecorProviderImpl(
|
||||
viewId = R.id.privacy_dot_bottom_left_container,
|
||||
alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
|
||||
alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
|
||||
layoutId = R.layout.privacy_dot_bottom_left),
|
||||
PrivacyDotCornerDecorProviderImpl(
|
||||
viewId = R.id.privacy_dot_bottom_right_container,
|
||||
alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
|
||||
alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
|
||||
layoutId = R.layout.privacy_dot_bottom_right)
|
||||
)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PrivacyDotCornerDecorProviderImpl(
|
||||
override val viewId: Int,
|
||||
@DisplayCutout.BoundsPosition override val alignedBound1: Int,
|
||||
@DisplayCutout.BoundsPosition override val alignedBound2: Int,
|
||||
private val layoutId: Int
|
||||
) : CornerDecorProvider() {
|
||||
|
||||
override fun inflateView(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup,
|
||||
@Surface.Rotation rotation: Int
|
||||
): View {
|
||||
inflater.inflate(layoutId, parent, true)
|
||||
return parent.getChildAt(parent.childCount - 1 /* latest new added child */)
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.IdRes;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Insets;
|
||||
@@ -63,9 +64,15 @@ import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowMetrics;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.decor.CornerDecorProvider;
|
||||
import com.android.systemui.decor.DecorProvider;
|
||||
import com.android.systemui.decor.OverlayWindow;
|
||||
import com.android.systemui.decor.PrivacyDotCornerDecorProviderImpl;
|
||||
import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.statusbar.events.PrivacyDotViewController;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
@@ -81,6 +88,7 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
@RunWithLooper
|
||||
@@ -96,6 +104,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
private SecureSettings mSecureSettings;
|
||||
private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
private FakeThreadFactory mThreadFactory;
|
||||
private ArrayList<DecorProvider> mDecorProviders;
|
||||
@Mock
|
||||
private TunerService mTunerService;
|
||||
@Mock
|
||||
@@ -106,6 +115,16 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
private PrivacyDotViewController mDotViewController;
|
||||
@Mock
|
||||
private TypedArray mMockTypedArray;
|
||||
@Mock
|
||||
private PrivacyDotDecorProviderFactory mPrivacyDotDecorProviderFactory;
|
||||
@Mock
|
||||
private CornerDecorProvider mPrivacyDotTopLeftDecorProvider;
|
||||
@Mock
|
||||
private CornerDecorProvider mPrivacyDotTopRightDecorProvider;
|
||||
@Mock
|
||||
private CornerDecorProvider mPrivacyDotBottomLeftDecorProvider;
|
||||
@Mock
|
||||
private CornerDecorProvider mPrivacyDotBottomRightDecorProvider;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@@ -129,9 +148,33 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
|
||||
when(mMockTypedArray.length()).thenReturn(0);
|
||||
|
||||
mPrivacyDotTopLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
|
||||
R.id.privacy_dot_top_left_container,
|
||||
DisplayCutout.BOUNDS_POSITION_TOP,
|
||||
DisplayCutout.BOUNDS_POSITION_LEFT,
|
||||
R.layout.privacy_dot_top_left));
|
||||
|
||||
mPrivacyDotTopRightDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
|
||||
R.id.privacy_dot_top_right_container,
|
||||
DisplayCutout.BOUNDS_POSITION_TOP,
|
||||
DisplayCutout.BOUNDS_POSITION_RIGHT,
|
||||
R.layout.privacy_dot_top_right));
|
||||
|
||||
mPrivacyDotBottomLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
|
||||
R.id.privacy_dot_bottom_left_container,
|
||||
DisplayCutout.BOUNDS_POSITION_BOTTOM,
|
||||
DisplayCutout.BOUNDS_POSITION_LEFT,
|
||||
R.layout.privacy_dot_bottom_left));
|
||||
|
||||
mPrivacyDotBottomRightDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
|
||||
R.id.privacy_dot_bottom_right_container,
|
||||
DisplayCutout.BOUNDS_POSITION_BOTTOM,
|
||||
DisplayCutout.BOUNDS_POSITION_RIGHT,
|
||||
R.layout.privacy_dot_bottom_right));
|
||||
|
||||
mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
|
||||
mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController,
|
||||
mThreadFactory) {
|
||||
mThreadFactory, mPrivacyDotDecorProviderFactory) {
|
||||
@Override
|
||||
public void start() {
|
||||
super.start();
|
||||
@@ -157,7 +200,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
private void verifyRoundedCornerViewsVisibility(
|
||||
@DisplayCutout.BoundsPosition final int overlayId,
|
||||
@View.Visibility final int visibility) {
|
||||
final View overlay = mScreenDecorations.mOverlays[overlayId];
|
||||
final View overlay = mScreenDecorations.mOverlays[overlayId].getRootView();
|
||||
final View left = overlay.findViewById(R.id.left);
|
||||
final View right = overlay.findViewById(R.id.right);
|
||||
assertNotNull(left);
|
||||
@@ -166,23 +209,42 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
assertThat(right.getVisibility()).isEqualTo(visibility);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private View findViewFromOverlays(@IdRes int id) {
|
||||
for (OverlayWindow overlay: mScreenDecorations.mOverlays) {
|
||||
if (overlay == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
View view = overlay.getRootView().findViewById(id);
|
||||
if (view != null) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void verifyTopDotViewsNullable(final boolean isAssertNull) {
|
||||
View tl = findViewFromOverlays(R.id.privacy_dot_top_left_container);
|
||||
View tr = findViewFromOverlays(R.id.privacy_dot_top_right_container);
|
||||
if (isAssertNull) {
|
||||
assertNull(mScreenDecorations.mTopLeftDot);
|
||||
assertNull(mScreenDecorations.mTopRightDot);
|
||||
assertNull(tl);
|
||||
assertNull(tr);
|
||||
} else {
|
||||
assertNotNull(mScreenDecorations.mTopLeftDot);
|
||||
assertNotNull(mScreenDecorations.mTopRightDot);
|
||||
assertNotNull(tl);
|
||||
assertNotNull(tr);
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyBottomDotViewsNullable(final boolean isAssertNull) {
|
||||
View bl = findViewFromOverlays(R.id.privacy_dot_bottom_left_container);
|
||||
View br = findViewFromOverlays(R.id.privacy_dot_bottom_right_container);
|
||||
if (isAssertNull) {
|
||||
assertNull(mScreenDecorations.mBottomLeftDot);
|
||||
assertNull(mScreenDecorations.mBottomRightDot);
|
||||
assertNull(bl);
|
||||
assertNull(br);
|
||||
} else {
|
||||
assertNotNull(mScreenDecorations.mBottomLeftDot);
|
||||
assertNotNull(mScreenDecorations.mBottomRightDot);
|
||||
assertNotNull(bl);
|
||||
assertNotNull(br);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,14 +255,18 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
|
||||
private void verifyTopDotViewsVisibility(@View.Visibility final int visibility) {
|
||||
verifyTopDotViewsNullable(false);
|
||||
assertThat(mScreenDecorations.mTopLeftDot.getVisibility()).isEqualTo(visibility);
|
||||
assertThat(mScreenDecorations.mTopRightDot.getVisibility()).isEqualTo(visibility);
|
||||
View tl = findViewFromOverlays(R.id.privacy_dot_top_left_container);
|
||||
View tr = findViewFromOverlays(R.id.privacy_dot_top_right_container);
|
||||
assertThat(tl.getVisibility()).isEqualTo(visibility);
|
||||
assertThat(tr.getVisibility()).isEqualTo(visibility);
|
||||
}
|
||||
|
||||
private void verifyBottomDotViewsVisibility(@View.Visibility final int visibility) {
|
||||
verifyBottomDotViewsNullable(false);
|
||||
assertThat(mScreenDecorations.mBottomLeftDot.getVisibility()).isEqualTo(visibility);
|
||||
assertThat(mScreenDecorations.mBottomRightDot.getVisibility()).isEqualTo(visibility);
|
||||
View bl = findViewFromOverlays(R.id.privacy_dot_bottom_left_container);
|
||||
View br = findViewFromOverlays(R.id.privacy_dot_bottom_right_container);
|
||||
assertThat(bl.getVisibility()).isEqualTo(visibility);
|
||||
assertThat(br.getVisibility()).isEqualTo(visibility);
|
||||
}
|
||||
|
||||
private void verifyDotViewsVisibility(@View.Visibility final int visibility) {
|
||||
@@ -219,32 +285,32 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
|
||||
if (left) {
|
||||
assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
|
||||
verify(mWindowManager, times(1))
|
||||
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]), any());
|
||||
verify(mWindowManager, times(1)).addView(
|
||||
eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()), any());
|
||||
} else {
|
||||
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
|
||||
}
|
||||
|
||||
if (top) {
|
||||
assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
|
||||
verify(mWindowManager, times(1))
|
||||
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
|
||||
verify(mWindowManager, times(1)).addView(
|
||||
eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()), any());
|
||||
} else {
|
||||
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
|
||||
}
|
||||
|
||||
if (right) {
|
||||
assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
|
||||
verify(mWindowManager, times(1))
|
||||
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]), any());
|
||||
verify(mWindowManager, times(1)).addView(
|
||||
eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()), any());
|
||||
} else {
|
||||
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
|
||||
}
|
||||
|
||||
if (bottom) {
|
||||
assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
|
||||
verify(mWindowManager, times(1))
|
||||
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
|
||||
verify(mWindowManager, times(1)).addView(
|
||||
eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()), any());
|
||||
} else {
|
||||
assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
|
||||
}
|
||||
@@ -381,18 +447,18 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
doReturn(null).when(mScreenDecorations).getCutout();
|
||||
|
||||
mScreenDecorations.start();
|
||||
View leftRoundedCorner =
|
||||
mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].findViewById(R.id.left);
|
||||
View rightRoundedCorner =
|
||||
mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].findViewById(R.id.right);
|
||||
View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
|
||||
.findViewById(R.id.left);
|
||||
View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
|
||||
.findViewById(R.id.right);
|
||||
verify(mScreenDecorations, atLeastOnce())
|
||||
.setSize(leftRoundedCorner, new Point(testTopRadius, testTopRadius));
|
||||
verify(mScreenDecorations, atLeastOnce())
|
||||
.setSize(rightRoundedCorner, new Point(testTopRadius, testTopRadius));
|
||||
leftRoundedCorner =
|
||||
mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].findViewById(R.id.left);
|
||||
rightRoundedCorner =
|
||||
mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].findViewById(R.id.right);
|
||||
leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()
|
||||
.findViewById(R.id.left);
|
||||
rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()
|
||||
.findViewById(R.id.right);
|
||||
verify(mScreenDecorations, atLeastOnce())
|
||||
.setSize(leftRoundedCorner, new Point(testBottomRadius, testBottomRadius));
|
||||
verify(mScreenDecorations, atLeastOnce())
|
||||
@@ -414,26 +480,26 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
mScreenDecorations.start();
|
||||
final Point topRadius = new Point(testTopRadius, testTopRadius);
|
||||
final Point bottomRadius = new Point(testBottomRadius, testBottomRadius);
|
||||
View leftRoundedCorner =
|
||||
mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].findViewById(R.id.left);
|
||||
View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
|
||||
.findViewById(R.id.left);
|
||||
boolean isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.left);
|
||||
verify(mScreenDecorations, atLeastOnce())
|
||||
.setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius);
|
||||
|
||||
View rightRoundedCorner =
|
||||
mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].findViewById(R.id.right);
|
||||
View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
|
||||
.findViewById(R.id.right);
|
||||
isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.right);
|
||||
verify(mScreenDecorations, atLeastOnce())
|
||||
.setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius);
|
||||
|
||||
leftRoundedCorner =
|
||||
mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].findViewById(R.id.left);
|
||||
leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()
|
||||
.findViewById(R.id.left);
|
||||
isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.left);
|
||||
verify(mScreenDecorations, atLeastOnce())
|
||||
.setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius);
|
||||
|
||||
rightRoundedCorner =
|
||||
mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].findViewById(R.id.right);
|
||||
rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()
|
||||
.findViewById(R.id.right);
|
||||
isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.right);
|
||||
verify(mScreenDecorations, atLeastOnce())
|
||||
.setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius);
|
||||
@@ -790,6 +856,22 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
|
||||
mScreenDecorations.onConfigurationChanged(new Configuration());
|
||||
verifyOverlaysExistAndAdded(true, false, true, false);
|
||||
|
||||
// Verify each privacy dot id appears only once
|
||||
mDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> {
|
||||
int findCount = 0;
|
||||
for (OverlayWindow overlay: mScreenDecorations.mOverlays) {
|
||||
if (overlay == null) {
|
||||
continue;
|
||||
}
|
||||
final View view = overlay.getRootView().findViewById(viewId);
|
||||
if (view != null) {
|
||||
findCount++;
|
||||
}
|
||||
}
|
||||
assertEquals(1, findCount);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -985,8 +1067,16 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
R.bool.config_roundedCornerMultipleRadius, multipleRadius);
|
||||
mContext.getOrCreateTestableResources().addOverride(
|
||||
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout);
|
||||
mContext.getOrCreateTestableResources().addOverride(
|
||||
R.bool.config_enablePrivacyDot, privacyDot);
|
||||
|
||||
mDecorProviders = new ArrayList<>();
|
||||
if (privacyDot) {
|
||||
mDecorProviders.add(mPrivacyDotTopLeftDecorProvider);
|
||||
mDecorProviders.add(mPrivacyDotTopRightDecorProvider);
|
||||
mDecorProviders.add(mPrivacyDotBottomLeftDecorProvider);
|
||||
mDecorProviders.add(mPrivacyDotBottomRightDecorProvider);
|
||||
}
|
||||
when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mDecorProviders);
|
||||
when(mPrivacyDotDecorProviderFactory.getHasProviders()).thenReturn(privacyDot);
|
||||
}
|
||||
|
||||
private DisplayCutout getDisplayCutoutForRotation(Insets safeInsets, Rect[] cutoutBounds) {
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.decor
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.DisplayCutout
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Surface
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.util.mockito.eq
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.anyInt
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
class OverlayWindowTest : SysuiTestCase() {
|
||||
|
||||
companion object {
|
||||
private val TEST_DECOR_VIEW_ID = R.id.privacy_dot_bottom_right_container
|
||||
private val TEST_DECOR_LAYOUT_ID = R.layout.privacy_dot_bottom_right
|
||||
}
|
||||
|
||||
private lateinit var overlay: OverlayWindow
|
||||
|
||||
@Mock private lateinit var layoutInflater: LayoutInflater
|
||||
@Mock private lateinit var decorProvider: DecorProvider
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
layoutInflater = spy(LayoutInflater.from(mContext))
|
||||
|
||||
overlay = OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT)
|
||||
|
||||
whenever(decorProvider.viewId).thenReturn(TEST_DECOR_VIEW_ID)
|
||||
whenever(decorProvider.inflateView(
|
||||
eq(layoutInflater),
|
||||
eq(overlay.rootView),
|
||||
anyInt())
|
||||
).then {
|
||||
val layoutInflater = it.getArgument<LayoutInflater>(0)
|
||||
val parent = it.getArgument<ViewGroup>(1)
|
||||
layoutInflater.inflate(TEST_DECOR_LAYOUT_ID, parent)
|
||||
return@then parent.getChildAt(parent.childCount - 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAnyBoundsPositionShallNoExceptionForConstructor() {
|
||||
OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_LEFT)
|
||||
OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_TOP)
|
||||
OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT)
|
||||
OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_BOTTOM)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddProvider() {
|
||||
@Surface.Rotation val rotation = Surface.ROTATION_270
|
||||
overlay.addDecorProvider(decorProvider, rotation)
|
||||
verify(decorProvider, Mockito.times(1)).inflateView(
|
||||
eq(layoutInflater), eq(overlay.rootView), eq(rotation))
|
||||
val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID)
|
||||
Assert.assertNotNull(viewFoundFromRootView)
|
||||
Assert.assertEquals(viewFoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveView() {
|
||||
@Surface.Rotation val rotation = Surface.ROTATION_270
|
||||
overlay.addDecorProvider(decorProvider, rotation)
|
||||
overlay.removeView(TEST_DECOR_VIEW_ID)
|
||||
val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID)
|
||||
Assert.assertNull(viewFoundFromRootView)
|
||||
Assert.assertNull(overlay.getView(TEST_DECOR_LAYOUT_ID))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.decor
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.DisplayCutout
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() {
|
||||
private lateinit var mPrivacyDotDecorProviderFactory: PrivacyDotDecorProviderFactory
|
||||
|
||||
@Mock private lateinit var resources: Resources
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
resources = spy(mContext.resources)
|
||||
mPrivacyDotDecorProviderFactory = PrivacyDotDecorProviderFactory(resources)
|
||||
}
|
||||
|
||||
private fun setPrivacyDotResources(isEnable: Boolean) {
|
||||
whenever(resources.getBoolean(R.bool.config_enablePrivacyDot)).thenReturn(isEnable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetNoCornerDecorProviderWithNoPrivacyDot() {
|
||||
setPrivacyDotResources(false)
|
||||
|
||||
Assert.assertEquals(false, mPrivacyDotDecorProviderFactory.hasProviders)
|
||||
Assert.assertEquals(0, mPrivacyDotDecorProviderFactory.providers.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGet4CornerDecorProvidersWithPrivacyDot() {
|
||||
setPrivacyDotResources(true)
|
||||
val providers = mPrivacyDotDecorProviderFactory.providers
|
||||
|
||||
Assert.assertEquals(true, mPrivacyDotDecorProviderFactory.hasProviders)
|
||||
Assert.assertEquals(4, providers.size)
|
||||
Assert.assertEquals(1, providers.count {
|
||||
((it.viewId == R.id.privacy_dot_top_left_container)
|
||||
and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
|
||||
and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT))
|
||||
})
|
||||
Assert.assertEquals(1, providers.count {
|
||||
((it.viewId == R.id.privacy_dot_top_right_container)
|
||||
and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
|
||||
and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
|
||||
})
|
||||
Assert.assertEquals(1, providers.count {
|
||||
((it.viewId == R.id.privacy_dot_bottom_left_container)
|
||||
and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM)
|
||||
and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT))
|
||||
})
|
||||
Assert.assertEquals(1, providers.count {
|
||||
((it.viewId == R.id.privacy_dot_bottom_right_container)
|
||||
and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM)
|
||||
and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user