diff --git a/android/brave_java_resources.gni b/android/brave_java_resources.gni index a8a89cc39a7c..bb395b378010 100644 --- a/android/brave_java_resources.gni +++ b/android/brave_java_resources.gni @@ -676,6 +676,7 @@ brave_java_resources = [ "java/res/drawable/ic_setbraveasdefault.xml", "java/res/drawable/ic_setbraveasdefault_dark.xml", "java/res/drawable/ic_shield_done_filled.xml", + "java/res/drawable/ic_shield_done_filled_20dp.xml", "java/res/drawable/ic_sort.xml", "java/res/drawable/ic_spot_graphic.xml", "java/res/drawable/ic_spot_graphic_dark.xml", @@ -712,12 +713,14 @@ brave_java_resources = [ "java/res/drawable/rounded_holo_button.xml", "java/res/drawable/rounded_shape.xml", "java/res/drawable/selected_indicator.xml", - "java/res/drawable/shields_tooltip_background.xml", + "java/res/drawable/shields_tooltip_background_1.xml", + "java/res/drawable/shields_tooltip_background_2.xml", "java/res/drawable/sync_icon.xml", "java/res/drawable/tip_amount.xml", "java/res/drawable/transparent_bg_bordered.xml", "java/res/drawable/wallet_disconnected_button.xml", "java/res/drawable/wallet_verify_button.xml", + "java/res/drawable/white_rounded_holo_button.xml", "java/res/font/poppins_light.ttf", "java/res/font/poppins_medium.ttf", "java/res/layout-land/brave_rewards_site_banner.xml", diff --git a/android/brave_java_sources.gni b/android/brave_java_sources.gni index b19a7c783015..0e5a8d2e7e6e 100644 --- a/android/brave_java_sources.gni +++ b/android/brave_java_sources.gni @@ -46,6 +46,9 @@ brave_java_sources = [ "../../brave/android/java/org/chromium/chrome/browser/custom_layout/HeightWrappingViewPager.java", "../../brave/android/java/org/chromium/chrome/browser/custom_layout/NonSwipeableViewPager.java", "../../brave/android/java/org/chromium/chrome/browser/custom_layout/VerticalViewPager.java", + "../../brave/android/java/org/chromium/chrome/browser/custom_layout/popup_window_tooltip/ArrowColorDrawable.java", + "../../brave/android/java/org/chromium/chrome/browser/custom_layout/popup_window_tooltip/PopupWindowTooltip.java", + "../../brave/android/java/org/chromium/chrome/browser/custom_layout/popup_window_tooltip/PopupWindowTooltipUtils.java", "../../brave/android/java/org/chromium/chrome/browser/document/BraveLauncherActivity.java", "../../brave/android/java/org/chromium/chrome/browser/download/BraveMimeUtils.java", "../../brave/android/java/org/chromium/chrome/browser/download/settings/BraveDownloadSettings.java", @@ -149,6 +152,7 @@ brave_java_sources = [ "../../brave/android/java/org/chromium/chrome/browser/shields/BraveShieldsHandler.java", "../../brave/android/java/org/chromium/chrome/browser/shields/BraveShieldsMenuObserver.java", "../../brave/android/java/org/chromium/chrome/browser/shields/BraveShieldsUtils.java", + "../../brave/android/java/org/chromium/chrome/browser/shields/ShieldsTooltipEnum.java", "../../brave/android/java/org/chromium/chrome/browser/signin/BraveSigninManager.java", "../../brave/android/java/org/chromium/chrome/browser/site_settings/DesktopModePreferences.java", "../../brave/android/java/org/chromium/chrome/browser/site_settings/PlayYTVideoInBrowserPreferences.java", diff --git a/android/java/org/chromium/chrome/browser/custom_layout/popup_window_tooltip/ArrowColorDrawable.java b/android/java/org/chromium/chrome/browser/custom_layout/popup_window_tooltip/ArrowColorDrawable.java new file mode 100644 index 000000000000..15e98e83ae57 --- /dev/null +++ b/android/java/org/chromium/chrome/browser/custom_layout/popup_window_tooltip/ArrowColorDrawable.java @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.chromium.chrome.browser.custom_layout.popup_window_tooltip; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; + +import androidx.annotation.ColorInt; + +public class ArrowColorDrawable extends ColorDrawable { + public static final int LEFT = 0, TOP = 1, RIGHT = 2, BOTTOM = 3, AUTO = 4; + + private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final int mBackgroundColor; + private Path mPath; + private final int mDirection; + + ArrowColorDrawable(@ColorInt int foregroundColor, int direction) { + this.mBackgroundColor = Color.TRANSPARENT; + this.mPaint.setColor(foregroundColor); + this.mDirection = direction; + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + updatePath(bounds); + } + + private synchronized void updatePath(Rect bounds) { + mPath = new Path(); + + switch (mDirection) { + case LEFT: + mPath.moveTo(bounds.width(), bounds.height()); + mPath.lineTo(0, (float) bounds.height() / 2); + mPath.lineTo(bounds.width(), 0); + mPath.lineTo(bounds.width(), bounds.height()); + break; + case TOP: + mPath.moveTo(0, bounds.height()); + mPath.lineTo((float) bounds.width() / 2, 0); + mPath.lineTo(bounds.width(), bounds.height()); + mPath.lineTo(0, bounds.height()); + break; + case RIGHT: + mPath.moveTo(0, 0); + mPath.lineTo(bounds.width(), (float) bounds.height() / 2); + mPath.lineTo(0, bounds.height()); + mPath.lineTo(0, 0); + break; + case BOTTOM: + mPath.moveTo(0, 0); + mPath.lineTo((float) bounds.width() / 2, bounds.height()); + mPath.lineTo(bounds.width(), 0); + mPath.lineTo(0, 0); + break; + } + + mPath.close(); + } + + @Override + public void draw(Canvas canvas) { + canvas.drawColor(mBackgroundColor); + if (mPath == null) updatePath(getBounds()); + canvas.drawPath(mPath, mPaint); + } + + @Override + public void setAlpha(int alpha) { + mPaint.setAlpha(alpha); + } + + public void setColor(@ColorInt int color) { + mPaint.setColor(color); + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + mPaint.setColorFilter(colorFilter); + } + + @Override + public int getOpacity() { + if (mPaint.getColorFilter() != null) { + return PixelFormat.TRANSLUCENT; + } + + switch (mPaint.getColor() >>> 24) { + case 255: + return PixelFormat.OPAQUE; + case 0: + return PixelFormat.TRANSPARENT; + } + return PixelFormat.TRANSLUCENT; + } +} diff --git a/android/java/org/chromium/chrome/browser/custom_layout/popup_window_tooltip/PopupWindowTooltip.java b/android/java/org/chromium/chrome/browser/custom_layout/popup_window_tooltip/PopupWindowTooltip.java new file mode 100644 index 000000000000..1024d67d32b5 --- /dev/null +++ b/android/java/org/chromium/chrome/browser/custom_layout/popup_window_tooltip/PopupWindowTooltip.java @@ -0,0 +1,501 @@ +/** + * Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.chromium.chrome.browser.custom_layout.popup_window_tooltip; + +import static org.chromium.ui.base.ViewUtils.dpToPx; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.PointF; +import android.graphics.RectF; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; + +import androidx.annotation.ColorInt; +import androidx.annotation.DimenRes; +import androidx.annotation.LayoutRes; + +import org.chromium.base.Log; +import org.chromium.chrome.R; + +public class PopupWindowTooltip implements PopupWindow.OnDismissListener { + private static final String TAG = PopupWindowTooltip.class.getSimpleName(); + + // Default Resources + private static final int mDefaultPopupWindowStyleRes = android.R.attr.popupWindowStyle; + private static final int mDefaultArrowColorRes = R.color.shields_tooltip_arrow_color_1; + private static final int mDefaultMarginRes = R.dimen.shields_tooltip_margin; + private static final int mDefaultPaddingRes = R.dimen.shields_tooltip_padding; + private static final int mDefaultArrowWidthRes = R.dimen.shields_tooltip_arrow_width; + private static final int mDefaultArrowHeightRes = R.dimen.shields_tooltip_arrow_height; + + private final Context mContext; + private OnDismissListener mOnDismissListener; + private OnShowListener mOnShowListener; + private PopupWindow mPopupWindow; + private final int mGravity; + private final int mArrowDirection; + private final boolean mDismissOnInsideTouch; + private final boolean mDismissOnOutsideTouch; + private final boolean mModal; + private final View mContentView; + private View mContentLayout; + private final View mAnchorView; + private final float mMaxWidth; + private ViewGroup mRootView; + private ImageView mArrowView; + private final Drawable mArrowColorDrawable; + private final float mMargin; + private final float mPadding; + private final float mArrowWidth; + private final float mArrowHeight; + private boolean dismissed = false; + private int width; + private int height; + + private PopupWindowTooltip(Builder builder) { + mContext = builder.context; + mGravity = builder.gravity; + mArrowDirection = builder.arrowDirection; + mDismissOnInsideTouch = builder.dismissOnInsideTouch; + mDismissOnOutsideTouch = builder.dismissOnOutsideTouch; + mModal = builder.modal; + mContentView = builder.contentView; + mAnchorView = builder.anchorView; + mMaxWidth = builder.maxWidth; + mArrowWidth = builder.arrowWidth; + mArrowHeight = builder.arrowHeight; + mArrowColorDrawable = builder.arrowColorDrawable; + mMargin = builder.margin; + mPadding = builder.padding; + mOnDismissListener = builder.onDismissListener; + mOnShowListener = builder.onShowListener; + mRootView = PopupWindowTooltipUtils.findFrameLayout(mAnchorView); + this.width = builder.width; + this.height = builder.height; + init(); + } + + private void init() { + configPopupWindow(); + configContentView(); + } + + private void configPopupWindow() { + mPopupWindow = new PopupWindow(mContext, null, mDefaultPopupWindowStyleRes); + mPopupWindow.setOnDismissListener(this); + mPopupWindow.setWidth(width); + mPopupWindow.setHeight(height); + mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + mPopupWindow.setOutsideTouchable(true); + mPopupWindow.setTouchable(true); + mPopupWindow.setTouchInterceptor(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + final int x = (int) event.getX(); + final int y = (int) event.getY(); + + if (!mDismissOnOutsideTouch && (event.getAction() == MotionEvent.ACTION_DOWN) + && ((x < 0) || (x >= mContentLayout.getMeasuredWidth()) || (y < 0) + || (y >= mContentLayout.getMeasuredHeight()))) { + return true; + } else if (!mDismissOnOutsideTouch + && event.getAction() == MotionEvent.ACTION_OUTSIDE) { + return true; + } else if ((event.getAction() == MotionEvent.ACTION_DOWN) + && mDismissOnInsideTouch) { + dismiss(); + return true; + } + return false; + } + }); + mPopupWindow.setClippingEnabled(false); + } + + public void show() { + verifyDismissed(); + + mContentLayout.getViewTreeObserver().addOnGlobalLayoutListener(mLocationLayoutListener); + mContentLayout.getViewTreeObserver().addOnGlobalLayoutListener(mAutoDismissLayoutListener); + + mRootView.post(new Runnable() { + @Override + public void run() { + if (mRootView.isShown()) + mPopupWindow.showAtLocation(mRootView, Gravity.NO_GRAVITY, mRootView.getWidth(), + mRootView.getHeight()); + else + Log.e(TAG, "Tooltip cannot be shown, root view is invalid or has been closed."); + } + }); + } + + private void verifyDismissed() { + if (dismissed) { + throw new IllegalArgumentException("Tooltip has been dismissed."); + } + } + + private PointF calculePopupLocation() { + PointF location = new PointF(); + + final RectF anchorRect = PopupWindowTooltipUtils.calculeRectInWindow(mAnchorView); + final PointF anchorCenter = new PointF(anchorRect.centerX(), anchorRect.centerY()); + + switch (mGravity) { + case Gravity.START: + location.x = anchorRect.left - mPopupWindow.getContentView().getWidth() - mMargin; + location.y = anchorCenter.y - mPopupWindow.getContentView().getHeight() / 2f; + break; + case Gravity.END: + location.x = anchorRect.right + mMargin; + location.y = anchorCenter.y - mPopupWindow.getContentView().getHeight() / 2f; + break; + case Gravity.TOP: + location.x = anchorCenter.x - mPopupWindow.getContentView().getWidth() / 2f; + location.y = anchorRect.top - mPopupWindow.getContentView().getHeight() - mMargin; + break; + case Gravity.BOTTOM: + location.x = anchorCenter.x - mPopupWindow.getContentView().getWidth() / 2f; + location.y = anchorRect.bottom + mMargin; + break; + case Gravity.CENTER: + location.x = anchorCenter.x - mPopupWindow.getContentView().getWidth() / 2f; + location.y = anchorCenter.y - mPopupWindow.getContentView().getHeight() / 2f; + break; + default: + throw new IllegalArgumentException( + "Gravity must have be CENTER, START, END, TOP or BOTTOM."); + } + + return location; + } + + private void configContentView() { + mContentView.setPadding((int) mPadding, (int) mPadding, (int) mPadding, (int) mPadding); + + LinearLayout linearLayout = new LinearLayout(mContext); + linearLayout.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + linearLayout.setOrientation(mArrowDirection == ArrowColorDrawable.LEFT + || mArrowDirection == ArrowColorDrawable.RIGHT + ? LinearLayout.HORIZONTAL + : LinearLayout.VERTICAL); + int layoutPadding = 0; + linearLayout.setPadding(layoutPadding, layoutPadding, layoutPadding, layoutPadding); + + mArrowView = new ImageView(mContext); + mArrowView.setImageDrawable(mArrowColorDrawable); + LinearLayout.LayoutParams arrowLayoutParams; + + if (mArrowDirection == ArrowColorDrawable.TOP + || mArrowDirection == ArrowColorDrawable.BOTTOM) { + arrowLayoutParams = + new LinearLayout.LayoutParams((int) mArrowWidth, (int) mArrowHeight, 0); + } else { + arrowLayoutParams = + new LinearLayout.LayoutParams((int) mArrowHeight, (int) mArrowWidth, 0); + } + + arrowLayoutParams.gravity = Gravity.CENTER; + mArrowView.setLayoutParams(arrowLayoutParams); + + if (mArrowDirection == ArrowColorDrawable.BOTTOM + || mArrowDirection == ArrowColorDrawable.RIGHT) { + linearLayout.addView(mContentView); + linearLayout.addView(mArrowView); + } else { + linearLayout.addView(mArrowView); + linearLayout.addView(mContentView); + } + + LinearLayout.LayoutParams contentViewParams = + new LinearLayout.LayoutParams(width, height, 0); + contentViewParams.gravity = Gravity.CENTER; + mContentView.setLayoutParams(contentViewParams); + + mContentLayout = linearLayout; + mContentLayout.setVisibility(View.INVISIBLE); + mPopupWindow.setContentView(mContentLayout); + } + + public void dismiss() { + if (dismissed) return; + + dismissed = true; + if (mPopupWindow != null) { + mPopupWindow.dismiss(); + } + } + public boolean isShowing() { + return mPopupWindow != null && mPopupWindow.isShowing(); + } + public T findViewById(int id) { + // noinspection unchecked + return (T) mContentLayout.findViewById(id); + } + @Override + public void onDismiss() { + dismissed = true; + mRootView = null; + if (mOnDismissListener != null) mOnDismissListener.onDismiss(this); + mOnDismissListener = null; + mPopupWindow.getContentView().getViewTreeObserver().removeOnGlobalLayoutListener( + mLocationLayoutListener); + mPopupWindow.getContentView().getViewTreeObserver().removeOnGlobalLayoutListener( + mArrowLayoutListener); + mPopupWindow.getContentView().getViewTreeObserver().removeOnGlobalLayoutListener( + mShowLayoutListener); + mPopupWindow.getContentView().getViewTreeObserver().removeOnGlobalLayoutListener( + mAutoDismissLayoutListener); + mPopupWindow = null; + } + private final ViewTreeObserver.OnGlobalLayoutListener mLocationLayoutListener = + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + final PopupWindow popup = mPopupWindow; + if (popup == null || dismissed) return; + if (mMaxWidth > 0 && mContentView.getWidth() > mMaxWidth) { + PopupWindowTooltipUtils.setWidth(mContentView, mMaxWidth); + popup.update(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + return; + } + popup.getContentView().getViewTreeObserver().removeOnGlobalLayoutListener(this); + popup.getContentView().getViewTreeObserver().addOnGlobalLayoutListener( + mArrowLayoutListener); + PointF location = calculePopupLocation(); + popup.setClippingEnabled(true); + popup.update((int) location.x, (int) location.y, popup.getWidth(), + popup.getHeight()); + popup.getContentView().requestLayout(); + } + }; + private final ViewTreeObserver.OnGlobalLayoutListener mArrowLayoutListener = + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + final PopupWindow popup = mPopupWindow; + if (popup == null || dismissed) return; + popup.getContentView().getViewTreeObserver().removeOnGlobalLayoutListener(this); + popup.getContentView().getViewTreeObserver().addOnGlobalLayoutListener( + mShowLayoutListener); + RectF achorRect = PopupWindowTooltipUtils.calculeRectOnScreen(mAnchorView); + RectF contentViewRect = + PopupWindowTooltipUtils.calculeRectOnScreen(mContentLayout); + float x, y; + if (mArrowDirection == ArrowColorDrawable.TOP + || mArrowDirection == ArrowColorDrawable.BOTTOM) { + x = mContentLayout.getPaddingLeft() + dpToPx(mContext, 2); + float centerX = + (contentViewRect.width() / 2f) - (mArrowView.getWidth() / 2f); + float newX = centerX - (contentViewRect.centerX() - achorRect.centerX()); + if (newX > x) { + if (newX + mArrowView.getWidth() + x > contentViewRect.width()) { + x = contentViewRect.width() - mArrowView.getWidth() - x; + } else { + x = newX; + } + } + y = mArrowView.getTop(); + y = y + (mArrowDirection == ArrowColorDrawable.BOTTOM ? -1 : +1); + } else { + y = mContentLayout.getPaddingTop() + dpToPx(mContext, 2); + float centerY = + (contentViewRect.height() / 2f) - (mArrowView.getHeight() / 2f); + float newY = centerY - (contentViewRect.centerY() - achorRect.centerY()); + if (newY > y) { + if (newY + mArrowView.getHeight() + y > contentViewRect.height()) { + y = contentViewRect.height() - mArrowView.getHeight() - y; + } else { + y = newY; + } + } + x = mArrowView.getLeft(); + x = x + (mArrowDirection == ArrowColorDrawable.RIGHT ? -1 : +1); + } + mArrowView.setX((int) x); + mArrowView.setY((int) y); + popup.getContentView().requestLayout(); + } + }; + private final ViewTreeObserver.OnGlobalLayoutListener mShowLayoutListener = + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + final PopupWindow popup = mPopupWindow; + if (popup == null || dismissed) return; + popup.getContentView().getViewTreeObserver().removeOnGlobalLayoutListener(this); + if (mOnShowListener != null) mOnShowListener.onShow(PopupWindowTooltip.this); + mOnShowListener = null; + mContentLayout.setVisibility(View.VISIBLE); + } + }; + private final ViewTreeObserver.OnGlobalLayoutListener mAutoDismissLayoutListener = + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + final PopupWindow popup = mPopupWindow; + if (popup == null || dismissed) return; + if (!mRootView.isShown()) dismiss(); + } + }; + public interface OnDismissListener { + void onDismiss(PopupWindowTooltip tooltip); + } + public interface OnShowListener { + void onShow(PopupWindowTooltip tooltip); + } + + public static class Builder { + private final Context context; + private boolean dismissOnInsideTouch = true; + private boolean dismissOnOutsideTouch = true; + private boolean modal = false; + private View contentView; + private View anchorView; + private int arrowDirection = ArrowColorDrawable.AUTO; + private int gravity = Gravity.BOTTOM; + private float maxWidth; + private Drawable arrowColorDrawable; + private float margin = -1; + private float padding = -1; + private OnDismissListener onDismissListener; + private OnShowListener onShowListener; + private int arrowColor; + private float arrowHeight; + private float arrowWidth; + private int width = ViewGroup.LayoutParams.WRAP_CONTENT; + private int height = ViewGroup.LayoutParams.WRAP_CONTENT; + public Builder(Context context) { + this.context = context; + } + public PopupWindowTooltip build() throws IllegalArgumentException { + validateArguments(); + if (arrowColor == 0) { + arrowColor = PopupWindowTooltipUtils.getColor(context, mDefaultArrowColorRes); + } + if (margin < 0) { + margin = context.getResources().getDimension(mDefaultMarginRes); + } + if (padding < 0) { + padding = context.getResources().getDimension(mDefaultPaddingRes); + } + if (arrowDirection == ArrowColorDrawable.AUTO) + arrowDirection = PopupWindowTooltipUtils.tooltipGravityToArrowDirection(gravity); + if (arrowColorDrawable == null) + arrowColorDrawable = new ArrowColorDrawable(arrowColor, arrowDirection); + if (arrowWidth == 0) + arrowWidth = context.getResources().getDimension(mDefaultArrowWidthRes); + if (arrowHeight == 0) + arrowHeight = context.getResources().getDimension(mDefaultArrowHeightRes); + return new PopupWindowTooltip(this); + } + private void validateArguments() throws IllegalArgumentException { + if (context == null) { + throw new IllegalArgumentException("Context not specified."); + } + if (anchorView == null) { + throw new IllegalArgumentException("Anchor view not specified."); + } + } + public Builder setWidth(int width) { + this.width = width; + return this; + } + public Builder setHeight(int height) { + this.height = height; + return this; + } + public Builder contentView(@LayoutRes int contentViewId) { + LayoutInflater inflater = + (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + this.contentView = inflater.inflate(contentViewId, null, false); + return this; + } + public Builder dismissOnInsideTouch(boolean dismissOnInsideTouch) { + this.dismissOnInsideTouch = dismissOnInsideTouch; + return this; + } + public Builder dismissOnOutsideTouch(boolean dismissOnOutsideTouch) { + this.dismissOnOutsideTouch = dismissOnOutsideTouch; + return this; + } + public Builder modal(boolean modal) { + this.modal = modal; + return this; + } + public Builder anchorView(View anchorView) { + this.anchorView = anchorView; + return this; + } + public Builder gravity(int gravity) { + this.gravity = gravity; + return this; + } + public Builder arrowDirection(int arrowDirection) { + this.arrowDirection = arrowDirection; + return this; + } + public Builder maxWidth(@DimenRes int maxWidthRes) { + this.maxWidth = context.getResources().getDimension(maxWidthRes); + return this; + } + public Builder maxWidth(float maxWidth) { + this.maxWidth = maxWidth; + return this; + } + public Builder padding(float padding) { + this.padding = padding; + return this; + } + public Builder padding(@DimenRes int paddingRes) { + this.padding = context.getResources().getDimension(paddingRes); + return this; + } + public Builder margin(float margin) { + this.margin = margin; + return this; + } + public Builder margin(@DimenRes int marginRes) { + this.margin = context.getResources().getDimension(marginRes); + return this; + } + public Builder arrowColor(@ColorInt int arrowColor) { + this.arrowColor = arrowColor; + return this; + } + public Builder arrowHeight(float arrowHeight) { + this.arrowHeight = arrowHeight; + return this; + } + public Builder arrowWidth(float arrowWidth) { + this.arrowWidth = arrowWidth; + return this; + } + public Builder onDismissListener(OnDismissListener onDismissListener) { + this.onDismissListener = onDismissListener; + return this; + } + public Builder onShowListener(OnShowListener onShowListener) { + this.onShowListener = onShowListener; + return this; + } + } +} diff --git a/android/java/org/chromium/chrome/browser/custom_layout/popup_window_tooltip/PopupWindowTooltipUtils.java b/android/java/org/chromium/chrome/browser/custom_layout/popup_window_tooltip/PopupWindowTooltipUtils.java new file mode 100644 index 000000000000..9fe44ba65240 --- /dev/null +++ b/android/java/org/chromium/chrome/browser/custom_layout/popup_window_tooltip/PopupWindowTooltipUtils.java @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.chromium.chrome.browser.custom_layout.popup_window_tooltip; + +import android.content.Context; +import android.graphics.RectF; +import android.os.Build; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.annotation.ColorRes; + +public final class PopupWindowTooltipUtils { + public static RectF calculeRectOnScreen(View view) { + int[] location = new int[2]; + view.getLocationOnScreen(location); + return new RectF(location[0], location[1], location[0] + view.getMeasuredWidth(), + location[1] + view.getMeasuredHeight()); + } + + public static RectF calculeRectInWindow(View view) { + int[] location = new int[2]; + view.getLocationInWindow(location); + return new RectF(location[0], location[1], location[0] + view.getMeasuredWidth(), + location[1] + view.getMeasuredHeight()); + } + + public static void setWidth(View view, float width) { + ViewGroup.LayoutParams params = view.getLayoutParams(); + if (params == null) { + params = new ViewGroup.LayoutParams((int) width, view.getHeight()); + } else { + params.width = (int) width; + } + view.setLayoutParams(params); + } + + public static int tooltipGravityToArrowDirection(int tooltipGravity) { + switch (tooltipGravity) { + case Gravity.START: + return ArrowColorDrawable.RIGHT; + case Gravity.END: + return ArrowColorDrawable.LEFT; + case Gravity.TOP: + return ArrowColorDrawable.BOTTOM; + case Gravity.BOTTOM: + return ArrowColorDrawable.TOP; + case Gravity.CENTER: + return ArrowColorDrawable.TOP; + default: + throw new IllegalArgumentException( + "Gravity must have be CENTER, START, END, TOP or BOTTOM."); + } + } + + private static ViewGroup.MarginLayoutParams getOrCreateMarginLayoutParams(View view) { + ViewGroup.LayoutParams lp = view.getLayoutParams(); + if (lp != null) { + if (lp instanceof ViewGroup.MarginLayoutParams) { + return (ViewGroup.MarginLayoutParams) lp; + } else { + return new ViewGroup.MarginLayoutParams(lp); + } + } else { + return new ViewGroup.MarginLayoutParams(view.getWidth(), view.getHeight()); + } + } + + public static int getColor(Context context, @ColorRes int colorRes) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return context.getColor(colorRes); + } else { + // noinspection deprecation + return context.getResources().getColor(colorRes); + } + } + + public static ViewGroup findFrameLayout(View anchorView) { + ViewGroup rootView = (ViewGroup) anchorView.getRootView(); + if (rootView.getChildCount() == 1 && rootView.getChildAt(0) instanceof FrameLayout) { + rootView = (ViewGroup) rootView.getChildAt(0); + } + return rootView; + } +} diff --git a/android/java/org/chromium/chrome/browser/onboarding/OnboardingPrefManager.java b/android/java/org/chromium/chrome/browser/onboarding/OnboardingPrefManager.java index e2b3d9f57d73..994016851348 100644 --- a/android/java/org/chromium/chrome/browser/onboarding/OnboardingPrefManager.java +++ b/android/java/org/chromium/chrome/browser/onboarding/OnboardingPrefManager.java @@ -45,7 +45,6 @@ public class OnboardingPrefManager { private static final String PREF_ONBOARDING_FOR_SKIP = "onboarding_for_skip"; private static final String PREF_ONBOARDING_SKIP_COUNT = "onboarding_skip_count"; private static final String PREF_SEARCH_ENGINE_ONBOARDING = "search_engine_onboarding"; - private static final String PREF_SHIELDS_TOOLTIP = "shields_tooltip"; private static final String PREF_SHOW_DEFAULT_BROWSER_MODAL_AFTER_P3A = "show_default_browser_modal_after_p3a"; public static final String PREF_BRAVE_STATS = "brave_stats"; @@ -193,16 +192,6 @@ public void setSearchEngineOnboardingShown(boolean isShown) { sharedPreferencesEditor.apply(); } - public boolean hasShieldsTooltipShown() { - return mSharedPreferences.getBoolean(PREF_SHIELDS_TOOLTIP, false); - } - - public void setShieldsTooltipShown(boolean isShown) { - SharedPreferences.Editor sharedPreferencesEditor = mSharedPreferences.edit(); - sharedPreferencesEditor.putBoolean(PREF_SHIELDS_TOOLTIP, isShown); - sharedPreferencesEditor.apply(); - } - public long getNextOnboardingDate() { return mSharedPreferences.getLong(PREF_NEXT_ONBOARDING_DATE, 0); } diff --git a/android/java/org/chromium/chrome/browser/shields/BraveShieldsHandler.java b/android/java/org/chromium/chrome/browser/shields/BraveShieldsHandler.java index 4acbe251b3f5..cd93a46b4c4c 100644 --- a/android/java/org/chromium/chrome/browser/shields/BraveShieldsHandler.java +++ b/android/java/org/chromium/chrome/browser/shields/BraveShieldsHandler.java @@ -193,12 +193,12 @@ public void show(View anchorView, Tab tab) { mBraveRewardsNativeWorker = BraveRewardsNativeWorker.getInstance(); mIconFetcher = new BraveRewardsHelper(tab); - mPopupWindow = showPopupMenu(anchorView, false); + mPopupWindow = showPopupMenu(anchorView); updateValues(mTabId); } - public PopupWindow showPopupMenu(View anchorView, boolean isTooltip) { + public PopupWindow showPopupMenu(View anchorView) { int rotation = ((Activity)mContext).getWindowManager().getDefaultDisplay().getRotation(); // This fixes the bug where the bottom of the menu starts at the top of // the keyboard, instead of overlapping the keyboard as it should. @@ -244,12 +244,8 @@ public PopupWindow showPopupMenu(View anchorView, boolean isTooltip) { LayoutInflater inflater = (LayoutInflater) anchorView.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - if (! isTooltip) { - mPopupView = inflater.inflate(R.layout.brave_shields_main_layout, null); - setUpViews(); - } else { - mPopupView = inflater.inflate(R.layout.brave_shields_tooltip_layout, null); - } + mPopupView = inflater.inflate(R.layout.brave_shields_main_layout, null); + setUpViews(); //Specify the length and width through constants int width; @@ -323,6 +319,15 @@ public int getTackersBlockedCount(int tabId) { return blockersInfo.mTrackersBlocked; } + public int getHttpsUpgradeCount(int tabId) { + if (!mTabsStat.containsKey(tabId)) { + return 0; + } + + BlockersInfo blockersInfo = mTabsStat.get(tabId); + return blockersInfo.mHTTPSUpgrades; + } + public void updateValues(int adsAndTrackers, int httpsUpgrades, int scriptsBlocked, int fingerprintsBlocked) { if (mContext == null) { return; @@ -911,7 +916,7 @@ private void SetFavIcon(Bitmap bmp) { @Override public void run() { ImageView iv = (ImageView) mPopupView.findViewById(R.id.site_favicon); - iv.setImageBitmap(BraveRewardsHelper.getCircularBitmap(bmp)); + if (iv != null) iv.setImageBitmap(BraveRewardsHelper.getCircularBitmap(bmp)); } }); } diff --git a/android/java/org/chromium/chrome/browser/shields/BraveShieldsUtils.java b/android/java/org/chromium/chrome/browser/shields/BraveShieldsUtils.java index 0483036ecb27..d62a0571245e 100644 --- a/android/java/org/chromium/chrome/browser/shields/BraveShieldsUtils.java +++ b/android/java/org/chromium/chrome/browser/shields/BraveShieldsUtils.java @@ -6,102 +6,132 @@ package org.chromium.chrome.browser.shields; import android.content.Context; +import android.content.SharedPreferences; + import org.json.JSONException; import org.json.JSONObject; +import org.chromium.base.ContextUtils; +import org.chromium.base.Log; +import org.chromium.base.ThreadUtils; +import org.chromium.base.task.AsyncTask; +import org.chromium.chrome.browser.ntp_background_images.NTPBackgroundImagesBridge; +import org.chromium.chrome.browser.profiles.Profile; + import java.io.BufferedReader; -import java.io.OutputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; - -import org.chromium.base.Log; -import org.chromium.base.ThreadUtils; -import org.chromium.base.ContextUtils; -import org.chromium.base.task.AsyncTask; -import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.ntp_background_images.NTPBackgroundImagesBridge; +import java.util.Arrays; +import java.util.List; public class BraveShieldsUtils { private static final String TAG = "Shields"; private static final String httpUrl = "https://laptop-updates.brave.com/1/webcompat"; - - public interface BraveShieldsCallback { - void braveShieldsSubmitted(); - } - - public static class BraveShieldsWorkerTask extends AsyncTask { - private String mDomain; - - public BraveShieldsWorkerTask(String domain) { - mDomain = domain; - } - - @Override - protected Void doInBackground() { - sendBraveShieldsFeedback(mDomain); - return null; - } - - @Override - protected void onPostExecute(Void result) { - assert ThreadUtils.runningOnUiThread(); - - if (isCancelled()) return; - } - } - - private static void sendBraveShieldsFeedback(String domain) { - Context context = ContextUtils.getApplicationContext(); - StringBuilder sb = new StringBuilder(); - - Profile mProfile = Profile.getLastUsedRegularProfile(); - NTPBackgroundImagesBridge mNTPBackgroundImagesBridge = NTPBackgroundImagesBridge.getInstance(mProfile); - - HttpURLConnection urlConnection = null; - try { - URL url = new URL(httpUrl); - urlConnection = (HttpURLConnection) url.openConnection(); - urlConnection.setDoOutput(true); - urlConnection.setRequestMethod("POST"); - urlConnection.setUseCaches(false); - urlConnection.setRequestProperty("Content-Type", "application/json"); - urlConnection.connect(); - - JSONObject jsonParam = new JSONObject(); - jsonParam.put("domain", domain); - jsonParam.put("api_key", mNTPBackgroundImagesBridge.getReferralApiKey()); - - OutputStream outputStream = urlConnection.getOutputStream(); - byte[] input = jsonParam.toString().getBytes(StandardCharsets.UTF_8.toString()); - outputStream.write(input, 0, input.length); - outputStream.flush(); - outputStream.close(); - - int HttpResult = urlConnection.getResponseCode(); - if (HttpResult == HttpURLConnection.HTTP_OK) { - BufferedReader br = new BufferedReader(new InputStreamReader( - urlConnection.getInputStream(), StandardCharsets.UTF_8.toString())); - String line = null; - while ((line = br.readLine()) != null) { - sb.append(line + "\n"); - } - br.close(); - } else { - Log.e(TAG, urlConnection.getResponseMessage()); - } - } catch (MalformedURLException e) { - Log.e(TAG, e.getMessage()); - } catch (IOException e) { - Log.e(TAG, e.getMessage()); - } catch (JSONException e) { - Log.e(TAG, e.getMessage()); - } finally { - if (urlConnection != null) - urlConnection.disconnect(); - } - } + public static final String PREF_SHIELDS_TOOLTIP = "shields_tooltip"; + public static final String PREF_SHIELDS_VIDEO_ADS_BLOCKED_TOOLTIP = + "shields_video_ads_blocked_tooltip"; + public static final String PREF_SHIELDS_ADS_TRACKER_BLOCKED_TOOLTIP = + "shields_ads_tracker_blocked_tooltip"; + public static final String PREF_SHIELDS_HTTPS_UPGRADE_TOOLTIP = + "shields_https_upgrade_tooltip"; + + public static final List videoSitesList = + Arrays.asList("youtube.com", "vimeo.com", "twitch.tv"); + public static final List videoSitesListJp = + Arrays.asList("nicovideo.jp", "tiktok.com", "instagram.com"); + + public static boolean isTooltipShown; + + public interface BraveShieldsCallback { + void braveShieldsSubmitted(); + } + + public static boolean hasShieldsTooltipShown(String tooltipType) { + SharedPreferences mSharedPreferences = ContextUtils.getAppSharedPreferences(); + return mSharedPreferences.getBoolean(tooltipType, false); + } + + public static void setShieldsTooltipShown(String tooltipType, boolean isShown) { + SharedPreferences mSharedPreferences = ContextUtils.getAppSharedPreferences(); + SharedPreferences.Editor sharedPreferencesEditor = mSharedPreferences.edit(); + sharedPreferencesEditor.putBoolean(tooltipType, isShown); + sharedPreferencesEditor.apply(); + } + + public static class BraveShieldsWorkerTask extends AsyncTask { + private String mDomain; + + public BraveShieldsWorkerTask(String domain) { + mDomain = domain; + } + + @Override + protected Void doInBackground() { + sendBraveShieldsFeedback(mDomain); + return null; + } + + @Override + protected void onPostExecute(Void result) { + assert ThreadUtils.runningOnUiThread(); + + if (isCancelled()) return; + } + } + + private static void sendBraveShieldsFeedback(String domain) { + Context context = ContextUtils.getApplicationContext(); + StringBuilder sb = new StringBuilder(); + + Profile mProfile = Profile.getLastUsedRegularProfile(); + NTPBackgroundImagesBridge mNTPBackgroundImagesBridge = + NTPBackgroundImagesBridge.getInstance(mProfile); + + HttpURLConnection urlConnection = null; + try { + URL url = new URL(httpUrl); + urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setDoOutput(true); + urlConnection.setRequestMethod("POST"); + urlConnection.setUseCaches(false); + urlConnection.setRequestProperty("Content-Type", "application/json"); + urlConnection.connect(); + + JSONObject jsonParam = new JSONObject(); + jsonParam.put("domain", domain); + jsonParam.put("api_key", mNTPBackgroundImagesBridge.getReferralApiKey()); + + OutputStream outputStream = urlConnection.getOutputStream(); + byte[] input = jsonParam.toString().getBytes(StandardCharsets.UTF_8.toString()); + outputStream.write(input, 0, input.length); + outputStream.flush(); + outputStream.close(); + + int HttpResult = urlConnection.getResponseCode(); + if (HttpResult == HttpURLConnection.HTTP_OK) { + BufferedReader br = new BufferedReader(new InputStreamReader( + urlConnection.getInputStream(), StandardCharsets.UTF_8.toString())); + String line = null; + while ((line = br.readLine()) != null) { + sb.append(line + "\n"); + } + br.close(); + } else { + Log.e(TAG, urlConnection.getResponseMessage()); + } + } catch (MalformedURLException e) { + Log.e(TAG, e.getMessage()); + } catch (IOException e) { + Log.e(TAG, e.getMessage()); + } catch (JSONException e) { + Log.e(TAG, e.getMessage()); + } finally { + if (urlConnection != null) urlConnection.disconnect(); + } + } } \ No newline at end of file diff --git a/android/java/org/chromium/chrome/browser/shields/ShieldsTooltipEnum.java b/android/java/org/chromium/chrome/browser/shields/ShieldsTooltipEnum.java new file mode 100644 index 000000000000..ca946aebbaed --- /dev/null +++ b/android/java/org/chromium/chrome/browser/shields/ShieldsTooltipEnum.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.chromium.chrome.browser.shields; + +import org.chromium.chrome.R; + +public enum ShieldsTooltipEnum { + ONE_TIME_ADS_TRACKER_BLOCKED_TOOLTIP( + ShieldsTooltipEnumConstants.ONE_TIME_ADS_TRACKER_BLOCKED_TOOLTIP, + R.string.tooltip_title_1, R.string.tooltip_text_1, + R.color.shields_tooltip_arrow_color_1, R.drawable.shields_tooltip_background_1), + VIDEO_ADS_BLOCKED_TOOLTIP(ShieldsTooltipEnumConstants.VIDEO_ADS_BLOCKED_TOOLTIP, + R.string.tooltip_title_2, R.string.tooltip_text_2, + R.color.shields_tooltip_arrow_color_2, R.drawable.shields_tooltip_background_2), + ADS_TRACKER_BLOCKED_TOOLTIP(ShieldsTooltipEnumConstants.ADS_TRACKER_BLOCKED_TOOLTIP, + R.string.tooltip_title_3, R.string.tooltip_text_3, + R.color.shields_tooltip_arrow_color_2, R.drawable.shields_tooltip_background_2), + HTTPS_UPGRADE_TOOLTIP(ShieldsTooltipEnumConstants.HTTPS_UPGRADE_TOOLTIP, + R.string.tooltip_title_4, R.string.tooltip_text_4, + R.color.shields_tooltip_arrow_color_2, R.drawable.shields_tooltip_background_2); + + private int id; + private int title; + private int text; + private int arrowColor; + private int tooltipBackground; + + ShieldsTooltipEnum(int id, int title, int text, int arrowColor, int tooltipBackground) { + this.id = id; + this.title = title; + this.text = text; + this.arrowColor = arrowColor; + this.tooltipBackground = tooltipBackground; + } + + public int getId() { + return id; + } + + public int getTitle() { + return title; + } + + public int getText() { + return text; + } + + public int getArrowColor() { + return arrowColor; + } + + public int getTooltipBackground() { + return tooltipBackground; + } + + interface ShieldsTooltipEnumConstants { + static final int ONE_TIME_ADS_TRACKER_BLOCKED_TOOLTIP = 0; + static final int VIDEO_ADS_BLOCKED_TOOLTIP = 1; + static final int ADS_TRACKER_BLOCKED_TOOLTIP = 2; + static final int HTTPS_UPGRADE_TOOLTIP = 3; + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/top/BraveToolbarLayout.java b/android/java/org/chromium/chrome/browser/toolbar/top/BraveToolbarLayout.java index d3ad4988c692..560448a99897 100644 --- a/android/java/org/chromium/chrome/browser/toolbar/top/BraveToolbarLayout.java +++ b/android/java/org/chromium/chrome/browser/toolbar/top/BraveToolbarLayout.java @@ -16,14 +16,18 @@ import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; +import android.text.Spannable; import android.text.SpannableString; +import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextPaint; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.text.style.ForegroundColorSpan; +import android.text.style.ImageSpan; import android.util.AttributeSet; import android.util.Pair; +import android.view.Gravity; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; @@ -38,6 +42,7 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.AppCompatImageView; +import androidx.core.content.ContextCompat; import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.BraveReflectionUtil; @@ -56,6 +61,8 @@ import org.chromium.chrome.browser.BraveRewardsPanelPopup; import org.chromium.chrome.browser.app.BraveActivity; import org.chromium.chrome.browser.brave_stats.BraveStatsUtil; +import org.chromium.chrome.browser.custom_layout.popup_window_tooltip.PopupWindowTooltip; +import org.chromium.chrome.browser.custom_layout.popup_window_tooltip.PopupWindowTooltipUtils; import org.chromium.chrome.browser.customtabs.CustomTabActivity; import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbar; import org.chromium.chrome.browser.dialogs.BraveAdsSignupDialog; @@ -78,6 +85,8 @@ import org.chromium.chrome.browser.settings.BraveSearchEngineUtils; import org.chromium.chrome.browser.shields.BraveShieldsHandler; import org.chromium.chrome.browser.shields.BraveShieldsMenuObserver; +import org.chromium.chrome.browser.shields.BraveShieldsUtils; +import org.chromium.chrome.browser.shields.ShieldsTooltipEnum; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabImpl; import org.chromium.chrome.browser.tab.TabSelectionType; @@ -107,11 +116,13 @@ import java.util.Calendar; import java.util.Date; import java.util.List; +import java.util.Locale; public abstract class BraveToolbarLayout extends ToolbarLayout implements OnClickListener, View.OnLongClickListener, BraveRewardsObserver, BraveRewardsNativeWorker.PublisherObserver { public static final String PREF_HIDE_BRAVE_REWARDS_ICON = "hide_brave_rewards_icon"; + private static final String JAPAN_COUNTRY_CODE = "JP"; private static final long MB_10 = 10000000; private static final long MINUTES_10 = 10 * 60 * 1000; @@ -141,7 +152,7 @@ public abstract class BraveToolbarLayout extends ToolbarLayout private boolean mIsNotificationPosted; private boolean mIsInitialNotificationPosted; // initial red circle notification - private PopupWindow mShieldsTooltipPopupWindow; + private PopupWindowTooltip mShieldsPopupWindowTooltip; private boolean mIsBottomToolbarVisible; @@ -235,21 +246,9 @@ public void blockEvent(int tabId, String block_type, String subresource) { .RESOURCE_IDENTIFIER_TRACKERS))) { addStatsToDb(block_type, subresource, currentTab.getUrlString()); } - if (!OnboardingPrefManager.getInstance().hasShieldsTooltipShown() - && PackageUtils.isFirstInstall(getContext())) { - mShieldsTooltipPopupWindow = - mBraveShieldsHandler.showPopupMenu(mBraveShieldsButton, true); - OnboardingPrefManager.getInstance().setShieldsTooltipShown(true); - mShieldsTooltipPopupWindow.getContentView().setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View view) { - mShieldsTooltipPopupWindow.dismiss(); - mShieldsTooltipPopupWindow = null; - showShieldsMenu(mBraveShieldsButton); - } - }); - } + if (mBraveShieldsButton != null && mBraveShieldsHandler != null + && !mBraveShieldsHandler.isShowing()) + checkForTooltip(currentTab); } @Override @@ -398,9 +397,110 @@ public void didSelectTab(Tab tab, @TabSelectionType int type, int lastId) { }; } + private void checkForTooltip(Tab tab) { + if (!BraveShieldsUtils.isTooltipShown) { + if (!BraveShieldsUtils.hasShieldsTooltipShown(BraveShieldsUtils.PREF_SHIELDS_TOOLTIP) + && mBraveShieldsHandler.getTackersBlockedCount(tab.getId()) + + mBraveShieldsHandler.getAdsBlockedCount(tab.getId()) + > 0) { + showTooltip(ShieldsTooltipEnum.ONE_TIME_ADS_TRACKER_BLOCKED_TOOLTIP); + BraveShieldsUtils.setShieldsTooltipShown( + BraveShieldsUtils.PREF_SHIELDS_TOOLTIP, true); + } else if (!BraveShieldsUtils.hasShieldsTooltipShown( + BraveShieldsUtils.PREF_SHIELDS_VIDEO_ADS_BLOCKED_TOOLTIP) + && shouldShowVideoTooltip(tab.getUrlString())) { + showTooltip(ShieldsTooltipEnum.VIDEO_ADS_BLOCKED_TOOLTIP); + BraveShieldsUtils.setShieldsTooltipShown( + BraveShieldsUtils.PREF_SHIELDS_VIDEO_ADS_BLOCKED_TOOLTIP, true); + } else if (!BraveShieldsUtils.hasShieldsTooltipShown( + BraveShieldsUtils.PREF_SHIELDS_ADS_TRACKER_BLOCKED_TOOLTIP) + && mBraveShieldsHandler.getTackersBlockedCount(tab.getId()) + + mBraveShieldsHandler.getAdsBlockedCount(tab.getId()) + > 10) { + showTooltip(ShieldsTooltipEnum.ADS_TRACKER_BLOCKED_TOOLTIP); + BraveShieldsUtils.setShieldsTooltipShown( + BraveShieldsUtils.PREF_SHIELDS_ADS_TRACKER_BLOCKED_TOOLTIP, true); + } else if (!BraveShieldsUtils.hasShieldsTooltipShown( + BraveShieldsUtils.PREF_SHIELDS_HTTPS_UPGRADE_TOOLTIP) + && mBraveShieldsHandler.getHttpsUpgradeCount(tab.getId()) > 0) { + showTooltip(ShieldsTooltipEnum.HTTPS_UPGRADE_TOOLTIP); + BraveShieldsUtils.setShieldsTooltipShown( + BraveShieldsUtils.PREF_SHIELDS_HTTPS_UPGRADE_TOOLTIP, true); + } + } + } + + private boolean shouldShowVideoTooltip(String tabUrl) { + try { + URL url = new URL(tabUrl); + for (String videoUrl : BraveShieldsUtils.videoSitesList) { + if (url.getHost().contains(videoUrl)) { + return true; + } + } + String countryCode = Locale.getDefault().getCountry(); + if (countryCode.equals(JAPAN_COUNTRY_CODE)) { + for (String videoUrl : BraveShieldsUtils.videoSitesListJp) { + if (url.getHost().contains(videoUrl)) { + return true; + } + } + } + return false; + } catch (Exception ex) { + // Do nothing if url is invalid. + return false; + } + } + + private void showTooltip(ShieldsTooltipEnum shieldsTooltipEnum) { + mShieldsPopupWindowTooltip = + new PopupWindowTooltip.Builder(getContext()) + .anchorView(mBraveShieldsButton) + .arrowColor(getResources().getColor(shieldsTooltipEnum.getArrowColor())) + .gravity(Gravity.BOTTOM) + .dismissOnOutsideTouch(true) + .dismissOnInsideTouch(false) + .modal(true) + .contentView(R.layout.brave_shields_tooltip_layout) + .build(); + mShieldsPopupWindowTooltip.findViewById(R.id.shields_tooltip_layout) + .setBackgroundDrawable(ContextCompat.getDrawable( + getContext(), shieldsTooltipEnum.getTooltipBackground())); + + if (shieldsTooltipEnum == ShieldsTooltipEnum.ONE_TIME_ADS_TRACKER_BLOCKED_TOOLTIP) { + Button btnTooltip = mShieldsPopupWindowTooltip.findViewById(R.id.btn_tooltip); + btnTooltip.setVisibility(View.VISIBLE); + btnTooltip.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + dismissShieldsTooltip(); + showShieldsMenu(mBraveShieldsButton); + } + }); + } + + TextView tooltipTitle = mShieldsPopupWindowTooltip.findViewById(R.id.txt_tooltip_title); + SpannableStringBuilder ssb = + new SpannableStringBuilder(new StringBuilder("\t\t") + .append(getContext().getResources().getString( + shieldsTooltipEnum.getTitle())) + .toString()); + ssb.setSpan(new ImageSpan(getContext(), R.drawable.ic_shield_done_filled_20dp), 0, 1, + Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + tooltipTitle.setText(ssb, TextView.BufferType.SPANNABLE); + + TextView tooltipText = mShieldsPopupWindowTooltip.findViewById(R.id.txt_tooltip_text); + tooltipText.setText(getContext().getResources().getString(shieldsTooltipEnum.getText())); + + mShieldsPopupWindowTooltip.show(); + BraveShieldsUtils.isTooltipShown = true; + } + public void dismissShieldsTooltip() { - if (mShieldsTooltipPopupWindow != null) { - mShieldsTooltipPopupWindow.dismiss(); + if (mShieldsPopupWindowTooltip != null && mShieldsPopupWindowTooltip.isShowing()) { + mShieldsPopupWindowTooltip.dismiss(); + mShieldsPopupWindowTooltip = null; } } @@ -414,6 +514,7 @@ public void reopenShieldsPanel() { @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); + dismissShieldsTooltip(); reopenShieldsPanel(); } diff --git a/android/java/res/drawable/ic_shield_done_filled_20dp.xml b/android/java/res/drawable/ic_shield_done_filled_20dp.xml new file mode 100644 index 000000000000..271c71561f2b --- /dev/null +++ b/android/java/res/drawable/ic_shield_done_filled_20dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/android/java/res/drawable/shields_tooltip_background.xml b/android/java/res/drawable/shields_tooltip_background_1.xml similarity index 90% rename from android/java/res/drawable/shields_tooltip_background.xml rename to android/java/res/drawable/shields_tooltip_background_1.xml index b4409b9d5b35..b8bf957c35fb 100644 --- a/android/java/res/drawable/shields_tooltip_background.xml +++ b/android/java/res/drawable/shields_tooltip_background_1.xml @@ -7,6 +7,7 @@ android:startColor="#F73A1C" android:endColor="#BF14A2" android:type="linear" /> + \ No newline at end of file diff --git a/android/java/res/drawable/shields_tooltip_background_2.xml b/android/java/res/drawable/shields_tooltip_background_2.xml new file mode 100644 index 000000000000..eaf3b8c9b93e --- /dev/null +++ b/android/java/res/drawable/shields_tooltip_background_2.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/java/res/drawable/white_rounded_holo_button.xml b/android/java/res/drawable/white_rounded_holo_button.xml new file mode 100644 index 000000000000..93a3ce4c939d --- /dev/null +++ b/android/java/res/drawable/white_rounded_holo_button.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/android/java/res/layout/brave_shields_tooltip_layout.xml b/android/java/res/layout/brave_shields_tooltip_layout.xml index 6be428370cd0..1192489dbe7a 100644 --- a/android/java/res/layout/brave_shields_tooltip_layout.xml +++ b/android/java/res/layout/brave_shields_tooltip_layout.xml @@ -6,39 +6,42 @@ + android:layout_height="wrap_content"> - +