Commit 0bf3d71d authored by Peter E Conn's avatar Peter E Conn Committed by Chromium LUCI CQ

🍱 Remove ChromeActivity dependence from PictureInPictureController.

Change-Id: I4bbba28ef64ee3c1b989f43a0878d3f14bd33dfe
Bug: 1155373
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2580883Reviewed-by: default avatarBrandon Wylie <wylieb@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Commit-Queue: Peter Conn <peconn@chromium.org>
Cr-Commit-Position: refs/heads/master@{#835629}
parent a9b0a2bc
...@@ -213,9 +213,6 @@ specific_include_rules = { ...@@ -213,9 +213,6 @@ specific_include_rules = {
"LanguageAskPrompt\.java": [ "LanguageAskPrompt\.java": [
"+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java", "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
], ],
"PictureInPictureController\.java": [
"+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
],
"ChromeTabModalPresenter\.java": [ "ChromeTabModalPresenter\.java": [
"+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java", "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
], ],
......
...@@ -939,7 +939,7 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent> ...@@ -939,7 +939,7 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
MultiWindowUtils.getInstance().isInMultiWindowMode(this)); MultiWindowUtils.getInstance().isInMultiWindowMode(this));
if (mPictureInPictureController != null) { if (mPictureInPictureController != null) {
mPictureInPictureController.cleanup(this); mPictureInPictureController.cleanup();
} }
VrModuleProvider.getDelegate().maybeRegisterVrEntryHook(this); VrModuleProvider.getDelegate().maybeRegisterVrEntryHook(this);
...@@ -954,9 +954,11 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent> ...@@ -954,9 +954,11 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
if (isActivityFinishingOrDestroyed()) return; if (isActivityFinishingOrDestroyed()) return;
if (mPictureInPictureController == null) { if (mPictureInPictureController == null) {
mPictureInPictureController = new PictureInPictureController(); mPictureInPictureController = new PictureInPictureController(
this, getActivityTabProvider(), getFullscreenManager());
} }
mPictureInPictureController.attemptPictureInPicture(this);
mPictureInPictureController.attemptPictureInPicture();
} }
@Override @Override
...@@ -1000,7 +1002,7 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent> ...@@ -1000,7 +1002,7 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
@Override @Override
public void onNewIntentWithNative(Intent intent) { public void onNewIntentWithNative(Intent intent) {
if (mPictureInPictureController != null) { if (mPictureInPictureController != null) {
mPictureInPictureController.cleanup(this); mPictureInPictureController.cleanup();
} }
super.onNewIntentWithNative(intent); super.onNewIntentWithNative(intent);
......
...@@ -7,7 +7,6 @@ package org.chromium.chrome.browser.media; ...@@ -7,7 +7,6 @@ package org.chromium.chrome.browser.media;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.app.PictureInPictureParams; import android.app.PictureInPictureParams;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Build; import android.os.Build;
...@@ -17,26 +16,21 @@ import android.util.Rational; ...@@ -17,26 +16,21 @@ import android.util.Rational;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.chromium.base.Callback;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.base.MathUtils; import org.chromium.base.MathUtils;
import org.chromium.base.annotations.VerifiesOnO; import org.chromium.base.annotations.VerifiesOnO;
import org.chromium.base.library_loader.LibraryLoader; import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.app.ChromeActivity; import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.fullscreen.FullscreenManager; import org.chromium.chrome.browser.fullscreen.FullscreenManager;
import org.chromium.chrome.browser.infobar.InfoBarContainer; import org.chromium.chrome.browser.infobar.InfoBarContainer;
import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabObserver; import org.chromium.chrome.browser.tab.TabObserver;
import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsObserver; import org.chromium.content_public.browser.WebContentsObserver;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
...@@ -57,12 +51,12 @@ public class PictureInPictureController { ...@@ -57,12 +51,12 @@ public class PictureInPictureController {
private static final int METRICS_ATTEMPT_RESULT_SUCCESS = 0; private static final int METRICS_ATTEMPT_RESULT_SUCCESS = 0;
private static final int METRICS_ATTEMPT_RESULT_NO_SYSTEM_SUPPORT = 1; private static final int METRICS_ATTEMPT_RESULT_NO_SYSTEM_SUPPORT = 1;
private static final int METRICS_ATTEMPT_RESULT_NO_FEATURE = 2; private static final int METRICS_ATTEMPT_RESULT_NO_FEATURE = 2;
private static final int METRICS_ATTEMPT_RESULT_NO_ACTIVITY_SUPPORT = 3; // Obsolete: private static final int METRICS_ATTEMPT_RESULT_NO_ACTIVITY_SUPPORT = 3;
private static final int METRICS_ATTEMPT_RESULT_ALREADY_RUNNING = 4; private static final int METRICS_ATTEMPT_RESULT_ALREADY_RUNNING = 4;
private static final int METRICS_ATTEMPT_RESULT_RESTARTING = 5; private static final int METRICS_ATTEMPT_RESULT_RESTARTING = 5;
private static final int METRICS_ATTEMPT_RESULT_FINISHING = 6; private static final int METRICS_ATTEMPT_RESULT_FINISHING = 6;
private static final int METRICS_ATTEMPT_RESULT_NO_WEBCONTENTS = 7; private static final int METRICS_ATTEMPT_RESULT_NO_WEB_CONTENTS = 7;
// private static final int METRICS_ATTEMPT_RESULT_NO_VIDEO = 8; // Obsolete: private static final int METRICS_ATTEMPT_RESULT_NO_VIDEO = 8;
private static final int METRICS_ATTEMPT_RESULT_COUNT = 9; private static final int METRICS_ATTEMPT_RESULT_COUNT = 9;
private static final String METRICS_END_REASON = "Media.VideoPersistence.EndReason"; private static final String METRICS_END_REASON = "Media.VideoPersistence.EndReason";
...@@ -80,18 +74,26 @@ public class PictureInPictureController { ...@@ -80,18 +74,26 @@ public class PictureInPictureController {
private static final float MAX_ASPECT_RATIO = 2.39f; private static final float MAX_ASPECT_RATIO = 2.39f;
/** Callbacks to cleanup after leaving PiP. */ /** Callbacks to cleanup after leaving PiP. */
private List<Callback<ChromeActivity>> mOnLeavePipCallbacks = new LinkedList<>(); private final List<Runnable> mOnLeavePipCallbacks = new LinkedList<>();
private final Activity mActivity;
private final ActivityTabProvider mActivityTabProvider;
private final FullscreenManager mFullscreenManager;
public PictureInPictureController(Activity activity, ActivityTabProvider activityTabProvider,
FullscreenManager fullscreenManager) {
mActivity = activity;
mActivityTabProvider = activityTabProvider;
mFullscreenManager = fullscreenManager;
}
/** /**
* Convenience method to get the {@link WebContents} from the active Tab of a * Convenience method to get the {@link WebContents} from the active Tab.
* {@link ChromeActivity}.
*/ */
@Nullable @Nullable
private static WebContents getWebContents(ChromeActivity activity) { private WebContents getWebContents() {
if (!activity.areTabModelsInitialized()) return null; Tab tab = mActivityTabProvider.get();
assert activity.getTabModelSelector() != null; if (tab == null) return null;
if (activity.getActivityTab() == null) return null; return tab.getWebContents();
return activity.getActivityTab().getWebContents();
} }
private static void recordAttemptResult(int result) { private static void recordAttemptResult(int result) {
...@@ -99,9 +101,12 @@ public class PictureInPictureController { ...@@ -99,9 +101,12 @@ public class PictureInPictureController {
METRICS_ATTEMPT_RESULT, result, METRICS_ATTEMPT_RESULT_COUNT); METRICS_ATTEMPT_RESULT, result, METRICS_ATTEMPT_RESULT_COUNT);
} }
private boolean shouldAttempt(ChromeActivity activity) { private boolean shouldAttempt() {
WebContents webContents = getWebContents(activity); WebContents webContents = getWebContents();
if (webContents == null) return false; if (webContents == null) {
recordAttemptResult(METRICS_ATTEMPT_RESULT_NO_WEB_CONTENTS);
return false;
}
// Non-null WebContents implies the native library has been loaded. // Non-null WebContents implies the native library has been loaded.
assert LibraryLoader.getInstance().isInitialized(); assert LibraryLoader.getInstance().isInitialized();
...@@ -117,47 +122,29 @@ public class PictureInPictureController { ...@@ -117,47 +122,29 @@ public class PictureInPictureController {
return false; return false;
} }
if (!activity.getPackageManager().hasSystemFeature( if (!mActivity.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_PICTURE_IN_PICTURE)) { PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
Log.d(TAG, "Activity does not have PiP feature."); Log.d(TAG, "Activity does not have PiP feature.");
recordAttemptResult(METRICS_ATTEMPT_RESULT_NO_FEATURE); recordAttemptResult(METRICS_ATTEMPT_RESULT_NO_FEATURE);
return false; return false;
} }
// TODO(peconn): Clean this up when we build with the O SDK.
try {
ActivityInfo info =
activity.getPackageManager().getActivityInfo(activity.getComponentName(), 0);
Boolean supports =
(Boolean) info.getClass().getMethod("supportsPictureInPicture").invoke(info);
if (!supports) {
Log.d(TAG, "Activity does not support PiP.");
recordAttemptResult(METRICS_ATTEMPT_RESULT_NO_ACTIVITY_SUPPORT);
return false;
}
} catch (PackageManager.NameNotFoundException | IllegalAccessException
| NoSuchMethodException | InvocationTargetException e) {
Log.d(TAG, "Exception checking whether Activity supports PiP.");
recordAttemptResult(METRICS_ATTEMPT_RESULT_NO_ACTIVITY_SUPPORT);
return false;
}
// Don't PiP if we are already in PiP. // Don't PiP if we are already in PiP.
if (activity.isInPictureInPictureMode()) { if (mActivity.isInPictureInPictureMode()) {
Log.d(TAG, "Activity is already in PiP."); Log.d(TAG, "Activity is already in PiP.");
recordAttemptResult(METRICS_ATTEMPT_RESULT_ALREADY_RUNNING); recordAttemptResult(METRICS_ATTEMPT_RESULT_ALREADY_RUNNING);
return false; return false;
} }
// This means the activity is going to be restarted, so don't PiP. // This means the activity is going to be restarted, so don't PiP.
if (activity.isChangingConfigurations()) { if (mActivity.isChangingConfigurations()) {
Log.d(TAG, "Activity is being restarted."); Log.d(TAG, "Activity is being restarted.");
recordAttemptResult(METRICS_ATTEMPT_RESULT_RESTARTING); recordAttemptResult(METRICS_ATTEMPT_RESULT_RESTARTING);
return false; return false;
} }
// Don't PiP if the activity is finishing. // Don't PiP if the activity is finishing.
if (activity.isFinishing()) { if (mActivity.isFinishing()) {
Log.d(TAG, "Activity is finishing."); Log.d(TAG, "Activity is finishing.");
recordAttemptResult(METRICS_ATTEMPT_RESULT_FINISHING); recordAttemptResult(METRICS_ATTEMPT_RESULT_FINISHING);
return false; return false;
...@@ -170,14 +157,14 @@ public class PictureInPictureController { ...@@ -170,14 +157,14 @@ public class PictureInPictureController {
/** /**
* Attempt to enter Picture in Picture mode if there is fullscreen video. * Attempt to enter Picture in Picture mode if there is fullscreen video.
*/ */
public void attemptPictureInPicture(final ChromeActivity activity) { public void attemptPictureInPicture() {
if (!shouldAttempt(activity)) return; if (!shouldAttempt()) return;
// Inform the WebContents when we enter and when we leave PiP. // Inform the WebContents when we enter and when we leave PiP.
final WebContents webContents = getWebContents(activity); final WebContents webContents = getWebContents();
assert webContents != null; assert webContents != null;
Rect bounds = getVideoBounds(webContents, activity); Rect bounds = getVideoBounds(webContents, mActivity);
PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder(); PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();
if (bounds != null) { if (bounds != null) {
builder.setAspectRatio(new Rational(bounds.width(), bounds.height())); builder.setAspectRatio(new Rational(bounds.width(), bounds.height()));
...@@ -185,7 +172,7 @@ public class PictureInPictureController { ...@@ -185,7 +172,7 @@ public class PictureInPictureController {
} }
try { try {
if (!activity.enterPictureInPictureMode(builder.build())) return; if (!mActivity.enterPictureInPictureMode(builder.build())) return;
} catch (IllegalStateException | IllegalArgumentException e) { } catch (IllegalStateException | IllegalArgumentException e) {
Log.e(TAG, "Error entering PiP with bounds (%d, %d): %s", Log.e(TAG, "Error entering PiP with bounds (%d, %d): %s",
bounds.width(), bounds.height(), e); bounds.width(), bounds.height(), e);
...@@ -194,57 +181,46 @@ public class PictureInPictureController { ...@@ -194,57 +181,46 @@ public class PictureInPictureController {
webContents.setHasPersistentVideo(true); webContents.setHasPersistentVideo(true);
// We don't want InfoBars displaying while in PiP, they cover too much content.
InfoBarContainer.get(activity.getActivityTab()).setHidden(true);
// Setup observers to dismiss the Activity on events that should end PiP. // Setup observers to dismiss the Activity on events that should end PiP.
final Tab activityTab = activity.getActivityTab(); final Tab activityTab = mActivityTabProvider.get();
mOnLeavePipCallbacks.add(new Callback<ChromeActivity>() { // We don't want InfoBars displaying while in PiP, they cover too much content.
@Override InfoBarContainer.get(activityTab).setHidden(true);
public void onResult(ChromeActivity activity2) {
webContents.setHasPersistentVideo(false); mOnLeavePipCallbacks.add(() -> {
InfoBarContainer.get(activityTab).setHidden(false); webContents.setHasPersistentVideo(false);
} InfoBarContainer.get(activityTab).setHidden(false);
}); });
final TabObserver tabObserver = new DismissActivityOnTabEventObserver(activity); TabObserver tabObserver = new DismissActivityOnTabEventObserver(mActivity);
final TabModelSelectorObserver tabModelSelectorObserver = ActivityTabProvider.ActivityTabObserver activityTabObserver =
new DismissActivityOnTabModelSelectorEventObserver(activity); new DismissActivityOnTabChangeObserver(mActivity, activityTab);
final WebContentsObserver webContentsObserver = WebContentsObserver webContentsObserver =
new DismissActivityOnWebContentsObserver(activity); new DismissActivityOnWebContentsObserver(mActivity);
final TabModelSelector tabModelSelector = activity.getTabModelSelector(); FullscreenManager.Observer fullscreenListener = new FullscreenManager.Observer() {
final FullscreenManager.Observer fullscreenListener = new FullscreenManager.Observer() {
@Override @Override
public void onExitFullscreen(Tab tab) { public void onExitFullscreen(Tab tab) {
dismissActivity(activity, METRICS_END_REASON_LEFT_FULLSCREEN); dismissActivity(mActivity, METRICS_END_REASON_LEFT_FULLSCREEN);
} }
}; };
FullscreenManager fullscreenManager = activity.getFullscreenManager();
fullscreenManager.addObserver(fullscreenListener);
activityTab.addObserver(tabObserver); activityTab.addObserver(tabObserver);
tabModelSelector.addObserver(tabModelSelectorObserver);
webContents.addObserver(webContentsObserver); webContents.addObserver(webContentsObserver);
mFullscreenManager.addObserver(fullscreenListener);
mOnLeavePipCallbacks.add(new Callback<ChromeActivity>() { mActivityTabProvider.addObserverAndTrigger(activityTabObserver);
@Override
public void onResult(ChromeActivity activity2) { mOnLeavePipCallbacks.add(() -> {
activityTab.removeObserver(tabObserver); activityTab.removeObserver(tabObserver);
tabModelSelector.removeObserver(tabModelSelectorObserver); webContents.removeObserver(webContentsObserver);
webContents.removeObserver(webContentsObserver); mFullscreenManager.removeObserver(fullscreenListener);
fullscreenManager.removeObserver(fullscreenListener); mActivityTabProvider.removeObserver(activityTabObserver);
}
}); });
final long startTimeMs = SystemClock.elapsedRealtime(); long startTimeMs = SystemClock.elapsedRealtime();
mOnLeavePipCallbacks.add(new Callback<ChromeActivity>() { mOnLeavePipCallbacks.add(() -> {
@Override long pipTimeMs = SystemClock.elapsedRealtime() - startTimeMs;
public void onResult(ChromeActivity activity2) { RecordHistogram.recordCustomTimesHistogram(METRICS_DURATION, pipTimeMs,
long pipTimeMs = SystemClock.elapsedRealtime() - startTimeMs; DateUtils.SECOND_IN_MILLIS * 7, DateUtils.HOUR_IN_MILLIS * 10, 50);
RecordHistogram.recordCustomTimesHistogram(METRICS_DURATION, pipTimeMs,
DateUtils.SECOND_IN_MILLIS * 7, DateUtils.HOUR_IN_MILLIS * 10, 50);
}
}); });
} }
...@@ -289,11 +265,11 @@ public class PictureInPictureController { ...@@ -289,11 +265,11 @@ public class PictureInPictureController {
/** /**
* If we have previously entered Picture in Picture, perform cleanup. * If we have previously entered Picture in Picture, perform cleanup.
*/ */
public void cleanup(ChromeActivity activity) { public void cleanup() {
cleanup(activity, METRICS_END_REASON_RESUME); cleanup(METRICS_END_REASON_RESUME);
} }
private void cleanup(ChromeActivity activity, int reason) { private void cleanup(int reason) {
// If `mOnLeavePipCallbacks` is empty, it means that the cleanup call happened while Chrome // If `mOnLeavePipCallbacks` is empty, it means that the cleanup call happened while Chrome
// was not PIP'ing. The early return also avoid recording the reason why the PIP session // was not PIP'ing. The early return also avoid recording the reason why the PIP session
// ended. // ended.
...@@ -301,8 +277,8 @@ public class PictureInPictureController { ...@@ -301,8 +277,8 @@ public class PictureInPictureController {
// This method can be called when we haven't been PiPed. We use Callbacks to ensure we only // This method can be called when we haven't been PiPed. We use Callbacks to ensure we only
// do cleanup if it is required. // do cleanup if it is required.
for (Callback<ChromeActivity> callback : mOnLeavePipCallbacks) { for (Runnable callback : mOnLeavePipCallbacks) {
callback.onResult(activity); callback.run();
} }
mOnLeavePipCallbacks.clear(); mOnLeavePipCallbacks.clear();
...@@ -311,21 +287,21 @@ public class PictureInPictureController { ...@@ -311,21 +287,21 @@ public class PictureInPictureController {
} }
/** Moves the Activity to the back and performs all cleanup. */ /** Moves the Activity to the back and performs all cleanup. */
private void dismissActivity(ChromeActivity activity, int reason) { private void dismissActivity(Activity activity, int reason) {
activity.moveTaskToBack(true); activity.moveTaskToBack(true);
cleanup(activity, reason); cleanup(reason);
} }
/** /**
* A class to dismiss the Activity when the tab: * A class to dismiss the Activity when the tab:
* - Closes. * - Closes.
* - Reparents: Attaches to a different activity. * - Re-parents (attaches to a different activity).
* - Crashes. * - Crashes.
* - Leaves fullscreen. * - Leaves fullscreen.
*/ */
private class DismissActivityOnTabEventObserver extends EmptyTabObserver { private class DismissActivityOnTabEventObserver extends EmptyTabObserver {
private final ChromeActivity mActivity; private final Activity mActivity;
public DismissActivityOnTabEventObserver(ChromeActivity activity) { public DismissActivityOnTabEventObserver(Activity activity) {
mActivity = activity; mActivity = activity;
} }
...@@ -350,21 +326,21 @@ public class PictureInPictureController { ...@@ -350,21 +326,21 @@ public class PictureInPictureController {
} }
} }
/** A class to dismiss the Activity when a new tab is created. */ /** A class to dismiss the Activity when the tab changes. */
private class DismissActivityOnTabModelSelectorEventObserver private class DismissActivityOnTabChangeObserver
extends EmptyTabModelSelectorObserver { extends ActivityTabProvider.HintlessActivityTabObserver {
private final ChromeActivity mActivity; private final Activity mActivity;
private final Tab mTab; private final Tab mCurrentTab;
public DismissActivityOnTabModelSelectorEventObserver(ChromeActivity activity) {
private DismissActivityOnTabChangeObserver(Activity activity, Tab currentTab) {
mActivity = activity; mActivity = activity;
mTab = activity.getActivityTab(); mCurrentTab = currentTab;
} }
@Override @Override
public void onChange() { public void onActivityTabChanged(Tab tab) {
if (mActivity.getActivityTab() != mTab) { if (mCurrentTab == tab) return;
dismissActivity(mActivity, METRICS_END_REASON_NEW_TAB); dismissActivity(mActivity, METRICS_END_REASON_NEW_TAB);
}
} }
} }
...@@ -374,9 +350,9 @@ public class PictureInPictureController { ...@@ -374,9 +350,9 @@ public class PictureInPictureController {
* if an iframe without the fullscreen video navigates. * if an iframe without the fullscreen video navigates.
*/ */
private class DismissActivityOnWebContentsObserver extends WebContentsObserver { private class DismissActivityOnWebContentsObserver extends WebContentsObserver {
private final ChromeActivity mActivity; private final Activity mActivity;
public DismissActivityOnWebContentsObserver(ChromeActivity activity) { public DismissActivityOnWebContentsObserver(Activity activity) {
mActivity = activity; mActivity = activity;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment