blob: 0fa26397e830f1a7b09bd7e268fe483c1532654c [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.compositor.bottombar.ephemeraltab;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.R;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
import org.chromium.components.browser_ui.widget.FadingShadow;
import org.chromium.components.browser_ui.widget.FadingShadowView;
import org.chromium.components.embedder_support.delegate.WebContentsDelegateAndroid;
import org.chromium.components.embedder_support.view.ContentView;
import org.chromium.components.thinwebview.ThinWebView;
import org.chromium.components.thinwebview.ThinWebViewConstraints;
import org.chromium.components.thinwebview.ThinWebViewFactory;
import org.chromium.components.url_formatter.SchemeDisplay;
import org.chromium.components.url_formatter.UrlFormatter;
import org.chromium.content_public.browser.RenderCoordinates;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.IntentRequestTracker;
import org.chromium.url.GURL;
/**
* Represents ephemeral tab content and the toolbar, which can be included inside the bottom sheet.
*/
public class EphemeralTabSheetContent implements BottomSheetContent {
/**
* The base duration of the settling animation of the sheet. 218 ms is a spec for material
* design (this is the minimum time a user is guaranteed to pay attention to something).
*/
private static final int BASE_ANIMATION_DURATION_MS = 218;
private static final float PEEK_TOOLBAR_HEIGHT_MULTIPLE = 2.f;
/** Ratio of the height when in full mode. Used in half-open variation. */
private static final float FULL_HEIGHT_RATIO = 0.9f;
private final Context mContext;
private final Runnable mOpenNewTabCallback;
private final Runnable mToolbarClickCallback;
private final Runnable mCloseButtonCallback;
private final int mToolbarHeightPx;
private ViewGroup mToolbarView;
private ViewGroup mSheetContentView;
private WebContents mWebContents;
private ContentView mWebContentView;
private ThinWebView mThinWebView;
private FadingShadowView mShadow;
private Drawable mCurrentFavicon;
private ImageView mFaviconView;
/**
* Constructor.
* @param context An Android context.
* @param openNewTabCallback Callback invoked to open a new tab.
* @param toolbarClickCallback Callback invoked when user clicks on the toolbar.
* @param closeButtonCallback Callback invoked when user clicks on the close button.
* @param maxViewHeight The height of the sheet in full height position.
* @param intentRequestTracker The {@link IntentRequestTracker} of the current activity.
*/
public EphemeralTabSheetContent(Context context, Runnable openNewTabCallback,
Runnable toolbarClickCallback, Runnable closeButtonCallback, int maxViewHeight,
IntentRequestTracker intentRequestTracker) {
mContext = context;
mOpenNewTabCallback = openNewTabCallback;
mToolbarClickCallback = toolbarClickCallback;
mCloseButtonCallback = closeButtonCallback;
mToolbarHeightPx =
mContext.getResources().getDimensionPixelSize(R.dimen.sheet_tab_toolbar_height);
createThinWebView((int) (maxViewHeight * FULL_HEIGHT_RATIO), intentRequestTracker);
createToolbarView();
}
/**
* Add web contents to the sheet.
* @param webContents The {@link WebContents} to be displayed.
* @param contentView The {@link ContentView} associated with the web contents.
* @param delegate The {@link WebContentsDelegateAndroid} that handles requests on WebContents.
*/
public void attachWebContents(
WebContents webContents, ContentView contentView, WebContentsDelegateAndroid delegate) {
mWebContents = webContents;
mWebContentView = contentView;
if (mWebContentView.getParent() != null) {
((ViewGroup) mWebContentView.getParent()).removeView(mWebContentView);
}
mThinWebView.attachWebContents(mWebContents, mWebContentView, delegate);
}
/**
* Create a ThinWebView, add it to the view hierarchy, which represents the contents of the
* bottom sheet.
*/
private void createThinWebView(int maxSheetHeight, IntentRequestTracker intentRequestTracker) {
mThinWebView = ThinWebViewFactory.create(
mContext, new ThinWebViewConstraints(), intentRequestTracker);
mSheetContentView = new FrameLayout(mContext);
mThinWebView.getView().setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, maxSheetHeight - mToolbarHeightPx));
mSheetContentView.addView(mThinWebView.getView());
mSheetContentView.setPadding(0, mToolbarHeightPx, 0, 0);
}
private void createToolbarView() {
mToolbarView =
(ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.sheet_tab_toolbar, null);
mShadow = mToolbarView.findViewById(R.id.shadow);
mShadow.init(ApiCompatibilityUtils.getColor(
mContext.getResources(), R.color.toolbar_shadow_color),
FadingShadow.POSITION_TOP);
ImageView openInNewTabButton = mToolbarView.findViewById(R.id.open_in_new_tab);
openInNewTabButton.setOnClickListener(view -> mOpenNewTabCallback.run());
View toolbar = mToolbarView.findViewById(R.id.toolbar);
toolbar.setOnClickListener(view -> mToolbarClickCallback.run());
View closeButton = mToolbarView.findViewById(R.id.close);
closeButton.setOnClickListener(view -> mCloseButtonCallback.run());
mFaviconView = mToolbarView.findViewById(R.id.favicon);
mCurrentFavicon = mFaviconView.getDrawable();
}
/**
* Resizes the thin webview as per the given new max height.
* @param maxViewHeight The maximum height of the view.
*/
void updateContentHeight(int maxViewHeight) {
if (maxViewHeight == 0) return;
ViewGroup.LayoutParams layoutParams = mThinWebView.getView().getLayoutParams();
// This should never be more than the tab height for it to function correctly.
// We scale it by |FULL_HEIGHT_RATIO| to make the size equal to that of
// ThinWebView and so it can leave a portion of the page below it visible.
layoutParams.height = (int) (maxViewHeight * FULL_HEIGHT_RATIO) - mToolbarHeightPx;
mSheetContentView.requestLayout();
}
/** Method to be called to start the favicon anmiation. */
public void startFaviconAnimation(Drawable favicon) {
if (favicon == null) {
mCurrentFavicon = null;
mFaviconView.setImageDrawable(null);
return;
}
// TODO(shaktisahu): Find out if there is a better way for this animation.
Drawable presentedDrawable = favicon;
if (mCurrentFavicon != null && !(mCurrentFavicon instanceof TransitionDrawable)) {
TransitionDrawable transitionDrawable =
new TransitionDrawable(new Drawable[] {mCurrentFavicon, favicon});
transitionDrawable.setCrossFadeEnabled(true);
transitionDrawable.startTransition(BASE_ANIMATION_DURATION_MS);
presentedDrawable = transitionDrawable;
}
mFaviconView.setImageDrawable(presentedDrawable);
mCurrentFavicon = favicon;
}
/** Sets the ephemeral tab title text. */
public void updateTitle(String title) {
TextView toolbarText = mToolbarView.findViewById(R.id.title);
toolbarText.setText(title);
}
/** Sets the ephemeral tab URL. */
public void updateURL(GURL url) {
TextView originView = mToolbarView.findViewById(R.id.origin);
originView.setText(
UrlFormatter.formatUrlForSecurityDisplay(url, SchemeDisplay.OMIT_HTTP_AND_HTTPS));
}
/** Sets the security icon. */
public void setSecurityIcon(@DrawableRes int resId) {
ImageView securityIcon = mToolbarView.findViewById(R.id.security_icon);
securityIcon.setImageResource(resId);
}
/** Sets the progress on the progress bar. */
public void setProgress(float progress) {
ProgressBar progressBar = mToolbarView.findViewById(R.id.progress_bar);
progressBar.setProgress(Math.round(progress * 100));
}
/** Called to show or hide the progress bar. */
public void setProgressVisible(boolean visible) {
ProgressBar progressBar = mToolbarView.findViewById(R.id.progress_bar);
progressBar.setVisibility(visible ? View.VISIBLE : View.GONE);
}
/**
* Called to show (with alpha) or hide the open in new tab button.
* @param fraction Alpha for the button when visible.
*/
public void showOpenInNewTabButton(float fraction) {
View button = mToolbarView.findViewById(R.id.open_in_new_tab);
// Start showing the button about halfway toward the full state.
if (fraction <= 0.5f) {
if (button.getVisibility() != View.GONE) button.setVisibility(View.GONE);
} else {
if (button.getVisibility() != View.VISIBLE) button.setVisibility(View.VISIBLE);
button.setAlpha((fraction - 0.5f) * 2.0f);
}
}
@Override
public View getContentView() {
return mSheetContentView;
}
@Nullable
@Override
public View getToolbarView() {
return mToolbarView;
}
@Override
public int getVerticalScrollOffset() {
return mWebContents == null
? 0
: RenderCoordinates.fromWebContents(mWebContents).getScrollYPixInt();
}
@Override
public void destroy() {
mThinWebView.destroy();
}
@Override
public int getPriority() {
return BottomSheetContent.ContentPriority.HIGH;
}
@Override
public boolean swipeToDismissEnabled() {
return true;
}
@Override
public int getPeekHeight() {
return HeightMode.DISABLED;
}
@Override
public float getHalfHeightRatio() {
return HeightMode.DEFAULT;
}
@Override
public float getFullHeightRatio() {
return HeightMode.WRAP_CONTENT;
}
@Override
public boolean handleBackPress() {
mCloseButtonCallback.run();
return true;
}
@Override
public int getSheetContentDescriptionStringId() {
return R.string.ephemeral_tab_sheet_description;
}
@Override
public int getSheetHalfHeightAccessibilityStringId() {
return R.string.ephemeral_tab_sheet_opened_half;
}
@Override
public int getSheetFullHeightAccessibilityStringId() {
return R.string.ephemeral_tab_sheet_opened_full;
}
@Override
public int getSheetClosedAccessibilityStringId() {
return R.string.ephemeral_tab_sheet_closed;
}
}