Skip to content

Commit

Permalink
Merge "Supports sending window layout info for activities that handle…
Browse files Browse the repository at this point in the history
… config changes" into androidx-main
  • Loading branch information
kford55 authored and Gerrit Code Review committed Jul 28, 2021
2 parents 4998a68 + 76ede5a commit 9c8233d
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
package androidx.window.layout

import android.content.Context
import android.content.pm.ActivityInfo
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.window.TestConfigChangeHandlingActivity
import androidx.window.WindowTestBase
import androidx.window.core.Version
import androidx.window.layout.ExtensionInterfaceCompat.ExtensionCallbackInterface
Expand All @@ -34,6 +37,9 @@ import com.nhaarman.mockitokotlin2.argThat
import com.nhaarman.mockitokotlin2.atLeastOnce
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Assert.assertNotNull
import org.junit.Assume.assumeTrue
import org.junit.Before
Expand All @@ -47,6 +53,7 @@ import org.mockito.ArgumentMatcher
*/
@LargeTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
public class SidecarCompatDeviceTest : WindowTestBase(), CompatDeviceTestInterface {

private lateinit var sidecarCompat: SidecarCompat
Expand All @@ -73,6 +80,49 @@ public class SidecarCompatDeviceTest : WindowTestBase(), CompatDeviceTestInterfa
}
}

@Test
fun testWindowLayoutCallbackOnConfigChange() {
val testScope = TestCoroutineScope()
testScope.runBlockingTest {
val scenario = ActivityScenario.launch(TestConfigChangeHandlingActivity::class.java)
val callbackInterface = mock<ExtensionCallbackInterface>()
scenario.onActivity { activity ->
val windowToken = getActivityWindowToken(activity)
assertNotNull(windowToken)
sidecarCompat.setExtensionCallback(callbackInterface)
sidecarCompat.onWindowLayoutChangeListenerAdded(activity)
activity.resetLayoutCounter()
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
activity.waitForLayout()
}
scenario.onActivity { activity ->
val windowToken = getActivityWindowToken(activity)
assertNotNull(windowToken)
val sidecarWindowLayoutInfo =
sidecarCompat.sidecar!!.getWindowLayoutInfo(windowToken)
verify(callbackInterface, atLeastOnce()).onWindowLayoutChanged(
any(),
argThat(SidecarMatcher(sidecarWindowLayoutInfo))
)
}
scenario.onActivity { activity ->
activity.resetLayoutCounter()
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
activity.waitForLayout()
}
scenario.onActivity { activity ->
val windowToken = getActivityWindowToken(activity)
assertNotNull(windowToken)
val updatedSidecarWindowLayoutInfo =
sidecarCompat.sidecar!!.getWindowLayoutInfo(windowToken)
verify(callbackInterface, atLeastOnce()).onWindowLayoutChanged(
any(),
argThat(SidecarMatcher(updatedSidecarWindowLayoutInfo))
)
}
}
}

private fun assumeExtensionV01() {
val sidecarVersion = SidecarCompat.sidecarVersion
assumeTrue(Version.VERSION_0_1 == sidecarVersion)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ package androidx.window.layout

import android.annotation.SuppressLint
import android.app.Activity
import android.content.ComponentCallbacks
import android.content.Context
import android.content.res.Configuration
import android.os.IBinder
import android.text.TextUtils
import android.util.Log
Expand Down Expand Up @@ -51,6 +53,9 @@ internal class SidecarCompat @VisibleForTesting constructor(
// Map of active listeners registered with #onWindowLayoutChangeListenerAdded() and not yet
// removed by #onWindowLayoutChangeListenerRemoved().
private val windowListenerRegisteredContexts = mutableMapOf<IBinder, Activity>()
// Map of activities registered to their component callbacks so we can keep track and
// remove when the activity is unregistered
private val componentCallbackMap = mutableMapOf<Activity, ComponentCallbacks>()
private var extensionCallback: ExtensionCallbackInterface? = null

constructor(context: Context) : this(
Expand Down Expand Up @@ -103,18 +108,49 @@ internal class SidecarCompat @VisibleForTesting constructor(
sidecar?.onDeviceStateListenersChanged(false)
}
extensionCallback?.onWindowLayoutChanged(activity, getWindowLayoutInfo(activity))
registerConfigurationChangeListener(activity)
}

private fun registerConfigurationChangeListener(activity: Activity) {
// Only register a component callback if we haven't already as register
// may be called multiple times for the same activity
if (componentCallbackMap[activity] == null) {
// Create a configuration change observer to send updated WindowLayoutInfo
// when the configuration of the app changes: b/186647126
val configChangeObserver = object : ComponentCallbacks {
override fun onConfigurationChanged(newConfig: Configuration) {
extensionCallback?.onWindowLayoutChanged(
activity,
getWindowLayoutInfo(activity)
)
}

override fun onLowMemory() {
return
}
}
componentCallbackMap[activity] = configChangeObserver
activity.registerComponentCallbacks(configChangeObserver)
}
}

override fun onWindowLayoutChangeListenerRemoved(activity: Activity) {
val windowToken = getActivityWindowToken(activity) ?: return
sidecar?.onWindowLayoutChangeListenerRemoved(windowToken)
unregisterComponentCallback(activity)
val isLast = windowListenerRegisteredContexts.size == 1
windowListenerRegisteredContexts.remove(windowToken)
if (isLast) {
sidecar?.onDeviceStateListenersChanged(true)
}
}

private fun unregisterComponentCallback(activity: Activity) {
val configChangeObserver = componentCallbackMap[activity]
activity.unregisterComponentCallbacks(configChangeObserver)
componentCallbackMap.remove(activity)
}

@SuppressLint("BanUncheckedReflection")
override fun validateExtensionInterface(): Boolean {
return try {
Expand Down

0 comments on commit 9c8233d

Please sign in to comment.