Loading viewcapturelib/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ android_test { "view_capture", "androidx.test.ext.junit", "androidx.test.rules", "flag-junit", "testables", "mockito-kotlin2", "mockito-target-extended-minus-junit4", Loading viewcapturelib/src/com/android/app/viewcapture/ViewCaptureAwareWindowManager.kt +7 −5 Original line number Diff line number Diff line Loading @@ -18,12 +18,11 @@ package com.android.app.viewcapture import android.content.Context import android.media.permission.SafeCloseable import android.os.IBinder import android.view.View import android.view.ViewGroup import android.view.Window import android.view.WindowManager import android.view.WindowManagerImpl import android.view.WindowManagerWrapper /** * [WindowManager] implementation to enable view tracing. Adds [ViewCapture] to associated window Loading @@ -32,9 +31,8 @@ import android.view.WindowManagerImpl */ internal class ViewCaptureAwareWindowManager( private val context: Context, private val parent: Window? = null, private val windowContextToken: IBinder? = null, ) : WindowManagerImpl(context, parent, windowContextToken) { private val base: WindowManager, ) : WindowManagerWrapper(base) { private var viewCaptureCloseableMap: MutableMap<View, SafeCloseable> = mutableMapOf() Loading @@ -55,6 +53,10 @@ internal class ViewCaptureAwareWindowManager( super.removeViewImmediate(view) } override fun createLocalWindowManager(parentWindow: Window): WindowManager { return ViewCaptureAwareWindowManager(context, base.createLocalWindowManager(parentWindow)) } private fun getViewName(view: View) = "." + view.javaClass.name private fun removeViewFromCloseableMap(view: View?) { Loading viewcapturelib/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerFactory.kt +11 −11 Original line number Diff line number Diff line Loading @@ -17,16 +17,13 @@ package com.android.app.viewcapture import android.content.Context import android.os.IBinder import android.os.Trace import android.os.Trace.TRACE_TAG_APP import android.view.Window import android.view.WindowManager import java.lang.ref.WeakReference import java.util.Collections import java.util.WeakHashMap /** Factory to create [Context] specific instances of [ViewCaptureAwareWindowManager]. */ object ViewCaptureAwareWindowManagerFactory { Loading @@ -43,19 +40,22 @@ object ViewCaptureAwareWindowManagerFactory { * no instance is cached; it creates, caches and returns a new instance. */ @JvmStatic fun getInstance( context: Context, parent: Window? = null, windowContextToken: IBinder? = null, ): WindowManager { Trace.traceCounter(TRACE_TAG_APP, "ViewCaptureAwareWindowManagerFactory#instanceMap.size", instanceMap.size) fun getInstance(context: Context): WindowManager { Trace.traceCounter( TRACE_TAG_APP, "ViewCaptureAwareWindowManagerFactory#instanceMap.size", instanceMap.size, ) val cachedWindowManager = instanceMap[context]?.get() if (cachedWindowManager != null) { return cachedWindowManager } else { val windowManager = ViewCaptureAwareWindowManager(context, parent, windowContextToken) val windowManager = ViewCaptureAwareWindowManager( context, context.getSystemService(WindowManager::class.java), ) instanceMap[context] = WeakReference(windowManager) return windowManager } Loading viewcapturelib/tests/com/android/app/viewcapture/ViewCaptureAwareWindowManagerTest.kt +86 −1 Original line number Diff line number Diff line Loading @@ -18,11 +18,23 @@ package com.android.app.viewcapture import android.content.Context import android.content.Intent import android.hardware.display.DisplayManager import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.view.Display.DEFAULT_DISPLAY import android.view.View import android.view.WindowManager import android.view.WindowManager.LayoutParams.TYPE_APPLICATION import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG import android.window.WindowContext import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.window.flags.Flags import com.google.common.truth.Truth.assertWithMessage import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import org.junit.Assert.assertTrue import org.junit.Rule import org.junit.Test Loading @@ -38,10 +50,16 @@ class ViewCaptureAwareWindowManagerTest { @get:Rule val activityScenarioRule = ActivityScenarioRule<TestActivity>(activityIntent) @get:Rule val mSetFlagsRule: SetFlagsRule = SetFlagsRule() @Test fun testAddView_verifyStartCaptureCall() { activityScenarioRule.scenario.onActivity { activity -> mViewCaptureAwareWindowManager = ViewCaptureAwareWindowManager(mContext) mViewCaptureAwareWindowManager = ViewCaptureAwareWindowManager( mContext, mContext.getSystemService(WindowManager::class.java), ) val activityDecorView = activity.window.decorView // removing view since it is already added to view hierarchy on declaration Loading @@ -55,4 +73,71 @@ class ViewCaptureAwareWindowManagerTest { assertTrue(viewCapture.mIsStarted) } } @EnableFlags(Flags.FLAG_ENABLE_WINDOW_CONTEXT_OVERRIDE_TYPE) @Test fun useWithWindowContext_attachWindow_attachToViewCaptureAwareWm() { val windowContext = mContext.createWindowContext( mContext.getSystemService(DisplayManager::class.java).getDisplay(DEFAULT_DISPLAY), TYPE_APPLICATION, null, /* options */ ) as WindowContext // Obtain ViewCaptureAwareWindowManager with WindowContext. mViewCaptureAwareWindowManager = ViewCaptureAwareWindowManagerFactory.getInstance(windowContext) as ViewCaptureAwareWindowManager // Attach to an Activity so that we can add an application parent window. val params = WindowManager.LayoutParams() activityScenarioRule.scenario.onActivity { activity -> params.token = activity.activityToken } // Create and attach an application window, and listen to OnAttachStateChangeListener. // We need to know when the parent window is attached and then we can add the attached // dialog. val listener = AttachStateListener() val parentWindow = View(windowContext) parentWindow.addOnAttachStateChangeListener(listener) windowContext.attachWindow(parentWindow) // Attach the parent window to ViewCaptureAwareWm activityScenarioRule.scenario.onActivity { mViewCaptureAwareWindowManager.addView(parentWindow, params) } // Wait for parent window to be attached. listener.mLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS) assertWithMessage("The WindowContext token must be attached.") .that(params.mWindowContextToken) .isEqualTo(windowContext.windowContextToken) val subWindow = View(windowContext) val subParams = WindowManager.LayoutParams(TYPE_APPLICATION_ATTACHED_DIALOG) // Attach the sub-window activityScenarioRule.scenario.onActivity { mViewCaptureAwareWindowManager.addView(subWindow, subParams) } assertWithMessage("The sub-window must be attached to the parent window") .that(subParams.token) .isEqualTo(parentWindow.windowToken) } private class AttachStateListener : View.OnAttachStateChangeListener { val mLatch: CountDownLatch = CountDownLatch(1) override fun onViewAttachedToWindow(v: View) { mLatch.countDown() } override fun onViewDetachedFromWindow(v: View) {} } companion object { private const val TIMEOUT_IN_SECONDS = 4L } } Loading
viewcapturelib/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ android_test { "view_capture", "androidx.test.ext.junit", "androidx.test.rules", "flag-junit", "testables", "mockito-kotlin2", "mockito-target-extended-minus-junit4", Loading
viewcapturelib/src/com/android/app/viewcapture/ViewCaptureAwareWindowManager.kt +7 −5 Original line number Diff line number Diff line Loading @@ -18,12 +18,11 @@ package com.android.app.viewcapture import android.content.Context import android.media.permission.SafeCloseable import android.os.IBinder import android.view.View import android.view.ViewGroup import android.view.Window import android.view.WindowManager import android.view.WindowManagerImpl import android.view.WindowManagerWrapper /** * [WindowManager] implementation to enable view tracing. Adds [ViewCapture] to associated window Loading @@ -32,9 +31,8 @@ import android.view.WindowManagerImpl */ internal class ViewCaptureAwareWindowManager( private val context: Context, private val parent: Window? = null, private val windowContextToken: IBinder? = null, ) : WindowManagerImpl(context, parent, windowContextToken) { private val base: WindowManager, ) : WindowManagerWrapper(base) { private var viewCaptureCloseableMap: MutableMap<View, SafeCloseable> = mutableMapOf() Loading @@ -55,6 +53,10 @@ internal class ViewCaptureAwareWindowManager( super.removeViewImmediate(view) } override fun createLocalWindowManager(parentWindow: Window): WindowManager { return ViewCaptureAwareWindowManager(context, base.createLocalWindowManager(parentWindow)) } private fun getViewName(view: View) = "." + view.javaClass.name private fun removeViewFromCloseableMap(view: View?) { Loading
viewcapturelib/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerFactory.kt +11 −11 Original line number Diff line number Diff line Loading @@ -17,16 +17,13 @@ package com.android.app.viewcapture import android.content.Context import android.os.IBinder import android.os.Trace import android.os.Trace.TRACE_TAG_APP import android.view.Window import android.view.WindowManager import java.lang.ref.WeakReference import java.util.Collections import java.util.WeakHashMap /** Factory to create [Context] specific instances of [ViewCaptureAwareWindowManager]. */ object ViewCaptureAwareWindowManagerFactory { Loading @@ -43,19 +40,22 @@ object ViewCaptureAwareWindowManagerFactory { * no instance is cached; it creates, caches and returns a new instance. */ @JvmStatic fun getInstance( context: Context, parent: Window? = null, windowContextToken: IBinder? = null, ): WindowManager { Trace.traceCounter(TRACE_TAG_APP, "ViewCaptureAwareWindowManagerFactory#instanceMap.size", instanceMap.size) fun getInstance(context: Context): WindowManager { Trace.traceCounter( TRACE_TAG_APP, "ViewCaptureAwareWindowManagerFactory#instanceMap.size", instanceMap.size, ) val cachedWindowManager = instanceMap[context]?.get() if (cachedWindowManager != null) { return cachedWindowManager } else { val windowManager = ViewCaptureAwareWindowManager(context, parent, windowContextToken) val windowManager = ViewCaptureAwareWindowManager( context, context.getSystemService(WindowManager::class.java), ) instanceMap[context] = WeakReference(windowManager) return windowManager } Loading
viewcapturelib/tests/com/android/app/viewcapture/ViewCaptureAwareWindowManagerTest.kt +86 −1 Original line number Diff line number Diff line Loading @@ -18,11 +18,23 @@ package com.android.app.viewcapture import android.content.Context import android.content.Intent import android.hardware.display.DisplayManager import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.view.Display.DEFAULT_DISPLAY import android.view.View import android.view.WindowManager import android.view.WindowManager.LayoutParams.TYPE_APPLICATION import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG import android.window.WindowContext import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.window.flags.Flags import com.google.common.truth.Truth.assertWithMessage import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import org.junit.Assert.assertTrue import org.junit.Rule import org.junit.Test Loading @@ -38,10 +50,16 @@ class ViewCaptureAwareWindowManagerTest { @get:Rule val activityScenarioRule = ActivityScenarioRule<TestActivity>(activityIntent) @get:Rule val mSetFlagsRule: SetFlagsRule = SetFlagsRule() @Test fun testAddView_verifyStartCaptureCall() { activityScenarioRule.scenario.onActivity { activity -> mViewCaptureAwareWindowManager = ViewCaptureAwareWindowManager(mContext) mViewCaptureAwareWindowManager = ViewCaptureAwareWindowManager( mContext, mContext.getSystemService(WindowManager::class.java), ) val activityDecorView = activity.window.decorView // removing view since it is already added to view hierarchy on declaration Loading @@ -55,4 +73,71 @@ class ViewCaptureAwareWindowManagerTest { assertTrue(viewCapture.mIsStarted) } } @EnableFlags(Flags.FLAG_ENABLE_WINDOW_CONTEXT_OVERRIDE_TYPE) @Test fun useWithWindowContext_attachWindow_attachToViewCaptureAwareWm() { val windowContext = mContext.createWindowContext( mContext.getSystemService(DisplayManager::class.java).getDisplay(DEFAULT_DISPLAY), TYPE_APPLICATION, null, /* options */ ) as WindowContext // Obtain ViewCaptureAwareWindowManager with WindowContext. mViewCaptureAwareWindowManager = ViewCaptureAwareWindowManagerFactory.getInstance(windowContext) as ViewCaptureAwareWindowManager // Attach to an Activity so that we can add an application parent window. val params = WindowManager.LayoutParams() activityScenarioRule.scenario.onActivity { activity -> params.token = activity.activityToken } // Create and attach an application window, and listen to OnAttachStateChangeListener. // We need to know when the parent window is attached and then we can add the attached // dialog. val listener = AttachStateListener() val parentWindow = View(windowContext) parentWindow.addOnAttachStateChangeListener(listener) windowContext.attachWindow(parentWindow) // Attach the parent window to ViewCaptureAwareWm activityScenarioRule.scenario.onActivity { mViewCaptureAwareWindowManager.addView(parentWindow, params) } // Wait for parent window to be attached. listener.mLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS) assertWithMessage("The WindowContext token must be attached.") .that(params.mWindowContextToken) .isEqualTo(windowContext.windowContextToken) val subWindow = View(windowContext) val subParams = WindowManager.LayoutParams(TYPE_APPLICATION_ATTACHED_DIALOG) // Attach the sub-window activityScenarioRule.scenario.onActivity { mViewCaptureAwareWindowManager.addView(subWindow, subParams) } assertWithMessage("The sub-window must be attached to the parent window") .that(subParams.token) .isEqualTo(parentWindow.windowToken) } private class AttachStateListener : View.OnAttachStateChangeListener { val mLatch: CountDownLatch = CountDownLatch(1) override fun onViewAttachedToWindow(v: View) { mLatch.countDown() } override fun onViewDetachedFromWindow(v: View) {} } companion object { private const val TIMEOUT_IN_SECONDS = 4L } }