diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java
index 46e4141d8..402ed2cad 100644
--- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java
+++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java
@@ -5,17 +5,23 @@
package org.mozilla.vrbrowser.ui.views;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.util.AttributeSet;
-
-import org.mozilla.vrbrowser.R;
+import android.view.MotionEvent;
import androidx.annotation.IdRes;
import androidx.appcompat.widget.AppCompatImageButton;
+import org.mozilla.vrbrowser.R;
+import org.mozilla.vrbrowser.ui.widgets.TooltipWidget;
+import org.mozilla.vrbrowser.ui.widgets.UIWidget;
+import org.mozilla.vrbrowser.utils.ViewUtils;
+
public class UIButton extends AppCompatImageButton implements CustomUIButton {
private enum State {
@@ -31,7 +37,12 @@ private enum State {
private @IdRes int mTintColorListRes;
private @IdRes int mPrivateModeTintColorListRes;
private @IdRes int mActiveModeTintColorListRes;
+ private TooltipWidget mTooltipView;
+ private String mTooltipText;
private State mState;
+ private int mTooltipDelay;
+ private float mTooltipDensity;
+ private ViewUtils.TooltipPosition mTooltipPosition;
public UIButton(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.imageButtonStyle);
@@ -45,22 +56,17 @@ public UIButton(Context context, AttributeSet attrs, int defStyleAttr) {
if (mTintColorListRes != 0) {
setTintColorList(mTintColorListRes);
}
- attributes.recycle();
-
- attributes = context.obtainStyledAttributes(attrs, R.styleable.UIButton, defStyleAttr, 0);
mPrivateModeBackground = attributes.getDrawable(R.styleable.UIButton_privateModeBackground);
- attributes.recycle();
-
- attributes = context.obtainStyledAttributes(attrs, R.styleable.UIButton, defStyleAttr, 0);
mActiveModeBackground = attributes.getDrawable(R.styleable.UIButton_activeModeBackground);
- attributes.recycle();
-
- attributes = context.obtainStyledAttributes(attrs, R.styleable.UIButton, defStyleAttr, 0);
mPrivateModeTintColorListRes = attributes.getResourceId(R.styleable.UIButton_privateModeTintColorList, 0);
- attributes.recycle();
-
- attributes = context.obtainStyledAttributes(attrs, R.styleable.UIButton, defStyleAttr, 0);
mActiveModeTintColorListRes = attributes.getResourceId(R.styleable.UIButton_activeModeTintColorList, 0);
+ mTooltipDelay = attributes.getInt(R.styleable.UIButton_tooltipDelay, getResources().getInteger(R.integer.tooltip_delay));
+ mTooltipPosition = ViewUtils.TooltipPosition.fromId(attributes.getInt(R.styleable.UIButton_tooltipPosition, ViewUtils.TooltipPosition.BOTTOM.ordinal()));
+ mTooltipDensity = attributes.getFloat(R.styleable.UIButton_tooltipDensity, getContext().getResources().getDisplayMetrics().density);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+ TypedArray arr = context.obtainStyledAttributes(attrs, new int [] {android.R.attr.tooltipText});
+ mTooltipText = arr.getString(0);
+ }
attributes.recycle();
mBackground = getBackground();
@@ -68,6 +74,37 @@ public UIButton(Context context, AttributeSet attrs, int defStyleAttr) {
mState = State.NORMAL;
}
+ @TargetApi(Build.VERSION_CODES.O)
+ public String getTooltip() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+ return mTooltipText;
+ else
+ return getTooltipText().toString();
+ }
+
+ @TargetApi(Build.VERSION_CODES.O)
+ public void setTooltip(String text) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+ mTooltipText = text;
+ else
+ setTooltipText(text);
+ }
+
+ @Override
+ public boolean onHoverEvent(MotionEvent event) {
+ if (getTooltip() != null) {
+ if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
+ getHandler().postDelayed(mShowTooltipRunnable, mTooltipDelay);
+
+ } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
+ getHandler().removeCallbacks(mShowTooltipRunnable);
+ getHandler().post(mHideTooltipRunnable);
+ }
+ }
+
+ return super.onHoverEvent(event);
+ }
+
public void setTintColorList(int aColorListId) {
mTintColorList = getContext().getResources().getColorStateList(
aColorListId,
@@ -144,4 +181,26 @@ private void setActive() {
}
}
+ private Runnable mShowTooltipRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mTooltipView != null && mTooltipView.isVisible())
+ return;
+
+ mTooltipView = new TooltipWidget(getContext());
+ mTooltipView.setText(getTooltip());
+ mTooltipView.setLayoutParams(UIButton.this, mTooltipPosition, mTooltipDensity);
+ mTooltipView.show(UIWidget.CLEAR_FOCUS);
+ }
+ };
+
+ private Runnable mHideTooltipRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mTooltipView != null) {
+ mTooltipView.hide(UIWidget.REMOVE_WIDGET);
+ }
+ }
+ };
+
}
diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TooltipWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TooltipWidget.java
new file mode 100644
index 000000000..b86f37a85
--- /dev/null
+++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TooltipWidget.java
@@ -0,0 +1,101 @@
+package org.mozilla.vrbrowser.ui.widgets;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.view.View;
+import android.widget.TextView;
+
+import org.mozilla.vrbrowser.R;
+import org.mozilla.vrbrowser.utils.ViewUtils;
+
+public class TooltipWidget extends UIWidget {
+
+ private View mTargetView;
+ private UIWidget mParentWidget;
+ protected TextView mText;
+ private PointF mTranslation;
+ private float mRatio;
+ private float mDensityRatio;
+
+ public TooltipWidget(Context aContext) {
+ super(aContext);
+
+ initialize();
+ }
+
+ private void initialize() {
+ inflate(getContext(), R.layout.tooltip, this);
+
+ mText = findViewById(R.id.tooltipText);
+ }
+
+ @Override
+ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) {
+ aPlacement.visible = false;
+ aPlacement.width = 0;
+ aPlacement.height = 0;
+ aPlacement.parentAnchorX = 0.0f;
+ aPlacement.parentAnchorY = 1.0f;
+ aPlacement.anchorX = 0.5f;
+ aPlacement.anchorY = 0.5f;
+ aPlacement.translationZ = WidgetPlacement.unitFromMeters(getContext(), R.dimen.tooltip_z_distance);
+ }
+
+ @Override
+ public void show(@ShowFlags int aShowFlags) {
+ measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mWidgetPlacement.translationX = mTranslation.x * (mRatio / mWidgetPlacement.density);
+ mWidgetPlacement.translationY = mTranslation.y * (mRatio / mWidgetPlacement.density);
+ int paddingH = getPaddingStart() + getPaddingEnd();
+ int paddingV = getPaddingTop() + getPaddingBottom();
+ mWidgetPlacement.width = (int)(WidgetPlacement.convertPixelsToDp(getContext(), getMeasuredWidth() + paddingH)/mDensityRatio);
+ mWidgetPlacement.height = (int)(WidgetPlacement.convertPixelsToDp(getContext(), getMeasuredHeight() + paddingV)/mDensityRatio);
+
+ super.show(aShowFlags);
+ }
+
+ public void setLayoutParams(View targetView) {
+ this.setLayoutParams(targetView, ViewUtils.TooltipPosition.BOTTOM);
+ }
+
+ public void setLayoutParams(View targetView, ViewUtils.TooltipPosition position) {
+ this.setLayoutParams(targetView, position, mWidgetPlacement.density);
+ }
+
+ public void setLayoutParams(View targetView, ViewUtils.TooltipPosition position, float density) {
+ mTargetView = targetView;
+ mParentWidget = ViewUtils.getParentWidget(mTargetView);
+ if (mParentWidget != null) {
+ mRatio = WidgetPlacement.worldToWidgetRatio(mParentWidget);
+ mWidgetPlacement.density = density;
+ mDensityRatio = mWidgetPlacement.density / getContext().getResources().getDisplayMetrics().density;
+
+ Rect offsetViewBounds = new Rect();
+ getDrawingRect(offsetViewBounds);
+ mParentWidget.offsetDescendantRectToMyCoords(mTargetView, offsetViewBounds);
+
+ mWidgetPlacement.parentHandle = mParentWidget.getHandle();
+ // At the moment we only support showing tooltips on top or bottom of the target view
+ if (position == ViewUtils.TooltipPosition.BOTTOM) {
+ mWidgetPlacement.anchorY = 1.0f;
+ mWidgetPlacement.parentAnchorY = 0.0f;
+ mTranslation = new PointF(
+ (offsetViewBounds.left + mTargetView.getWidth() / 2) * mDensityRatio,
+ -offsetViewBounds.top * mDensityRatio);
+ } else {
+ mWidgetPlacement.anchorY = 0.0f;
+ mWidgetPlacement.parentAnchorY = 1.0f;
+ mTranslation = new PointF(
+ (offsetViewBounds.left + mTargetView.getWidth() / 2) * mDensityRatio,
+ offsetViewBounds.top * mDensityRatio);
+ }
+ }
+ }
+
+ public void setText(String text) {
+ mText.setText(text);
+ }
+
+}
diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java
index 7b99a9deb..8a033bb04 100644
--- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java
+++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java
@@ -248,10 +248,12 @@ private void handleSessionState() {
if (isPrivateMode) {
mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS);
mPrivateButton.setImageResource(R.drawable.ic_icon_private_browsing_on);
+ mPrivateButton.setTooltip(getResources().getString(R.string.private_browsing_exit_tooltip));
} else {
mWidgetManager.popWorldBrightness(this);
mPrivateButton.setImageResource(R.drawable.ic_icon_private_browsing);
+ mPrivateButton.setTooltip(getResources().getString(R.string.private_browsing_enter_tooltip));
}
}
@@ -329,11 +331,13 @@ private void onHelpButtonClicked() {
@Override
public void onBookmarksShown() {
+ mBookmarksButton.setTooltip(getResources().getString(R.string.close_bookmarks_tooltip));
mBookmarksButton.setActiveMode(true);
}
@Override
public void onBookmarksHidden() {
+ mBookmarksButton.setTooltip(getResources().getString(R.string.open_bookmarks_tooltip));
mBookmarksButton.setActiveMode(false);
}
diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetPlacement.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetPlacement.java
index be907f509..7a15daff0 100644
--- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetPlacement.java
+++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetPlacement.java
@@ -10,6 +10,8 @@
import android.util.DisplayMetrics;
import android.util.TypedValue;
+import androidx.annotation.NonNull;
+
public class WidgetPlacement {
static final float WORLD_DPI_RATIO = 2.0f/720.0f;
@@ -126,4 +128,9 @@ public static float convertPixelsToDp(Context aContext, float px){
return px / ((float) aContext.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}
+ public static float worldToWidgetRatio(@NonNull UIWidget widget) {
+ float widgetWorldWidth = widget.mWidgetPlacement.worldWidth;
+ return ((widgetWorldWidth/widget.mWidgetPlacement.width)/WORLD_DPI_RATIO);
+ }
+
}
diff --git a/app/src/common/shared/org/mozilla/vrbrowser/utils/ViewUtils.java b/app/src/common/shared/org/mozilla/vrbrowser/utils/ViewUtils.java
new file mode 100644
index 000000000..07b3cb65e
--- /dev/null
+++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/ViewUtils.java
@@ -0,0 +1,44 @@
+package org.mozilla.vrbrowser.utils;
+
+import android.view.View;
+import android.view.ViewParent;
+
+import androidx.annotation.NonNull;
+
+import org.mozilla.vrbrowser.ui.widgets.UIWidget;
+
+public class ViewUtils {
+
+ public enum TooltipPosition {
+ TOP(0), BOTTOM(1);
+ int id;
+
+ TooltipPosition(int id) {
+ this.id = id;
+ }
+
+ public static TooltipPosition fromId(int id) {
+ for (TooltipPosition f : values()) {
+ if (f.id == id) return f;
+ }
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public static UIWidget getParentWidget(@NonNull View view) {
+ if (view == null)
+ return null;
+
+ ViewParent v = view.getParent();
+ if (v instanceof UIWidget) {
+ return (UIWidget)v;
+
+ } else if (v instanceof View){
+ return getParentWidget((View)v);
+
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/app/src/main/res/drawable/tooltip_background.xml b/app/src/main/res/drawable/tooltip_background.xml
new file mode 100644
index 000000000..85a6c45b6
--- /dev/null
+++ b/app/src/main/res/drawable/tooltip_background.xml
@@ -0,0 +1,16 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/navigation_bar.xml b/app/src/main/res/layout/navigation_bar.xml
index 5738ac407..42d149850 100644
--- a/app/src/main/res/layout/navigation_bar.xml
+++ b/app/src/main/res/layout/navigation_bar.xml
@@ -1,5 +1,6 @@
-
+
@@ -85,6 +92,7 @@
diff --git a/app/src/main/res/layout/tooltip.xml b/app/src/main/res/layout/tooltip.xml
new file mode 100644
index 000000000..c014e9506
--- /dev/null
+++ b/app/src/main/res/layout/tooltip.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/tray.xml b/app/src/main/res/layout/tray.xml
index 4805dc8d7..9ddbe97f3 100644
--- a/app/src/main/res/layout/tray.xml
+++ b/app/src/main/res/layout/tray.xml
@@ -1,5 +1,6 @@
-
+
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index a9e831577..c042640a9 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -20,9 +20,18 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 4fe175ae2..a4d267985 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -31,7 +31,9 @@
#5d5d5d
#805d5d5d
#8c898a
+ #808c898a
#d73e5a
#8F000000
#7FFFFFFF
+ #f5f6fa
diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml
index 148d25e0c..a0cd6170b 100644
--- a/app/src/main/res/values/dimen.xml
+++ b/app/src/main/res/values/dimen.xml
@@ -121,6 +121,7 @@
- 1.2
165dp
40dp
+ - 4.0
585dp
@@ -164,6 +165,7 @@
30dp
+ 28sp
24sp
22sp
18sp
@@ -187,4 +189,8 @@
2dp
1dp
+
+
+ - 0.02
+ - 1000
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e14e3d6d5..d73c973e1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -629,10 +629,22 @@
speech-to-text recognition. -->
Voice Search
+
+ User Agent
+
Show site info.
+
+ Use Servo
+
+
+ Resize
+
Enter Private Browsing
@@ -644,11 +656,32 @@
Open in a new window.
+
+ Bookmark this page
+
Bookmarks
+
+ Open Bookmarks
+
+
+ Close Bookmarks
+
+
+ Settings
+
+
+ Open a New Window
+