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

Commit

Permalink
Added support for tooltips (#1400)
Browse files Browse the repository at this point in the history
* Added support for tooltips

* Improved widget size calculation and layouting

* Fixed visual issues
  • Loading branch information
keianhzo authored and MortimerGoro committed Jul 19, 2019
1 parent 3540f71 commit ed251cf
Show file tree
Hide file tree
Showing 14 changed files with 347 additions and 17 deletions.
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;
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);
}

}
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

0 comments on commit ed251cf

Please sign in to comment.