/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.samsung.spensdk.example.tools;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Paint.Cap;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
import com.samsung.spensdk.example.R;
public class ToolHoverPopup implements View.OnHoverListener{
static final boolean DEBUG = true;
static final String TAG = "HoverPopup";
private static final int MSG_SHOW_POPUP = 1;
private static final int MSG_DISMISS_POPUP = 2;
private static final int HOVER_DETECT_TIME_MS = 300;
private static final int POPUP_TIMEOUT_MS = 10 * 1000;
public static final int TYPE_NONE = 0; // turn off HoverPopup
public static final int TYPE_TOOLTIP = 1; // toolTip type
public static final int TYPE_USER_CUSTOM = 2;
private static final int ID_TOOLTIP_VIEW = 0x07010001;
protected final int MARGIN_FOR_HOVER_RING = 8; // 8dp
private HoverPopupHandler mHandler;
protected final Context mContext;
protected final View mParentView;
protected PopupWindow mPopup;
protected int mPopupType = TYPE_NONE;
protected boolean mEnabled;
protected int mHoverDetectTimeMS;
protected int mPopupGravity;
protected int mPopupPosX;
protected int mPopupPosY;
protected int mHoveringPointX;
protected int mHoveringPointY;
protected int mContentResId;
protected int mAnimationStyle;
protected View mContentView;
protected ViewGroup.LayoutParams mContentLP;
protected HoverPopupContainer mContentContainer;
protected CharSequence mContentText;
protected boolean mIsGuideLineEnabled;
protected int mGuideLineFadeOffset;
protected int mPopupOffsetX;
protected int mPopupOffsetY;
protected int mGuideRingDrawableId;
protected int mGuideLineColor;
protected int mWindowGapX;
protected int mWindowGapY;
/**
* Basic constructor to instantiate
*
* @param parentView The view that created/made this class
*/
@Deprecated
public ToolHoverPopup(View parentView) {
this(parentView, TYPE_TOOLTIP);
}
/**
* Basic constructor to instantiate
*
* @param parentView The view that created/made this class
* @param type The type of HoverPopup Window, it can be one of the "TYPE_NONE",
*/
public ToolHoverPopup(View parentView, int type) {
mParentView = parentView;
mContext = parentView.getContext();
mPopupType = type;
initInstance();
registerHover();
}
private void registerHover(){
mParentView.setOnHoverListener(this);
}
public boolean onHover(View v, MotionEvent event){
final int action = event.getAction();
if (action == MotionEvent.ACTION_HOVER_ENTER){
setHoveringPoint((int)event.getRawX(), (int)event.getRawY());
show();
} else if (action == MotionEvent.ACTION_HOVER_MOVE){
setHoveringPoint((int) event.getRawX(), (int) event.getRawY());
// update popup for moving guide line
if (mIsGuideLineEnabled && isShowing()) {
View popupView = mPopup.getContentView();
if (popupView instanceof HoverPopupContainer) {
((HoverPopupContainer) mPopup.getContentView()).setGuideLineEndPoint(
(int) event.getRawX() - mPopupPosX - mWindowGapX, (int) event.getRawY() - mPopupPosY - mWindowGapY);
((HoverPopupContainer) popupView).updateDecoration();
}
}
} else if (action == MotionEvent.ACTION_HOVER_EXIT){
dismiss();
}
return true;
}
/**
* Initialize instances
*/
protected void initInstance() {
mPopup = null;
mEnabled = true;
mHoverDetectTimeMS = HOVER_DETECT_TIME_MS;
mPopupGravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP_ABOVE;
mPopupPosX = 0;
mPopupPosY = 0;
mHoveringPointX = 0;
mHoveringPointY = 0;
mPopupOffsetX = 0;
mPopupOffsetY = 0;
mWindowGapX = 0;
mWindowGapY = 0;
mContentText = null;
mHandler = null;
mAnimationStyle = 0;
mIsGuideLineEnabled = false;
mGuideLineFadeOffset = 0;
mContentView = null;
mContentContainer = null;
mGuideRingDrawableId = R.drawable.hover_ic_point_normal;
mGuideLineColor = 0xFF9ca2a9;
}
/**
* Check the condition that needs for showing.
*
* @return true if HoverPopup can be shown, false if showing condition is not satisfied.
*/
public boolean isHoverPopupPossible() {
boolean ret = true;
if (mPopupType == TYPE_NONE) {
ret = false;
} else if (mPopupType == TYPE_TOOLTIP) {
if (mParentView == null || TextUtils.isEmpty(getTooltipText())) {
ret = false;
}
}
return ret;
}
@Deprecated
public void setEnabled(boolean enabled) {
mEnabled = enabled;
}
@Deprecated
public boolean getEnabled() {
return mEnabled;
}
/**
* Returns the parent View that called/made this HoverPopup.
*
* @return a ViewParent or null if this ViewParent does not have a parent
*/
public View getParentView() {
return mParentView;
}
/**
* Set a popup's content. If want to use custom view, Popup Type should be "TYPE_USER_CUSTOM"
*
* @param resId layout resource ID
*/
@Deprecated
public void setContent(int resId) {
mContentResId = resId;
}
/**
* Set a popup's content. If want to use custom view, Popup Type should be "TYPE_USER_CUSTOM"
*
* @param view the new content view for Popup
*/
public void setContent(View view) {
setContent(view, (view != null)?view.getLayoutParams():null);
}
/**
* Set a popup's content. If want to use custom view, Popup Type should be "TYPE_USER_CUSTOM"
*
* @param view the new content view with specific LayoutParam. This supports only FrameLayout.LayoutParam.
* Note that you can use "WRAP_CONTENT" or absolute width/height for LayoutParam attribute.
* "FILL_PARENT" operates as "WRAP_CONTENT"
*/
public void setContent(View view, ViewGroup.LayoutParams lp) {
mContentView = view;
mContentLP = lp;
}
/**
* Sets the text for HoverPopup. this is used in some specific TYPE.
* if uses TYPE_TOOLTIP, this text will be shown instead of "contentDescription"
*
* @param text Text
*/
public void setContent(CharSequence text) {
mContentText = text;
}
/**
* Return the view used as the content of the Popup window.
*
* @return View The popup's content
*/
public View getContent() {
return mContentView;
}
/**
* Indicate whether this hover Popup is showing on screen.
*
* @return true if the popup is showing, false otherwise
*/
protected boolean isShowing() {
if (mPopup != null) {
return mPopup.isShowing();
}
return false;
}
/**
* Sets a gravity of hover popup. basically, Popup is based on parent View that called popup.
*
* @param gravity The gravity which controls the placement of the popup window. HoverPopup.Gravity's types can be used.
*/
public void setPopupGravity(int gravity) {
mPopupGravity = gravity;
}
/**
* Sets the time that detecting hovering.
*
* @param ms The time, milliseconds
*/
public void setHoverDetectTime(int ms) {
mHoverDetectTimeMS = ms;
}
/**
* Sets a Popup offset.
*
* @param x The popup's x location offset
* @param y The popup's y location offset
*/
public void setPopupPosOffset(int x, int y) {
mPopupOffsetX = x;
mPopupOffsetY = y;
}
/**
* Sets a point that now hovering. This is used for drawing guide line.
*
* @param x The absolute X point on Window
* @param y The absolute Y point on Window
*/
public void setHoveringPoint(int x, int y) {
mHoveringPointX = x;
mHoveringPointY = y;
}
@Deprecated
protected CharSequence getPriorityContentText() {
if (!TextUtils.isEmpty(mContentText)) {
return mContentText;
} else if (!TextUtils.isEmpty(mParentView.getContentDescription())) {
return mParentView.getContentDescription();
}
return null;
}
/**
* Returns the text for ToolTip.
*
* @return The Tooltip's text. NULL, if text is empty.
*/
private CharSequence getTooltipText(){
if (!TextUtils.isEmpty(mContentText)) {
return mContentText;
} else if (!TextUtils.isEmpty(mParentView.getContentDescription())) {
return mParentView.getContentDescription();
}
return null;
}
@Deprecated
public void show() {
show(mPopupType);
}
/**
* Display the hover popup to window. This checks the condition and sends the Message to show after N sec.
* Note that do not call this directly.
*
* @param type The type of the Hover Popup
*/
public void show(int type) {
if (type != mPopupType) {
mPopupType = type;
}
// return if TYPE is none.
if (!mEnabled || type == TYPE_NONE || !isHoverPopupPossible()
|| isShowing())
return;
// send message to show.
if (getHandler().hasMessages(MSG_SHOW_POPUP)) {
return;
// getHandler().removeMessages(MSG_SHOW_POPUP);
}
getHandler().sendEmptyMessageDelayed(MSG_SHOW_POPUP, mHoverDetectTimeMS);
}
/**
* Internal method for making/showing popup. calls some methods sequentially.
*/
private void showPopup() {
if (mPopup != null) {
mPopup.dismiss();
}
createPopupWindow();
setPopupContent();
updateHoverPopup();
}
/**
* Create a PopupWindow and set a options if want to use custom popup window
* with style, override this.
*
* @return The PopupWindow
*/
protected PopupWindow createPopupWindow() {
if (mPopup == null) {
mPopup = new PopupWindow(mParentView.getContext());
mPopup.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
mPopup.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
mPopup.setTouchable(false);
mPopup.setClippingEnabled(false);
mPopup.setBackgroundDrawable(null);
// mPopup.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
}
return mPopup;
}
/**
* Create a content view according to Popup Type
*/
private void setPopupContent() {
/**
* 1. get a view as per each mPopupType. 2. call a listener that
* override by USER.
*/
switch (mPopupType) {
case TYPE_NONE:
mContentView = null;
break;
case TYPE_TOOLTIP:
makeToolTipContentView();
break;
case TYPE_USER_CUSTOM:
if (mContentView == null) {
if (mContentResId != 0) {
LayoutInflater inflater = LayoutInflater.from(mContext);
try {
View v = inflater.inflate(mContentResId, null);
mContentView = v;
} catch (InflateException ie) {
mContentView = null;
}
}
}
break;
default:
mContentView = null;
break;
}
}
/**
* Create a content view for TYPE_TOOLTIP
*/
private void makeToolTipContentView() {
CharSequence text = getTooltipText();
if (TextUtils.isEmpty(text)) {
// if description is not set, do not show tooltip.
mContentView = null;
} else {
// otherwise, inflate Tooltip view and set Text.
if (mContentView == null || mContentView.getId() != ID_TOOLTIP_VIEW) {
LayoutInflater inflater = LayoutInflater.from(mContext);
mContentView = inflater.inflate(R.layout.hover_tooltip_popup, null);
mContentView.setId(ID_TOOLTIP_VIEW);
}
((TextView) mContentView).setText(text);
}
}
/**
* Compute popup location with a gravity and offset that received.
*
* @param gravity the gravity of object. HoverGravity's attributes can be used.
* @param offX the popup's x location offset
* @param offY the popup's y location offset
*
*/
protected void computePopupPosition(View anchor, int gravity, int offX, int offY) {
// can not compute position.
if (mContentView == null) {
return;
}
View anchorView = (anchor != null)?anchor:mParentView;
final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
int[] anchorLocOnScr = new int[2];
int[] anchorLocInWindow = new int[2];
anchorView.getLocationOnScreen(anchorLocOnScr);
anchorView.getLocationInWindow(anchorLocInWindow);
// window rect that can be drawn.
Rect displayFrame = new Rect();
anchorView.getWindowVisibleDisplayFrame(displayFrame);
if (DEBUG){
Log.e(TAG, "anchor location on screen x : " + anchorLocOnScr[0] + " y: " + anchorLocOnScr[1]);
Log.e(TAG, "anchor location in window x : " + anchorLocInWindow[0] + " y: " + anchorLocInWindow[1]);
}
// get a anchor rect.
Rect anchorRect;
if (!anchorView.getApplicationWindowToken().equals(anchorView.getWindowToken())){
// use a location that on screen. because popup is inflated with App window token.
mWindowGapX = 0;
mWindowGapY = 0;
anchorRect = new Rect(anchorLocOnScr[0], anchorLocOnScr[1],
(anchorLocOnScr[0] + anchorView.getWidth()),
(anchorLocOnScr[1] + anchorView.getHeight()));
/* if anchorView is on sub-panel window, fix displayFrame rect.
* because Rect just has child's(sub-panel) displayFrame */
displayFrame.left = 0;
displayFrame.right = displayMetrics.widthPixels;
displayFrame.top = 0;
displayFrame.bottom = displayMetrics.heightPixels;
} else {
// use a location that in window.
mWindowGapX = anchorLocOnScr[0] - anchorLocInWindow[0];
mWindowGapY = anchorLocOnScr[1] - anchorLocInWindow[1];
anchorRect = new Rect(anchorLocInWindow[0], anchorLocInWindow[1],
(anchorLocInWindow[0] + anchorView.getWidth()),
(anchorLocInWindow[1] + anchorView.getHeight()));
}
displayFrame.left = Math.max(0, displayFrame.left);
displayFrame.right = Math.min(displayMetrics.widthPixels, displayFrame.right);
displayFrame.top = Math.max(0, displayFrame.top);
displayFrame.bottom = Math.min(displayMetrics.heightPixels, displayFrame.bottom);
/**
* measure contentView size.
*/
int contentWidth, contentHeight;
int widthMeasureSpec, heightMeasureSpec;
if (mContentLP == null) {
widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(
displayMetrics.widthPixels, View.MeasureSpec.AT_MOST);
heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
displayMetrics.heightPixels, View.MeasureSpec.AT_MOST);
} else {
if (mContentLP.width >= 0) {
widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(
mContentLP.width, View.MeasureSpec.EXACTLY);
} else { // wrap_content
widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(
displayMetrics.widthPixels,
View.MeasureSpec.AT_MOST);
}
if (mContentLP.height >= 0) {
heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
mContentLP.height, View.MeasureSpec.EXACTLY);
} else { // wrap_content
heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
displayMetrics.heightPixels,
View.MeasureSpec.AT_MOST);
}
}
mContentView.measure(widthMeasureSpec, heightMeasureSpec);
contentWidth = mContentView.getMeasuredWidth();
contentHeight = mContentView.getMeasuredHeight();
mPopup.setWidth(contentWidth);
mPopup.setHeight(contentHeight);
/**
* get a location according to the gravity
*/
// find X,Y position according to mPopupGravity.
int hGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
int vGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
int posX;
int posY;
if (gravity == Gravity.NO_GRAVITY){
//if no gravity, just use offset value.
posX = offX;
posY = offY;
} else {
// horizontal
switch (hGravity) {
case Gravity.LEFT_OUTSIDE:
posX = anchorRect.left - contentWidth;
break;
case Gravity.LEFT:
posX = anchorRect.left;
break;
case Gravity.LEFT_CENTER_AXIS:
posX = anchorRect.centerX() - contentWidth;
break;
case Gravity.CENTER_HORIZONTAL:
posX = anchorRect.centerX() - (contentWidth/2);
break;
case Gravity.RIGHT_CENTER_AXIS:
posX = anchorRect.centerX();
break;
case Gravity.RIGHT:
posX = anchorRect.right - contentWidth;
break;
case Gravity.RIGHT_OUTSIDE:
posX = anchorRect.right;
break;
case Gravity.CENTER_HORIZONTAL_ON_WINDOW:
posX = displayFrame.centerX() - (contentWidth / 2);
break;
case Gravity.CENTER_HORIZONTAL_ON_POINT:
posX = mHoveringPointX - (contentWidth / 2) - mWindowGapX;
break;
default:
posX = offX;
break;
}
// add X offset
posX += mPopupOffsetX;
// vertical
switch (vGravity) {
case Gravity.TOP_ABOVE:
posY = anchorRect.top - contentHeight;
break;
case Gravity.TOP:
posY = anchorRect.top;
break;
case Gravity.CENTER_VERTICAL:
posY = anchorRect.centerY() - (contentHeight / 2);
break;
case Gravity.BOTTOM:
posY = anchorRect.bottom - contentHeight;
break;
case Gravity.BOTTOM_UNDER:
posY = anchorRect.bottom;
break;
default:
posY = offY;
break;
}
// add Y offset
posY += mPopupOffsetY;
}
/**
* check window boundary
*/
posX = Math.min(posX, (displayFrame.right - contentWidth));
posX = Math.max(0, posX);
if (posY < displayFrame.top){ // if view is over the top boundary
// if gravity is TOP_ABOVE, move to BOTTOM_UNDER. otherwise, just pull into window boundary.
if (vGravity == Gravity.TOP_ABOVE){
posY = anchorRect.bottom;
posY -= mPopupOffsetY; // adjust offset
} else {
posY = Math.max(displayFrame.top, posY);
}
} else if ((posY+contentHeight) > displayFrame.bottom) { // if view is over the bottom boundary
// if gravity is BOTTOM_UNDER, move to TOP_ABOVE. otherwise, just pull into window boundary.
if (vGravity == Gravity.BOTTOM_UNDER){
posY = anchorRect.top - contentHeight;
posY -= mPopupOffsetY; // adjust offset
} else {
posY = Math.min((displayFrame.bottom-contentHeight) , posY);
}
}
/**
* Draw guide line
*/
boolean canDraw = false;
if ((posY <= (anchorRect.top - contentHeight)) || (posY >= anchorRect.bottom)) {
canDraw = true;
}
if (mIsGuideLineEnabled && canDraw) {
// get margin for hover ring.
int marginForHoverRing = convertDPtoPX(MARGIN_FOR_HOVER_RING,
displayMetrics);
// get left/right of the container
int containerLeftOnWindow = Math.max(
Math.min(posX, (anchorRect.left - marginForHoverRing)), displayFrame.left);
int containerRightOnWindow = Math.min(
Math.max((posX + contentWidth),(anchorRect.right + marginForHoverRing)),
displayFrame.right);
// check where popup present.
boolean isPopupAboveHorizontal;
if (posY > anchorRect.centerY()) {
isPopupAboveHorizontal = false;
} else {
isPopupAboveHorizontal = true;
}
// set width/height of the container
if (mContentContainer == null) {
mContentContainer = new HoverPopupContainer(mContext);
mContentContainer.setBackgroundColor(Color.TRANSPARENT);
mContentContainer.setGuideLine(mGuideRingDrawableId, mGuideLineColor);
}
// add a contentView to Container.
ViewGroup.LayoutParams contentLP = mContentView.getLayoutParams();
if (contentLP == null) {
mContentView.setLayoutParams(new FrameLayout.LayoutParams(contentWidth, contentHeight));
} else {
contentLP.width = contentWidth;
contentLP.height = contentHeight;
}
if (mContentContainer.getChildCount() == 0) {
mContentContainer.addView(mContentView);
}
mPopup.setWidth(-2); // Wrap_content
mPopup.setHeight(-2); // Wrap_content
// set padding
int containerPaddingLeft = Math.abs(containerLeftOnWindow - posX);
int containerPaddingRight = Math.abs(containerRightOnWindow - (posX + contentWidth));
int containerPaddingTop;
if (isPopupAboveHorizontal) {
int containerPaddingBottom = (anchorRect.bottom + marginForHoverRing) - (posY + contentHeight);
mContentContainer.setPadding(containerPaddingLeft, 0, containerPaddingRight, containerPaddingBottom);
// set a container position on window
posX = containerLeftOnWindow;
//posY = posY;
// set Guide line, position is based on container. **Start : center
// of the contentView, End : hover point.
int hoverPointXonContainer = mHoveringPointX - posX - mWindowGapX;
int hoverPointYonContainer = mHoveringPointY - posY - mWindowGapY;
mContentContainer.setGuideLine((containerPaddingLeft + contentWidth / 2),
contentHeight - mGuideLineFadeOffset, hoverPointXonContainer,
hoverPointYonContainer, true);
} else {
containerPaddingTop = (posY - (anchorRect.top - marginForHoverRing));
mContentContainer.setPadding(containerPaddingLeft, containerPaddingTop, containerPaddingRight, 0);
// set a container position on window
posX = containerLeftOnWindow;
posY = posY - containerPaddingTop;
// set Guide line, position is based on container. **Start : center
// of the contentView, End : hover point.
int hoverPointXonContainer = mHoveringPointX - posX - mWindowGapX;
int hoverPointYonContainer = mHoveringPointY - posY - mWindowGapY;
mContentContainer.setGuideLine((containerPaddingLeft + contentWidth / 2),
containerPaddingTop + mGuideLineFadeOffset, hoverPointXonContainer,
hoverPointYonContainer, true);
}
} else {
mContentContainer = null;
}
mPopupPosX = posX;
mPopupPosY = posY;
}
/**
* Updates the hover popup window
*/
public void updateHoverPopup() {
updateHoverPopup(mParentView, mPopupGravity, mPopupOffsetX, mPopupOffsetY);
}
/**
* Updates the hover popup window
*/
public void updateHoverPopup(View anchor, int gravity, int offsetX, int offsetY) {
if (mPopup == null) {
if (DEBUG) Log.e(TAG, "updateHoverPopup(), returned dueto mPopup==null ");
return;
}
// compute position.
computePopupPosition(anchor, gravity, offsetX, offsetY);
// set contents to Popup
if (mIsGuideLineEnabled && mContentContainer != null) {
mPopup.setContentView(mContentContainer);
} else {
mPopup.setContentView(mContentView);
}
if (mPopup.getContentView() == null) {
if (DEBUG) Log.e(TAG, "updateHoverPopup(), returns dueto mPopup.getContentView()==null");
return;
}
// set animation.
mPopup.setAnimationStyle(mAnimationStyle);
if (mPopup.isShowing()) {
mPopup.update(mPopupPosX, mPopupPosY, -1, -1);
} else {
// If window type of current view is sub-panel, attach to window with ApplicationWindowToken.
mPopup.showAtLocation(anchor, Gravity.NO_GRAVITY, mPopupPosX, mPopupPosY);
}
}
/**
* Change the animation style resource for this popup.
*
* @param aniStyle Animation style to use when the popup appears
* and disappears.
*/
public void setAnimationStyle(int aniStyle) {
mAnimationStyle = aniStyle;
}
/**
* Sets a guide line on/off. the line is drawn between content view and hovering point.
*
* @param enabled True to draw line.
*/
public void setGuideLineEnabled(boolean enabled) {
mIsGuideLineEnabled = enabled;
}
/**
* Sets a length of fading edge of the content background image.
* This is for fitting a guide line and Content more closely.
*
* @param offset The length of fading edge
*/
public void setGuideLineFadeOffset(int offset) {
mGuideLineFadeOffset = convertDPtoPX(offset, null);
}
/**
* Sets a guide line style
*/
public void setGuideLineStyle(int ringDrawable, int lineColor) {
mGuideRingDrawableId = ringDrawable;
mGuideLineColor = lineColor;
}
/**
* This is called from onHoverEvent of the View.java.
* Note that Do not call this method directly.
*/
/*
public boolean onHoverEvent(MotionEvent event) {
final int action = event.getAction();
if (action == MotionEvent.ACTION_HOVER_MOVE) {
setHoveringPoint((int) event.getRawX(), (int) event.getRawY());
// update popup for moving guide line
if (mIsGuideLineEnabled && isShowing()) {
View popupView = mPopup.getContentView();
if (popupView instanceof HoverPopupContainer) {
((HoverPopupContainer) mPopup.getContentView()).setGuideLineEndPoint(
(int) event.getRawX() - mPopupPosX - mWindowGapX, (int) event.getRawY() - mPopupPosY - mWindowGapY);
((HoverPopupContainer) popupView).updateDecoration();
}
}
}
return false;
}*/
/**
* Dismiss the hover popup and clears handler messages
* This is for fitting a guide line and Content more closely.
*/
public void dismiss() {
// if (getHandler().hasMessages(MSG_DISMISS_POPUP)) {
// getHandler().removeMessages(MSG_DISMISS_POPUP);
// }
// getHandler().sendEmptyMessage(MSG_DISMISS_POPUP);
dismissPopup();
}
/**
* Dismiss the hover popup and clears handler messages
* This is for fitting a guide line and Content more closely.
*/
private void dismissPopup() {
// remove pending message and dismiss popup
getHandler().removeMessages(MSG_SHOW_POPUP);
getHandler().removeMessages(MSG_DISMISS_POPUP);
if (mPopup != null) {
mPopup.dismiss();
}
}
/**
* Utility method to convert DP to Pixel.
*/
protected int convertDPtoPX(int dp, DisplayMetrics displayMetrics) {
if (displayMetrics == null) {
displayMetrics = mContext.getResources().getDisplayMetrics();
}
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics);
}
private Handler getHandler() {
if (mHandler == null)
mHandler = new HoverPopupHandler();
return mHandler;
}
private class HoverPopupHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (DEBUG)
Log.e(TAG, "handleMessage : " + ((msg.what == MSG_SHOW_POPUP) ? "SHOW" : "DISMISS"));
switch (msg.what) {
case MSG_SHOW_POPUP:
showPopup();
sendEmptyMessageDelayed(MSG_DISMISS_POPUP, POPUP_TIMEOUT_MS);
break;
case MSG_DISMISS_POPUP:
dismissPopup();
break;
}
}
};
protected class HoverPopupContainer extends FrameLayout {
static final String TAG = "HoverPopupContainer";
static final boolean DEBUG = true;
private int mLineStartX, mLineStartY;
private int mLineEndX, mLineEndY;
// private int mOldLineEndX = -1;
// private int mOldLineEndY = -1;
// private int mChildImageFadeHeight;
private boolean mIsRingEnabled = false;
private Paint mLinePaint;
private Drawable mRingDrawable;
private int mRingWidth, mRingHeight;
public HoverPopupContainer(Context context) {
super(context);
}
public void setGuideLine(int drawableId, int lineColor){
mRingDrawable = getResources().getDrawable(drawableId);
mRingWidth = mRingDrawable.getIntrinsicWidth();
mRingHeight = mRingDrawable.getIntrinsicHeight();
mRingDrawable.setBounds(0,0,mRingWidth,mRingHeight);
mLinePaint = new Paint();
mLinePaint.setStrokeWidth(3);
mLinePaint.setStrokeCap(Cap.ROUND);
mLinePaint.setColor(lineColor);
mLinePaint.setAntiAlias(true);
}
public void updateDecoration() {
if (DEBUG) Log.d(TAG, "HoverPopupContainer.updateContainer()");
invalidate();
// if (getPaddingTop() > getPaddingBottom()){
// // invalidate upper area.
// postInvalidate(0, 0, getWidth(), Math.max(mLineStartY, mLineEndY));
// } else {
// postInvalidate(0, Math.min(mLineStartY, mLineEndY), getWidth(), getHeight());
// }
}
// this received position of the hover point.
public void setGuideLine(int startX, int startY, int endX, int endY, boolean ringEnabled) {
if (DEBUG)
Log.d(TAG, "HoverPopupContainer.updateContainer()");
mLineStartX = startX;
mLineStartY = startY;
mLineEndX = endX;
mLineEndY = endY;
mIsRingEnabled = ringEnabled;
}
public void setGuideLineEndPoint(int pointX, int pointY) {
mLineEndX = pointX;
mLineEndY = pointY;
}
// public void setChildImageFadeHeight(int height) {
// mChildImageFadeHeight = height;
// }
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
// check precondition
if (DEBUG)
Log.d(TAG, "HoverPopupContainer.onDraw() RingEnabled : " + mIsRingEnabled
+ " s.x : " + mLineStartX + " s.y : " + mLineStartY + " e.x : " + mLineEndX
+ " e.y : " + mLineEndY + " Drawable : " + mRingDrawable);
if (getChildCount() == 0 || getChildAt(0) == null) {
return;
}
if (mRingDrawable == null){
setGuideLine(R.drawable.hover_ic_point, 0xFF9ca2a9);
}
// draw ring
if (mIsRingEnabled) {
canvas.save();
canvas.translate((mLineEndX - mRingWidth/2), (mLineEndY - mRingHeight/2));
mRingDrawable.draw(canvas);
canvas.restore();
if (mLineStartY < mLineEndY) {
canvas.drawLine(mLineStartX, mLineStartY, mLineEndX, (mLineEndY - mRingHeight/2 + 2), mLinePaint);
} else if (mLineStartY > mLineEndY) {
canvas.drawLine(mLineStartX, mLineStartY, mLineEndX, (mLineEndY + mRingHeight/2 - 2), mLinePaint);
}
} else {
// draw line
canvas.drawLine(mLineStartX, mLineStartY, mLineEndX, mLineEndY, mLinePaint);
}
}
protected boolean pointInValidPaddingArea(int localX, int localY) {
boolean ret = false;
if (getPaddingTop() > getPaddingBottom()) {
if ((localX < getWidth()) && (localY <= getPaddingTop())) {
ret = true;
}
} else if (getPaddingTop() < getPaddingBottom()) {
if ((localX < getWidth()) && (localY >= (getHeight() - getPaddingBottom()))) {
ret = true;
}
} else {
ret = false;
}
return ret;
}
}
final static public class Gravity {
/** Constant indicating that no gravity has been set **/
public static final int NO_GRAVITY = 0x00000000;
/** Place the object in the center of its container in both the vertical and the horizontal axis**/
public static final int CENTER = 0x00000011;
/** Binary mask to get the absolute vertical gravity of a gravity. **/
public static final int VERTICAL_GRAVITY_MASK = 0x0000F0F0;
/** Place object in the vertical center of its container. **/
public static final int CENTER_VERTICAL = 0x00000010;
/** Place object to meet the top of its container. **/
public static final int TOP = 0x00000030;
/** Place object above its container. **/
public static final int TOP_ABOVE = 0x00003030;
/** Place object to meet the bottom of it's container. **/
public static final int BOTTOM = 0x00000050;
/** Place object under its container. **/
public static final int BOTTOM_UNDER = 0x00005050;
/** Binary mask to get the absolute horizontal gravity of a gravity. **/
public static final int HORIZONTAL_GRAVITY_MASK = 0x00000F0F;
/** Place object in the horizontal center of its container. **/
public static final int CENTER_HORIZONTAL = 0x00000001;
/** Place object to meet the left of its container **/
public static final int LEFT = 0x00000003;
/** Place object to meet its right side and container's center_horizontal **/
public static final int LEFT_CENTER_AXIS = 0x00000103;
/** Place object in the left of its container **/
public static final int LEFT_OUTSIDE = 0x00000303;
/** Place object to meet its left side and container's center_horizontal **/
public static final int RIGHT_CENTER_AXIS = 0x00000105;
/** Place object to meet the right of its container **/
public static final int RIGHT = 0x00000005;
/** Place object in the right of its container **/
public static final int RIGHT_OUTSIDE = 0x00000505;
/** Place object in the horizontal center of its container. **/
public static final int CENTER_HORIZONTAL_ON_WINDOW = 0x00000101;
/** Place object in the horizontal center of its container. **/
public static final int CENTER_HORIZONTAL_ON_POINT = 0x00000201;
}
}