Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Added support for tooltips #1400

Merged
merged 3 commits into from
Jul 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 73 additions & 14 deletions app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);
Expand All @@ -45,29 +56,55 @@ 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();

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,
Expand Down Expand Up @@ -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);
}
}
};

}
Original file line number Diff line number Diff line change
@@ -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;
keianhzo marked this conversation as resolved.
Show resolved Hide resolved
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);
keianhzo marked this conversation as resolved.
Show resolved Hide resolved
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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}

Expand Down Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}

}
44 changes: 44 additions & 0 deletions app/src/common/shared/org/mozilla/vrbrowser/utils/ViewUtils.java
Original file line number Diff line number Diff line change
@@ -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;
}
}

}
16 changes: 16 additions & 0 deletions app/src/main/res/drawable/tooltip_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="30dp" />
<stroke android:width="@dimen/blur_radius" android:color="@color/fog_blur" />
</shape>
</item>
<item android:start="@dimen/blur_radius_half" android:end="@dimen/blur_radius_half" android:top="@dimen/blur_radius_half" android:bottom="@dimen/blur_radius_half">
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="30dp" />
<solid android:color="@color/rhino" />
<stroke android:width="2dp" android:color="@color/rhino_blur" />
</shape>
</item>
</layer-list>
Loading