Reland "[clank] Set web contents visibility based on occlusion"
This is a reland of commit d9120f8afd
Original change's description:
> [clank] Set web contents visibility based on occlusion
>
> This CL uses the trusted presentation API to determine occlusion for
> clank windows and plumbs it through to web contents visibility.
>
> from Android system server
>
> Low-Coverage-Reason: HARD_TO_TEST This code interfaces with messages
> Bug: 349735915
> Test: CQ
> Change-Id: I4b6d14cd36bea6e1b56aca8714665281d0cbc76b
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5754164
> Commit-Queue: Eliot Courtney <edcourtney@chromium.org>
> Reviewed-by: Ted Choc <tedchoc@chromium.org>
> Reviewed-by: Richard (Torne) Coles <torne@chromium.org>
> Reviewed-by: David Trainor <dtrainor@chromium.org>
> Reviewed-by: Simeon Anfinrud <sanfin@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1380048}
Low-Coverage-Reason: HARD_TO_TEST Interfaces with Android system server
Bug: 349735915
Change-Id: Ib8e35e9ab127cec262bda2f1da02b57a505668c3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6013410
Commit-Queue: Eliot Courtney <edcourtney@chromium.org>
Reviewed-by: Richard (Torne) Coles <torne@chromium.org>
Reviewed-by: Ted Choc <tedchoc@chromium.org>
Reviewed-by: Simeon Anfinrud <sanfin@chromium.org>
Reviewed-by: David Trainor <dtrainor@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1383440}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
575e2da0f0
commit
6b9b09c6e2
android_webview/java/src/org/chromium/android_webview
chrome
android
java
src
org
chromium
chrome
browser
javatests
src
org
chromium
chrome
browser
junit
src
org
chromium
chrome
browser
autofill
iban
save_card
vcn
share
tab
tabmodel
browser
commerce
merchant_viewer
android
javatests
src
org
chromium
chrome
browser
merchant_viewer
facilitated_payments
ui
android
internal
java
src
org
chromium
chrome
browser
facilitated_payments
password_manager
android
grouped_affiliations
java
src
org
chromium
chrome
browser
grouped_affiliations
share
android
javatests
src
org
chromium
chrome
browser
share
ui
android
default_browser_promo
java
src
org
chromium
chrome
browser
ui
default_browser_promo
hats
internal
java
src
org
chromium
chrome
browser
omnibox
java
src
org
chromium
chrome
browser
omnibox
chromecast/browser/android/apk/src/org/chromium/chromecast/shell
components
browser_ui
contacts_picker
android
java
src
org
chromium
components
browser_ui
contacts_picker
photo_picker
android
java
src
org
chromium
components
browser_ui
photo_picker
thin_webview
internal
java
src
org
chromium
components
thinwebview
internal
content/shell/android
browsertests
src
org
chromium
content_shell
browsertests
shell_apk
src
org
chromium
content_shell_apk
ui/android/java/src/org/chromium/ui/base
@ -1858,11 +1858,14 @@ public class AwContents implements SmartClipProvider {
|
||||
context,
|
||||
listenToActivityState,
|
||||
IntentRequestTracker.createFromActivity(activity),
|
||||
/* insetObserver= */ null);
|
||||
/* insetObserver= */ null,
|
||||
/* trackOcclusion= */ false);
|
||||
}
|
||||
wrapper = new WindowAndroidWrapper(activityWindow);
|
||||
} else {
|
||||
wrapper = new WindowAndroidWrapper(new WindowAndroid(context));
|
||||
wrapper =
|
||||
new WindowAndroidWrapper(
|
||||
new WindowAndroid(context, /* trackOcclusion= */ false));
|
||||
}
|
||||
sContextWindowMap.put(context, wrapper);
|
||||
}
|
||||
|
@ -87,7 +87,8 @@ public class ChromeWindow extends ActivityWindowAndroid {
|
||||
/* listenToActivityState= */ true,
|
||||
activityKeyboardVisibilityDelegate,
|
||||
intentRequestTracker,
|
||||
insetObserver);
|
||||
insetObserver,
|
||||
/* trackOcclusion= */ false);
|
||||
assert insetObserver != null;
|
||||
mCompositorViewHolderSupplier = compositorViewHolderSupplier;
|
||||
mModalDialogManagerSupplier = modalDialogManagerSupplier;
|
||||
|
@ -289,7 +289,7 @@ public class WarmupManager {
|
||||
|
||||
// These are effectively unused as they will be set when finishing reparenting.
|
||||
TabDelegateFactory delegateFactory = CustomTabDelegateFactory.createEmpty();
|
||||
WindowAndroid window = new WindowAndroid(context);
|
||||
WindowAndroid window = new WindowAndroid(context, /* trackOcclusion= */ false);
|
||||
|
||||
// TODO(crbug.com/40174356): Set isIncognito flag here if spare tabs are allowed for
|
||||
// incognito mode.
|
||||
|
@ -100,7 +100,12 @@ public class CreatorActivity extends SnackbarActivity {
|
||||
|
||||
IntentRequestTracker intentRequestTracker = IntentRequestTracker.createFromActivity(this);
|
||||
mWindowAndroid =
|
||||
new ActivityWindowAndroid(this, false, intentRequestTracker, getInsetObserver());
|
||||
new ActivityWindowAndroid(
|
||||
this,
|
||||
false,
|
||||
intentRequestTracker,
|
||||
getInsetObserver(),
|
||||
/* trackOcclusion= */ false);
|
||||
|
||||
TabShareDelegateImpl tabshareDelegate =
|
||||
new TabShareDelegateImpl(
|
||||
|
2
chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestrator.java
2
chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestrator.java
@ -285,7 +285,7 @@ public class ArchivedTabModelOrchestrator extends TabModelOrchestrator implement
|
||||
Context context = ContextUtils.getApplicationContext();
|
||||
// TODO(crbug.com/331841977): Investigate removing the WindowAndroid requirement when
|
||||
// creating tabs.
|
||||
mWindow = new WindowAndroid(context);
|
||||
mWindow = new WindowAndroid(context, /* trackOcclusion= */ false);
|
||||
mArchivedTabCreator = new ArchivedTabCreator(mWindow);
|
||||
mRegularTabCreator = regularTabCreator;
|
||||
|
||||
|
@ -65,7 +65,8 @@ public class DeviceLockActivity extends SynchronousInitializationActivity
|
||||
this,
|
||||
/* listenToActivityState= */ true,
|
||||
IntentRequestTracker.createFromActivity(this),
|
||||
getInsetObserver());
|
||||
getInsetObserver(),
|
||||
/* trackOcclusion= */ false);
|
||||
mIntentRequestTracker = mWindowAndroid.getIntentRequestTracker();
|
||||
|
||||
Bundle fragmentArgs = getIntent().getBundleExtra(ARGUMENT_FRAGMENT_ARGS);
|
||||
|
@ -845,6 +845,7 @@ public class FirstRunActivity extends FirstRunActivityBase implements FirstRunPa
|
||||
this,
|
||||
/* listenToActivityState= */ true,
|
||||
getIntentRequestTracker(),
|
||||
getInsetObserver());
|
||||
getInsetObserver(),
|
||||
/* trackOcclusion= */ false);
|
||||
}
|
||||
}
|
||||
|
@ -676,7 +676,8 @@ public class PictureInPictureActivity extends AsyncInitializationActivity {
|
||||
this,
|
||||
/* listenToActivityState= */ true,
|
||||
getIntentRequestTracker(),
|
||||
getInsetObserver());
|
||||
getInsetObserver(),
|
||||
/* trackOcclusion= */ false);
|
||||
}
|
||||
|
||||
@CalledByNative
|
||||
|
@ -269,7 +269,8 @@ public class SearchActivity extends AsyncInitializationActivity
|
||||
/* listenToActivityState= */ true,
|
||||
new ActivityKeyboardVisibilityDelegate(new WeakReference(this)),
|
||||
getIntentRequestTracker(),
|
||||
getInsetObserver()) {
|
||||
getInsetObserver(),
|
||||
/* trackOcclusion= */ false) {
|
||||
@Override
|
||||
public ModalDialogManager getModalDialogManager() {
|
||||
return SearchActivity.this.getModalDialogManager();
|
||||
|
@ -271,7 +271,8 @@ public class SigninAndHistorySyncActivity extends FirstRunActivityBase
|
||||
this,
|
||||
/* listenToActivityState= */ true,
|
||||
getIntentRequestTracker(),
|
||||
getInsetObserver());
|
||||
getInsetObserver(),
|
||||
/* trackOcclusion= */ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -98,7 +98,8 @@ public class SyncConsentActivity extends SynchronousInitializationActivity
|
||||
this,
|
||||
/* listenToActivityState= */ true,
|
||||
IntentRequestTracker.createFromActivity(this),
|
||||
getInsetObserver());
|
||||
getInsetObserver(),
|
||||
/* trackOcclusion= */ false);
|
||||
}
|
||||
return mWindowAndroid;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import org.jni_zero.CalledByNative;
|
||||
import org.jni_zero.JniType;
|
||||
import org.jni_zero.NativeMethods;
|
||||
|
||||
import org.chromium.base.Callback;
|
||||
import org.chromium.base.CommandLine;
|
||||
import org.chromium.base.ContextUtils;
|
||||
import org.chromium.base.Log;
|
||||
@ -199,6 +200,9 @@ class TabImpl implements Tab, SensitiveContentClient.Observer {
|
||||
/** Whether or not the Tab is currently visible to the user. */
|
||||
private boolean mIsHidden = true;
|
||||
|
||||
/** Called when the current window's occlusion changes. */
|
||||
private final Callback<Boolean> mOcclusionCallback = (v) -> updateWebContentsVisibility();
|
||||
|
||||
/**
|
||||
* Importance of the WebContents currently attached to this tab. Note the key difference from
|
||||
* |mIsHidden| is that a tab is hidden when the application is hidden, but the importance is not
|
||||
@ -618,6 +622,7 @@ class TabImpl implements Tab, SensitiveContentClient.Observer {
|
||||
// We must check this as an additional condition to detachment for this case to continue
|
||||
// to work. See https://crbug.com/1501849.
|
||||
mIsDetached = window == null || !windowHasActivity(window);
|
||||
updateWebContentsVisibility();
|
||||
}
|
||||
|
||||
private boolean checkAttached() {
|
||||
@ -922,6 +927,19 @@ class TabImpl implements Tab, SensitiveContentClient.Observer {
|
||||
return mIsDestroyed;
|
||||
}
|
||||
|
||||
private void updateWebContentsVisibility() {
|
||||
var webContents = getWebContents();
|
||||
if (webContents == null) return;
|
||||
if (mIsHidden) {
|
||||
webContents.updateWebContentsVisibility(Visibility.HIDDEN);
|
||||
} else if (!mIsDetached && mWindowAndroid.getOcclusionSupplier().get()) {
|
||||
// If we are not attached to a window, occlusion does not make sense.
|
||||
webContents.updateWebContentsVisibility(Visibility.OCCLUDED);
|
||||
} else {
|
||||
webContents.updateWebContentsVisibility(Visibility.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void show(@TabSelectionType int type, @TabLoadIfNeededCaller int caller) {
|
||||
try {
|
||||
@ -947,9 +965,7 @@ class TabImpl implements Tab, SensitiveContentClient.Observer {
|
||||
// call.
|
||||
TabImplJni.get().onShow(mNativeTabAndroid);
|
||||
|
||||
if (getWebContents() != null) {
|
||||
getWebContents().updateWebContentsVisibility(Visibility.VISIBLE);
|
||||
}
|
||||
updateWebContentsVisibility();
|
||||
|
||||
// If the NativePage was frozen while in the background (see NativePageAssassin),
|
||||
// recreate the NativePage now.
|
||||
@ -984,10 +1000,7 @@ class TabImpl implements Tab, SensitiveContentClient.Observer {
|
||||
if (isHidden()) return;
|
||||
mIsHidden = true;
|
||||
updateInteractableState();
|
||||
|
||||
if (getWebContents() != null) {
|
||||
getWebContents().updateWebContentsVisibility(Visibility.HIDDEN);
|
||||
}
|
||||
updateWebContentsVisibility();
|
||||
|
||||
// Allow this tab's NativePage to be frozen if it stays hidden for a while.
|
||||
NativePageAssassin.getInstance().tabHidden(this);
|
||||
@ -1257,10 +1270,19 @@ class TabImpl implements Tab, SensitiveContentClient.Observer {
|
||||
void updateWindowAndroid(WindowAndroid windowAndroid) {
|
||||
// TODO(yusufo): mWindowAndroid can never be null until crbug.com/657007 is fixed.
|
||||
assert windowAndroid != null;
|
||||
|
||||
if (mWindowAndroid != null) {
|
||||
mWindowAndroid.getOcclusionSupplier().removeObserver(mOcclusionCallback);
|
||||
}
|
||||
|
||||
mWindowAndroid = windowAndroid;
|
||||
WebContents webContents = getWebContents();
|
||||
if (webContents != null) webContents.setTopLevelNativeWindow(mWindowAndroid);
|
||||
|
||||
windowAndroid.getOcclusionSupplier().addObserver(mOcclusionCallback);
|
||||
|
||||
// updateIsDetached will also update the web contents visibility if the
|
||||
// occlusion has changed.
|
||||
updateIsDetached(windowAndroid);
|
||||
}
|
||||
|
||||
@ -1714,7 +1736,7 @@ class TabImpl implements Tab, SensitiveContentClient.Observer {
|
||||
bounds.bottom);
|
||||
}
|
||||
initWebContents(webContents);
|
||||
webContents.updateWebContentsVisibility(Visibility.VISIBLE);
|
||||
updateWebContentsVisibility();
|
||||
});
|
||||
|
||||
if (didStartLoad) {
|
||||
|
@ -78,7 +78,8 @@ public class SelectFileDialogTest {
|
||||
activity,
|
||||
/* listenToActivityState= */ true,
|
||||
IntentRequestTracker.createFromActivity(activity),
|
||||
insetObserver);
|
||||
insetObserver,
|
||||
/* trackOcclusion= */ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -116,7 +116,7 @@ public class BookmarkToolbarTest extends BlankUiTestActivityTestCase {
|
||||
mActivity = getActivity();
|
||||
ThreadUtils.runOnUiThreadBlocking(
|
||||
() -> {
|
||||
mWindowAndroid = new WindowAndroid(mActivity);
|
||||
mWindowAndroid = new WindowAndroid(mActivity, /* trackOcclusion= */ false);
|
||||
mContentView = new LinearLayout(mActivity);
|
||||
mContentView.setBackgroundColor(Color.WHITE);
|
||||
FrameLayout.LayoutParams params =
|
||||
|
@ -172,7 +172,8 @@ public class OverlayPanelBaseTest {
|
||||
mActivity,
|
||||
/* listenToActivityState= */ true,
|
||||
IntentRequestTracker.createFromActivity(mActivity),
|
||||
mInsetObserver);
|
||||
mInsetObserver,
|
||||
/* trackOcclusion= */ false);
|
||||
OverlayPanelManager panelManager = new OverlayPanelManager();
|
||||
mExpandPanel =
|
||||
new MockOverlayPanel(
|
||||
|
@ -277,7 +277,8 @@ public class OverlayPanelEventFilterTest {
|
||||
mActivity,
|
||||
/* listenToActivityState= */ true,
|
||||
IntentRequestTracker.createFromActivity(mActivity),
|
||||
mInsetObserver);
|
||||
mInsetObserver,
|
||||
/* trackOcclusion= */ false);
|
||||
|
||||
mPanel =
|
||||
new MockOverlayPanel(
|
||||
|
@ -176,7 +176,8 @@ public class OverlayPanelManagerTest {
|
||||
mActivity,
|
||||
/* listenToActivityState= */ true,
|
||||
IntentRequestTracker.createFromActivity(mActivity),
|
||||
mInsetObserver);
|
||||
mInsetObserver,
|
||||
/* trackOcclusion= */ false);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,8 @@ public class AndroidPaymentAppFinderUnitTest extends BlankUiTestActivityTestCase
|
||||
getActivity(),
|
||||
/* listenToActivityState= */ true,
|
||||
IntentRequestTracker.createFromActivity(getActivity()),
|
||||
mInsetObserver);
|
||||
mInsetObserver,
|
||||
/* trackOcclusion= */ false);
|
||||
});
|
||||
|
||||
NativeLibraryTestUtils.loadNativeLibraryAndInitBrowserProcess();
|
||||
|
@ -74,7 +74,7 @@ public final class AutofillSaveIbanBottomSheetBridgeTest {
|
||||
Activity activity = Robolectric.buildActivity(Activity.class).create().get();
|
||||
// set a MaterialComponents theme which is required for the `OutlinedBox` text field.
|
||||
activity.setTheme(R.style.Theme_BrowserUI_DayNight);
|
||||
mWindow = new WindowAndroid(activity);
|
||||
mWindow = new WindowAndroid(activity, /* trackOcclusion= */ false);
|
||||
BottomSheetControllerFactory.attach(mWindow, mBottomSheetController);
|
||||
LayoutManagerAppUtils.attach(mWindow, mLayoutManager);
|
||||
MockTabModel tabModel = new MockTabModel(mProfile, /* delegate= */ null);
|
||||
|
@ -57,7 +57,7 @@ public final class AutofillSaveCardBottomSheetBridgeTest {
|
||||
public void setUp() {
|
||||
mJniMocker.mock(AutofillSaveCardBottomSheetBridgeJni.TEST_HOOKS, mBridgeNatives);
|
||||
Activity activity = Robolectric.buildActivity(Activity.class).create().get();
|
||||
mWindow = new WindowAndroid(activity);
|
||||
mWindow = new WindowAndroid(activity, /* trackOcclusion= */ false);
|
||||
BottomSheetControllerFactory.attach(mWindow, mBottomSheetController);
|
||||
LayoutManagerAppUtils.attach(mWindow, mLayoutManager);
|
||||
MockTabModel tabModel = new MockTabModel(mProfile, /* delegate= */ null);
|
||||
|
@ -104,7 +104,7 @@ public final class AutofillVcnEnrollBottomSheetBridgeTest {
|
||||
mJniMocker.mock(AutofillVcnEnrollBottomSheetBridgeJni.TEST_HOOKS, mBridgeNatives);
|
||||
Activity activity = Robolectric.buildActivity(Activity.class).create().get();
|
||||
mShadowActivity = shadowOf(activity);
|
||||
mWindow = new WindowAndroid(activity);
|
||||
mWindow = new WindowAndroid(activity, /* trackOcclusion= */ false);
|
||||
when(mPersonalDataManager.getCustomImageForAutofillSuggestionIfAvailable(
|
||||
ISSUER_ICON_URL,
|
||||
CardIconSpecs.create(mWindow.getContext().get(), ImageSize.SMALL)))
|
||||
|
@ -81,7 +81,7 @@ public final class AutofillVcnEnrollBottomSheetCoordinatorTest {
|
||||
|
||||
PersonalDataManagerFactory.setInstanceForTesting(mPersonalDataManager);
|
||||
Activity activity = buildActivity(Activity.class).create().get();
|
||||
mWindow = new WindowAndroid(activity);
|
||||
mWindow = new WindowAndroid(activity, /* trackOcclusion= */ false);
|
||||
BottomSheetControllerFactory.attach(mWindow, mBottomSheetController);
|
||||
setUpCreditCardWithCardArtUrl();
|
||||
mCoordinator =
|
||||
|
@ -58,7 +58,7 @@ public final class AutofillVcnEnrollBottomSheetMediatorTest {
|
||||
public void setUp() {
|
||||
Activity activity = Robolectric.buildActivity(Activity.class).create().get();
|
||||
mModel = new PropertyModel.Builder(AutofillVcnEnrollBottomSheetProperties.ALL_KEYS).build();
|
||||
mWindow = new WindowAndroid(activity);
|
||||
mWindow = new WindowAndroid(activity, /* trackOcclusion= */ false);
|
||||
BottomSheetControllerFactory.attach(mWindow, mBottomSheetController);
|
||||
when(mLifecycle.canBegin()).thenReturn(true);
|
||||
mMediator = new AutofillVcnEnrollBottomSheetMediator(mContent, mLifecycle, mModel);
|
||||
|
@ -203,7 +203,8 @@ public class ShareHelperMultiInstanceUnitTest {
|
||||
mActivity,
|
||||
/* listenToActivityState= */ false,
|
||||
mIntentRequestTracker,
|
||||
/* insetObserver= */ null);
|
||||
/* insetObserver= */ null,
|
||||
/* trackOcclusion= */ false);
|
||||
}
|
||||
|
||||
public SingleWindowTestInstance startShare() {
|
||||
|
@ -78,7 +78,8 @@ public class ShareHelperUnitTest {
|
||||
mActivity,
|
||||
/* listenToActivityState= */ false,
|
||||
IntentRequestTracker.createFromActivity(mActivity),
|
||||
/* insetObserver= */ null);
|
||||
/* insetObserver= */ null,
|
||||
/* trackOcclusion= */ false);
|
||||
mImageUri = Uri.parse(IMAGE_URI);
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import org.chromium.base.Token;
|
||||
import org.chromium.base.supplier.ObservableSupplierImpl;
|
||||
import org.chromium.base.test.BaseRobolectricTestRunner;
|
||||
import org.chromium.base.test.util.Features.DisableFeatures;
|
||||
import org.chromium.base.test.util.Features.EnableFeatures;
|
||||
@ -94,6 +95,7 @@ public class TabUnitTest {
|
||||
|
||||
doReturn(mWeakReferenceActivity).when(mWindowAndroid).getActivity();
|
||||
doReturn(mWeakReferenceContext).when(mWindowAndroid).getContext();
|
||||
doReturn(new ObservableSupplierImpl<>(false)).when(mWindowAndroid).getOcclusionSupplier();
|
||||
doReturn(mActivity).when(mWeakReferenceActivity).get();
|
||||
doReturn(mContext).when(mWeakReferenceContext).get();
|
||||
doReturn(mContext).when(mContext).getApplicationContext();
|
||||
|
1
chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/ArchivedTabModelSelectorImplTest.java
1
chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/ArchivedTabModelSelectorImplTest.java
@ -229,6 +229,7 @@ public class ArchivedTabModelSelectorImplTest {
|
||||
WindowAndroid window = mock(WindowAndroid.class);
|
||||
WeakReference<Context> weakContext = new WeakReference<>(mContext);
|
||||
when(window.getContext()).thenReturn(weakContext);
|
||||
doReturn(new ObservableSupplierImpl<>(false)).when(window).getOcclusionSupplier();
|
||||
tab.updateAttachment(window, mTabDelegateFactory);
|
||||
|
||||
Assert.assertEquals(
|
||||
|
@ -10,6 +10,7 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@ -31,6 +32,7 @@ import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
import org.chromium.base.Callback;
|
||||
import org.chromium.base.supplier.ObservableSupplierImpl;
|
||||
import org.chromium.base.test.BaseRobolectricTestRunner;
|
||||
import org.chromium.base.test.util.JniMocker;
|
||||
import org.chromium.chrome.browser.flags.ActivityType;
|
||||
@ -112,6 +114,7 @@ public class TabModelImplUnitTest {
|
||||
|
||||
when(mWindowAndroid.getActivity()).thenReturn(mWeakReferenceActivity);
|
||||
when(mWindowAndroid.getContext()).thenReturn(mWeakReferenceContext);
|
||||
doReturn(new ObservableSupplierImpl<>(false)).when(mWindowAndroid).getOcclusionSupplier();
|
||||
when(mTabGroupModelFilter.getValidPosition(any(), anyInt()))
|
||||
.thenAnswer(i -> i.getArguments()[1]);
|
||||
|
||||
|
@ -320,6 +320,7 @@ public class TabModelSelectorImplTest {
|
||||
WindowAndroid window = mock(WindowAndroid.class);
|
||||
WeakReference<Context> weakContext = new WeakReference<>(mContext);
|
||||
when(window.getContext()).thenReturn(weakContext);
|
||||
doReturn(new ObservableSupplierImpl<>(false)).when(window).getOcclusionSupplier();
|
||||
tab.updateAttachment(window, mTabDelegateFactory);
|
||||
|
||||
Assert.assertEquals(
|
||||
|
@ -86,7 +86,7 @@ public class MerchantTrustBottomSheetCoordinatorTest extends BlankUiTestActivity
|
||||
mActivity = getActivity();
|
||||
ThreadUtils.runOnUiThreadBlocking(
|
||||
() -> {
|
||||
mWindowAndroid = new WindowAndroid(mActivity);
|
||||
mWindowAndroid = new WindowAndroid(mActivity, /* trackOcclusion= */ false);
|
||||
mDetailsTabCoordinator =
|
||||
new MerchantTrustBottomSheetCoordinator(
|
||||
mActivity,
|
||||
|
@ -111,7 +111,7 @@ public class FacilitatedPaymentsPaymentMethodsViewBridgeTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
mApplicationContext = ApplicationProvider.getApplicationContext();
|
||||
mWindow = new WindowAndroid(mApplicationContext);
|
||||
mWindow = new WindowAndroid(mApplicationContext, /* trackOcclusion= */ false);
|
||||
BottomSheetControllerFactory.attach(mWindow, mBottomSheetController);
|
||||
mViewBridge =
|
||||
FacilitatedPaymentsPaymentMethodsViewBridge.create(
|
||||
|
@ -58,7 +58,9 @@ public class AcknowledgeGroupedCredentialSheetModuleTest {
|
||||
public void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
mJniMocker.mock(AcknowledgeGroupedCredentialSheetBridgeJni.TEST_HOOKS, mBridgeJniMock);
|
||||
mWindowAndroid = new WindowAndroid(ContextUtils.getApplicationContext());
|
||||
mWindowAndroid =
|
||||
new WindowAndroid(
|
||||
ContextUtils.getApplicationContext(), /* trackOcclusion= */ false);
|
||||
setUpBottomSheetController();
|
||||
mBridge = new AcknowledgeGroupedCredentialSheetBridge(TEST_NATIVE_POINTER, mWindowAndroid);
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ public class SaveBitmapDelegateTest {
|
||||
private int mPermissionResult = PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
public TestWindowAndroid(Context context) {
|
||||
super(context);
|
||||
super(context, /* trackOcclusion= */ false);
|
||||
}
|
||||
|
||||
public void setPermissionResults(int result) {
|
||||
|
@ -189,7 +189,8 @@ public class AndroidShareSheetControllerUnitTest {
|
||||
mActivity,
|
||||
false,
|
||||
IntentRequestTracker.createFromActivity(mActivity),
|
||||
mInsetObserver);
|
||||
mInsetObserver,
|
||||
/* trackOcclusion= */ false);
|
||||
mPrintCallback = new PayloadCallbackHelper<>();
|
||||
// Set up mock tab
|
||||
doReturn(mWindow).when(mTab).getWindowAndroid();
|
||||
|
@ -77,7 +77,8 @@ public class DefaultBrowserPromoUtilsTest {
|
||||
mActivity,
|
||||
false,
|
||||
IntentRequestTracker.createFromActivity(mActivity),
|
||||
mInsetObserver);
|
||||
mInsetObserver,
|
||||
/* trackOcclusion= */ false);
|
||||
TrackerFactory.setTrackerForTests(mMockTracker);
|
||||
MessagesFactory.attachMessageDispatcher(mWindowAndroid, mMockMessageDispatcher);
|
||||
SearchEngineChoiceService.setInstanceForTests(mMockSearchEngineChoiceService);
|
||||
|
@ -72,7 +72,8 @@ public class SurveyUiDelegateBridgeUnitTest {
|
||||
mActivity,
|
||||
false,
|
||||
IntentRequestTracker.createFromActivity(mActivity),
|
||||
mInsetObserver);
|
||||
mInsetObserver,
|
||||
/* trackOcclusion= */ false);
|
||||
MessagesFactory.attachMessageDispatcher(mWindow, mMockMessageDispatcher);
|
||||
TabModelSelectorSupplier.setInstanceForTesting(mTabModelSelector);
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ public final class StatusMediatorUnitTest {
|
||||
mContext =
|
||||
new ContextThemeWrapper(
|
||||
ContextUtils.getApplicationContext(), R.style.Theme_BrowserUI_DayNight);
|
||||
mWindowAndroid = new WindowAndroid(mContext);
|
||||
mWindowAndroid = new WindowAndroid(mContext, /* trackOcclusion= */ false);
|
||||
|
||||
SearchEngineUtils.setInstanceForTesting(mSearchEngineUtils);
|
||||
|
||||
|
@ -124,7 +124,7 @@ public class VoiceRecognitionHandlerUnitTest {
|
||||
var activity = Robolectric.buildActivity(Activity.class).setup().get();
|
||||
|
||||
mProfileSupplier = new ObservableSupplierImpl<>();
|
||||
mWindowAndroid = spy(new WindowAndroid(activity));
|
||||
mWindowAndroid = spy(new WindowAndroid(activity, /* trackOcclusion= */ false));
|
||||
mHandler = spy(new VoiceRecognitionHandler(mDelegate, mProfileSupplier));
|
||||
mHandler.addObserver(mObserver);
|
||||
|
||||
|
@ -42,7 +42,8 @@ class CastWebContentsScopes {
|
||||
activity,
|
||||
/* listenToActivityState= */ true,
|
||||
IntentRequestTracker.createFromActivity(activity),
|
||||
/* insetObserver= */ null);
|
||||
/* insetObserver= */ null,
|
||||
/* trackOcclusion= */ false);
|
||||
},
|
||||
backgroundColor);
|
||||
}
|
||||
@ -51,24 +52,38 @@ class CastWebContentsScopes {
|
||||
Activity activity, FrameLayout layout, @ColorInt int backgroundColor) {
|
||||
layout.setBackgroundColor(backgroundColor);
|
||||
return onLayoutInternal(
|
||||
activity, layout, () -> new WindowAndroid(activity), backgroundColor);
|
||||
activity,
|
||||
layout,
|
||||
() -> new WindowAndroid(activity, /* trackOcclusion= */ false),
|
||||
backgroundColor);
|
||||
}
|
||||
|
||||
static Observer<WebContents> onLayoutView(Context context, FrameLayout layout,
|
||||
@ColorInt int backgroundColor, WindowTokenProvider windowTokenProvider) {
|
||||
static Observer<WebContents> onLayoutView(
|
||||
Context context,
|
||||
FrameLayout layout,
|
||||
@ColorInt int backgroundColor,
|
||||
WindowTokenProvider windowTokenProvider) {
|
||||
layout.setBackgroundColor(backgroundColor);
|
||||
return onLayoutInternal(context, layout, () -> new WindowAndroid(context) {
|
||||
@Override
|
||||
public IBinder getWindowToken() {
|
||||
return windowTokenProvider.provideWindowToken();
|
||||
}
|
||||
}, backgroundColor);
|
||||
return onLayoutInternal(
|
||||
context,
|
||||
layout,
|
||||
() ->
|
||||
new WindowAndroid(context, /* trackOcclusion= */ false) {
|
||||
@Override
|
||||
public IBinder getWindowToken() {
|
||||
return windowTokenProvider.provideWindowToken();
|
||||
}
|
||||
},
|
||||
backgroundColor);
|
||||
}
|
||||
|
||||
// Note: the |windowFactory| should create a new instance of a WindowAndroid each time it is
|
||||
// invoked.
|
||||
private static Observer<WebContents> onLayoutInternal(Context context, FrameLayout layout,
|
||||
Supplier<WindowAndroid> windowFactory, @ColorInt int backgroundColor) {
|
||||
private static Observer<WebContents> onLayoutInternal(
|
||||
Context context,
|
||||
FrameLayout layout,
|
||||
Supplier<WindowAndroid> windowFactory,
|
||||
@ColorInt int backgroundColor) {
|
||||
return (WebContents webContents) -> {
|
||||
WindowAndroid window = windowFactory.get();
|
||||
ContentViewRenderView contentViewRenderView =
|
||||
@ -116,7 +131,7 @@ class CastWebContentsScopes {
|
||||
|
||||
public static Observer<WebContents> withoutLayout(Context context) {
|
||||
return (WebContents webContents) -> {
|
||||
WindowAndroid window = new WindowAndroid(context);
|
||||
WindowAndroid window = new WindowAndroid(context, /* trackOcclusion= */ false);
|
||||
ContentView contentView = ContentView.createContentView(context, webContents);
|
||||
WebContentsRegistry.initializeWebContents(webContents, contentView, window);
|
||||
// Enable display of current webContents.
|
||||
|
@ -145,7 +145,8 @@ public class ContactsPickerDialogTest
|
||||
mActivity,
|
||||
/* listenToActivityState= */ true,
|
||||
IntentRequestTracker.createFromActivity(mActivity),
|
||||
mInsetObserver);
|
||||
mInsetObserver,
|
||||
/* trackOcclusion= */ false);
|
||||
});
|
||||
mWebContents = Mockito.mock(WebContents.class);
|
||||
when(mWebContents.getTopLevelNativeWindow()).thenReturn(mWindowAndroid);
|
||||
|
@ -139,7 +139,8 @@ public class PhotoPickerDialogTest extends BlankUiTestActivityTestCase
|
||||
getActivity(),
|
||||
/* listenToActivityState= */ true,
|
||||
IntentRequestTracker.createFromActivity(getActivity()),
|
||||
/* insetObserver= */ null);
|
||||
/* insetObserver= */ null,
|
||||
/* trackOcclusion= */ false);
|
||||
});
|
||||
ThreadUtils.runOnUiThreadBlocking(
|
||||
() -> {
|
||||
|
@ -54,9 +54,10 @@ public class ThinWebViewImpl extends FrameLayout implements ThinWebView {
|
||||
context,
|
||||
/* listenToActivityState= */ true,
|
||||
intentRequestTracker,
|
||||
/* insetObserver= */ null);
|
||||
/* insetObserver= */ null,
|
||||
/* trackOcclusion= */ false);
|
||||
} else {
|
||||
mWindowAndroid = new WindowAndroid(context);
|
||||
mWindowAndroid = new WindowAndroid(context, /* trackOcclusion= */ false);
|
||||
}
|
||||
mCompositorView = new CompositorViewImpl(context, mWindowAndroid, constraints);
|
||||
|
||||
|
@ -67,7 +67,8 @@ public abstract class ContentShellBrowserTestActivity extends NativeBrowserTestA
|
||||
this,
|
||||
/* listenToActivityState= */ true,
|
||||
intentRequestTracker,
|
||||
/* insetObserver= */ null);
|
||||
/* insetObserver= */ null,
|
||||
/* trackOcclusion= */ false);
|
||||
mShellManager.setWindow(mWindowAndroid);
|
||||
|
||||
Window wind = this.getWindow();
|
||||
|
@ -68,7 +68,8 @@ public class ContentShellActivity extends Activity {
|
||||
this,
|
||||
listenToActivityState,
|
||||
mIntentRequestTracker,
|
||||
/* insetObserver= */ null);
|
||||
/* insetObserver= */ null,
|
||||
/* trackOcclusion= */ false);
|
||||
mIntentRequestTracker.restoreInstanceState(savedInstanceState);
|
||||
mShellManager.setWindow(mWindowAndroid);
|
||||
// Set up the animation placeholder to be the SurfaceView. This disables the
|
||||
|
@ -38,12 +38,14 @@ public class ActivityWindowAndroid extends WindowAndroid
|
||||
* @param context Context wrapping an activity associated with the WindowAndroid.
|
||||
* @param listenToActivityState Whether to listen to activity state changes.
|
||||
* @param intentRequestTracker The {@link IntentRequestTracker} of the current activity.
|
||||
* @param trackOcclusion Whether to track occlusion of the window.
|
||||
*/
|
||||
public ActivityWindowAndroid(
|
||||
Context context,
|
||||
boolean listenToActivityState,
|
||||
IntentRequestTracker intentRequestTracker,
|
||||
@Nullable InsetObserver insetObserver) {
|
||||
@Nullable InsetObserver insetObserver,
|
||||
boolean trackOcclusion) {
|
||||
this(
|
||||
context,
|
||||
listenToActivityState,
|
||||
@ -52,7 +54,8 @@ public class ActivityWindowAndroid extends WindowAndroid
|
||||
new ActivityKeyboardVisibilityDelegate(
|
||||
new WeakReference<Activity>(ContextUtils.activityFromContext(context))),
|
||||
intentRequestTracker,
|
||||
insetObserver);
|
||||
insetObserver,
|
||||
trackOcclusion);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -62,13 +65,15 @@ public class ActivityWindowAndroid extends WindowAndroid
|
||||
* @param listenToActivityState Whether to listen to activity state changes.
|
||||
* @param keyboardVisibilityDelegate Delegate which handles keyboard visibility.
|
||||
* @param intentRequestTracker The {@link IntentRequestTracker} of the current activity.
|
||||
* @param trackOcclusion Whether to track occlusion of the window.
|
||||
*/
|
||||
public ActivityWindowAndroid(
|
||||
Context context,
|
||||
boolean listenToActivityState,
|
||||
@NonNull ActivityKeyboardVisibilityDelegate keyboardVisibilityDelegate,
|
||||
IntentRequestTracker intentRequestTracker,
|
||||
InsetObserver insetObserver) {
|
||||
InsetObserver insetObserver,
|
||||
boolean trackOcclusion) {
|
||||
this(
|
||||
context,
|
||||
listenToActivityState,
|
||||
@ -76,7 +81,8 @@ public class ActivityWindowAndroid extends WindowAndroid
|
||||
new WeakReference<Activity>(ContextUtils.activityFromContext(context))),
|
||||
keyboardVisibilityDelegate,
|
||||
intentRequestTracker,
|
||||
insetObserver);
|
||||
insetObserver,
|
||||
trackOcclusion);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,6 +92,7 @@ public class ActivityWindowAndroid extends WindowAndroid
|
||||
* @param listenToActivityState Whether to listen to activity state changes.
|
||||
* @param activityAndroidPermissionDelegate Delegates which handles android permissions.
|
||||
* @param intentRequestTracker The {@link IntentRequestTracker} of the current activity.
|
||||
* @param trackOcclusion Whether to track occlusion of the window.
|
||||
*/
|
||||
private ActivityWindowAndroid(
|
||||
Context context,
|
||||
@ -93,8 +100,9 @@ public class ActivityWindowAndroid extends WindowAndroid
|
||||
ActivityAndroidPermissionDelegate activityAndroidPermissionDelegate,
|
||||
ActivityKeyboardVisibilityDelegate activityKeyboardVisibilityDelegate,
|
||||
IntentRequestTracker intentRequestTracker,
|
||||
InsetObserver insetObserver) {
|
||||
super(context, intentRequestTracker, insetObserver);
|
||||
InsetObserver insetObserver,
|
||||
boolean trackOcclusion) {
|
||||
super(context, intentRequestTracker, insetObserver, trackOcclusion);
|
||||
Activity activity = ContextUtils.activityFromContext(context);
|
||||
if (activity == null) {
|
||||
throw new IllegalArgumentException("Context is not and does not wrap an Activity");
|
||||
|
@ -24,6 +24,7 @@ import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.window.TrustedPresentationThresholds;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
@ -43,6 +44,10 @@ import org.chromium.base.ObserverList;
|
||||
import org.chromium.base.PackageManagerUtils;
|
||||
import org.chromium.base.TraceEvent;
|
||||
import org.chromium.base.UnownedUserDataHost;
|
||||
import org.chromium.base.supplier.ObservableSupplier;
|
||||
import org.chromium.base.supplier.ObservableSupplierImpl;
|
||||
import org.chromium.base.task.PostTask;
|
||||
import org.chromium.base.task.TaskTraits;
|
||||
import org.chromium.ui.InsetObserver;
|
||||
import org.chromium.ui.KeyboardVisibilityDelegate;
|
||||
import org.chromium.ui.display.DisplayAndroid;
|
||||
@ -58,10 +63,14 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/** The window base class that has the minimum functionality. */
|
||||
@JNINamespace("ui")
|
||||
public class WindowAndroid implements AndroidPermissionDelegate, DisplayAndroidObserver {
|
||||
public class WindowAndroid
|
||||
implements AndroidPermissionDelegate,
|
||||
DisplayAndroidObserver,
|
||||
View.OnAttachStateChangeListener {
|
||||
private static final String TAG = "WindowAndroid";
|
||||
private static final ImmutableWeakReference<Activity> NULL_ACTIVITY_WEAK_REF =
|
||||
new ImmutableWeakReference<>(null);
|
||||
@ -184,16 +193,28 @@ public class WindowAndroid implements AndroidPermissionDelegate, DisplayAndroidO
|
||||
|
||||
private ModalDialogManager mModalDialogManagerForTesting;
|
||||
|
||||
private Consumer<Boolean> mOcclusionObserver;
|
||||
|
||||
private final boolean mTrackOcclusion;
|
||||
|
||||
/** True when this window is occluded. */
|
||||
private final ObservableSupplierImpl<Boolean> mOcclusionSupplier =
|
||||
new ObservableSupplierImpl<>(false);
|
||||
|
||||
/**
|
||||
* @param context The application {@link Context}.
|
||||
* @param trackOcclusion Whether to track occlusion of the window.
|
||||
*/
|
||||
public WindowAndroid(Context context) {
|
||||
this(context, DisplayAndroid.getNonMultiDisplay(context));
|
||||
public WindowAndroid(Context context, boolean trackOcclusion) {
|
||||
this(context, DisplayAndroid.getNonMultiDisplay(context), trackOcclusion);
|
||||
}
|
||||
|
||||
protected WindowAndroid(
|
||||
Context context, IntentRequestTracker tracker, InsetObserver insetObserver) {
|
||||
this(context, DisplayAndroid.getNonMultiDisplay(context));
|
||||
Context context,
|
||||
IntentRequestTracker tracker,
|
||||
InsetObserver insetObserver,
|
||||
boolean trackOcclusion) {
|
||||
this(context, DisplayAndroid.getNonMultiDisplay(context), trackOcclusion);
|
||||
mIntentRequestTracker = (IntentRequestTrackerImpl) tracker;
|
||||
mInsetObserver = insetObserver;
|
||||
}
|
||||
@ -201,9 +222,10 @@ public class WindowAndroid implements AndroidPermissionDelegate, DisplayAndroidO
|
||||
/**
|
||||
* @param context The application {@link Context}.
|
||||
* @param display The application {@link DisplayAndroid}.
|
||||
* @param trackOcclusion Whether to track occlusion of the window.
|
||||
*/
|
||||
@SuppressLint("UseSparseArrays")
|
||||
protected WindowAndroid(Context context, DisplayAndroid display) {
|
||||
protected WindowAndroid(Context context, DisplayAndroid display, boolean trackOcclusion) {
|
||||
mLifetimeAssert = LifetimeAssert.create(this);
|
||||
// context does not have the same lifetime guarantees as an application context so we can't
|
||||
// hold a strong reference to it.
|
||||
@ -234,6 +256,75 @@ public class WindowAndroid implements AndroidPermissionDelegate, DisplayAndroidO
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S_V2) {
|
||||
mOverlayTransformApiHelper = OverlayTransformApiHelper.create(this);
|
||||
}
|
||||
|
||||
mTrackOcclusion = trackOcclusion;
|
||||
if (mTrackOcclusion) {
|
||||
var decorView = getDecorView();
|
||||
assert decorView != null;
|
||||
|
||||
// If the decor view is already attached to the window the listener won't be called.
|
||||
// In this case, the window token exists so we can register the occlusion observer.
|
||||
if (decorView.isAttachedToWindow()) {
|
||||
maybeRegisterOcclusionObserver(getWindowToken());
|
||||
}
|
||||
decorView.addOnAttachStateChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) {
|
||||
maybeRegisterOcclusionObserver(v.getWindowToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) {
|
||||
maybeUnregisterOcclusionObserver();
|
||||
}
|
||||
|
||||
private void maybeRegisterOcclusionObserver(IBinder windowToken) {
|
||||
if (!mTrackOcclusion || Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
return;
|
||||
}
|
||||
assert mOcclusionObserver == null;
|
||||
|
||||
Context context = getContext().get();
|
||||
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
var thresholds = new TrustedPresentationThresholds(Float.MIN_VALUE, Float.MIN_VALUE, 1);
|
||||
mOcclusionObserver =
|
||||
new Consumer<Boolean>() {
|
||||
@Override
|
||||
public void accept(Boolean visible) {
|
||||
mOcclusionSupplier.set(!visible);
|
||||
}
|
||||
};
|
||||
|
||||
assert windowToken != null;
|
||||
wm.registerTrustedPresentationListener(
|
||||
windowToken,
|
||||
thresholds,
|
||||
(r) -> {
|
||||
PostTask.postTask(TaskTraits.UI_DEFAULT, r);
|
||||
},
|
||||
mOcclusionObserver);
|
||||
}
|
||||
|
||||
private void maybeUnregisterOcclusionObserver() {
|
||||
if (!mTrackOcclusion || Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
return;
|
||||
}
|
||||
assert mOcclusionObserver != null;
|
||||
|
||||
Context context = getContext().get();
|
||||
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
wm.unregisterTrustedPresentationListener(mOcclusionObserver);
|
||||
|
||||
mOcclusionObserver = null;
|
||||
}
|
||||
|
||||
/** A supplier that returns whether the window is occluded or not. */
|
||||
public ObservableSupplier<Boolean> getOcclusionSupplier() {
|
||||
return mOcclusionSupplier;
|
||||
}
|
||||
|
||||
private static boolean isTv(Context context) {
|
||||
@ -245,7 +336,9 @@ public class WindowAndroid implements AndroidPermissionDelegate, DisplayAndroidO
|
||||
|
||||
@CalledByNativeForTesting
|
||||
private static long createForTesting() {
|
||||
WindowAndroid windowAndroid = new WindowAndroid(ContextUtils.getApplicationContext());
|
||||
WindowAndroid windowAndroid =
|
||||
new WindowAndroid(
|
||||
ContextUtils.getApplicationContext(), /* trackOcclusion= */ false);
|
||||
// |windowAndroid.getNativePointer()| creates native WindowAndroid object
|
||||
// which stores a global ref to |windowAndroid|. Therefore |windowAndroid|
|
||||
// is not immediately eligible for gc.
|
||||
@ -873,6 +966,13 @@ public class WindowAndroid implements AndroidPermissionDelegate, DisplayAndroidO
|
||||
return mContextRef;
|
||||
}
|
||||
|
||||
/** Return the decor view, or null. */
|
||||
private View getDecorView() {
|
||||
Window window = getWindow();
|
||||
if (window == null) return null;
|
||||
return window.getDecorView();
|
||||
}
|
||||
|
||||
/** Return the current window token, or null. */
|
||||
public IBinder getWindowToken() {
|
||||
Window window = getWindow();
|
||||
|
Reference in New Issue
Block a user