Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ece2bee8 authored by Charles Chen's avatar Charles Chen Committed by Android (Google) Code Review
Browse files

Merge "Implement ViewCaptureAwareWm with wrapper" into main

parents aa543bdb de26e7fc
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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",
+7 −5
Original line number Diff line number Diff line
@@ -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
@@ -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()

@@ -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?) {
+11 −11
Original line number Diff line number Diff line
@@ -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 {

@@ -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
        }
+86 −1
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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
    }
}