Commit e6778682 authored by Jazz Xu's avatar Jazz Xu Committed by Commit Bot

[PiP Android API] Add WindowAndroid observer and Tab observer.

This CL includes:
1. Make OverlayWindowAndroid an WindowAndroid observer so that it
can observe compositor state.
2. Add a Tab observer to PictureInPictureActivity so that when the
tab that initiate PiP is destroyed or Chrome instance is closed,
PictureInPictureActivity is notified and close properly.

Bug: 964378
Change-Id: Iaab2ee5b1be3acf14d7380588aae63b0d666ec6e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1717237Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Commit-Queue: Jazz Xu <jazzhsu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#683246}
parent 7df42c3a
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package org.chromium.chrome.browser.media; package org.chromium.chrome.browser.media;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
...@@ -11,6 +13,10 @@ import org.chromium.base.ContextUtils; ...@@ -11,6 +13,10 @@ import org.chromium.base.ContextUtils;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.NativeMethods; import org.chromium.base.annotations.NativeMethods;
import org.chromium.chrome.browser.init.AsyncInitializationActivity; import org.chromium.chrome.browser.init.AsyncInitializationActivity;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.ui.base.ActivityWindowAndroid;
import org.chromium.ui.base.WindowAndroid;
/** /**
* A picture in picture activity which get created when requesting * A picture in picture activity which get created when requesting
...@@ -19,6 +25,41 @@ import org.chromium.chrome.browser.init.AsyncInitializationActivity; ...@@ -19,6 +25,41 @@ import org.chromium.chrome.browser.init.AsyncInitializationActivity;
*/ */
public class PictureInPictureActivity extends AsyncInitializationActivity { public class PictureInPictureActivity extends AsyncInitializationActivity {
private static long sNativeOverlayWindowAndroid; private static long sNativeOverlayWindowAndroid;
private static Tab sInitiatorTab;
private static int sInitiatorTabTaskID;
private static InitiatorTabObserver sTabObserver;
private static class InitiatorTabObserver extends EmptyTabObserver {
private enum Status { OK, DESTROYED }
private PictureInPictureActivity mActivity;
private Status mStatus;
InitiatorTabObserver() {
mStatus = Status.OK;
}
public void setActivity(PictureInPictureActivity activity) {
mActivity = activity;
}
public Status getStatus() {
return mStatus;
}
@Override
public void onDestroyed(Tab tab) {
if (tab.isClosing() || !isInitiatorTabAlive()) {
mStatus = Status.DESTROYED;
if (mActivity != null) mActivity.finish();
}
}
@Override
public void onCrash(Tab tab) {
mStatus = Status.DESTROYED;
if (mActivity != null) mActivity.finish();
}
}
@Override @Override
protected void triggerLayoutInflation() { protected void triggerLayoutInflation() {
...@@ -35,41 +76,66 @@ public class PictureInPictureActivity extends AsyncInitializationActivity { ...@@ -35,41 +76,66 @@ public class PictureInPictureActivity extends AsyncInitializationActivity {
super.onStart(); super.onStart();
enterPictureInPictureMode(); enterPictureInPictureMode();
// Finish the activity if OverlayWindowAndroid has already been destroyed. // Finish the activity if OverlayWindowAndroid has already been destroyed
if (sNativeOverlayWindowAndroid == 0) { // or InitiatorTab has been destroyed by user or crashed.
if (sNativeOverlayWindowAndroid == 0
|| sTabObserver.getStatus() == InitiatorTabObserver.Status.DESTROYED) {
this.finish(); this.finish();
return; return;
} }
PictureInPictureActivityJni.get().onActivityStart(sNativeOverlayWindowAndroid, this); sTabObserver.setActivity(this);
PictureInPictureActivityJni.get().onActivityStart(
sNativeOverlayWindowAndroid, this, getWindowAndroid());
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
sNativeOverlayWindowAndroid = 0;
sInitiatorTab.removeObserver(sTabObserver);
sInitiatorTab = null;
sTabObserver = null;
}
if (sNativeOverlayWindowAndroid == 0) return; @Override
protected ActivityWindowAndroid createWindowAndroid() {
return new ActivityWindowAndroid(this);
}
PictureInPictureActivityJni.get().onActivityDestroy(sNativeOverlayWindowAndroid); @SuppressLint("NewApi")
sNativeOverlayWindowAndroid = 0; private static boolean isInitiatorTabAlive() {
ActivityManager activityManager =
(ActivityManager) ContextUtils.getApplicationContext().getSystemService(
Context.ACTIVITY_SERVICE);
for (ActivityManager.AppTask appTask : activityManager.getAppTasks()) {
if (appTask.getTaskInfo().id == sInitiatorTabTaskID) return true;
}
return false;
} }
@CalledByNative @CalledByNative
private void close() { private void close() {
sNativeOverlayWindowAndroid = 0;
this.finish(); this.finish();
} }
@CalledByNative @CalledByNative
private static void createActivity(long nativeOverlayWindowAndroid) { private static void createActivity(long nativeOverlayWindowAndroid, Object initiatorTab) {
Context context = ContextUtils.getApplicationContext(); Context context = ContextUtils.getApplicationContext();
Intent intent = new Intent(context, PictureInPictureActivity.class); Intent intent = new Intent(context, PictureInPictureActivity.class);
// Dissociate OverlayWindowAndroid if there is one already. // Dissociate OverlayWindowAndroid if there is one already.
if (sNativeOverlayWindowAndroid != 0) if (sNativeOverlayWindowAndroid != 0)
PictureInPictureActivityJni.get().onActivityDestroy(sNativeOverlayWindowAndroid); PictureInPictureActivityJni.get().destroy(sNativeOverlayWindowAndroid);
sNativeOverlayWindowAndroid = nativeOverlayWindowAndroid; sNativeOverlayWindowAndroid = nativeOverlayWindowAndroid;
sInitiatorTab = (Tab) initiatorTab;
sInitiatorTabTaskID = sInitiatorTab.getActivity().getTaskId();
sTabObserver = new InitiatorTabObserver();
sInitiatorTab.addObserver(sTabObserver);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);
...@@ -84,8 +150,9 @@ public class PictureInPictureActivity extends AsyncInitializationActivity { ...@@ -84,8 +150,9 @@ public class PictureInPictureActivity extends AsyncInitializationActivity {
@NativeMethods @NativeMethods
interface Natives { interface Natives {
void onActivityStart(long nativeOverlayWindowAndroid, PictureInPictureActivity self); void onActivityStart(long nativeOverlayWindowAndroid, PictureInPictureActivity self,
WindowAndroid window);
void onActivityDestroy(long nativeOverlayWindowAndroid); void destroy(long nativeOverlayWindowAndroid);
} }
} }
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/android/jni_android.h" #include "base/android/jni_android.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "chrome/android/chrome_jni_headers/PictureInPictureActivity_jni.h" #include "chrome/android/chrome_jni_headers/PictureInPictureActivity_jni.h"
#include "chrome/browser/android/tab_android.h"
#include "content/public/browser/overlay_window.h" #include "content/public/browser/overlay_window.h"
#include "content/public/browser/picture_in_picture_window_controller.h" #include "content/public/browser/picture_in_picture_window_controller.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
...@@ -21,8 +22,10 @@ OverlayWindowAndroid::OverlayWindowAndroid( ...@@ -21,8 +22,10 @@ OverlayWindowAndroid::OverlayWindowAndroid(
content::PictureInPictureWindowController* controller) content::PictureInPictureWindowController* controller)
: controller_(controller) { : controller_(controller) {
JNIEnv* env = base::android::AttachCurrentThread(); JNIEnv* env = base::android::AttachCurrentThread();
Java_PictureInPictureActivity_createActivity(env, Java_PictureInPictureActivity_createActivity(
reinterpret_cast<long>(this)); env, reinterpret_cast<long>(this),
TabAndroid::FromWebContents(controller_->GetInitiatorWebContents())
->GetJavaObject());
} }
OverlayWindowAndroid::~OverlayWindowAndroid() { OverlayWindowAndroid::~OverlayWindowAndroid() {
...@@ -33,12 +36,25 @@ OverlayWindowAndroid::~OverlayWindowAndroid() { ...@@ -33,12 +36,25 @@ OverlayWindowAndroid::~OverlayWindowAndroid() {
void OverlayWindowAndroid::OnActivityStart( void OverlayWindowAndroid::OnActivityStart(
JNIEnv* env, JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj) { const base::android::JavaParamRef<jobject>& obj,
const base::android::JavaParamRef<jobject>& jwindow_android) {
java_ref_ = JavaObjectWeakGlobalRef(env, obj); java_ref_ = JavaObjectWeakGlobalRef(env, obj);
window_android_ = ui::WindowAndroid::FromJavaWindowAndroid(jwindow_android);
window_android_->AddObserver(this);
} }
void OverlayWindowAndroid::OnActivityDestroy(JNIEnv* env) { void OverlayWindowAndroid::OnActivityStopped() {
Destroy(nullptr);
}
void OverlayWindowAndroid::Destroy(JNIEnv* env) {
java_ref_.reset(); java_ref_.reset();
if (window_android_) {
window_android_->RemoveObserver(this);
window_android_ = nullptr;
}
controller_->CloseAndFocusInitiator(); controller_->CloseAndFocusInitiator();
controller_->OnWindowDestroyed(); controller_->OnWindowDestroyed();
} }
...@@ -47,6 +63,9 @@ void OverlayWindowAndroid::Close() { ...@@ -47,6 +63,9 @@ void OverlayWindowAndroid::Close() {
if (java_ref_.is_uninitialized()) if (java_ref_.is_uninitialized())
return; return;
DCHECK(window_android_);
window_android_->RemoveObserver(this);
window_android_ = nullptr;
JNIEnv* env = base::android::AttachCurrentThread(); JNIEnv* env = base::android::AttachCurrentThread();
Java_PictureInPictureActivity_close(env, java_ref_.get(env)); Java_PictureInPictureActivity_close(env, java_ref_.get(env));
controller_->OnWindowDestroyed(); controller_->OnWindowDestroyed();
......
...@@ -8,26 +8,42 @@ ...@@ -8,26 +8,42 @@
#include "base/android/jni_weak_ref.h" #include "base/android/jni_weak_ref.h"
#include "base/android/scoped_java_ref.h" #include "base/android/scoped_java_ref.h"
#include "content/public/browser/overlay_window.h" #include "content/public/browser/overlay_window.h"
#include "ui/android/window_android.h"
#include "ui/android/window_android_observer.h"
#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
class OverlayWindowAndroid : public content::OverlayWindow { class OverlayWindowAndroid : public content::OverlayWindow,
public ui::WindowAndroidObserver {
public: public:
explicit OverlayWindowAndroid( explicit OverlayWindowAndroid(
content::PictureInPictureWindowController* controller); content::PictureInPictureWindowController* controller);
~OverlayWindowAndroid() override; ~OverlayWindowAndroid() override;
void OnActivityStart(JNIEnv* env, void OnActivityStart(
const base::android::JavaParamRef<jobject>& obj); JNIEnv* env,
void OnActivityDestroy(JNIEnv* env); const base::android::JavaParamRef<jobject>& obj,
const base::android::JavaParamRef<jobject>& jwindow_android);
void Destroy(JNIEnv* env);
// ui::WindowAndroidObserver implementation.
void OnCompositingDidCommit() override {}
void OnRootWindowVisibilityChanged(bool visible) override {}
void OnAttachCompositor() override {}
void OnDetachCompositor() override {}
void OnAnimate(base::TimeTicks frame_begin_time) override {}
void OnActivityStopped() override;
void OnActivityStarted() override {}
void OnCursorVisibilityChanged(bool visible) override {}
void OnFallbackCursorModeToggled(bool is_on) override {}
// OverlayWindow implementation.
bool IsActive() override; bool IsActive() override;
void Close() override; void Close() override;
void ShowInactive() override {} void ShowInactive() override {}
void Hide() override; void Hide() override;
bool IsVisible() override; bool IsVisible() override;
bool IsAlwaysOnTop() override; bool IsAlwaysOnTop() override;
// Retrieves the window's current bounds, including its window.
gfx::Rect GetBounds() override; gfx::Rect GetBounds() override;
void UpdateVideoSize(const gfx::Size& natural_size) override {} void UpdateVideoSize(const gfx::Size& natural_size) override {}
void SetPlaybackState(PlaybackState playback_state) override {} void SetPlaybackState(PlaybackState playback_state) override {}
...@@ -42,6 +58,7 @@ class OverlayWindowAndroid : public content::OverlayWindow { ...@@ -42,6 +58,7 @@ class OverlayWindowAndroid : public content::OverlayWindow {
private: private:
// A weak reference to Java PictureInPictureActivity object. // A weak reference to Java PictureInPictureActivity object.
JavaObjectWeakGlobalRef java_ref_; JavaObjectWeakGlobalRef java_ref_;
ui::WindowAndroid* window_android_;
content::PictureInPictureWindowController* controller_; content::PictureInPictureWindowController* controller_;
}; };
......
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