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
Browse files Browse the repository at this point in the history
wip
  • Loading branch information
keianhzo committed Jul 15, 2019
1 parent abdbc36 commit 82d1c8f
Show file tree
Hide file tree
Showing 14 changed files with 352 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,120 @@
package org.mozilla.vrbrowser.ui.widgets;

import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
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 int mPaddingH;
private int mPaddingV;

public TooltipWidget(Context aContext) {
super(aContext);

initialize();
}

private void initialize() {
inflate(getContext(), R.layout.tooltip, this);

mText = findViewById(R.id.tooltipText);

ViewGroup layout = findViewById(R.id.layout);
mPaddingH = layout.getPaddingStart() + layout.getPaddingEnd();
mPaddingV = layout.getPaddingTop() + layout.getPaddingBottom();
}

@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);
mWidgetPlacement.width = (int)((getMeasuredWidth() + mPaddingH)/mWidgetPlacement.density);
mWidgetPlacement.height = (int)((getMeasuredHeight() + mPaddingV)/mWidgetPlacement.density);
super.show(aShowFlags);

ViewTreeObserver viewTreeObserver = getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
getViewTreeObserver().removeOnGlobalLayoutListener(this);
mWidgetPlacement.width = (int)(getWidth() / mWidgetPlacement.density);
mWidgetPlacement.height = (int)(getHeight() / mWidgetPlacement.density);
mWidgetManager.updateWidget(TooltipWidget.this);
}
});
}
}

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);

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.density = density;
mWidgetPlacement.anchorY = 1.0f;
mWidgetPlacement.parentAnchorY = 0.0f;
float densityRatio = mWidgetPlacement.density / getContext().getResources().getDisplayMetrics().density;
mTranslation = new PointF(
(offsetViewBounds.left + mTargetView.getWidth() / 2) * densityRatio,
-offsetViewBounds.top * densityRatio);
} else {
mWidgetPlacement.density = density;
mWidgetPlacement.anchorY = 0.0f;
mWidgetPlacement.parentAnchorY = 1.0f;
float densityRatio = mWidgetPlacement.density / getContext().getResources().getDisplayMetrics().density;
mTranslation = new PointF(
(offsetViewBounds.left + mTargetView.getWidth() / 2) * densityRatio,
offsetViewBounds.top * densityRatio);
}
}
}

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;
}
}

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

0 comments on commit 82d1c8f

Please sign in to comment.