DroidFish: Use the TourGuide library to display usage hints the first
time the program is started.
BIN
DroidFish/res/drawable-hdpi/fab_bg_mini.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
DroidFish/res/drawable-hdpi/fab_bg_normal.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
DroidFish/res/drawable-mdpi/fab_bg_mini.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
DroidFish/res/drawable-mdpi/fab_bg_normal.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
DroidFish/res/drawable-xhdpi/drop_shadow.9.png
Normal file
After Width: | Height: | Size: 690 B |
BIN
DroidFish/res/drawable-xhdpi/fab_bg_mini.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
DroidFish/res/drawable-xhdpi/fab_bg_normal.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
DroidFish/res/drawable-xxhdpi/fab_bg_mini.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
DroidFish/res/drawable-xxhdpi/fab_bg_normal.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
DroidFish/res/drawable-xxxhdpi/fab_bg_mini.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
DroidFish/res/drawable-xxxhdpi/fab_bg_normal.png
Normal file
After Width: | Height: | Size: 12 KiB |
|
@ -26,6 +26,7 @@
|
|||
layout="@layout/title">
|
||||
</include>
|
||||
<LinearLayout
|
||||
android:id="@+id/buttons"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -25,49 +25,55 @@
|
|||
android:paddingTop="1dp">
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:includeFontPadding="true"
|
||||
android:fontFamily="monospace"
|
||||
android:typeface="monospace"
|
||||
android:textSize="12sp"/>
|
||||
<ImageButton
|
||||
android:id="@+id/custom3Button"
|
||||
android:contentDescription="@string/buttonDesc_custom3"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="32dp">
|
||||
</ImageButton>
|
||||
<ImageButton
|
||||
android:id="@+id/custom2Button"
|
||||
android:contentDescription="@string/buttonDesc_custom2"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="32dp">
|
||||
</ImageButton>
|
||||
<ImageButton
|
||||
android:id="@+id/custom1Button"
|
||||
android:contentDescription="@string/buttonDesc_custom1"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="32dp">
|
||||
</ImageButton>
|
||||
<ImageButton
|
||||
android:id="@+id/modeButton"
|
||||
android:contentDescription="@string/buttonDesc_mode"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="32dp">
|
||||
</ImageButton>
|
||||
<ImageButton
|
||||
android:id="@+id/undoButton"
|
||||
android:contentDescription="@string/buttonDesc_back"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="32dp">
|
||||
</ImageButton>
|
||||
<ImageButton
|
||||
android:id="@+id/redoButton"
|
||||
android:contentDescription="@string/buttonDesc_forward"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="32dp">
|
||||
</ImageButton>
|
||||
<LinearLayout
|
||||
android:id="@+id/buttons"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<ImageButton
|
||||
android:id="@+id/custom3Button"
|
||||
android:contentDescription="@string/buttonDesc_custom3"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="32dp">
|
||||
</ImageButton>
|
||||
<ImageButton
|
||||
android:id="@+id/custom2Button"
|
||||
android:contentDescription="@string/buttonDesc_custom2"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="32dp">
|
||||
</ImageButton>
|
||||
<ImageButton
|
||||
android:id="@+id/custom1Button"
|
||||
android:contentDescription="@string/buttonDesc_custom1"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="32dp">
|
||||
</ImageButton>
|
||||
<ImageButton
|
||||
android:id="@+id/modeButton"
|
||||
android:contentDescription="@string/buttonDesc_mode"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="32dp">
|
||||
</ImageButton>
|
||||
<ImageButton
|
||||
android:id="@+id/undoButton"
|
||||
android:contentDescription="@string/buttonDesc_back"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="32dp">
|
||||
</ImageButton>
|
||||
<ImageButton
|
||||
android:id="@+id/redoButton"
|
||||
android:contentDescription="@string/buttonDesc_forward"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="32dp">
|
||||
</ImageButton>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<view
|
||||
class="org.petero.droidfish.MyRelativeLayout"
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
layout="@layout/title">
|
||||
</include>
|
||||
<LinearLayout
|
||||
android:id="@+id/buttons"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
40
DroidFish/res/layout/tooltip.xml
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!--<View-->
|
||||
<!--android:layout_width="wrap_content"-->
|
||||
<!--android:layout_height="5dp"-->
|
||||
<!--android:background="@drawable/shadow_upward" />-->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/toolTip_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="#e74c3c"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:padding="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="Default Title"
|
||||
android:textColor="@color/White"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="Default Description"
|
||||
android:textColor="@color/White" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -240,6 +240,12 @@
|
|||
<li>
|
||||
Syzygy tablebases probing code, Copyright © 2011-2013 Ronald de Man.
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/worker8/TourGuide">TourGuide</a> library, Copyright © 2015 Tan Jun Rong.
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/Tesla-Redux/android-floating-action-button">Floating action button</a> library, Copyright © 2014 str4d and Jerzy Chalupski.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Translations</h3>
|
||||
|
|
7
DroidFish/res/values-v14/colors.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="default_normal">@android:color/holo_blue_dark</color>
|
||||
<color name="default_pressed">@android:color/holo_blue_light</color>
|
||||
<color name="default_disabled">@android:color/darker_gray</color>
|
||||
<color name="White">#FFFFFF</color>
|
||||
</resources>
|
39
DroidFish/res/values/attrs.xml
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="FloatingActionButton">
|
||||
<attr name="fab_colorPressed" format="color"/>
|
||||
<attr name="fab_colorDisabled" format="color"/>
|
||||
<attr name="fab_colorNormal" format="color"/>
|
||||
<attr name="fab_icon" format="reference"/>
|
||||
<attr name="fab_size" format="enum">
|
||||
<enum name="normal" value="0"/>
|
||||
<enum name="mini" value="1"/>
|
||||
</attr>
|
||||
<attr name="fab_title" format="string"/>
|
||||
<attr name="fab_stroke_visible" format="boolean"/>
|
||||
</declare-styleable>
|
||||
<declare-styleable name="AddFloatingActionButton">
|
||||
<attr name="fab_plusIconColor" format="color"/>
|
||||
</declare-styleable>
|
||||
<declare-styleable name="FloatingActionsMenu">
|
||||
<attr name="fab_addButtonColorPressed" format="color"/>
|
||||
<attr name="fab_addButtonColorNormal" format="color"/>
|
||||
<attr name="fab_addButtonSize" format="enum">
|
||||
<enum name="normal" value="0"/>
|
||||
<enum name="mini" value="1"/>
|
||||
</attr>
|
||||
<attr name="fab_addButtonPlusIconColor" format="color"/>
|
||||
<attr name="fab_addButtonStrokeVisible" format="boolean"/>
|
||||
<attr name="fab_labelStyle" format="reference"/>
|
||||
<attr name="fab_labelsPosition" format="enum">
|
||||
<enum name="left" value="0"/>
|
||||
<enum name="right" value="1"/>
|
||||
</attr>
|
||||
<attr name="fab_expandDirection" format="enum">
|
||||
<enum name="up" value="0"/>
|
||||
<enum name="down" value="1"/>
|
||||
<enum name="left" value="2"/>
|
||||
<enum name="right" value="3"/>
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
</resources>
|
7
DroidFish/res/values/colors.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="default_normal">#ff0099cc</color><!-- android.R.color.holo_blue_dark -->
|
||||
<color name="default_pressed">#ff33b5e5</color><!-- android.R.color.holo_blue_light -->
|
||||
<color name="default_disabled">#aaa</color><!-- android.R.color.darker_gray -->
|
||||
<color name="White">#FFFFFF</color>
|
||||
</resources>
|
17
DroidFish/res/values/dimens.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<resources>
|
||||
<dimen name="fab_size_normal">56dp</dimen>
|
||||
<dimen name="fab_size_mini">40dp</dimen>
|
||||
|
||||
<dimen name="fab_icon_size">24dp</dimen>
|
||||
|
||||
<dimen name="fab_plus_icon_size">14dp</dimen>
|
||||
<dimen name="fab_plus_icon_stroke">2dp</dimen>
|
||||
|
||||
<dimen name="fab_shadow_offset">3dp</dimen>
|
||||
<dimen name="fab_shadow_radius">9dp</dimen>
|
||||
|
||||
<dimen name="fab_stroke_width">1dp</dimen>
|
||||
|
||||
<dimen name="fab_actions_spacing">16dp</dimen>
|
||||
<dimen name="fab_labels_margin">8dp</dimen>
|
||||
</resources>
|
5
DroidFish/res/values/ids.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item name="fab_expand_menu_button" type="id"/>
|
||||
<item name="fab_label" type="id"/>
|
||||
</resources>
|
|
@ -413,6 +413,20 @@ you are not actively using the program.\
|
|||
<string name="prefs_rtbPath_summary">Directory where Syzygy tablebases are installed. Leave blank to use default directory</string>
|
||||
<string name="prefs_rtbPathNet_title">Syzygy Network Directory</string>
|
||||
<string name="prefs_rtbPathNet_summary">Directory for network engines where Syzygy tablebases are installed.</string>
|
||||
<string name="prefs_guideShowOnStart_title">Startup Guide</string>
|
||||
<string name="prefs_guideShowOnStart_summary">Show the startup guide the next time the program is started.</string>
|
||||
<string name="tour_leftMenu_title">Left Menu</string>
|
||||
<string name="tour_leftMenu_desc">To open the left menu, tap on the left side of the title bar or swipe from the left side of the screen towards the right side.</string>
|
||||
<string name="tour_rightMenu_title">Right Menu</string>
|
||||
<string name="tour_rightMenu_desc">To open the right menu, tap on the right side of the title bar or swipe from the right side of the screen towards the left side.</string>
|
||||
<string name="tour_chessBoard_title">Chess Board</string>
|
||||
<string name="tour_chessBoard_desc">Touch and hold the chess board to open the tools menu.</string>
|
||||
<string name="tour_buttons_title">Buttons</string>
|
||||
<string name="tour_buttons_desc">Tap a button to invoke its action. Touch and hold a button to open a menu containing secondary actions. To configure button actions go to Left Menu > Settings > Behavior > Configure Buttons.</string>
|
||||
<string name="tour_moveList_title">Move List</string>
|
||||
<string name="tour_moveList_desc">Tap a move in the move list to set the chess board to the corresponding position. Touch and hold the move list to open the Edit Game menu.</string>
|
||||
<string name="tour_analysis_title">Analysis information</string>
|
||||
<string name="tour_analysis_desc">When the engine is thinking, touch and hold the analysis information to open the Analysis menu.</string>
|
||||
<string name="buttonDesc_custom1">@string/prefs_custom_button_1</string>
|
||||
<string name="buttonDesc_custom2">@string/prefs_custom_button_2</string>
|
||||
<string name="buttonDesc_custom3">@string/prefs_custom_button_3</string>
|
||||
|
|
|
@ -518,6 +518,12 @@
|
|||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:title="@string/prefs_other">
|
||||
<CheckBoxPreference
|
||||
android:key="guideShowOnStart"
|
||||
android:title="@string/prefs_guideShowOnStart_title"
|
||||
android:summary="@string/prefs_guideShowOnStart_summary"
|
||||
android:defaultValue="true">
|
||||
</CheckBoxPreference>
|
||||
<PreferenceScreen
|
||||
android:key="bookSettings"
|
||||
android:title="@string/prefs_bookSettings_title"
|
||||
|
|
|
@ -0,0 +1,431 @@
|
|||
package net.i2p.android.ext.floatingactionbutton;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.Shader.TileMode;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.graphics.drawable.ShapeDrawable.ShaderFactory;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.graphics.drawable.shapes.OvalShape;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.support.annotation.ColorRes;
|
||||
import android.support.annotation.DimenRes;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.petero.droidfish.R;
|
||||
|
||||
public class FloatingActionButton extends ImageButton {
|
||||
|
||||
public static final int SIZE_NORMAL = 0;
|
||||
public static final int SIZE_MINI = 1;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({ SIZE_NORMAL, SIZE_MINI })
|
||||
public @interface FAB_SIZE {
|
||||
}
|
||||
|
||||
int mColorNormal;
|
||||
int mColorPressed;
|
||||
int mColorDisabled;
|
||||
String mTitle;
|
||||
@DrawableRes
|
||||
private int mIcon;
|
||||
private Drawable mIconDrawable;
|
||||
private int mSize;
|
||||
|
||||
private float mCircleSize;
|
||||
private float mShadowRadius;
|
||||
private float mShadowOffset;
|
||||
private int mDrawableSize;
|
||||
boolean mStrokeVisible;
|
||||
|
||||
public FloatingActionButton(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public FloatingActionButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
public FloatingActionButton(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
void init(Context context, AttributeSet attributeSet) {
|
||||
TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionButton, 0, 0);
|
||||
mColorNormal = attr.getColor(R.styleable.FloatingActionButton_fab_colorNormal, getColor(R.color.default_normal));
|
||||
mColorPressed = attr.getColor(R.styleable.FloatingActionButton_fab_colorPressed, getColor(R.color.default_pressed));
|
||||
mColorDisabled = attr.getColor(R.styleable.FloatingActionButton_fab_colorDisabled, getColor(R.color.default_disabled));
|
||||
mSize = attr.getInt(R.styleable.FloatingActionButton_fab_size, SIZE_NORMAL);
|
||||
mIcon = attr.getResourceId(R.styleable.FloatingActionButton_fab_icon, 0);
|
||||
mTitle = attr.getString(R.styleable.FloatingActionButton_fab_title);
|
||||
mStrokeVisible = attr.getBoolean(R.styleable.FloatingActionButton_fab_stroke_visible, true);
|
||||
attr.recycle();
|
||||
|
||||
updateCircleSize();
|
||||
mShadowRadius = getDimension(R.dimen.fab_shadow_radius);
|
||||
mShadowOffset = getDimension(R.dimen.fab_shadow_offset);
|
||||
updateDrawableSize();
|
||||
|
||||
updateBackground();
|
||||
}
|
||||
|
||||
private void updateDrawableSize() {
|
||||
mDrawableSize = (int) (mCircleSize + 2 * mShadowRadius);
|
||||
}
|
||||
|
||||
private void updateCircleSize() {
|
||||
mCircleSize = getDimension(mSize == SIZE_NORMAL ? R.dimen.fab_size_normal : R.dimen.fab_size_mini);
|
||||
}
|
||||
|
||||
public void setSize(@FAB_SIZE int size) {
|
||||
if (size != SIZE_MINI && size != SIZE_NORMAL) {
|
||||
throw new IllegalArgumentException("Use @FAB_SIZE constants only!");
|
||||
}
|
||||
|
||||
if (mSize != size) {
|
||||
mSize = size;
|
||||
updateCircleSize();
|
||||
updateDrawableSize();
|
||||
updateBackground();
|
||||
}
|
||||
}
|
||||
|
||||
@FAB_SIZE
|
||||
public int getSize() {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
public void setIcon(@DrawableRes int icon) {
|
||||
if (mIcon != icon) {
|
||||
mIcon = icon;
|
||||
mIconDrawable = null;
|
||||
updateBackground();
|
||||
}
|
||||
}
|
||||
|
||||
public void setIconDrawable(@NonNull Drawable iconDrawable) {
|
||||
if (mIconDrawable != iconDrawable) {
|
||||
mIcon = 0;
|
||||
mIconDrawable = iconDrawable;
|
||||
updateBackground();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current Color for normal state.
|
||||
*/
|
||||
public int getColorNormal() {
|
||||
return mColorNormal;
|
||||
}
|
||||
|
||||
public void setColorNormalResId(@ColorRes int colorNormal) {
|
||||
setColorNormal(getColor(colorNormal));
|
||||
}
|
||||
|
||||
public void setColorNormal(int color) {
|
||||
if (mColorNormal != color) {
|
||||
mColorNormal = color;
|
||||
updateBackground();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current color for pressed state.
|
||||
*/
|
||||
public int getColorPressed() {
|
||||
return mColorPressed;
|
||||
}
|
||||
|
||||
public void setColorPressedResId(@ColorRes int colorPressed) {
|
||||
setColorPressed(getColor(colorPressed));
|
||||
}
|
||||
|
||||
public void setColorPressed(int color) {
|
||||
if (mColorPressed != color) {
|
||||
mColorPressed = color;
|
||||
updateBackground();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current color for disabled state.
|
||||
*/
|
||||
public int getColorDisabled() {
|
||||
return mColorDisabled;
|
||||
}
|
||||
|
||||
public void setColorDisabledResId(@ColorRes int colorDisabled) {
|
||||
setColorDisabled(getColor(colorDisabled));
|
||||
}
|
||||
|
||||
public void setColorDisabled(int color) {
|
||||
if (mColorDisabled != color) {
|
||||
mColorDisabled = color;
|
||||
updateBackground();
|
||||
}
|
||||
}
|
||||
|
||||
public void setStrokeVisible(boolean visible) {
|
||||
if (mStrokeVisible != visible) {
|
||||
mStrokeVisible = visible;
|
||||
updateBackground();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isStrokeVisible() {
|
||||
return mStrokeVisible;
|
||||
}
|
||||
|
||||
int getColor(@ColorRes int id) {
|
||||
return getResources().getColor(id);
|
||||
}
|
||||
|
||||
float getDimension(@DimenRes int id) {
|
||||
return getResources().getDimension(id);
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
mTitle = title;
|
||||
TextView label = getLabelView();
|
||||
if (label != null) {
|
||||
label.setText(title);
|
||||
}
|
||||
}
|
||||
|
||||
TextView getLabelView() {
|
||||
return (TextView) getTag(R.id.fab_label);
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(mDrawableSize, mDrawableSize);
|
||||
}
|
||||
|
||||
void updateBackground() {
|
||||
final float strokeWidth = getDimension(R.dimen.fab_stroke_width);
|
||||
final float halfStrokeWidth = strokeWidth / 2f;
|
||||
|
||||
LayerDrawable layerDrawable = new LayerDrawable(
|
||||
new Drawable[] {
|
||||
getResources().getDrawable(mSize == SIZE_NORMAL ? R.drawable.fab_bg_normal : R.drawable.fab_bg_mini),
|
||||
createFillDrawable(strokeWidth),
|
||||
createOuterStrokeDrawable(strokeWidth),
|
||||
getIconDrawable()
|
||||
});
|
||||
|
||||
int iconOffset = (int) (mCircleSize - getDimension(R.dimen.fab_icon_size)) / 2;
|
||||
|
||||
int circleInsetHorizontal = (int) (mShadowRadius);
|
||||
int circleInsetTop = (int) (mShadowRadius - mShadowOffset);
|
||||
int circleInsetBottom = (int) (mShadowRadius + mShadowOffset);
|
||||
|
||||
layerDrawable.setLayerInset(1,
|
||||
circleInsetHorizontal,
|
||||
circleInsetTop,
|
||||
circleInsetHorizontal,
|
||||
circleInsetBottom);
|
||||
|
||||
layerDrawable.setLayerInset(2,
|
||||
(int) (circleInsetHorizontal - halfStrokeWidth),
|
||||
(int) (circleInsetTop - halfStrokeWidth),
|
||||
(int) (circleInsetHorizontal - halfStrokeWidth),
|
||||
(int) (circleInsetBottom - halfStrokeWidth));
|
||||
|
||||
layerDrawable.setLayerInset(3,
|
||||
circleInsetHorizontal + iconOffset,
|
||||
circleInsetTop + iconOffset,
|
||||
circleInsetHorizontal + iconOffset,
|
||||
circleInsetBottom + iconOffset);
|
||||
|
||||
setBackgroundCompat(layerDrawable);
|
||||
}
|
||||
|
||||
Drawable getIconDrawable() {
|
||||
if (mIconDrawable != null) {
|
||||
return mIconDrawable;
|
||||
} else if (mIcon != 0) {
|
||||
return getResources().getDrawable(mIcon);
|
||||
} else {
|
||||
return new ColorDrawable(Color.TRANSPARENT);
|
||||
}
|
||||
}
|
||||
|
||||
private StateListDrawable createFillDrawable(float strokeWidth) {
|
||||
StateListDrawable drawable = new StateListDrawable();
|
||||
drawable.addState(new int[] { -android.R.attr.state_enabled }, createCircleDrawable(mColorDisabled, strokeWidth));
|
||||
drawable.addState(new int[] { android.R.attr.state_pressed }, createCircleDrawable(mColorPressed, strokeWidth));
|
||||
drawable.addState(new int[] { }, createCircleDrawable(mColorNormal, strokeWidth));
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private Drawable createCircleDrawable(int color, float strokeWidth) {
|
||||
int alpha = Color.alpha(color);
|
||||
int opaqueColor = opaque(color);
|
||||
|
||||
ShapeDrawable fillDrawable = new ShapeDrawable(new OvalShape());
|
||||
|
||||
final Paint paint = fillDrawable.getPaint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(opaqueColor);
|
||||
|
||||
Drawable[] layers = {
|
||||
fillDrawable,
|
||||
createInnerStrokesDrawable(opaqueColor, strokeWidth)
|
||||
};
|
||||
|
||||
LayerDrawable drawable = alpha == 255 || !mStrokeVisible
|
||||
? new LayerDrawable(layers)
|
||||
: new TranslucentLayerDrawable(alpha, layers);
|
||||
|
||||
int halfStrokeWidth = (int) (strokeWidth / 2f);
|
||||
drawable.setLayerInset(1, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth);
|
||||
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private static class TranslucentLayerDrawable extends LayerDrawable {
|
||||
private final int mAlpha;
|
||||
|
||||
public TranslucentLayerDrawable(int alpha, Drawable... layers) {
|
||||
super(layers);
|
||||
mAlpha = alpha;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
Rect bounds = getBounds();
|
||||
canvas.saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom, mAlpha, Canvas.ALL_SAVE_FLAG);
|
||||
super.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
private Drawable createOuterStrokeDrawable(float strokeWidth) {
|
||||
ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape());
|
||||
|
||||
final Paint paint = shapeDrawable.getPaint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeWidth(strokeWidth);
|
||||
paint.setStyle(Style.STROKE);
|
||||
paint.setColor(Color.BLACK);
|
||||
paint.setAlpha(opacityToAlpha(0.02f));
|
||||
|
||||
return shapeDrawable;
|
||||
}
|
||||
|
||||
private int opacityToAlpha(float opacity) {
|
||||
return (int) (255f * opacity);
|
||||
}
|
||||
|
||||
private int darkenColor(int argb) {
|
||||
return adjustColorBrightness(argb, 0.9f);
|
||||
}
|
||||
|
||||
private int lightenColor(int argb) {
|
||||
return adjustColorBrightness(argb, 1.1f);
|
||||
}
|
||||
|
||||
private int adjustColorBrightness(int argb, float factor) {
|
||||
float[] hsv = new float[3];
|
||||
Color.colorToHSV(argb, hsv);
|
||||
|
||||
hsv[2] = Math.min(hsv[2] * factor, 1f);
|
||||
|
||||
return Color.HSVToColor(Color.alpha(argb), hsv);
|
||||
}
|
||||
|
||||
private int halfTransparent(int argb) {
|
||||
return Color.argb(
|
||||
Color.alpha(argb) / 2,
|
||||
Color.red(argb),
|
||||
Color.green(argb),
|
||||
Color.blue(argb)
|
||||
);
|
||||
}
|
||||
|
||||
private int opaque(int argb) {
|
||||
return Color.rgb(
|
||||
Color.red(argb),
|
||||
Color.green(argb),
|
||||
Color.blue(argb)
|
||||
);
|
||||
}
|
||||
|
||||
private Drawable createInnerStrokesDrawable(final int color, float strokeWidth) {
|
||||
if (!mStrokeVisible) {
|
||||
return new ColorDrawable(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape());
|
||||
|
||||
final int bottomStrokeColor = darkenColor(color);
|
||||
final int bottomStrokeColorHalfTransparent = halfTransparent(bottomStrokeColor);
|
||||
final int topStrokeColor = lightenColor(color);
|
||||
final int topStrokeColorHalfTransparent = halfTransparent(topStrokeColor);
|
||||
|
||||
final Paint paint = shapeDrawable.getPaint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeWidth(strokeWidth);
|
||||
paint.setStyle(Style.STROKE);
|
||||
shapeDrawable.setShaderFactory(new ShaderFactory() {
|
||||
@Override
|
||||
public Shader resize(int width, int height) {
|
||||
return new LinearGradient(width / 2, 0, width / 2, height,
|
||||
new int[] { topStrokeColor, topStrokeColorHalfTransparent, color, bottomStrokeColorHalfTransparent, bottomStrokeColor },
|
||||
new float[] { 0f, 0.2f, 0.5f, 0.8f, 1f },
|
||||
TileMode.CLAMP
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return shapeDrawable;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@SuppressLint("NewApi")
|
||||
private void setBackgroundCompat(Drawable drawable) {
|
||||
if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
|
||||
setBackground(drawable);
|
||||
} else {
|
||||
setBackgroundDrawable(drawable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibility(int visibility) {
|
||||
TextView label = getLabelView();
|
||||
if (label != null) {
|
||||
label.setVisibility(visibility);
|
||||
}
|
||||
|
||||
super.setVisibility(visibility);
|
||||
}
|
||||
}
|
|
@ -60,6 +60,12 @@ import org.petero.droidfish.gamelogic.TimeControlData;
|
|||
import org.petero.droidfish.tb.Probe;
|
||||
import org.petero.droidfish.tb.ProbeResult;
|
||||
|
||||
import tourguide.tourguide.Overlay;
|
||||
import tourguide.tourguide.Pointer;
|
||||
import tourguide.tourguide.Sequence;
|
||||
import tourguide.tourguide.ToolTip;
|
||||
import tourguide.tourguide.TourGuide;
|
||||
|
||||
import com.kalab.chess.enginesupport.ChessEngine;
|
||||
import com.kalab.chess.enginesupport.ChessEngineResolver;
|
||||
import com.larvalabs.svgandroid.SVG;
|
||||
|
@ -191,7 +197,9 @@ public class DroidFish extends Activity
|
|||
private TextView status;
|
||||
private ScrollView moveListScroll;
|
||||
private MoveListView moveList;
|
||||
private View thinkingScroll;
|
||||
private TextView thinking;
|
||||
private View buttons;
|
||||
private ImageButton custom1Button, custom2Button, custom3Button;
|
||||
private ImageButton modeButton, undoButton, redoButton;
|
||||
private ButtonActions custom1ButtonActions, custom2ButtonActions, custom3ButtonActions;
|
||||
|
@ -254,6 +262,9 @@ public class DroidFish extends Activity
|
|||
private Typeface figNotation;
|
||||
private Typeface defaultThinkingListTypeFace;
|
||||
|
||||
private boolean guideShowOnStart;
|
||||
private TourGuide tourGuide;
|
||||
|
||||
|
||||
/** Defines all configurable button actions. */
|
||||
private ActionFactory actionFactory = new ActionFactory() {
|
||||
|
@ -486,6 +497,94 @@ public class DroidFish extends Activity
|
|||
else
|
||||
loadPGNFromFile(intentFilename);
|
||||
}
|
||||
|
||||
startTourGuide();
|
||||
}
|
||||
|
||||
private void startTourGuide(){
|
||||
if (!guideShowOnStart)
|
||||
return;
|
||||
|
||||
tourGuide = TourGuide.init(this);
|
||||
ArrayList<TourGuide> guides = new ArrayList<TourGuide>();
|
||||
|
||||
TourGuide tg = TourGuide.init(this);
|
||||
tg.setToolTip(new ToolTip()
|
||||
.setTitle(getString(R.string.tour_leftMenu_title))
|
||||
.setDescription(getString(R.string.tour_leftMenu_desc))
|
||||
.setGravity(Gravity.BOTTOM | Gravity.RIGHT));
|
||||
tg.playLater(whiteTitleText);
|
||||
guides.add(tg);
|
||||
|
||||
tg = TourGuide.init(this);
|
||||
tg.setToolTip(new ToolTip()
|
||||
.setTitle(getString(R.string.tour_rightMenu_title))
|
||||
.setDescription(getString(R.string.tour_rightMenu_desc))
|
||||
.setGravity(Gravity.BOTTOM | Gravity.LEFT));
|
||||
tg.playLater(blackTitleText);
|
||||
guides.add(tg);
|
||||
|
||||
tg = TourGuide.init(this);
|
||||
int gravity = !landScapeView() ? Gravity.BOTTOM : leftHandedView() ? Gravity.LEFT : Gravity.RIGHT;
|
||||
tg.setToolTip(new ToolTip()
|
||||
.setTitle(getString(R.string.tour_chessBoard_title))
|
||||
.setDescription(getString(R.string.tour_chessBoard_desc))
|
||||
.setGravity(gravity));
|
||||
tg.playLater(cb);
|
||||
guides.add(tg);
|
||||
|
||||
tg = TourGuide.init(this);
|
||||
gravity = !landScapeView() ? Gravity.TOP : Gravity.BOTTOM;
|
||||
tg.setToolTip(new ToolTip()
|
||||
.setTitle(getString(R.string.tour_buttons_title))
|
||||
.setDescription(getString(R.string.tour_buttons_desc))
|
||||
.setGravity(gravity));
|
||||
tg.playLater(buttons);
|
||||
guides.add(tg);
|
||||
|
||||
tg = TourGuide.init(this);
|
||||
gravity = !landScapeView() ? Gravity.TOP : leftHandedView() ? Gravity.RIGHT : Gravity.LEFT;
|
||||
tg.setToolTip(new ToolTip()
|
||||
.setTitle(getString(R.string.tour_moveList_title))
|
||||
.setDescription(getString(R.string.tour_moveList_desc))
|
||||
.setGravity(gravity));
|
||||
tg.playLater(moveListScroll);
|
||||
guides.add(tg);
|
||||
|
||||
tg = TourGuide.init(this);
|
||||
tg.setToolTip(new ToolTip()
|
||||
.setTitle(getString(R.string.tour_analysis_title))
|
||||
.setDescription(getString(R.string.tour_analysis_desc))
|
||||
.setGravity(Gravity.TOP));
|
||||
tg.playLater(thinkingScroll);
|
||||
guides.add(tg);
|
||||
|
||||
tg.setOverlay(new Overlay()
|
||||
.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
guideShowOnStart = false;
|
||||
Editor editor = settings.edit();
|
||||
editor.putBoolean("guideShowOnStart", false);
|
||||
editor.commit();
|
||||
tourGuide.next();
|
||||
tourGuide = null;
|
||||
}
|
||||
}));
|
||||
|
||||
Sequence sequence = new Sequence.SequenceBuilder()
|
||||
.add(guides.toArray(new TourGuide[guides.size()]))
|
||||
.setDefaultOverlay(new Overlay()
|
||||
.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
tourGuide.next();
|
||||
}
|
||||
}))
|
||||
.setDefaultPointer(new Pointer())
|
||||
.setContinueMethod(Sequence.ContinueMethod.OverlayListener)
|
||||
.build();
|
||||
tourGuide.playInSequence(sequence);
|
||||
}
|
||||
|
||||
// Unicode code points for chess pieces
|
||||
|
@ -664,12 +763,20 @@ public class DroidFish extends Activity
|
|||
updateThinkingInfo();
|
||||
ctrl.updateRemainingTime();
|
||||
ctrl.updateMaterialDiffList();
|
||||
if (tourGuide != null) {
|
||||
tourGuide.cleanUp();
|
||||
tourGuide = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Return true if the current orientation is landscape. */
|
||||
private final boolean landScapeView() {
|
||||
return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
|
||||
}
|
||||
|
||||
/** Return true if left-handed layout should be used. */
|
||||
private final boolean leftHandedView() {
|
||||
return settings.getBoolean("leftHanded", false) &&
|
||||
(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
|
||||
return settings.getBoolean("leftHanded", false) && landScapeView();
|
||||
}
|
||||
|
||||
/** Re-read preferences settings. */
|
||||
|
@ -708,6 +815,7 @@ public class DroidFish extends Activity
|
|||
status = (TextView)findViewById(R.id.status);
|
||||
moveListScroll = (ScrollView)findViewById(R.id.scrollView);
|
||||
moveList = (MoveListView)findViewById(R.id.moveList);
|
||||
thinkingScroll = (View)findViewById(R.id.scrollViewBot);
|
||||
thinking = (TextView)findViewById(R.id.thinking);
|
||||
defaultThinkingListTypeFace = thinking.getTypeface();
|
||||
status.setFocusable(false);
|
||||
|
@ -909,6 +1017,7 @@ public class DroidFish extends Activity
|
|||
}
|
||||
});
|
||||
|
||||
buttons = (View)findViewById(R.id.buttons);
|
||||
custom1Button = (ImageButton)findViewById(R.id.custom1Button);
|
||||
custom1ButtonActions.setImageButton(custom1Button, this);
|
||||
custom2Button = (ImageButton)findViewById(R.id.custom2Button);
|
||||
|
@ -1078,6 +1187,8 @@ public class DroidFish extends Activity
|
|||
custom3ButtonActions.readPrefs(settings, actionFactory);
|
||||
updateButtons();
|
||||
|
||||
guideShowOnStart = settings.getBoolean("guideShowOnStart", true);
|
||||
|
||||
bookOptions.filename = settings.getString("bookFile", "");
|
||||
bookOptions.maxLength = getIntSetting("bookMaxLength", 1000000);
|
||||
bookOptions.preferMainLines = settings.getBoolean("bookPreferMainLines", false);
|
||||
|
|
311
DroidFish/src/tourguide/tourguide/FrameLayoutWithHole.java
Normal file
|
@ -0,0 +1,311 @@
|
|||
package tourguide.tourguide;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.app.Activity;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.support.v4.view.MotionEventCompat;
|
||||
import android.text.TextPaint;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.FrameLayout;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* TODO: document your custom view class.
|
||||
*/
|
||||
public class FrameLayoutWithHole extends FrameLayout {
|
||||
private TextPaint mTextPaint;
|
||||
private Activity mActivity;
|
||||
private TourGuide.MotionType mMotionType;
|
||||
private Paint mEraser;
|
||||
|
||||
Bitmap mEraserBitmap;
|
||||
private Canvas mEraserCanvas;
|
||||
private Paint mPaint;
|
||||
private Paint transparentPaint;
|
||||
private View mViewHole; // This is the targeted view to be highlighted, where the hole should be placed
|
||||
private int mRadius;
|
||||
private int [] mPos;
|
||||
private float mDensity;
|
||||
private Overlay mOverlay;
|
||||
|
||||
private ArrayList<AnimatorSet> mAnimatorSetArrayList;
|
||||
|
||||
public void setViewHole(View viewHole) {
|
||||
this.mViewHole = viewHole;
|
||||
enforceMotionType();
|
||||
}
|
||||
public void addAnimatorSet(AnimatorSet animatorSet){
|
||||
if (mAnimatorSetArrayList==null){
|
||||
mAnimatorSetArrayList = new ArrayList<AnimatorSet>();
|
||||
}
|
||||
mAnimatorSetArrayList.add(animatorSet);
|
||||
}
|
||||
private void enforceMotionType(){
|
||||
Log.d("tourguide", "enforceMotionType 1");
|
||||
if (mViewHole!=null) {Log.d("tourguide","enforceMotionType 2");
|
||||
if (mMotionType!=null && mMotionType == TourGuide.MotionType.ClickOnly) {
|
||||
Log.d("tourguide","enforceMotionType 3");
|
||||
Log.d("tourguide","only Clicking");
|
||||
mViewHole.setOnTouchListener(new OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||
mViewHole.getParent().requestDisallowInterceptTouchEvent(true);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else if (mMotionType!=null && mMotionType == TourGuide.MotionType.SwipeOnly) {
|
||||
Log.d("tourguide","enforceMotionType 4");
|
||||
Log.d("tourguide","only Swiping");
|
||||
mViewHole.setClickable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FrameLayoutWithHole(Activity context, View view) {
|
||||
this(context, view, TourGuide.MotionType.AllowAll);
|
||||
}
|
||||
public FrameLayoutWithHole(Activity context, View view, TourGuide.MotionType motionType) {
|
||||
this(context, view, motionType, new Overlay());
|
||||
}
|
||||
|
||||
public FrameLayoutWithHole(Activity context, View view, TourGuide.MotionType motionType, Overlay overlay) {
|
||||
super(context);
|
||||
mActivity = context;
|
||||
mViewHole = view;
|
||||
init(null, 0);
|
||||
enforceMotionType();
|
||||
mOverlay = overlay;
|
||||
|
||||
int [] pos = new int[2];
|
||||
mViewHole.getLocationOnScreen(pos);
|
||||
mPos = pos;
|
||||
|
||||
mDensity = context.getResources().getDisplayMetrics().density;
|
||||
int padding = (int)(20 * mDensity);
|
||||
|
||||
if (mViewHole.getHeight() > mViewHole.getWidth()) {
|
||||
mRadius = mViewHole.getHeight()/2 + padding;
|
||||
} else {
|
||||
mRadius = mViewHole.getWidth()/2 + padding;
|
||||
}
|
||||
mMotionType = motionType;
|
||||
}
|
||||
private void init(AttributeSet attrs, int defStyle) {
|
||||
// Load attributes
|
||||
// final TypedArray a = getContext().obtainStyledAttributes(
|
||||
// attrs, FrameLayoutWithHole, defStyle, 0);
|
||||
//
|
||||
//
|
||||
// a.recycle();
|
||||
setWillNotDraw(false);
|
||||
// Set up a default TextPaint object
|
||||
mTextPaint = new TextPaint();
|
||||
mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||
mTextPaint.setTextAlign(Paint.Align.LEFT);
|
||||
|
||||
Point size = new Point();
|
||||
size.x = mActivity.getResources().getDisplayMetrics().widthPixels;
|
||||
size.y = mActivity.getResources().getDisplayMetrics().heightPixels;
|
||||
|
||||
mEraserBitmap = Bitmap.createBitmap(size.x, size.y, Bitmap.Config.ARGB_8888);
|
||||
mEraserCanvas = new Canvas(mEraserBitmap);
|
||||
|
||||
mPaint = new Paint();
|
||||
mPaint.setColor(0xcc000000);
|
||||
transparentPaint = new Paint();
|
||||
transparentPaint.setColor(getResources().getColor(android.R.color.transparent));
|
||||
transparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||
|
||||
mEraser = new Paint();
|
||||
mEraser.setColor(0xFFFFFFFF);
|
||||
mEraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||
mEraser.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
Log.d("tourguide","getHeight: "+ size.y);
|
||||
Log.d("tourguide","getWidth: " + size.x);
|
||||
|
||||
}
|
||||
|
||||
private boolean mCleanUpLock = false;
|
||||
protected void cleanUp(){
|
||||
if (getParent() != null) {
|
||||
if (mOverlay!=null && mOverlay.mExitAnimation!=null) {
|
||||
performOverlayExitAnimation();
|
||||
} else {
|
||||
((ViewGroup) this.getParent()).removeView(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void performOverlayExitAnimation(){
|
||||
if (!mCleanUpLock) {
|
||||
final FrameLayout _pointerToFrameLayout = this;
|
||||
mCleanUpLock = true;
|
||||
Log.d("tourguide","Overlay exit animation listener is overwritten...");
|
||||
mOverlay.mExitAnimation.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override public void onAnimationStart(Animation animation) {}
|
||||
@Override public void onAnimationRepeat(Animation animation) {}
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
((ViewGroup) _pointerToFrameLayout.getParent()).removeView(_pointerToFrameLayout);
|
||||
}
|
||||
});
|
||||
this.startAnimation(mOverlay.mExitAnimation);
|
||||
}
|
||||
}
|
||||
/* comment this whole method to cause a memory leak */
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
/* cleanup reference to prevent memory leak */
|
||||
mEraserCanvas.setBitmap(null);
|
||||
mEraserBitmap = null;
|
||||
|
||||
if (mAnimatorSetArrayList != null && mAnimatorSetArrayList.size() > 0){
|
||||
for(int i=0;i<mAnimatorSetArrayList.size();i++){
|
||||
mAnimatorSetArrayList.get(i).end();
|
||||
mAnimatorSetArrayList.get(i).removeAllListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Show an event in the LogCat view, for debugging */
|
||||
private void dumpEvent(MotionEvent event) {
|
||||
String names[] = { "DOWN" , "UP" , "MOVE" , "CANCEL" , "OUTSIDE" ,
|
||||
"POINTER_DOWN" , "POINTER_UP" , "7?" , "8?" , "9?" };
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int action = event.getAction();
|
||||
int actionCode = action & MotionEvent.ACTION_MASK;
|
||||
sb.append("event ACTION_" ).append(names[actionCode]);
|
||||
if (actionCode == MotionEvent.ACTION_POINTER_DOWN
|
||||
|| actionCode == MotionEvent.ACTION_POINTER_UP) {
|
||||
sb.append("(pid " ).append(
|
||||
action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
|
||||
sb.append(")" );
|
||||
}
|
||||
sb.append("[" );
|
||||
for (int i = 0; i < event.getPointerCount(); i++) {
|
||||
sb.append("#" ).append(i);
|
||||
sb.append("(pid " ).append(event.getPointerId(i));
|
||||
sb.append(")=" ).append((int) event.getX(i));
|
||||
sb.append("," ).append((int) event.getY(i));
|
||||
if (i + 1 < event.getPointerCount())
|
||||
sb.append(";" );
|
||||
}
|
||||
sb.append("]" );
|
||||
Log.d("tourguide", sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
//first check if the location button should handle the touch event
|
||||
dumpEvent(ev);
|
||||
int action = MotionEventCompat.getActionMasked(ev);
|
||||
if(mViewHole != null) {
|
||||
int[] pos = new int[2];
|
||||
mViewHole.getLocationOnScreen(pos);
|
||||
Log.d("tourguide", "[dispatchTouchEvent] mViewHole.getHeight(): "+mViewHole.getHeight());
|
||||
Log.d("tourguide", "[dispatchTouchEvent] mViewHole.getWidth(): "+mViewHole.getWidth());
|
||||
|
||||
Log.d("tourguide", "[dispatchTouchEvent] Touch X(): "+ev.getRawX());
|
||||
Log.d("tourguide", "[dispatchTouchEvent] Touch Y(): "+ev.getRawY());
|
||||
|
||||
// Log.d("tourguide", "[dispatchTouchEvent] X of image: "+pos[0]);
|
||||
// Log.d("tourguide", "[dispatchTouchEvent] Y of image: "+pos[1]);
|
||||
|
||||
Log.d("tourguide", "[dispatchTouchEvent] X lower bound: "+ pos[0]);
|
||||
Log.d("tourguide", "[dispatchTouchEvent] X higher bound: "+(pos[0] +mViewHole.getWidth()));
|
||||
|
||||
Log.d("tourguide", "[dispatchTouchEvent] Y lower bound: "+ pos[1]);
|
||||
Log.d("tourguide", "[dispatchTouchEvent] Y higher bound: "+(pos[1] +mViewHole.getHeight()));
|
||||
|
||||
if(ev.getRawY() >= pos[1] && ev.getRawY() <= (pos[1] + mViewHole.getHeight()) && ev.getRawX() >= pos[0] && ev.getRawX() <= (pos[0] + mViewHole.getWidth())) { //location button event
|
||||
Log.d("tourguide","to the BOTTOM!");
|
||||
Log.d("tourguide",""+ev.getAction());
|
||||
|
||||
// switch(action) {
|
||||
// case (MotionEvent.ACTION_DOWN) :
|
||||
// Log.d("tourguide","Action was DOWN");
|
||||
// return false;
|
||||
// case (MotionEvent.ACTION_MOVE) :
|
||||
// Log.d("tourguide","Action was MOVE");
|
||||
// return true;
|
||||
// case (MotionEvent.ACTION_UP) :
|
||||
// Log.d("tourguide","Action was UP");
|
||||
//// ev.setAction(MotionEvent.ACTION_DOWN|MotionEvent.ACTION_UP);
|
||||
//// return super.dispatchTouchEvent(ev);
|
||||
// return false;
|
||||
// case (MotionEvent.ACTION_CANCEL) :
|
||||
// Log.d("tourguide","Action was CANCEL");
|
||||
// return true;
|
||||
// case (MotionEvent.ACTION_OUTSIDE) :
|
||||
// Log.d("tourguide","Movement occurred outside bounds " +
|
||||
// "of current screen element");
|
||||
// return true;
|
||||
// default :
|
||||
// return super.dispatchTouchEvent(ev);
|
||||
// }
|
||||
// return mViewHole.onTouchEvent(ev);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
mEraserBitmap.eraseColor(Color.TRANSPARENT);
|
||||
|
||||
if (mOverlay!=null) {
|
||||
mEraserCanvas.drawColor(mOverlay.mBackgroundColor);
|
||||
int padding = (int) (10 * mDensity);
|
||||
if (mOverlay.mStyle == Overlay.Style.Rectangle) {
|
||||
mEraserCanvas.drawRect(mPos[0] - padding, mPos[1] - padding, mPos[0] + mViewHole.getWidth() + padding, mPos[1] + mViewHole.getHeight() + padding, mEraser);
|
||||
} else {
|
||||
mEraserCanvas.drawCircle(mPos[0] + mViewHole.getWidth() / 2, mPos[1] + mViewHole.getHeight() / 2, mRadius, mEraser);
|
||||
}
|
||||
}
|
||||
canvas.drawBitmap(mEraserBitmap, 0, 0, null);
|
||||
|
||||
}
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
if (mOverlay!=null && mOverlay.mEnterAnimation!=null) {
|
||||
this.startAnimation(mOverlay.mEnterAnimation);
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Convenient method to obtain screen width in pixel
|
||||
*
|
||||
* @param activity
|
||||
* @return screen width in pixel
|
||||
*/
|
||||
public int getScreenWidth(Activity activity){
|
||||
return activity.getResources().getDisplayMetrics().widthPixels;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Convenient method to obtain screen height in pixel
|
||||
*
|
||||
* @param activity
|
||||
* @return screen width in pixel
|
||||
*/
|
||||
public int getScreenHeight(Activity activity){
|
||||
return activity.getResources().getDisplayMetrics().heightPixels;
|
||||
}
|
||||
}
|
83
DroidFish/src/tourguide/tourguide/Overlay.java
Normal file
|
@ -0,0 +1,83 @@
|
|||
package tourguide.tourguide;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
|
||||
/**
|
||||
* Created by tanjunrong on 6/20/15.
|
||||
*/
|
||||
public class Overlay {
|
||||
public int mBackgroundColor;
|
||||
public boolean mDisableClick;
|
||||
public Style mStyle;
|
||||
public Animation mEnterAnimation, mExitAnimation;
|
||||
public View.OnClickListener mOnClickListener;
|
||||
|
||||
public enum Style {
|
||||
Circle, Rectangle
|
||||
}
|
||||
public Overlay() {
|
||||
this(true, Color.parseColor("#55000000"), Style.Circle);
|
||||
}
|
||||
|
||||
public Overlay(boolean disableClick, int backgroundColor, Style style) {
|
||||
mDisableClick = disableClick;
|
||||
mBackgroundColor = backgroundColor;
|
||||
mStyle = style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color
|
||||
* @param backgroundColor
|
||||
* @return return ToolTip instance for chaining purpose
|
||||
*/
|
||||
public Overlay setBackgroundColor(int backgroundColor){
|
||||
mBackgroundColor = backgroundColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if you want to block all user input to pass through this overlay, set to false if you want to allow user input under the overlay
|
||||
* @param yes_no
|
||||
* @return return Overlay instance for chaining purpose
|
||||
*/
|
||||
public Overlay disableClick(boolean yes_no){
|
||||
mDisableClick = yes_no;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Overlay setStyle(Style style){
|
||||
mStyle = style;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set enter animation
|
||||
* @param enterAnimation
|
||||
* @return return Overlay instance for chaining purpose
|
||||
*/
|
||||
public Overlay setEnterAnimation(Animation enterAnimation){
|
||||
mEnterAnimation = enterAnimation;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set exit animation
|
||||
* @param exitAnimation
|
||||
* @return return Overlay instance for chaining purpose
|
||||
*/
|
||||
public Overlay setExitAnimation(Animation exitAnimation){
|
||||
mExitAnimation = exitAnimation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set onClickListener for the Overlay
|
||||
* @param onClickListener
|
||||
* @return return Overlay instance for chaining purpose
|
||||
*/
|
||||
public Overlay setOnClickListener(View.OnClickListener onClickListener){
|
||||
mOnClickListener=onClickListener;
|
||||
return this;
|
||||
}
|
||||
}
|
41
DroidFish/src/tourguide/tourguide/Pointer.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
package tourguide.tourguide;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.view.Gravity;
|
||||
|
||||
/**
|
||||
* Created by tanjunrong on 6/20/15.
|
||||
*/
|
||||
public class Pointer {
|
||||
public int mGravity = Gravity.CENTER;
|
||||
public int mColor = Color.WHITE;
|
||||
|
||||
public Pointer() {
|
||||
this(Gravity.CENTER, Color.parseColor("#FFFFFF"));
|
||||
}
|
||||
|
||||
public Pointer(int gravity, int color) {
|
||||
this.mGravity = gravity;
|
||||
this.mColor = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set color
|
||||
* @param color
|
||||
* @return return Pointer instance for chaining purpose
|
||||
*/
|
||||
public Pointer setColor(int color){
|
||||
mColor = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set gravity
|
||||
* @param gravity
|
||||
* @return return Pointer instance for chaining purpose
|
||||
*/
|
||||
public Pointer setGravity(int gravity){
|
||||
mGravity = gravity;
|
||||
return this;
|
||||
}
|
||||
}
|
219
DroidFish/src/tourguide/tourguide/Sequence.java
Normal file
|
@ -0,0 +1,219 @@
|
|||
package tourguide.tourguide;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Created by aaronliew on 8/7/15.
|
||||
*/
|
||||
public class Sequence {
|
||||
TourGuide [] mTourGuideArray;
|
||||
Overlay mDefaultOverlay;
|
||||
ToolTip mDefaultToolTip;
|
||||
Pointer mDefaultPointer;
|
||||
|
||||
ContinueMethod mContinueMethod;
|
||||
boolean mDisableTargetButton;
|
||||
public int mCurrentSequence;
|
||||
TourGuide mParentTourGuide;
|
||||
public enum ContinueMethod {
|
||||
Overlay, OverlayListener
|
||||
}
|
||||
private Sequence(SequenceBuilder builder){
|
||||
this.mTourGuideArray = builder.mTourGuideArray;
|
||||
this.mDefaultOverlay = builder.mDefaultOverlay;
|
||||
this.mDefaultToolTip = builder.mDefaultToolTip;
|
||||
this.mDefaultPointer = builder.mDefaultPointer;
|
||||
this.mContinueMethod = builder.mContinueMethod;
|
||||
this.mCurrentSequence = builder.mCurrentSequence;
|
||||
|
||||
// TODO: to be implemented
|
||||
this.mDisableTargetButton = builder.mDisableTargetButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the parent TourGuide that will run this Sequence
|
||||
*/
|
||||
protected void setParentTourGuide(TourGuide parentTourGuide){
|
||||
mParentTourGuide = parentTourGuide;
|
||||
|
||||
if(mContinueMethod == ContinueMethod.Overlay) {
|
||||
for (final TourGuide tourGuide : mTourGuideArray) {
|
||||
tourGuide.mOverlay.mOnClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mParentTourGuide.next();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TourGuide getNextTourGuide() {
|
||||
return mTourGuideArray[mCurrentSequence];
|
||||
}
|
||||
|
||||
public ContinueMethod getContinueMethod() {
|
||||
return mContinueMethod;
|
||||
}
|
||||
|
||||
public TourGuide[] getTourGuideArray() {
|
||||
return mTourGuideArray;
|
||||
}
|
||||
|
||||
public Overlay getDefaultOverlay() {
|
||||
return mDefaultOverlay;
|
||||
}
|
||||
|
||||
public ToolTip getDefaultToolTip() {
|
||||
return mDefaultToolTip;
|
||||
}
|
||||
|
||||
public ToolTip getToolTip() {
|
||||
// individual tour guide has higher priority
|
||||
if (mTourGuideArray[mCurrentSequence].mToolTip != null){
|
||||
return mTourGuideArray[mCurrentSequence].mToolTip;
|
||||
} else {
|
||||
return mDefaultToolTip;
|
||||
}
|
||||
}
|
||||
|
||||
public Overlay getOverlay() {
|
||||
// Overlay is used as a method to proceed to next TourGuide, so the default overlay is already assigned appropriately if needed
|
||||
return mTourGuideArray[mCurrentSequence].mOverlay;
|
||||
}
|
||||
|
||||
public Pointer getPointer() {
|
||||
// individual tour guide has higher priority
|
||||
if (mTourGuideArray[mCurrentSequence].mPointer != null){
|
||||
return mTourGuideArray[mCurrentSequence].mPointer;
|
||||
} else {
|
||||
return mDefaultPointer;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SequenceBuilder {
|
||||
TourGuide [] mTourGuideArray;
|
||||
Overlay mDefaultOverlay;
|
||||
ToolTip mDefaultToolTip;
|
||||
Pointer mDefaultPointer;
|
||||
ContinueMethod mContinueMethod;
|
||||
int mCurrentSequence;
|
||||
boolean mDisableTargetButton;
|
||||
|
||||
public SequenceBuilder add(TourGuide... tourGuideArray){
|
||||
mTourGuideArray = tourGuideArray;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SequenceBuilder setDefaultOverlay(Overlay defaultOverlay){
|
||||
mDefaultOverlay = defaultOverlay;
|
||||
return this;
|
||||
}
|
||||
|
||||
// This might not be useful, but who knows.. maybe someone needs it
|
||||
public SequenceBuilder setDefaultToolTip(ToolTip defaultToolTip){
|
||||
mDefaultToolTip = defaultToolTip;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SequenceBuilder setDefaultPointer(Pointer defaultPointer){
|
||||
mDefaultPointer = defaultPointer;
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO: this is an uncompleted feature, make it private first
|
||||
// This is intended to be used to disable the button, so people cannot click on in during a Tour, instead, people can only click on Next button or Overlay to proceed
|
||||
private SequenceBuilder setDisableButton(boolean disableTargetButton){
|
||||
mDisableTargetButton = disableTargetButton;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param continueMethod ContinueMethod.Overlay or ContinueMethod.OverlayListener
|
||||
* ContnueMethod.Overlay - clicking on Overlay will make TourGuide proceed to the next one.
|
||||
* ContinueMethod.OverlayListener - you need to provide OverlayListeners, and call tourGuideHandler.next() in the listener to proceed to the next one.
|
||||
*/
|
||||
public SequenceBuilder setContinueMethod(ContinueMethod continueMethod){
|
||||
mContinueMethod = continueMethod;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Sequence build(){
|
||||
mCurrentSequence = 0;
|
||||
checkIfContinueMethodNull();
|
||||
checkAtLeastTwoTourGuideSupplied();
|
||||
checkOverlayListener(mContinueMethod);
|
||||
|
||||
return new Sequence(this);
|
||||
}
|
||||
private void checkIfContinueMethodNull(){
|
||||
if (mContinueMethod == null){
|
||||
throw new IllegalArgumentException("Continue Method is not set. Please provide ContinueMethod in setContinueMethod");
|
||||
}
|
||||
}
|
||||
private void checkAtLeastTwoTourGuideSupplied() {
|
||||
if (mTourGuideArray == null || mTourGuideArray.length <= 1){
|
||||
throw new IllegalArgumentException("In order to run a sequence, you must at least supply 2 TourGuide into Sequence using add()");
|
||||
}
|
||||
}
|
||||
private void checkOverlayListener(ContinueMethod continueMethod) {
|
||||
if(continueMethod == ContinueMethod.OverlayListener){
|
||||
boolean pass = true;
|
||||
if (mDefaultOverlay != null && mDefaultOverlay.mOnClickListener != null) {
|
||||
pass = true;
|
||||
// when default listener is available, we loop through individual tour guide, and
|
||||
// assign default listener to individual tour guide
|
||||
for (TourGuide tourGuide : mTourGuideArray) {
|
||||
if (tourGuide.mOverlay == null) {
|
||||
tourGuide.mOverlay = mDefaultOverlay;
|
||||
}
|
||||
if (tourGuide.mOverlay != null && tourGuide.mOverlay.mOnClickListener == null) {
|
||||
tourGuide.mOverlay.mOnClickListener = mDefaultOverlay.mOnClickListener;
|
||||
}
|
||||
}
|
||||
} else { // case where: default listener is not available
|
||||
|
||||
for (TourGuide tourGuide : mTourGuideArray) {
|
||||
//Both of the overlay and default listener is not null, throw the error
|
||||
if (tourGuide.mOverlay != null && tourGuide.mOverlay.mOnClickListener == null) {
|
||||
pass = false;
|
||||
break;
|
||||
} else if (tourGuide.mOverlay == null){
|
||||
pass = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!pass){
|
||||
throw new IllegalArgumentException("ContinueMethod.OverlayListener is chosen as the ContinueMethod, but no Default Overlay Listener is set, or not all Overlay.mListener is set for all the TourGuide passed in.");
|
||||
}
|
||||
} else if(continueMethod == ContinueMethod.Overlay){
|
||||
// when Overlay ContinueMethod is used, listener must not be supplied (to avoid unexpected result)
|
||||
boolean pass = true;
|
||||
if (mDefaultOverlay != null && mDefaultOverlay.mOnClickListener != null) {
|
||||
pass = false;
|
||||
} else {
|
||||
for (TourGuide tourGuide : mTourGuideArray) {
|
||||
if (tourGuide.mOverlay != null && tourGuide.mOverlay.mOnClickListener != null ) {
|
||||
pass = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mDefaultOverlay != null) {
|
||||
for (TourGuide tourGuide : mTourGuideArray) {
|
||||
if (tourGuide.mOverlay == null) {
|
||||
tourGuide.mOverlay = mDefaultOverlay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!pass) {
|
||||
throw new IllegalArgumentException("ContinueMethod.Overlay is chosen as the ContinueMethod, but either default overlay listener is still set OR individual overlay listener is still set, make sure to clear all Overlay listener");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
119
DroidFish/src/tourguide/tourguide/ToolTip.java
Normal file
|
@ -0,0 +1,119 @@
|
|||
package tourguide.tourguide;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.BounceInterpolator;
|
||||
|
||||
/**
|
||||
* Created by tanjunrong on 6/17/15.
|
||||
*/
|
||||
public class ToolTip {
|
||||
public String mTitle, mDescription;
|
||||
public int mBackgroundColor, mTextColor;
|
||||
public Animation mEnterAnimation, mExitAnimation;
|
||||
public boolean mShadow;
|
||||
public int mGravity;
|
||||
public View.OnClickListener mOnClickListener;
|
||||
|
||||
public ToolTip(){
|
||||
/* default values */
|
||||
mTitle = "";
|
||||
mDescription = "";
|
||||
mBackgroundColor = Color.parseColor("#3498db");
|
||||
mTextColor = Color.parseColor("#FFFFFF");
|
||||
|
||||
mEnterAnimation = new AlphaAnimation(0f, 1f);
|
||||
mEnterAnimation.setDuration(1000);
|
||||
mEnterAnimation.setFillAfter(true);
|
||||
mEnterAnimation.setInterpolator(new BounceInterpolator());
|
||||
mShadow = true;
|
||||
|
||||
// TODO: exit animation
|
||||
mGravity = Gravity.CENTER;
|
||||
}
|
||||
/**
|
||||
* Set title text
|
||||
* @param title
|
||||
* @return return ToolTip instance for chaining purpose
|
||||
*/
|
||||
public ToolTip setTitle(String title){
|
||||
mTitle = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set description text
|
||||
* @param description
|
||||
* @return return ToolTip instance for chaining purpose
|
||||
*/
|
||||
public ToolTip setDescription(String description){
|
||||
mDescription = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color
|
||||
* @param backgroundColor
|
||||
* @return return ToolTip instance for chaining purpose
|
||||
*/
|
||||
public ToolTip setBackgroundColor(int backgroundColor){
|
||||
mBackgroundColor = backgroundColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color
|
||||
* @param textColor
|
||||
* @return return ToolTip instance for chaining purpose
|
||||
*/
|
||||
public ToolTip setTextColor(int textColor){
|
||||
mTextColor = textColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set enter animation
|
||||
* @param enterAnimation
|
||||
* @return return ToolTip instance for chaining purpose
|
||||
*/
|
||||
public ToolTip setEnterAnimation(Animation enterAnimation){
|
||||
mEnterAnimation = enterAnimation;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set exit animation
|
||||
* @param exitAnimation
|
||||
* @return return ToolTip instance for chaining purpose
|
||||
*/
|
||||
// TODO:
|
||||
// public ToolTip setExitAnimation(Animation exitAnimation){
|
||||
// mExitAnimation = exitAnimation;
|
||||
// return this;
|
||||
// }
|
||||
/**
|
||||
* Set the gravity, the setGravity is centered relative to the targeted button
|
||||
* @param gravity Gravity.CENTER, Gravity.TOP, Gravity.BOTTOM, etc
|
||||
* @return return ToolTip instance for chaining purpose
|
||||
*/
|
||||
public ToolTip setGravity(int gravity){
|
||||
mGravity = gravity;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set if you want to have setShadow
|
||||
* @param shadow
|
||||
* @return return ToolTip instance for chaining purpose
|
||||
*/
|
||||
public ToolTip setShadow(boolean shadow){
|
||||
mShadow = shadow;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ToolTip setOnClickListener(View.OnClickListener onClickListener){
|
||||
mOnClickListener = onClickListener;
|
||||
return this;
|
||||
}
|
||||
}
|
633
DroidFish/src/tourguide/tourguide/TourGuide.java
Normal file
|
@ -0,0 +1,633 @@
|
|||
package tourguide.tourguide;
|
||||
|
||||
import org.petero.droidfish.R;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.Activity;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Point;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import net.i2p.android.ext.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
/**
|
||||
* Created by tanjunrong on 2/10/15.
|
||||
*/
|
||||
public class TourGuide {
|
||||
/**
|
||||
* This describes the animation techniques
|
||||
* */
|
||||
public enum Technique {
|
||||
Click, HorizontalLeft, HorizontalRight, VerticalUpward, VerticalDownward
|
||||
}
|
||||
|
||||
/**
|
||||
* This describes the allowable motion, for example if you want the users to learn about clicking, but want to stop them from swiping, then use ClickOnly
|
||||
*/
|
||||
public enum MotionType {
|
||||
AllowAll, ClickOnly, SwipeOnly
|
||||
}
|
||||
private Technique mTechnique;
|
||||
private View mHighlightedView;
|
||||
private Activity mActivity;
|
||||
private MotionType mMotionType;
|
||||
private FrameLayoutWithHole mFrameLayout;
|
||||
private View mToolTipViewGroup;
|
||||
public ToolTip mToolTip;
|
||||
public Pointer mPointer;
|
||||
public Overlay mOverlay;
|
||||
|
||||
private Sequence mSequence;
|
||||
|
||||
/*************
|
||||
*
|
||||
* Public API
|
||||
*
|
||||
*************/
|
||||
|
||||
/* Static builder */
|
||||
public static TourGuide init(Activity activity){
|
||||
return new TourGuide(activity);
|
||||
}
|
||||
|
||||
/* Constructor */
|
||||
public TourGuide(Activity activity){
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the animation to be used
|
||||
* @param technique Animation to be used
|
||||
* @return return TourGuide instance for chaining purpose
|
||||
*/
|
||||
public TourGuide with(Technique technique) {
|
||||
mTechnique = technique;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets which motion type is motionType
|
||||
* @param motionType
|
||||
* @return return TourGuide instance for chaining purpose
|
||||
*/
|
||||
public TourGuide motionType(MotionType motionType){
|
||||
mMotionType = motionType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the duration
|
||||
* @param view the view in which the tutorial button will be placed on top of
|
||||
* @return return TourGuide instance for chaining purpose
|
||||
*/
|
||||
public TourGuide playOn(View view){
|
||||
mHighlightedView = view;
|
||||
setupView();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the overlay
|
||||
* @param overlay this overlay object should contain the attributes of the overlay, such as background color, animation, Style, etc
|
||||
* @return return TourGuide instance for chaining purpose
|
||||
*/
|
||||
public TourGuide setOverlay(Overlay overlay){
|
||||
mOverlay = overlay;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set the toolTip
|
||||
* @param toolTip this toolTip object should contain the attributes of the ToolTip, such as, the title text, and the description text, background color, etc
|
||||
* @return return TourGuide instance for chaining purpose
|
||||
*/
|
||||
public TourGuide setToolTip(ToolTip toolTip){
|
||||
mToolTip = toolTip;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set the Pointer
|
||||
* @param pointer this pointer object should contain the attributes of the Pointer, such as the pointer color, pointer gravity, etc, refer to @Link{pointer}
|
||||
* @return return TourGuide instance for chaining purpose
|
||||
*/
|
||||
public TourGuide setPointer(Pointer pointer){
|
||||
mPointer = pointer;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Clean up the tutorial that is added to the activity
|
||||
*/
|
||||
public void cleanUp(){
|
||||
mFrameLayout.cleanUp();
|
||||
if (mToolTipViewGroup!=null) {
|
||||
((ViewGroup) mActivity.getWindow().getDecorView()).removeView(mToolTipViewGroup);
|
||||
}
|
||||
}
|
||||
|
||||
public TourGuide playLater(View view){
|
||||
mHighlightedView = view;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**************************
|
||||
* Sequence related method
|
||||
**************************/
|
||||
|
||||
public TourGuide playInSequence(Sequence sequence){
|
||||
setSequence(sequence);
|
||||
next();
|
||||
return this;
|
||||
}
|
||||
|
||||
public TourGuide setSequence(Sequence sequence){
|
||||
mSequence = sequence;
|
||||
mSequence.setParentTourGuide(this);
|
||||
for (TourGuide tourGuide : sequence.mTourGuideArray){
|
||||
if (tourGuide.mHighlightedView == null) {
|
||||
throw new NullPointerException("Please specify the view using 'playLater' method");
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public TourGuide next(){
|
||||
if (mFrameLayout!=null) {
|
||||
cleanUp();
|
||||
}
|
||||
|
||||
if (mSequence.mCurrentSequence < mSequence.mTourGuideArray.length) {
|
||||
setToolTip(mSequence.getToolTip());
|
||||
setPointer(mSequence.getPointer());
|
||||
setOverlay(mSequence.getOverlay());
|
||||
|
||||
mHighlightedView = mSequence.getNextTourGuide().mHighlightedView;
|
||||
|
||||
setupView();
|
||||
mSequence.mCurrentSequence++;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return FrameLayoutWithHole that is used as overlay
|
||||
*/
|
||||
public FrameLayoutWithHole getOverlay(){
|
||||
return mFrameLayout;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return the ToolTip container View
|
||||
*/
|
||||
public View getToolTip(){
|
||||
return mToolTipViewGroup;
|
||||
}
|
||||
/******
|
||||
*
|
||||
* Private methods
|
||||
*
|
||||
*******/
|
||||
//TODO: move into Pointer
|
||||
private int getXBasedOnGravity(int width){
|
||||
int [] pos = new int[2];
|
||||
mHighlightedView.getLocationOnScreen(pos);
|
||||
int x = pos[0];
|
||||
if((mPointer.mGravity & Gravity.RIGHT) == Gravity.RIGHT){
|
||||
return x+mHighlightedView.getWidth()-width;
|
||||
} else if ((mPointer.mGravity & Gravity.LEFT) == Gravity.LEFT) {
|
||||
return x;
|
||||
} else { // this is center
|
||||
return x+mHighlightedView.getWidth()/2-width/2;
|
||||
}
|
||||
}
|
||||
//TODO: move into Pointer
|
||||
private int getYBasedOnGravity(int height){
|
||||
int [] pos = new int[2];
|
||||
mHighlightedView.getLocationInWindow(pos);
|
||||
int y = pos[1];
|
||||
if((mPointer.mGravity & Gravity.BOTTOM) == Gravity.BOTTOM){
|
||||
return y+mHighlightedView.getHeight()-height;
|
||||
} else if ((mPointer.mGravity & Gravity.TOP) == Gravity.TOP) {
|
||||
return y;
|
||||
}else { // this is center
|
||||
return y+mHighlightedView.getHeight()/2-height/2;
|
||||
}
|
||||
}
|
||||
|
||||
private void setupView(){
|
||||
// TODO: throw exception if either mActivity, mDuration, mHighlightedView is null
|
||||
checking();
|
||||
final ViewTreeObserver viewTreeObserver = mHighlightedView.getViewTreeObserver();
|
||||
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
// make sure this only run once
|
||||
mHighlightedView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
||||
|
||||
/* Initialize a frame layout with a hole */
|
||||
mFrameLayout = new FrameLayoutWithHole(mActivity, mHighlightedView, mMotionType, mOverlay);
|
||||
/* handle click disable */
|
||||
handleDisableClicking(mFrameLayout);
|
||||
|
||||
/* setup floating action button */
|
||||
if (mPointer != null) {
|
||||
FloatingActionButton fab = setupAndAddFABToFrameLayout(mFrameLayout);
|
||||
performAnimationOn(fab);
|
||||
}
|
||||
setupFrameLayout();
|
||||
/* setup tooltip view */
|
||||
setupToolTip();
|
||||
}
|
||||
});
|
||||
}
|
||||
private void checking(){
|
||||
// There is not check for tooltip because tooltip can be null, it means there no tooltip will be shown
|
||||
|
||||
}
|
||||
private void handleDisableClicking(FrameLayoutWithHole frameLayoutWithHole){
|
||||
// 1. if user provides an overlay listener, use that as 1st priority
|
||||
if (mOverlay != null && mOverlay.mOnClickListener!=null) {
|
||||
frameLayoutWithHole.setClickable(true);
|
||||
frameLayoutWithHole.setOnClickListener(mOverlay.mOnClickListener);
|
||||
}
|
||||
// 2. if overlay listener is not provided, check if it's disabled
|
||||
else if (mOverlay != null && mOverlay.mDisableClick) {
|
||||
Log.w("tourguide", "Overlay's default OnClickListener is null, it will proceed to next tourguide when it is clicked");
|
||||
frameLayoutWithHole.setViewHole(mHighlightedView);
|
||||
frameLayoutWithHole.setSoundEffectsEnabled(false);
|
||||
frameLayoutWithHole.setOnClickListener(new View.OnClickListener() {
|
||||
@Override public void onClick(View v) {} // do nothing, disabled.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void setupToolTip(){
|
||||
final FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
if (mToolTip != null) {
|
||||
/* inflate and get views */
|
||||
ViewGroup parent = (ViewGroup) mActivity.getWindow().getDecorView();
|
||||
LayoutInflater layoutInflater = mActivity.getLayoutInflater();
|
||||
mToolTipViewGroup = layoutInflater.inflate(R.layout.tooltip, null);
|
||||
View toolTipContainer = mToolTipViewGroup.findViewById(R.id.toolTip_container);
|
||||
TextView toolTipTitleTV = (TextView) mToolTipViewGroup.findViewById(R.id.title);
|
||||
TextView toolTipDescriptionTV = (TextView) mToolTipViewGroup.findViewById(R.id.description);
|
||||
|
||||
/* set tooltip attributes */
|
||||
toolTipContainer.setBackgroundColor(mToolTip.mBackgroundColor);
|
||||
if (mToolTip.mTitle == null){
|
||||
toolTipTitleTV.setVisibility(View.GONE);
|
||||
} else {
|
||||
toolTipTitleTV.setText(mToolTip.mTitle);
|
||||
}
|
||||
if (mToolTip.mDescription == null){
|
||||
toolTipDescriptionTV.setVisibility(View.GONE);
|
||||
} else {
|
||||
toolTipDescriptionTV.setText(mToolTip.mDescription);
|
||||
}
|
||||
|
||||
|
||||
mToolTipViewGroup.startAnimation(mToolTip.mEnterAnimation);
|
||||
|
||||
/* add setShadow if it's turned on */
|
||||
if (mToolTip.mShadow) {
|
||||
mToolTipViewGroup.setBackgroundDrawable(mActivity.getResources().getDrawable(R.drawable.drop_shadow));
|
||||
}
|
||||
|
||||
/* position and size calculation */
|
||||
int [] pos = new int[2];
|
||||
mHighlightedView.getLocationOnScreen(pos);
|
||||
int targetViewX = pos[0];
|
||||
final int targetViewY = pos[1];
|
||||
|
||||
// get measured size of tooltip
|
||||
mToolTipViewGroup.measure(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
|
||||
int toolTipMeasuredWidth = mToolTipViewGroup.getMeasuredWidth();
|
||||
int toolTipMeasuredHeight = mToolTipViewGroup.getMeasuredHeight();
|
||||
|
||||
Point resultPoint = new Point(); // this holds the final position of tooltip
|
||||
float density = mActivity.getResources().getDisplayMetrics().density;
|
||||
final float adjustment = 10 * density; //adjustment is that little overlapping area of tooltip and targeted button
|
||||
|
||||
// calculate x position, based on gravity, tooltipMeasuredWidth, parent max width, x position of target view, adjustment
|
||||
if (toolTipMeasuredWidth > parent.getWidth()){
|
||||
resultPoint.x = getXForTooTip(mToolTip.mGravity, parent.getWidth(), targetViewX, adjustment);
|
||||
} else {
|
||||
resultPoint.x = getXForTooTip(mToolTip.mGravity, toolTipMeasuredWidth, targetViewX, adjustment);
|
||||
}
|
||||
|
||||
resultPoint.y = getYForTooTip(mToolTip.mGravity, toolTipMeasuredHeight, targetViewY, adjustment);
|
||||
|
||||
// add view to parent
|
||||
// ((ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content)).addView(mToolTipViewGroup, layoutParams);
|
||||
parent.addView(mToolTipViewGroup, layoutParams);
|
||||
|
||||
// 1. width < screen check
|
||||
if (toolTipMeasuredWidth > parent.getWidth()){
|
||||
mToolTipViewGroup.getLayoutParams().width = parent.getWidth();
|
||||
toolTipMeasuredWidth = parent.getWidth();
|
||||
}
|
||||
// 2. x left boundary check
|
||||
if (resultPoint.x < 0){
|
||||
mToolTipViewGroup.getLayoutParams().width = toolTipMeasuredWidth + resultPoint.x; //since point.x is negative, use plus
|
||||
resultPoint.x = 0;
|
||||
}
|
||||
// 3. x right boundary check
|
||||
int tempRightX = resultPoint.x + toolTipMeasuredWidth;
|
||||
if ( tempRightX > parent.getWidth()){
|
||||
mToolTipViewGroup.getLayoutParams().width = parent.getWidth() - resultPoint.x; //since point.x is negative, use plus
|
||||
}
|
||||
|
||||
// pass toolTip onClickListener into toolTipViewGroup
|
||||
if (mToolTip.mOnClickListener!=null) {
|
||||
mToolTipViewGroup.setOnClickListener(mToolTip.mOnClickListener);
|
||||
}
|
||||
|
||||
// TODO: no boundary check for height yet, this is a unlikely case though
|
||||
// height boundary can be fixed by user changing the gravity to the other size, since there are plenty of space vertically compared to horizontally
|
||||
|
||||
// this needs an viewTreeObserver, that's because TextView measurement of it's vertical height is not accurate (didn't take into account of multiple lines yet) before it's rendered
|
||||
// re-calculate height again once it's rendered
|
||||
final ViewTreeObserver viewTreeObserver = mToolTipViewGroup.getViewTreeObserver();
|
||||
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
mToolTipViewGroup.getViewTreeObserver().removeGlobalOnLayoutListener(this);// make sure this only run once
|
||||
|
||||
int fixedY;
|
||||
int toolTipHeightAfterLayouted = mToolTipViewGroup.getHeight();
|
||||
fixedY = getYForTooTip(mToolTip.mGravity, toolTipHeightAfterLayouted, targetViewY, adjustment);
|
||||
layoutParams.setMargins((int)mToolTipViewGroup.getX(),fixedY,0,0);
|
||||
}
|
||||
});
|
||||
|
||||
// set the position using setMargins on the left and top
|
||||
layoutParams.setMargins(resultPoint.x, resultPoint.y, 0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int getXForTooTip(int gravity, int toolTipMeasuredWidth, int targetViewX, float adjustment){
|
||||
int x;
|
||||
if ((gravity & Gravity.LEFT) == Gravity.LEFT){
|
||||
x = targetViewX - toolTipMeasuredWidth + (int)adjustment;
|
||||
} else if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
|
||||
x = targetViewX + mHighlightedView.getWidth() - (int)adjustment;
|
||||
} else {
|
||||
x = targetViewX + mHighlightedView.getWidth() / 2 - toolTipMeasuredWidth / 2;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
private int getYForTooTip(int gravity, int toolTipMeasuredHeight, int targetViewY, float adjustment){
|
||||
int y;
|
||||
if ((gravity & Gravity.TOP) == Gravity.TOP) {
|
||||
|
||||
if (((gravity & Gravity.LEFT) == Gravity.LEFT) || ((gravity & Gravity.RIGHT) == Gravity.RIGHT)) {
|
||||
y = targetViewY - toolTipMeasuredHeight + (int)adjustment;
|
||||
} else {
|
||||
y = targetViewY - toolTipMeasuredHeight - (int)adjustment;
|
||||
}
|
||||
} else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
|
||||
if (((gravity & Gravity.LEFT) == Gravity.LEFT) || ((gravity & Gravity.RIGHT) == Gravity.RIGHT)) {
|
||||
y = targetViewY + mHighlightedView.getHeight() - (int) adjustment;
|
||||
} else {
|
||||
y = targetViewY + mHighlightedView.getHeight() + (int) adjustment;
|
||||
}
|
||||
} else { // this is center
|
||||
if (((gravity & Gravity.LEFT) == Gravity.LEFT) || ((gravity & Gravity.RIGHT) == Gravity.RIGHT)) {
|
||||
y = targetViewY + mHighlightedView.getHeight() / 2 - (int) adjustment;
|
||||
} else {
|
||||
y = targetViewY + mHighlightedView.getHeight() / 2 + (int) adjustment;
|
||||
}
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
private FloatingActionButton setupAndAddFABToFrameLayout(final FrameLayoutWithHole frameLayoutWithHole){
|
||||
// invisFab is invisible, and it's only used for getting the width and height
|
||||
final FloatingActionButton invisFab = new FloatingActionButton(mActivity);
|
||||
invisFab.setSize(FloatingActionButton.SIZE_MINI);
|
||||
invisFab.setVisibility(View.INVISIBLE);
|
||||
((ViewGroup)mActivity.getWindow().getDecorView()).addView(invisFab);
|
||||
|
||||
// fab is the real fab that is going to be added
|
||||
final FloatingActionButton fab = new FloatingActionButton(mActivity);
|
||||
fab.setBackgroundColor(Color.BLUE);
|
||||
fab.setSize(FloatingActionButton.SIZE_MINI);
|
||||
fab.setColorNormal(mPointer.mColor);
|
||||
fab.setStrokeVisible(false);
|
||||
fab.setClickable(false);
|
||||
|
||||
// When invisFab is layouted, it's width and height can be used to calculate the correct position of fab
|
||||
final ViewTreeObserver viewTreeObserver = invisFab.getViewTreeObserver();
|
||||
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
// make sure this only run once
|
||||
invisFab.getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
||||
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
frameLayoutWithHole.addView(fab, params);
|
||||
|
||||
// measure size of image to be placed
|
||||
params.setMargins(getXBasedOnGravity(invisFab.getWidth()), getYBasedOnGravity(invisFab.getHeight()), 0, 0);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return fab;
|
||||
}
|
||||
|
||||
private void setupFrameLayout(){
|
||||
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
|
||||
ViewGroup contentArea = (ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content);
|
||||
int [] pos = new int[2];
|
||||
contentArea.getLocationOnScreen(pos);
|
||||
// frameLayoutWithHole's coordinates are calculated taking full screen height into account
|
||||
// but we're adding it to the content area only, so we need to offset it to the same Y value of contentArea
|
||||
|
||||
layoutParams.setMargins(0,-pos[1],0,0);
|
||||
contentArea.addView(mFrameLayout, layoutParams);
|
||||
}
|
||||
|
||||
private void performAnimationOn(final View view){
|
||||
|
||||
if (mTechnique != null && mTechnique == Technique.HorizontalLeft){
|
||||
|
||||
final AnimatorSet animatorSet = new AnimatorSet();
|
||||
final AnimatorSet animatorSet2 = new AnimatorSet();
|
||||
Animator.AnimatorListener lis1 = new Animator.AnimatorListener() {
|
||||
@Override public void onAnimationStart(Animator animator) {}
|
||||
@Override public void onAnimationCancel(Animator animator) {}
|
||||
@Override public void onAnimationRepeat(Animator animator) {}
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
view.setScaleX(1f);
|
||||
view.setScaleY(1f);
|
||||
view.setTranslationX(0);
|
||||
animatorSet2.start();
|
||||
}
|
||||
};
|
||||
Animator.AnimatorListener lis2 = new Animator.AnimatorListener() {
|
||||
@Override public void onAnimationStart(Animator animator) {}
|
||||
@Override public void onAnimationCancel(Animator animator) {}
|
||||
@Override public void onAnimationRepeat(Animator animator) {}
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
view.setScaleX(1f);
|
||||
view.setScaleY(1f);
|
||||
view.setTranslationX(0);
|
||||
animatorSet.start();
|
||||
}
|
||||
};
|
||||
|
||||
long fadeInDuration = 800;
|
||||
long scaleDownDuration = 800;
|
||||
long goLeftXDuration = 2000;
|
||||
long fadeOutDuration = goLeftXDuration;
|
||||
float translationX = getScreenWidth()/2;
|
||||
|
||||
final ValueAnimator fadeInAnim = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);
|
||||
fadeInAnim.setDuration(fadeInDuration);
|
||||
final ObjectAnimator scaleDownX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.85f);
|
||||
scaleDownX.setDuration(scaleDownDuration);
|
||||
final ObjectAnimator scaleDownY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.85f);
|
||||
scaleDownY.setDuration(scaleDownDuration);
|
||||
final ObjectAnimator goLeftX = ObjectAnimator.ofFloat(view, "translationX", -translationX);
|
||||
goLeftX.setDuration(goLeftXDuration);
|
||||
final ValueAnimator fadeOutAnim = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f);
|
||||
fadeOutAnim.setDuration(fadeOutDuration);
|
||||
|
||||
final ValueAnimator fadeInAnim2 = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);
|
||||
fadeInAnim2.setDuration(fadeInDuration);
|
||||
final ObjectAnimator scaleDownX2 = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.85f);
|
||||
scaleDownX2.setDuration(scaleDownDuration);
|
||||
final ObjectAnimator scaleDownY2 = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.85f);
|
||||
scaleDownY2.setDuration(scaleDownDuration);
|
||||
final ObjectAnimator goLeftX2 = ObjectAnimator.ofFloat(view, "translationX", -translationX);
|
||||
goLeftX2.setDuration(goLeftXDuration);
|
||||
final ValueAnimator fadeOutAnim2 = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f);
|
||||
fadeOutAnim2.setDuration(fadeOutDuration);
|
||||
|
||||
animatorSet.play(fadeInAnim);
|
||||
animatorSet.play(scaleDownX).with(scaleDownY).after(fadeInAnim);
|
||||
animatorSet.play(goLeftX).with(fadeOutAnim).after(scaleDownY);
|
||||
|
||||
animatorSet2.play(fadeInAnim2);
|
||||
animatorSet2.play(scaleDownX2).with(scaleDownY2).after(fadeInAnim2);
|
||||
animatorSet2.play(goLeftX2).with(fadeOutAnim2).after(scaleDownY2);
|
||||
|
||||
animatorSet.addListener(lis1);
|
||||
animatorSet2.addListener(lis2);
|
||||
animatorSet.start();
|
||||
|
||||
/* these animatorSets are kept track in FrameLayout, so that they can be cleaned up when FrameLayout is detached from window */
|
||||
mFrameLayout.addAnimatorSet(animatorSet);
|
||||
mFrameLayout.addAnimatorSet(animatorSet2);
|
||||
} else if (mTechnique != null && mTechnique == Technique.HorizontalRight){
|
||||
|
||||
} else if (mTechnique != null && mTechnique == Technique.VerticalUpward){
|
||||
|
||||
} else if (mTechnique != null && mTechnique == Technique.VerticalDownward){
|
||||
|
||||
} else { // do click for default case
|
||||
final AnimatorSet animatorSet = new AnimatorSet();
|
||||
final AnimatorSet animatorSet2 = new AnimatorSet();
|
||||
Animator.AnimatorListener lis1 = new Animator.AnimatorListener() {
|
||||
@Override public void onAnimationStart(Animator animator) {}
|
||||
@Override public void onAnimationCancel(Animator animator) {}
|
||||
@Override public void onAnimationRepeat(Animator animator) {}
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
view.setScaleX(1f);
|
||||
view.setScaleY(1f);
|
||||
view.setTranslationX(0);
|
||||
animatorSet2.start();
|
||||
}
|
||||
};
|
||||
Animator.AnimatorListener lis2 = new Animator.AnimatorListener() {
|
||||
@Override public void onAnimationStart(Animator animator) {}
|
||||
@Override public void onAnimationCancel(Animator animator) {}
|
||||
@Override public void onAnimationRepeat(Animator animator) {}
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
view.setScaleX(1f);
|
||||
view.setScaleY(1f);
|
||||
view.setTranslationX(0);
|
||||
animatorSet.start();
|
||||
}
|
||||
};
|
||||
|
||||
long fadeInDuration = 800;
|
||||
long scaleDownDuration = 800;
|
||||
long fadeOutDuration = 800;
|
||||
long delay = 1000;
|
||||
|
||||
final ValueAnimator delayAnim = ObjectAnimator.ofFloat(view, "translationX", 0);
|
||||
delayAnim.setDuration(delay);
|
||||
final ValueAnimator fadeInAnim = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);
|
||||
fadeInAnim.setDuration(fadeInDuration);
|
||||
final ObjectAnimator scaleDownX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.85f);
|
||||
scaleDownX.setDuration(scaleDownDuration);
|
||||
final ObjectAnimator scaleDownY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.85f);
|
||||
scaleDownY.setDuration(scaleDownDuration);
|
||||
final ObjectAnimator scaleUpX = ObjectAnimator.ofFloat(view, "scaleX", 0.85f, 1f);
|
||||
scaleUpX.setDuration(scaleDownDuration);
|
||||
final ObjectAnimator scaleUpY = ObjectAnimator.ofFloat(view, "scaleY", 0.85f, 1f);
|
||||
scaleUpY.setDuration(scaleDownDuration);
|
||||
final ValueAnimator fadeOutAnim = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f);
|
||||
fadeOutAnim.setDuration(fadeOutDuration);
|
||||
|
||||
final ValueAnimator delayAnim2 = ObjectAnimator.ofFloat(view, "translationX", 0);
|
||||
delayAnim2.setDuration(delay);
|
||||
final ValueAnimator fadeInAnim2 = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);
|
||||
fadeInAnim2.setDuration(fadeInDuration);
|
||||
final ObjectAnimator scaleDownX2 = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.85f);
|
||||
scaleDownX2.setDuration(scaleDownDuration);
|
||||
final ObjectAnimator scaleDownY2 = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.85f);
|
||||
scaleDownY2.setDuration(scaleDownDuration);
|
||||
final ObjectAnimator scaleUpX2 = ObjectAnimator.ofFloat(view, "scaleX", 0.85f, 1f);
|
||||
scaleUpX2.setDuration(scaleDownDuration);
|
||||
final ObjectAnimator scaleUpY2 = ObjectAnimator.ofFloat(view, "scaleY", 0.85f, 1f);
|
||||
scaleUpY2.setDuration(scaleDownDuration);
|
||||
final ValueAnimator fadeOutAnim2 = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f);
|
||||
fadeOutAnim2.setDuration(fadeOutDuration);
|
||||
view.setAlpha(0);
|
||||
animatorSet.setStartDelay(mToolTip != null ? mToolTip.mEnterAnimation.getDuration() : 0);
|
||||
animatorSet.play(fadeInAnim);
|
||||
animatorSet.play(scaleDownX).with(scaleDownY).after(fadeInAnim);
|
||||
animatorSet.play(scaleUpX).with(scaleUpY).with(fadeOutAnim).after(scaleDownY);
|
||||
animatorSet.play(delayAnim).after(scaleUpY);
|
||||
|
||||
animatorSet2.play(fadeInAnim2);
|
||||
animatorSet2.play(scaleDownX2).with(scaleDownY2).after(fadeInAnim2);
|
||||
animatorSet2.play(scaleUpX2).with(scaleUpY2).with(fadeOutAnim2).after(scaleDownY2);
|
||||
animatorSet2.play(delayAnim2).after(scaleUpY2);
|
||||
|
||||
animatorSet.addListener(lis1);
|
||||
animatorSet2.addListener(lis2);
|
||||
animatorSet.start();
|
||||
|
||||
/* these animatorSets are kept track in FrameLayout, so that they can be cleaned up when FrameLayout is detached from window */
|
||||
mFrameLayout.addAnimatorSet(animatorSet);
|
||||
mFrameLayout.addAnimatorSet(animatorSet2);
|
||||
}
|
||||
}
|
||||
private int getScreenWidth(){
|
||||
if (mActivity!=null) {
|
||||
return mActivity.getResources().getDisplayMetrics().widthPixels;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|