diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt new file mode 100644 index 0000000000000..c962e5155697e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 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.display.data.repository + +import android.content.Context +import android.content.res.Configuration +import android.util.DisplayMetrics +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.dagger.DisplayMetricsRepoLog +import com.android.systemui.statusbar.policy.ConfigurationController +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.stateIn + +/** Repository tracking display-related metrics like display height and width. */ +@SysUISingleton +class DisplayMetricsRepository +@Inject +constructor( + @Application scope: CoroutineScope, + configurationController: ConfigurationController, + displayMetricsHolder: DisplayMetrics, + context: Context, + @DisplayMetricsRepoLog logBuffer: LogBuffer, +) { + + private val displayMetrics: StateFlow = + conflatedCallbackFlow { + val callback = + object : ConfigurationController.ConfigurationListener { + override fun onConfigChanged(newConfig: Configuration?) { + context.display.getMetrics(displayMetricsHolder) + trySend(displayMetricsHolder) + } + } + configurationController.addCallback(callback) + awaitClose { configurationController.removeCallback(callback) } + } + .onEach { + logBuffer.log( + "DisplayMetrics", + LogLevel.INFO, + { str1 = it.toString() }, + { "New metrics: $str1" }, + ) + } + .stateIn(scope, SharingStarted.Eagerly, displayMetricsHolder) + + /** Returns the current display height in pixels. */ + val heightPixels: Int + get() = displayMetrics.value.heightPixels +} diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/DisplayMetricsRepoLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/DisplayMetricsRepoLog.kt new file mode 100644 index 0000000000000..fa9ec880244ba --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/DisplayMetricsRepoLog.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 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.log.dagger + +import javax.inject.Qualifier + +/** A [com.android.systemui.log.LogBuffer] for display metrics related logging. */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class DisplayMetricsRepoLog diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 3497285d69292..b5759e3a31405 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -488,4 +488,12 @@ public class LogModule { public static LogBuffer provideDreamLogBuffer(LogBufferFactory factory) { return factory.create("DreamLog", 250); } + + /** Provides a {@link LogBuffer} for display metrics related logs. */ + @Provides + @SysUISingleton + @DisplayMetricsRepoLog + public static LogBuffer provideDisplayMetricsRepoLogBuffer(LogBufferFactory factory) { + return factory.create("DisplayMetricsRepo", 50); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java index 59afb18195dd6..9702bfc148730 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java @@ -18,19 +18,19 @@ package com.android.systemui.statusbar; import android.view.View; +import com.android.systemui.display.data.repository.DisplayMetricsRepository; import com.android.systemui.plugins.qs.QS; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.phone.CentralSurfaces; /** * Calculates and moves the QS frame vertically. */ public abstract class QsFrameTranslateController { - protected CentralSurfaces mCentralSurfaces; + protected DisplayMetricsRepository mDisplayMetricsRepository; - public QsFrameTranslateController(CentralSurfaces centralSurfaces) { - mCentralSurfaces = centralSurfaces; + public QsFrameTranslateController(DisplayMetricsRepository displayMetricsRepository) { + mDisplayMetricsRepository = displayMetricsRepository; } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java index 85b522cbd9d54..e429b8b4815a5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java @@ -19,9 +19,9 @@ package com.android.systemui.statusbar; import android.view.View; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.display.data.repository.DisplayMetricsRepository; import com.android.systemui.plugins.qs.QS; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.phone.CentralSurfaces; import javax.inject.Inject; @@ -34,8 +34,8 @@ import javax.inject.Inject; public class QsFrameTranslateImpl extends QsFrameTranslateController { @Inject - public QsFrameTranslateImpl(CentralSurfaces centralSurfaces) { - super(centralSurfaces); + public QsFrameTranslateImpl(DisplayMetricsRepository displayMetricsRepository) { + super(displayMetricsRepository); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 0242e913e573e..4ba09e175b8b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -40,6 +40,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; import com.android.systemui.Dumpable; import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.display.data.repository.DisplayMetricsRepository; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.qs.QSPanelController; @@ -250,8 +251,12 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { @Override void dump(PrintWriter pwOriginal, String[] args); + /** @deprecated Use {@link DisplayMetricsRepository} instead. */ + @Deprecated float getDisplayWidth(); + /** @deprecated Use {@link DisplayMetricsRepository} instead. */ + @Deprecated float getDisplayHeight(); void readyForKeyguardDone(); @@ -394,6 +399,9 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { void setLaunchEmergencyActionOnFinishedWaking(boolean launch); QSPanelController getQSPanelController(); + + /** @deprecated Use {@link DisplayMetricsRepository} instead. */ + @Deprecated float getDisplayDensity(); void extendDozePulse(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 4ae4c52d19baf..88ccae624dd0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -2114,16 +2114,19 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } @Override + @Deprecated public float getDisplayDensity() { return mDisplayMetrics.density; } @Override + @Deprecated public float getDisplayWidth() { return mDisplayMetrics.widthPixels; } @Override + @Deprecated public float getDisplayHeight() { return mDisplayMetrics.heightPixels; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayMetricsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayMetricsRepositoryTest.kt new file mode 100644 index 0000000000000..dd741b4167e9a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayMetricsRepositoryTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2023 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.display.data.repository + +import android.content.Context +import android.util.DisplayMetrics +import android.view.Display +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.log.LogBuffer +import com.android.systemui.statusbar.policy.FakeConfigurationController +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class DisplayMetricsRepositoryTest : SysuiTestCase() { + private lateinit var underTest: DisplayMetricsRepository + + private val testScope = TestScope(StandardTestDispatcher()) + private val configurationController = FakeConfigurationController() + + private val displayMetrics = + DisplayMetrics().apply { this.heightPixels = INITIAL_HEIGHT_PIXELS } + private val mockContext: Context = mock() + private val mockDisplay: Display = mock() + + @Before + fun setUp() { + underTest = + DisplayMetricsRepository( + testScope.backgroundScope, + configurationController, + displayMetrics, + mockContext, + LogBuffer("TestBuffer", maxSize = 10, logcatEchoTracker = mock()) + ) + whenever(mockContext.display).thenReturn(mockDisplay) + } + + @Test + fun heightPixels_getsInitialValue() { + assertThat(underTest.heightPixels).isEqualTo(INITIAL_HEIGHT_PIXELS) + } + + @Test + fun heightPixels_configChanged_heightUpdated() = + testScope.runTest { + runCurrent() + + updateDisplayMetrics(456) + configurationController.notifyConfigurationChanged() + runCurrent() + + assertThat(underTest.heightPixels).isEqualTo(456) + + updateDisplayMetrics(23) + configurationController.notifyConfigurationChanged() + runCurrent() + + assertThat(underTest.heightPixels).isEqualTo(23) + } + + private fun updateDisplayMetrics(newHeight: Int) { + whenever(mockDisplay.getMetrics(displayMetrics)).thenAnswer { + it.getArgument(0).heightPixels = newHeight + Unit + } + } + + private companion object { + const val INITIAL_HEIGHT_PIXELS = 345 + } +}