Merge "Privacy chip (3/many)" into sc-dev
This commit is contained in:
@@ -91,6 +91,7 @@ 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.Tunable;
|
||||
import com.android.systemui.util.concurrency.ThreadFactory;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -128,6 +129,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
||||
private CameraAvailabilityListener mCameraListener;
|
||||
private final UserTracker mUserTracker;
|
||||
private final PrivacyDotViewController mDotViewController;
|
||||
private final ThreadFactory mThreadFactory;
|
||||
|
||||
//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
|
||||
@@ -215,7 +217,8 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
||||
BroadcastDispatcher broadcastDispatcher,
|
||||
TunerService tunerService,
|
||||
UserTracker userTracker,
|
||||
PrivacyDotViewController dotViewController) {
|
||||
PrivacyDotViewController dotViewController,
|
||||
ThreadFactory threadFactory) {
|
||||
super(context);
|
||||
mMainHandler = handler;
|
||||
mSecureSettings = secureSettings;
|
||||
@@ -223,6 +226,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
||||
mTunerService = tunerService;
|
||||
mUserTracker = userTracker;
|
||||
mDotViewController = dotViewController;
|
||||
mThreadFactory = threadFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -233,7 +237,8 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
||||
}
|
||||
mHandler = startHandlerThread();
|
||||
mHandler.post(this::startOnScreenDecorationsThread);
|
||||
mDotViewController.setUiExecutor(mHandler::post);
|
||||
mDotViewController.setUiExecutor(
|
||||
mThreadFactory.buildDelayableExecutorOnLooper(mHandler.getLooper()));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -643,7 +648,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
|
||||
|
||||
int newRotation = mContext.getDisplay().getRotation();
|
||||
if (mRotation != newRotation) {
|
||||
mDotViewController.updateRotation(newRotation);
|
||||
mDotViewController.setNewRotation(newRotation);
|
||||
}
|
||||
|
||||
if (mPendingRotationChange) {
|
||||
|
||||
@@ -17,20 +17,25 @@
|
||||
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.internal.annotations.GuardedBy
|
||||
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 com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher
|
||||
import com.android.systemui.statusbar.phone.StatusBarMarginUpdatedListener
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
|
||||
import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
|
||||
import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
|
||||
import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
|
||||
|
||||
import java.lang.IllegalStateException
|
||||
import java.util.concurrent.Executor
|
||||
@@ -54,31 +59,33 @@ import javax.inject.Inject
|
||||
@SysUISingleton
|
||||
class PrivacyDotViewController @Inject constructor(
|
||||
@Main private val mainExecutor: Executor,
|
||||
private val stateController: StatusBarStateController,
|
||||
private val locationPublisher: StatusBarLocationPublisher,
|
||||
private 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
|
||||
// Only can be modified on @UiThread
|
||||
private var currentViewState: ViewState = ViewState()
|
||||
|
||||
@GuardedBy("lock")
|
||||
private var nextViewState: ViewState = currentViewState.copy()
|
||||
set(value) {
|
||||
field = value
|
||||
scheduleUpdate()
|
||||
}
|
||||
private val lock = Object()
|
||||
private var cancelRunnable: Runnable? = null
|
||||
|
||||
// Privacy dots are created in ScreenDecoration's UiThread, which is not the main thread
|
||||
private var uiExecutor: Executor? = null
|
||||
private var uiExecutor: DelayableExecutor? = null
|
||||
private var e: DelayableExecutor? = null
|
||||
|
||||
private val views: Sequence<View>
|
||||
get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
|
||||
@@ -89,29 +96,78 @@ class PrivacyDotViewController @Inject constructor(
|
||||
setStatusBarMargins(marginLeft, marginRight)
|
||||
}
|
||||
})
|
||||
|
||||
stateController.addCallback(object : StatusBarStateController.StateListener {
|
||||
override fun onExpandedChanged(isExpanded: Boolean) {
|
||||
setStatusBarExpanded(isExpanded)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun setUiExecutor(e: Executor) {
|
||||
fun setUiExecutor(e: DelayableExecutor) {
|
||||
uiExecutor = e
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun updateRotation(rot: Int) {
|
||||
dlog("updateRotation: ")
|
||||
if (rot == rotation) {
|
||||
return
|
||||
fun setNewRotation(rot: Int) {
|
||||
dlog("updateRotation: $rot")
|
||||
|
||||
synchronized(lock) {
|
||||
if (rot == nextViewState.rotation) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// A rotation has started, hide the views to avoid flicker
|
||||
// If we rotated, hide all dotes until the next state resolves
|
||||
setCornerVisibilities(View.INVISIBLE)
|
||||
|
||||
if (hasMultipleHeights && (rotation % 2) != (rot % 2)) {
|
||||
// we've changed from vertical to horizontal; update status bar height
|
||||
needsHeightUpdate = true
|
||||
}
|
||||
val newCorner = selectDesignatedCorner(rot)
|
||||
val index = newCorner.cornerIndex()
|
||||
|
||||
rotation = rot
|
||||
needsRotationUpdate = true
|
||||
val h = when (rot) {
|
||||
ROTATION_NONE, ROTATION_UPSIDE_DOWN -> sbHeightPortrait
|
||||
ROTATION_LANDSCAPE, ROTATION_SEASCAPE -> sbHeightLandscape
|
||||
else -> 0
|
||||
}
|
||||
synchronized(lock) {
|
||||
nextViewState = nextViewState.copy(
|
||||
rotation = rot,
|
||||
height = h,
|
||||
designatedCorner = newCorner,
|
||||
cornerIndex = index)
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private fun hideDotView(dot: View, animate: Boolean) {
|
||||
dot.clearAnimation()
|
||||
if (animate) {
|
||||
dot.animate()
|
||||
.setDuration(DURATION)
|
||||
.setInterpolator(Interpolators.ALPHA_OUT)
|
||||
.alpha(0f)
|
||||
.withEndAction { dot.visibility = View.INVISIBLE }
|
||||
.start()
|
||||
} else {
|
||||
dot.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private fun showDotView(dot: View, animate: Boolean) {
|
||||
dot.clearAnimation()
|
||||
if (animate) {
|
||||
dot.visibility = View.VISIBLE
|
||||
dot.alpha = 0f
|
||||
dot.animate()
|
||||
.alpha(1f)
|
||||
.setDuration(DURATION)
|
||||
.setInterpolator(Interpolators.ALPHA_IN)
|
||||
.start()
|
||||
} else {
|
||||
dot.visibility = View.VISIBLE
|
||||
dot.alpha = 1f
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@@ -127,14 +183,14 @@ class PrivacyDotViewController @Inject constructor(
|
||||
|
||||
// Update the gravity and margins of the privacy views
|
||||
@UiThread
|
||||
private fun updateRotations() {
|
||||
private fun updateRotations(rotation: Int) {
|
||||
// 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))
|
||||
val rotatedCorner = rotatedCorner(cornerForView(corner), rotation)
|
||||
(corner.layoutParams as FrameLayout.LayoutParams).apply {
|
||||
gravity = rotatedCorner.toGravity()
|
||||
}
|
||||
@@ -147,26 +203,24 @@ class PrivacyDotViewController @Inject constructor(
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private fun updateCornerSizes() {
|
||||
private fun updateCornerSizes(l: Int, r: Int, rotation: Int) {
|
||||
views.forEach { corner ->
|
||||
val rotatedCorner = rotatedCorner(cornerForView(corner))
|
||||
val w = widthForCorner(rotatedCorner)
|
||||
Log.d(TAG, "updateCornerSizes: setting (${cornerForView(corner)}) to $w")
|
||||
val rotatedCorner = rotatedCorner(cornerForView(corner), rotation)
|
||||
val w = widthForCorner(rotatedCorner, l, r)
|
||||
(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? {
|
||||
private fun selectDesignatedCorner(r: Int): View? {
|
||||
if (!this::tl.isInitialized) {
|
||||
return null
|
||||
}
|
||||
|
||||
val isRtl = tl.isLayoutRtl
|
||||
|
||||
return when (rotation) {
|
||||
return when (r) {
|
||||
0 -> if (isRtl) tl else tr
|
||||
1 -> if (isRtl) tr else br
|
||||
2 -> if (isRtl) br else bl
|
||||
@@ -177,23 +231,17 @@ class PrivacyDotViewController @Inject constructor(
|
||||
|
||||
// Track the current designated corner and maybe animate to a new rotation
|
||||
@UiThread
|
||||
private fun updateDesignatedCorner(newCorner: View) {
|
||||
designatedCorner = newCorner
|
||||
|
||||
if (animationScheduler.hasPersistentDot) {
|
||||
fadeInDot()
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private fun fadeInDot() {
|
||||
designatedCorner?.let { dot ->
|
||||
dot.visibility = View.VISIBLE
|
||||
dot.alpha = 0f
|
||||
dot.animate()
|
||||
.alpha(1.0f)
|
||||
.setDuration(300)
|
||||
.start()
|
||||
private fun updateDesignatedCorner(newCorner: View?, shouldShowDot: Boolean) {
|
||||
if (shouldShowDot) {
|
||||
newCorner?.apply {
|
||||
clearAnimation()
|
||||
visibility = View.VISIBLE
|
||||
alpha = 0f
|
||||
animate()
|
||||
.alpha(1.0f)
|
||||
.setDuration(300)
|
||||
.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +262,7 @@ class PrivacyDotViewController @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun rotatedCorner(corner: Int): Int {
|
||||
private fun rotatedCorner(corner: Int, rotation: Int): Int {
|
||||
var modded = corner - rotation
|
||||
if (modded < 0) {
|
||||
modded += 4
|
||||
@@ -223,10 +271,10 @@ class PrivacyDotViewController @Inject constructor(
|
||||
return modded
|
||||
}
|
||||
|
||||
private fun widthForCorner(corner: Int): Int {
|
||||
private fun widthForCorner(corner: Int, left: Int, right: Int): Int {
|
||||
return when (corner) {
|
||||
TOP_LEFT, BOTTOM_LEFT -> leftSize
|
||||
TOP_RIGHT, BOTTOM_RIGHT -> rightSize
|
||||
TOP_LEFT, BOTTOM_LEFT -> left
|
||||
TOP_RIGHT, BOTTOM_RIGHT -> right
|
||||
else -> throw IllegalArgumentException("Unknown corner")
|
||||
}
|
||||
}
|
||||
@@ -244,10 +292,16 @@ class PrivacyDotViewController @Inject constructor(
|
||||
bl = bottomLeft
|
||||
br = bottomRight
|
||||
|
||||
designatedCorner = selectDesignatedCorner()
|
||||
val dc = selectDesignatedCorner(0)
|
||||
val index = dc.cornerIndex()
|
||||
|
||||
mainExecutor.execute {
|
||||
animationScheduler.addCallback(systemStatusAnimationCallback)
|
||||
}
|
||||
|
||||
synchronized(lock) {
|
||||
nextViewState = nextViewState.copy(designatedCorner = dc, cornerIndex = index)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,8 +311,6 @@ class PrivacyDotViewController @Inject constructor(
|
||||
fun setStatusBarHeights(portrait: Int, landscape: Int) {
|
||||
sbHeightPortrait = portrait
|
||||
sbHeightLandscape = landscape
|
||||
|
||||
hasMultipleHeights = portrait != landscape
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,85 +320,109 @@ class PrivacyDotViewController @Inject constructor(
|
||||
* @param right space between the status bar contents and the right side of the screen
|
||||
*/
|
||||
private 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)
|
||||
dlog("setStatusBarMargins l=$left r=$right")
|
||||
synchronized(lock) {
|
||||
nextViewState = nextViewState.copy(marginLeft = left, marginRight = right)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doUpdates(rot: Boolean, height: Boolean, width: Boolean) {
|
||||
dlog("doUpdates: ")
|
||||
var newDesignatedCorner: View? = null
|
||||
/**
|
||||
* We won't show the dot when quick settings is showing
|
||||
*/
|
||||
private fun setStatusBarExpanded(expanded: Boolean) {
|
||||
synchronized(lock) {
|
||||
nextViewState = nextViewState.copy(hideDotForQuickSettings = expanded)
|
||||
}
|
||||
}
|
||||
|
||||
if (rot) {
|
||||
needsRotationUpdate = false
|
||||
updateRotations()
|
||||
newDesignatedCorner = selectDesignatedCorner()
|
||||
private fun scheduleUpdate() {
|
||||
dlog("scheduleUpdate: ")
|
||||
|
||||
cancelRunnable?.run()
|
||||
cancelRunnable = uiExecutor?.executeDelayed({
|
||||
processNextViewState()
|
||||
}, 100)
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private fun processNextViewState() {
|
||||
dlog("processNextViewState: ")
|
||||
|
||||
val newState: ViewState
|
||||
synchronized(lock) {
|
||||
newState = nextViewState.copy()
|
||||
}
|
||||
|
||||
if (height) {
|
||||
needsHeightUpdate = false
|
||||
updateHeights(rotation)
|
||||
resolveState(newState)
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private fun resolveState(state: ViewState) {
|
||||
dlog("resolveState $state")
|
||||
if (state == currentViewState) {
|
||||
dlog("resolveState: skipping")
|
||||
return
|
||||
}
|
||||
|
||||
if (width) {
|
||||
needsMarginUpdate = false
|
||||
updateCornerSizes()
|
||||
if (state.rotation != currentViewState.rotation) {
|
||||
// A rotation has started, hide the views to avoid flicker
|
||||
updateRotations(state.rotation)
|
||||
}
|
||||
|
||||
if (newDesignatedCorner != null && newDesignatedCorner != designatedCorner) {
|
||||
updateDesignatedCorner(newDesignatedCorner)
|
||||
if (state.height != currentViewState.height) {
|
||||
updateHeights(state.rotation)
|
||||
}
|
||||
|
||||
if (state.marginLeft != currentViewState.marginLeft ||
|
||||
state.marginRight != currentViewState.marginRight) {
|
||||
updateCornerSizes(state.marginLeft, state.marginRight, state.rotation)
|
||||
}
|
||||
|
||||
if (state.designatedCorner != currentViewState.designatedCorner) {
|
||||
updateDesignatedCorner(state.designatedCorner, state.shouldShowDot())
|
||||
}
|
||||
|
||||
if (state.needsLayout(currentViewState)) {
|
||||
views.forEach { it.requestLayout() }
|
||||
}
|
||||
|
||||
val shouldShow = state.shouldShowDot()
|
||||
if (shouldShow != currentViewState.shouldShowDot()) {
|
||||
if (shouldShow && state.designatedCorner != null) {
|
||||
showDotView(state.designatedCorner, true)
|
||||
} else if (!shouldShow && state.designatedCorner != null) {
|
||||
hideDotView(state.designatedCorner, true)
|
||||
}
|
||||
}
|
||||
|
||||
currentViewState = state
|
||||
}
|
||||
|
||||
private val systemStatusAnimationCallback: SystemStatusAnimationCallback =
|
||||
object : SystemStatusAnimationCallback {
|
||||
override fun onSystemStatusAnimationTransitionToPersistentDot(
|
||||
showAnimation: Boolean
|
||||
): Animator? {
|
||||
if (designatedCorner == null) {
|
||||
return null
|
||||
} else if (!showAnimation) {
|
||||
uiExecutor?.execute { fadeInDot() }
|
||||
return null
|
||||
override fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? {
|
||||
synchronized(lock) {
|
||||
nextViewState = nextViewState.copy(systemPrivacyEventIsActive = true)
|
||||
}
|
||||
|
||||
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
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onHidePersistentDot(): Animator? {
|
||||
if (designatedCorner == null) {
|
||||
return null
|
||||
synchronized(lock) {
|
||||
nextViewState = nextViewState.copy(systemPrivacyEventIsActive = false)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
private fun View?.cornerIndex(): Int {
|
||||
if (this != null) {
|
||||
return cornerForView(this)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
private fun dlog(s: String) {
|
||||
@@ -382,3 +458,26 @@ private fun Int.innerGravity(): Int {
|
||||
else -> throw IllegalArgumentException("Not a corner")
|
||||
}
|
||||
}
|
||||
|
||||
private data class ViewState(
|
||||
// don't @ me with names
|
||||
val systemPrivacyEventIsActive: Boolean = false,
|
||||
val hideDotForQuickSettings: Boolean = false,
|
||||
val statusBarExpanded: Boolean = false,
|
||||
val rotation: Int = 0,
|
||||
val height: Int = 0,
|
||||
val marginLeft: Int = 0,
|
||||
val marginRight: Int = 0,
|
||||
val cornerIndex: Int = -1,
|
||||
val designatedCorner: View? = null
|
||||
) {
|
||||
fun shouldShowDot(): Boolean {
|
||||
return systemPrivacyEventIsActive && !hideDotForQuickSettings
|
||||
}
|
||||
|
||||
fun needsLayout(other: ViewState): Boolean {
|
||||
return rotation != other.rotation ||
|
||||
marginRight != other.marginRight ||
|
||||
height != other.height
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
scheduleEvent(event)
|
||||
} else if (event.forceVisible) {
|
||||
hasPersistentDot = true
|
||||
notifyTransitionToPersistentDot(showAnimation = false)
|
||||
notifyTransitionToPersistentDot()
|
||||
}
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
@@ -202,7 +202,7 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
|
||||
aSet2.play(chipAnimator).before(systemAnimator)
|
||||
if (hasPersistentDot) {
|
||||
val dotAnim = notifyTransitionToPersistentDot(showAnimation = true)
|
||||
val dotAnim = notifyTransitionToPersistentDot()
|
||||
if (dotAnim != null) aSet2.playTogether(systemAnimator, dotAnim)
|
||||
}
|
||||
|
||||
@@ -214,9 +214,9 @@ class SystemStatusAnimationScheduler @Inject constructor(
|
||||
}, DELAY)
|
||||
}
|
||||
|
||||
private fun notifyTransitionToPersistentDot(showAnimation: Boolean): Animator? {
|
||||
private fun notifyTransitionToPersistentDot(): Animator? {
|
||||
val anims: List<Animator> = listeners.mapNotNull {
|
||||
it.onSystemStatusAnimationTransitionToPersistentDot(showAnimation)
|
||||
it.onSystemStatusAnimationTransitionToPersistentDot()
|
||||
}
|
||||
if (anims.isNotEmpty()) {
|
||||
val aSet = AnimatorSet()
|
||||
@@ -326,9 +326,7 @@ interface SystemStatusAnimationCallback {
|
||||
@JvmDefault fun onSystemChromeAnimationEnd() {}
|
||||
|
||||
// Best method name, change my mind
|
||||
@JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot(
|
||||
showAnimation: Boolean
|
||||
): Animator? { return null }
|
||||
@JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? { return null }
|
||||
@JvmDefault fun onHidePersistentDot(): Animator? { return null }
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,15 @@
|
||||
|
||||
package com.android.systemui.util.concurrency;
|
||||
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Factory for building Executors running on a unique named thread.
|
||||
*
|
||||
* Use this when our generally available @Main, @Background, @UiBackground, @LongRunning, or
|
||||
* similar global qualifiers don't quite cut it. Note that the methods here create entirely new
|
||||
* similar global qualifiers don't quite cut it. Note that the methods here can create entirely new
|
||||
* threads; there are no singletons here. Use responsibly.
|
||||
*/
|
||||
public interface ThreadFactory {
|
||||
@@ -41,4 +43,9 @@ public interface ThreadFactory {
|
||||
* implementation. Assume this is the case and use responsibly.
|
||||
**/
|
||||
DelayableExecutor buildDelayableExecutorOnNewThread(String threadName);
|
||||
|
||||
/**
|
||||
* Return an {@link DelayableExecutor} running the given Looper
|
||||
**/
|
||||
DelayableExecutor buildDelayableExecutorOnLooper(Looper looper);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.util.concurrency;
|
||||
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -35,4 +36,8 @@ class ThreadFactoryImpl implements ThreadFactory {
|
||||
handlerThread.start();
|
||||
return new ExecutorImpl(handlerThread.getLooper());
|
||||
}
|
||||
|
||||
public DelayableExecutor buildDelayableExecutorOnLooper(Looper looper) {
|
||||
return new ExecutorImpl(looper);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,8 +65,12 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.statusbar.events.PrivacyDotViewController;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
import com.android.systemui.util.concurrency.FakeThreadFactory;
|
||||
import com.android.systemui.util.concurrency.ThreadFactory;
|
||||
import com.android.systemui.util.settings.FakeSettings;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -89,6 +93,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
private DisplayManager mDisplayManager;
|
||||
private SecureSettings mSecureSettings;
|
||||
private Handler mMainHandler;
|
||||
private ThreadFactory mThreadFactory;
|
||||
@Mock
|
||||
private TunerService mTunerService;
|
||||
@Mock
|
||||
@@ -105,6 +110,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
mTestableLooper = TestableLooper.get(this);
|
||||
mMainHandler = new Handler(mTestableLooper.getLooper());
|
||||
mSecureSettings = new FakeSettings();
|
||||
mThreadFactory = new FakeThreadFactory(new FakeExecutor(new FakeSystemClock()));
|
||||
|
||||
mWindowManager = mock(WindowManager.class);
|
||||
WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
|
||||
@@ -119,7 +125,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
|
||||
mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
|
||||
|
||||
mScreenDecorations = spy(new ScreenDecorations(mContext, mMainHandler, mSecureSettings,
|
||||
mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController) {
|
||||
mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController,
|
||||
mThreadFactory) {
|
||||
@Override
|
||||
public void start() {
|
||||
super.start();
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.util.concurrency;
|
||||
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
@@ -37,4 +39,9 @@ public class FakeThreadFactory implements ThreadFactory {
|
||||
public DelayableExecutor buildDelayableExecutorOnNewThread(String threadName) {
|
||||
return mFakeExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DelayableExecutor buildDelayableExecutorOnLooper(Looper looper) {
|
||||
return mFakeExecutor;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user