Merge "Create StatusBarBoundsProvider which provided bounds of the left and right side icons" into tm-qpr-dev am: cdae841b07

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19085608

Change-Id: If6efc991d3598406898bf568d9d9cb73120e9d8f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
TreeHugger Robot
2022-07-21 16:45:34 +00:00
committed by Automerger Merge Worker
9 changed files with 413 additions and 7 deletions

View File

@@ -0,0 +1,92 @@
/*
* 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.statusbar.phone
import android.graphics.Rect
import android.view.View
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.END_SIDE_CONTENT
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.START_SIDE_CONTENT
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope
import com.android.systemui.util.boundsOnScreen
import javax.inject.Inject
import javax.inject.Named
/** Provides various bounds within the status bar. */
@StatusBarFragmentScope
class StatusBarBoundsProvider
@Inject
constructor(
private val changeListeners: Set<@JvmSuppressWildcards BoundsChangeListener>,
@Named(START_SIDE_CONTENT) private val startSideContent: View,
@Named(END_SIDE_CONTENT) private val endSideContent: View,
) : StatusBarFragmentComponent.Startable {
interface BoundsChangeListener {
fun onStatusBarBoundsChanged()
}
private var previousBounds =
BoundsPair(start = startSideContent.boundsOnScreen, end = endSideContent.boundsOnScreen)
private val layoutListener =
View.OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
val newBounds = BoundsPair(start = visibleStartSideBounds, end = visibleEndSideBounds)
if (previousBounds != newBounds) {
previousBounds = newBounds
changeListeners.forEach { it.onStatusBarBoundsChanged() }
}
}
override fun start() {
startSideContent.addOnLayoutChangeListener(layoutListener)
endSideContent.addOnLayoutChangeListener(layoutListener)
}
override fun stop() {
startSideContent.removeOnLayoutChangeListener(layoutListener)
endSideContent.removeOnLayoutChangeListener(layoutListener)
}
/**
* Returns the bounds of the end side of the status bar that are visible to the user. The end
* side is right when in LTR and is left when in RTL.
*
* Note that even though the layout might be larger, here we only return the bounds for what is
* visible to the user.
*
* The end side of the status bar contains the multi-user switcher and status icons such as
* wi-fi, battery, etc
*/
val visibleEndSideBounds: Rect
get() = endSideContent.boundsOnScreen
/**
* Returns the bounds of the start side of the status bar that are visible to the user. The
* start side is left when in LTR and is right when in RTL.
*
* Note that even though the layout might be larger, here we only return the bounds for what is
* visible to the user.
*
* The start side of the status bar contains the operator name, clock, on-going call chip, and
* notifications.
*/
val visibleStartSideBounds: Rect
get() = startSideContent.boundsOnScreen
}
private data class BoundsPair(val start: Rect, val end: Rect)

View File

@@ -30,6 +30,7 @@ import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.biometrics.AuthRippleView;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -269,7 +270,8 @@ public abstract class StatusBarViewModule {
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
SecureSettings secureSettings,
@Main Executor mainExecutor
@Main Executor mainExecutor,
DumpManager dumpManager
) {
return new CollapsedStatusBarFragment(statusBarFragmentComponentFactory,
ongoingCallController,
@@ -289,7 +291,8 @@ public abstract class StatusBarViewModule {
collapsedStatusBarFragmentLogger,
operatorNameViewControllerFactory,
secureSettings,
mainExecutor);
mainExecutor,
dumpManager);
}
/**

View File

@@ -34,6 +34,8 @@ import android.os.Parcelable;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
@@ -43,9 +45,11 @@ import android.widget.LinearLayout;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController;
@@ -66,6 +70,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent.Startable;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -76,9 +81,12 @@ import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListen
import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -89,7 +97,7 @@ import java.util.concurrent.Executor;
@SuppressLint("ValidFragment")
public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks,
StatusBarStateController.StateListener,
SystemStatusAnimationCallback {
SystemStatusAnimationCallback, Dumpable {
public static final String TAG = "CollapsedStatusBarFragment";
private static final String EXTRA_PANEL_STATE = "panel_state";
@@ -124,8 +132,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
private final SecureSettings mSecureSettings;
private final Executor mMainExecutor;
private final DumpManager mDumpManager;
private List<String> mBlockedIcons = new ArrayList<>();
private Map<Startable, Startable.State> mStartableStates = new ArrayMap<>();
private SignalCallback mSignalCallback = new SignalCallback() {
@Override
@@ -185,7 +195,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
SecureSettings secureSettings,
@Main Executor mainExecutor
@Main Executor mainExecutor,
DumpManager dumpManager
) {
mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
mOngoingCallController = ongoingCallController;
@@ -206,6 +217,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
mSecureSettings = secureSettings;
mMainExecutor = mainExecutor;
mDumpManager = dumpManager;
}
@Override
@@ -217,8 +229,15 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mDumpManager.registerDumpable(getClass().getSimpleName(), this);
mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(this);
mStatusBarFragmentComponent.init();
mStartableStates.clear();
for (Startable startable : mStatusBarFragmentComponent.getStartables()) {
mStartableStates.put(startable, Startable.State.STARTING);
startable.start();
mStartableStates.put(startable, Startable.State.STARTED);
}
mStatusBar = (PhoneStatusBarView) view;
View contents = mStatusBar.findViewById(R.id.status_bar_contents);
@@ -321,6 +340,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
mCarrierConfigTracker.removeCallback(mCarrierConfigCallback);
mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener);
for (Startable startable : mStatusBarFragmentComponent.getStartables()) {
mStartableStates.put(startable, Startable.State.STOPPING);
startable.stop();
mStartableStates.put(startable, Startable.State.STOPPED);
}
mDumpManager.unregisterDumpable(getClass().getSimpleName());
}
/** Initializes views related to the notification icon area. */
@@ -670,4 +696,23 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
updateStatusBarLocation(left, right);
}
};
@Override
public void dump(PrintWriter printWriter, String[] args) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, /* singleIndent= */" ");
StatusBarFragmentComponent component = mStatusBarFragmentComponent;
if (component == null) {
pw.println("StatusBarFragmentComponent is null");
} else {
Set<Startable> startables = component.getStartables();
pw.println("Startables: " + startables.size());
pw.increaseIndent();
for (Startable startable : startables) {
Startable.State startableState = mStartableStates.getOrDefault(startable,
Startable.State.NONE);
pw.println(startable + ", state: " + startableState);
}
pw.decreaseIndent();
}
}
}

