// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.FrameLayout;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import org.chromium.base.ContextUtils;
/**
* A view that is used to render the web contents in fullscreen mode, ie.
* html controls and subtitles, over the {@link ContentVideoView}.
*/
public class FullScreenView extends FrameLayout {
private AwViewMethods mAwViewMethods;
private final AwContents mAwContents;
private InternalAccessAdapter mInternalAccessAdapter;
private OnBackInvokedCallback mOnBackInvokedCallback;
public FullScreenView(Context context, AwViewMethods awViewMethods, AwContents awContents) {
super(context);
setAwViewMethods(awViewMethods);
mAwContents = awContents;
mInternalAccessAdapter = new InternalAccessAdapter();
mOnBackInvokedCallback =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
? () -> {
if (mAwContents.isFullScreen()) {
mAwContents.requestExitFullscreen();
}
}
: null;
}
public InternalAccessAdapter getInternalAccessAdapter() {
return mInternalAccessAdapter;
}
public void setAwViewMethods(AwViewMethods awViewMethods) {
mAwViewMethods = awViewMethods;
}
@Override
public void onDraw(final Canvas canvas) {
mAwViewMethods.onDraw(canvas);
}
@Override
public void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
mAwViewMethods.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public boolean requestFocus(final int direction, final Rect previouslyFocusedRect) {
mAwViewMethods.requestFocus();
return super.requestFocus(direction, previouslyFocusedRect);
}
@Override
public void setLayerType(int layerType, Paint paint) {
super.setLayerType(layerType, paint);
mAwViewMethods.setLayerType(layerType, paint);
}
@Override
public InputConnection onCreateInputConnection(final EditorInfo outAttrs) {
return mAwViewMethods.onCreateInputConnection(outAttrs);
}
@Override
public boolean onKeyUp(final int keyCode, final KeyEvent event) {
return mAwViewMethods.onKeyUp(keyCode, event);
}
@Override
public boolean dispatchKeyEvent(final KeyEvent event) {
// We didn't add the sdk check here as as the developer may not add the flag so we fallback
// to the old way it's confirmed that only one back mechanism will be triggered here or the
// dispatchCallback
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
&& event.getAction() == KeyEvent.ACTION_UP
&& mAwContents.isFullScreen()) {
mAwContents.requestExitFullscreen();
return true;
}
return mAwViewMethods.dispatchKeyEvent(event);
}
@Override
public boolean onTouchEvent(final MotionEvent event) {
return mAwViewMethods.onTouchEvent(event);
}
@Override
public boolean onHoverEvent(final MotionEvent event) {
return mAwViewMethods.onHoverEvent(event);
}
@Override
public boolean onGenericMotionEvent(final MotionEvent event) {
return mAwViewMethods.onGenericMotionEvent(event);
}
@Override
public void onConfigurationChanged(final Configuration newConfig) {
mAwViewMethods.onConfigurationChanged(newConfig);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Activity hostActivity = ContextUtils.activityFromContext(getContext());
if (hostActivity != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
hostActivity
.getOnBackInvokedDispatcher()
.registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback);
}
mAwViewMethods.onAttachedToWindow();
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
Activity hostActivity = ContextUtils.activityFromContext(getContext());
if (hostActivity != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
hostActivity
.getOnBackInvokedDispatcher()
.unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
}
mOnBackInvokedCallback = null;
mAwViewMethods.onDetachedFromWindow();
}
@Override
public void onWindowFocusChanged(final boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
mAwViewMethods.onWindowFocusChanged(hasWindowFocus);
}
@Override
public void onFocusChanged(
final boolean focused, final int direction, final Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
mAwViewMethods.onFocusChanged(focused, direction, previouslyFocusedRect);
}
@Override
public void onSizeChanged(final int w, final int h, final int ow, final int oh) {
super.onSizeChanged(w, h, ow, oh);
mAwViewMethods.onSizeChanged(w, h, ow, oh);
}
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
mAwViewMethods.onVisibilityChanged(changedView, visibility);
}
@Override
public void onWindowVisibilityChanged(final int visibility) {
super.onWindowVisibilityChanged(visibility);
mAwViewMethods.onWindowVisibilityChanged(visibility);
}
@Override
public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
mAwViewMethods.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
@Override
public void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
mAwViewMethods.onContainerViewScrollChanged(l, t, oldl, oldt);
}
@Override
public int computeHorizontalScrollRange() {
return mAwViewMethods.computeHorizontalScrollRange();
}
@Override
public int computeHorizontalScrollOffset() {
return mAwViewMethods.computeHorizontalScrollOffset();
}
@Override
public int computeVerticalScrollRange() {
return mAwViewMethods.computeVerticalScrollRange();
}
@Override
public int computeVerticalScrollOffset() {
return mAwViewMethods.computeVerticalScrollOffset();
}
@Override
public int computeVerticalScrollExtent() {
return mAwViewMethods.computeVerticalScrollExtent();
}
@Override
public void computeScroll() {
mAwViewMethods.computeScroll();
}
@Override
public AccessibilityNodeProvider getAccessibilityNodeProvider() {
return mAwViewMethods.getAccessibilityNodeProvider();
}
@Override
public boolean performAccessibilityAction(final int action, final Bundle arguments) {
return mAwViewMethods.performAccessibilityAction(action, arguments);
}
// AwContents.InternalAccessDelegate implementation --------------------------------------
private class InternalAccessAdapter implements AwContents.InternalAccessDelegate {
@Override
public boolean super_onKeyUp(int keyCode, KeyEvent event) {
return FullScreenView.super.onKeyUp(keyCode, event);
}
@Override
public boolean super_dispatchKeyEvent(KeyEvent event) {
return FullScreenView.super.dispatchKeyEvent(event);
}
@Override
public boolean super_onGenericMotionEvent(MotionEvent event) {
return FullScreenView.super.onGenericMotionEvent(event);
}
@Override
public void super_onConfigurationChanged(Configuration newConfig) {
// Intentional no-op
}
@Override
public int super_getScrollBarStyle() {
return FullScreenView.super.getScrollBarStyle();
}
@Override
public void super_startActivityForResult(Intent intent, int requestCode) {
throw new RuntimeException(
"FullScreenView InternalAccessAdapter shouldn't call startActivityForResult. "
+ "See AwContents#startActivityForResult");
}
@Override
public void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix) {
FullScreenView.this.onScrollChanged(lPix, tPix, oldlPix, oldtPix);
}
@Override
public void overScrollBy(
int deltaX,
int deltaY,
int scrollX,
int scrollY,
int scrollRangeX,
int scrollRangeY,
int maxOverScrollX,
int maxOverScrollY,
boolean isTouchEvent) {
FullScreenView.this.overScrollBy(
deltaX,
deltaY,
scrollX,
scrollY,
scrollRangeX,
scrollRangeY,
maxOverScrollX,
maxOverScrollY,
isTouchEvent);
}
@Override
public void super_scrollTo(int scrollX, int scrollY) {
FullScreenView.super.scrollTo(scrollX, scrollY);
}
@Override
public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
FullScreenView.this.setMeasuredDimension(measuredWidth, measuredHeight);
}
}
}