View File

@@ -23,9 +23,12 @@ import com.android.systemui.statusbar.phone.LightsOutNotifController;
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
import com.android.systemui.statusbar.phone.StatusBarDemoMode;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import java.util.Set;
import dagger.BindsInstance;
import dagger.Subcomponent;
@@ -41,7 +44,10 @@ import dagger.Subcomponent;
* should be included here or in {@link StatusBarFragmentModule}.
*/
@Subcomponent(modules = {StatusBarFragmentModule.class})
@Subcomponent(modules = {
StatusBarFragmentModule.class,
StatusBarStartablesModule.class
})
@StatusBarFragmentScope
public interface StatusBarFragmentComponent {
/** Simple factory. */
@@ -51,6 +57,18 @@ public interface StatusBarFragmentComponent {
@BindsInstance CollapsedStatusBarFragment collapsedStatusBarFragment);
}
/**
* Performs initialization logic after {@link StatusBarFragmentComponent} has been constructed.
*/
interface Startable {
void start();
void stop();
enum State {
NONE, STARTING, STARTED, STOPPING, STOPPED
}
}
/**
* Initialize anything extra for the component. Must be called after the component is created.
*/
@@ -92,4 +110,10 @@ public interface StatusBarFragmentComponent {
/** */
@StatusBarFragmentScope
PhoneStatusBarTransitions getPhoneStatusBarTransitions();
/** */
Set<Startable> getStartables();
/** */
StatusBarBoundsProvider getBoundsProvider();
}

View File

@@ -26,6 +26,7 @@ import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
@@ -34,12 +35,14 @@ import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import java.util.Optional;
import java.util.Set;
import javax.inject.Named;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.Multibinds;
/** Dagger module for {@link StatusBarFragmentComponent}. */
@Module
@@ -48,6 +51,8 @@ public interface StatusBarFragmentModule {
String LIGHTS_OUT_NOTIF_VIEW = "lights_out_notif_view";
String OPERATOR_NAME_VIEW = "operator_name_view";
String OPERATOR_NAME_FRAME_VIEW = "operator_name_frame_view";
String START_SIDE_CONTENT = "start_side_content";
String END_SIDE_CONTENT = "end_side_content";
/** */
@Provides
@@ -65,6 +70,22 @@ public interface StatusBarFragmentModule {
return view.findViewById(R.id.battery);
}
/** */
@Provides
@StatusBarFragmentScope
@Named(START_SIDE_CONTENT)
static View startSideContent(@RootView PhoneStatusBarView view) {
return view.findViewById(R.id.status_bar_start_side_content);
}
/** */
@Provides
@StatusBarFragmentScope
@Named(END_SIDE_CONTENT)
static View endSideContent(@RootView PhoneStatusBarView view) {
return view.findViewById(R.id.status_bar_end_side_content);
}
/** */
@Provides
@StatusBarFragmentScope
@@ -138,4 +159,8 @@ public interface StatusBarFragmentModule {
static HeadsUpStatusBarView providesHeasdUpStatusBarView(@RootView PhoneStatusBarView view) {
return view.findViewById(R.id.heads_up_status_bar_view);
}
/** */
@Multibinds
Set<StatusBarBoundsProvider.BoundsChangeListener> boundsChangeListeners();
}

View File

@@ -0,0 +1,32 @@
/*
* 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.statusbar.phone.fragment.dagger
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
@Module
internal interface StatusBarStartablesModule {
@Binds
@IntoSet
fun statusBarBoundsCalculator(
statusBarBoundsProvider: StatusBarBoundsProvider
): StatusBarFragmentComponent.Startable
}

View File

@@ -16,7 +16,9 @@
package com.android.systemui.util
import android.graphics.Rect
import android.util.IndentingPrintWriter
import android.view.View
import android.view.ViewGroup
import java.io.PrintWriter
@@ -45,4 +47,12 @@ inline fun PrintWriter.indentIfPossible(block: PrintWriter.() -> Unit) {
if (this is IndentingPrintWriter) increaseIndent()
block()
if (this is IndentingPrintWriter) decreaseIndent()
}
}
/** Convenience extension property for [View.getBoundsOnScreen]. */
val View.boundsOnScreen: Rect
get() {
val bounds = Rect()
getBoundsOnScreen(bounds)
return bounds
}

View File

@@ -0,0 +1,171 @@
/*
* 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.statusbar.phone
import android.graphics.Rect
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.View
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider.BoundsChangeListener
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
class StatusBarBoundsProviderTest : SysuiTestCase() {
companion object {
private val START_SIDE_BOUNDS = Rect(50, 100, 150, 200)
private val END_SIDE_BOUNDS = Rect(250, 300, 350, 400)
}
@Mock private lateinit var boundsChangeListener: BoundsChangeListener
private lateinit var boundsProvider: StatusBarBoundsProvider
private lateinit var startSideContent: View
private lateinit var endSideContent: View
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
startSideContent = spy(FrameLayout(context)).apply { setBoundsOnScreen(START_SIDE_BOUNDS) }
endSideContent = spy(FrameLayout(context)).apply { setBoundsOnScreen(END_SIDE_BOUNDS) }
boundsProvider =
StatusBarBoundsProvider(setOf(boundsChangeListener), startSideContent, endSideContent)
}
@Test
fun visibleStartSideBounds_returnsBoundsFromStartSideContentView() {
assertThat(boundsProvider.visibleStartSideBounds).isEqualTo(START_SIDE_BOUNDS)
}
@Test
fun visibleEndSideBounds_returnsBoundsFromEndSideContentView() {
assertThat(boundsProvider.visibleEndSideBounds).isEqualTo(END_SIDE_BOUNDS)
}
@Test
fun startBoundsChange_afterStart_notifiesListener() {
boundsProvider.start()
val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 }
startSideContent.setBoundsOnScreen(newBounds)
verify(boundsChangeListener).onStatusBarBoundsChanged()
}
@Test
fun startBoundsChange_beforeStart_doesNotNotifyListener() {
val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 }
startSideContent.setBoundsOnScreen(newBounds)
verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
}
@Test
fun startBoundsChange_afterStop_doesNotNotifyListener() {
boundsProvider.start()
boundsProvider.stop()
val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 }
startSideContent.setBoundsOnScreen(newBounds)
verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
}
@Test
fun startLayoutChange_afterStart_boundsOnScreenSame_doesNotNotifyListener() {
boundsProvider.start()
val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 }
startSideContent.layout(newBounds)
verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
}
@Test
fun endBoundsChange_afterStart_notifiesListener() {
boundsProvider.start()
val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 }
endSideContent.setBoundsOnScreen(newBounds)
verify(boundsChangeListener).onStatusBarBoundsChanged()
}
@Test
fun endBoundsChange_beforeStart_doesNotNotifyListener() {
val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 }
endSideContent.setBoundsOnScreen(newBounds)
verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
}
@Test
fun endBoundsChange_afterStop_doesNotNotifyListener() {
boundsProvider.start()
boundsProvider.stop()
val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 }
endSideContent.setBoundsOnScreen(newBounds)
verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
}
@Test
fun endLayoutChange_afterStart_boundsOnScreenSame_doesNotNotifyListener() {
boundsProvider.start()
val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 }
endSideContent.layout(newBounds)
verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
}
}
private fun View.setBoundsOnScreen(bounds: Rect) {
doAnswer { invocation ->
val boundsOutput = invocation.arguments[0] as Rect
boundsOutput.set(bounds)
return@doAnswer Unit
}
.`when`(this)
.getBoundsOnScreen(any())
layout(bounds.left, bounds.top, bounds.right, bounds.bottom)
}
private fun View.layout(rect: Rect) {
layout(rect.left, rect.top, rect.right, rect.bottom)
}

View File

@@ -41,6 +41,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogcatEchoTracker;
@@ -107,6 +108,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
private NotificationPanelViewController mNotificationPanelViewController;
@Mock
private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
@Mock
private DumpManager mDumpManager;
public CollapsedStatusBarFragmentTest() {
super(CollapsedStatusBarFragment.class);
@@ -386,7 +389,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
),
mOperatorNameViewControllerFactory,
mSecureSettings,
mExecutor);
mExecutor,
mDumpManager);
}
private void setUpDaggerComponent() {