diff --git a/.gitignore b/.gitignore
index 433d269..29932c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,3 +34,5 @@ target/
#Maven release files
*.releaseBackup
*.versionsBackup
+robodemo-lib/project.properties
+robodemo-sample/project.properties
diff --git a/README.md b/README.md
index c62702c..6fabd59 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,7 @@ RoboDemo
========
RoboDemo is a ShowCase library for Android to demonstrate to users how a given Activity works.
-
-A sample is available in the [download area](https://github.com/stephanenicolas/RoboDemo/downloads) of the repository.
+Additionally use RoboDemo to walk a user through a first time introduction to the app or show them how to use a new feature.
Screenshots
-----------
@@ -29,18 +28,18 @@ There are some cases where applications require more complex interactions from u
or new interactions not covered by Android UI Guidelines.
RoboDemo eases creating showcases / explaining / demonstrating of such activities to users.
-It will display an overlay activity to illustrate the `Activity` under showcase. The explanations consist of a serie of
+It will display an overlay to illustrate the `Activity` under showcase. The explanations consist of a series of
points to click on and their associated labels. The `Activity` under showcase is dimmed and the showcase highlights
transparent areas to point views or positions users have to click.
-Creation of `DemoActivity` is straightforward, have a look at the sample to put in place RoboDemo in your own app :
+Creation of `DemoFragment` is straightforward, have a look at the sample to put RoboDemo in your own app :
-1. create a `DemoActivity`, using a custom `DrawAdapter`
+1. create a `DemoFragment`, use a custom xml layout
2. in the `Activity` undershowcase, pass views or coordinates and their associated labels.
-RoboDemo has been designed to be convinient.
+RoboDemo can also walk a user through your own app. Great for first time use or explaining a feature that is not used often. Look in the sample to see how to switch from a showcase to a walkthrough.
-To learn more, visit [RoboDemo Starter Guide](https://github.com/stephanenicolas/RoboDemo/wiki/RoboDemo-Starter-Guide) and [browse RoboDemo Javadocs online](http://stephanenicolas.github.com/RoboDemo/apidocs/index.html).
+To learn more, visit [RoboDemo Fragment Starter Guide](https://github.com/ericharlow/RoboDemo/wiki/RoboDemo-FragmentStarterGuide) and [browse RoboDemo Javadocs online](http://stephanenicolas.github.com/RoboDemo/apidocs/index.html).
Customization
-------------
@@ -52,18 +51,7 @@ RoboDemo can be customized in different ways :
* using custom drawable and text locations
* and some more for sure...
-To learn more, visit [RoboDemo Starter Guide](https://github.com/stephanenicolas/RoboDemo/wiki/RoboDemo-Starter-Guide) and [browse RoboDemo Javadocs online](http://stephanenicolas.github.com/RoboDemo/apidocs/index.html).
-
-
-Know limitations
-----------------
-
-The base class for DemoActivity is based on `android.app.Activity`. Unfortunately, this can't cover all inheritance cases for projects
-based on ActionBarSherlock or RoboGuice or a custom Activity base class per project.
-
-In that case, we recommend using all classes from the library as well but rewrite your own `DemoActivity` changing only its super class.
-
-In the case you use ActionBarSherlock, check the code comments, they will give you hints to support ActionBarSherlock themes.
+To learn more, visit [RoboDemo Fragment Starter Guide](https://github.com/ericharlow/RoboDemo/wiki/RoboDemo-FragmentStarterGuide) and [browse RoboDemo Javadocs online](http://stephanenicolas.github.com/RoboDemo/apidocs/index.html).
Modules
-------
diff --git a/robodemo-lib-1.0.0.jar b/robodemo-lib-1.0.0.jar
deleted file mode 100644
index 1a883cb..0000000
Binary files a/robodemo-lib-1.0.0.jar and /dev/null differ
diff --git a/robodemo-lib-2.2.0.jar b/robodemo-lib-2.2.0.jar
new file mode 100644
index 0000000..c214e5f
Binary files /dev/null and b/robodemo-lib-2.2.0.jar differ
diff --git a/robodemo-lib/AndroidManifest.xml b/robodemo-lib/AndroidManifest.xml
index 8c0d0b4..fa0cf5c 100644
--- a/robodemo-lib/AndroidManifest.xml
+++ b/robodemo-lib/AndroidManifest.xml
@@ -4,7 +4,7 @@
android:versionName="1.0" >
+ * boolean neverShowDemoAgain = RoboDemo.isNeverShowAgain( this, demoFragmentId );
+ *
+ * if ( !neverShowDemoAgain ) {
+ * //create an ArrayList named arrayListPoints.
+ *
+ * DemoFragment f = DemoFragment.newInstance(arrayListPoints);
+ * f.show(getFragmentManager(), DemoFragment.TAG);
+ * }
+ *
+ *
+ * @author ericharlow
+ *
+ */
+public class DemoFragment extends DialogFragment {
+
+ public static final String TAG = "DemoFragment";
+ public final static String DEMO_FRAGMENT_ID = "demo-main-fragment";
+ public final static String DEMO_FRAGMENT_SHOW = "demo-fragment-show";
+
+ private ArrayList< LabeledPoint > listPoints;
+ private String demoFragmentId;
+ private int mResourceId;
+
+ private DrawView drawView;
+ private CheckBox checkBox;
+
+ /**
+ * Create a DemoFragment using a list of LabeledPoints.
+ * @param arrayListPoints - list of LabeledPoints to show.
+ * @return The DemoFragment.
+ */
+ public static DemoFragment newInstance(ArrayList< LabeledPoint > arrayListPoints) {
+ return newInstance(R.layout.fragment_demo, arrayListPoints);
+ }
+
+ /**
+ * Create a customized DemoFragment with a xml layout and supplied list of LabeledPoints.
+ * @param resource - custom layout.
+ * @param arrayListPoints - list of LabeledPoints to show.
+ * @return The DemoFragment.
+ * @see DrawView
+ */
+ public static DemoFragment newInstance(int resource, ArrayList< LabeledPoint > arrayListPoints) {
+ DemoFragment f = new DemoFragment();
+ f.demoFragmentId = DEMO_FRAGMENT_ID;
+ f.mResourceId = resource;
+ f.listPoints = arrayListPoints;
+ return f;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setStyle(STYLE_NO_FRAME, R.style.Theme_RoboDemo);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+
+ if (mResourceId == 0) {
+ mResourceId = savedInstanceState.getInt(DEMO_FRAGMENT_ID);
+ }
+
+ View v = inflater.inflate(mResourceId, null);
+
+ View temp = v.findViewById( R.id.drawView_move_content_demo );
+ if (temp != null) {
+ drawView = (DrawView) temp;
+ }
+
+ temp = v.findViewById( R.id.checkbox_demo_never_again );
+ if (temp != null)
+ checkBox = (CheckBox) temp;
+
+ temp = v.findViewById(R.id.textview_demo_never_again);
+ if (temp != null)
+ temp.setOnClickListener(createDemoNeverAgainListener());
+
+ temp = v.findViewById(R.id.button_demo_finish);
+ if (temp != null)
+ temp.setOnClickListener(createFinishButtonListener());
+
+ //TODO: add a delegate, and provide the view for custom listeners to be added
+
+ return v;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ if (drawView == null)
+ return;
+
+ drawView.setAnimationListener( new DrawViewAnimationListener() );
+ drawView.setDrawViewAdapterLabeledPoints(listPoints);
+ drawView.setTouchDispatchDelegate(new FragmentTouchDispatchDelegate());
+ }
+
+ /**
+ * Removes any previous fragment identified by the tag.
+ */
+ @Override
+ public void show(FragmentManager manager, String tag) {
+ Fragment prev = manager.findFragmentByTag(tag);
+ if (prev != null) {
+ FragmentTransaction ft = manager.beginTransaction();
+ ft.remove(prev).commit();
+ }
+ super.show(manager, tag);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putInt(DEMO_FRAGMENT_ID, mResourceId);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.setOnKeyListener(new OnKeyListener() {
+
+ @Override
+ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
+ if( keyCode == KeyEvent.KEYCODE_BACK){
+ finish(null);// need to take care of our mess and state
+ return true;
+ }
+ return false;
+ }
+ });
+ return dialog;
+ }
+
+ public void checkNeverShowAgain( View view ) {
+ checkBox.setChecked( !checkBox.isChecked() );
+ }
+
+ public void finish( View view ) {
+ if ( checkBox != null && checkBox.isChecked() ) {
+ RoboDemo.setNeverShowAgain(getActivity(), demoFragmentId, true);
+ } else {
+ RoboDemo.showAgain(getActivity(), demoFragmentId);
+ }
+
+ RoboDemo.setShowDemo(getActivity(), DemoFragment.DEMO_FRAGMENT_SHOW, false);
+ removeSelf();
+ }
+
+ private void setButtonsVisible( boolean visible ) {
+ View view = getView();
+ if (view == null)
+ return;
+
+ final View layoutButtons = view.findViewById( R.id.layout_demo_buttons );
+ if (layoutButtons == null)
+ return;
+
+ int animationResId = visible ? android.R.anim.fade_in : android.R.anim.fade_out;
+ Animation animation = AnimationUtils.loadAnimation( getActivity(), animationResId );
+ animation.setDuration( getResources().getInteger( android.R.integer.config_shortAnimTime ) );
+ animation.setAnimationListener( new ButtonsAnimationListener( visible, layoutButtons ) );
+ layoutButtons.startAnimation( animation );
+ }
+
+ private boolean removeSelf() {
+ FragmentManager fManager = getFragmentManager();
+ fManager.beginTransaction().remove(this).commit();
+ return true;
+ }
+
+ public String getDemoFragmentId() {
+ return demoFragmentId;
+ }
+
+ public void setDemoFragmentId(String demoFragmentId) {
+ this.demoFragmentId = demoFragmentId;
+ }
+
+ private OnClickListener createFinishButtonListener() {
+ return new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ finish(v);
+ }
+ };
+ }
+
+ private OnClickListener createDemoNeverAgainListener() {
+ return new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ checkNeverShowAgain(v);
+ }
+ };
+ }
+
+ /**
+ * Animate the buttons at the bottom of the screen.
+ *
+ * @author sni
+ *
+ */
+ private final class DrawViewAnimationListener implements AnimationListener {
+
+ @Override
+ public void onAnimationStart( Animation animation ) {
+ setButtonsVisible( false );
+ }
+
+ @Override
+ public void onAnimationRepeat( Animation animation ) {
+
+ }
+
+ @Override
+ public void onAnimationEnd( Animation animation ) {
+ setButtonsVisible( true );
+ }
+ }
+
+ private final class ButtonsAnimationListener implements AnimationListener {
+ private final boolean visibleAtEnd;
+ private final View layoutButtons;
+
+ private ButtonsAnimationListener( boolean visibleAtEnd, View layoutButtons ) {
+ this.visibleAtEnd = visibleAtEnd;
+ this.layoutButtons = layoutButtons;
+ }
+
+ @Override
+ public void onAnimationStart( Animation animation ) {
+ layoutButtons.setVisibility( View.VISIBLE );
+ }
+
+ @Override
+ public void onAnimationRepeat( Animation animation ) {
+ }
+
+ @Override
+ public void onAnimationEnd( Animation animation ) {
+ layoutButtons.setVisibility( visibleAtEnd ? View.VISIBLE : View.GONE );
+ }
+ }
+
+ /**
+ * Gives the Context for the {@link TouchDispatchDelegate}.
+ * @author ericharlow
+ */
+ private class FragmentTouchDispatchDelegate implements TouchDispatchDelegate {
+
+ @Override
+ public boolean sendTouchEvent(MotionEvent event) {
+ View v = getActivity().getWindow().getDecorView();
+ if (v != null)
+ return v.dispatchTouchEvent(event);
+ else
+ return false;
+ }
+
+ }
+
+}
diff --git a/robodemo-lib/src/com/octo/android/robodemo/DrawView.java b/robodemo-lib/src/com/octo/android/robodemo/DrawView.java
index aa1371f..31f3071 100644
--- a/robodemo-lib/src/com/octo/android/robodemo/DrawView.java
+++ b/robodemo-lib/src/com/octo/android/robodemo/DrawView.java
@@ -1,9 +1,12 @@
package com.octo.android.robodemo;
import java.lang.ref.WeakReference;
+import java.util.List;
import android.content.Context;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff.Mode;
@@ -13,7 +16,9 @@
import android.os.Handler;
import android.os.Message;
import android.text.Layout;
+import android.text.TextPaint;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation.AnimationListener;
@@ -22,47 +27,73 @@
* draw.
*
* @author sni
- *
+ * @author ericharlow
*/
public class DrawView extends View {
private static final int DRAW_UNDER_TEXT_CORNER_RADIUS = 7;
+ private static final int DEFAULT_FONT_SIZE = 22;
/**
* The defaut delay between points in animation in ms.
*/
private static final long DELAY_BETWEEN_POINTS = 2000;
+ private static final boolean ISAUTOMATEDTESTMODE = false;
private DrawViewAdapter drawViewAdapter;
-
+ private int handlerType;
private AnimatorHandler handler;
private int currentPointPositionToDisplay = 0;
- private long delayBetweenPoints = DELAY_BETWEEN_POINTS;
- private boolean isShowingAllPointsAtTheEndOfAnimation = true;
- private boolean isDrawingOnePointAtATime = false;
+ private long delayBetweenPoints;
+ private boolean isShowingAllPointsAtTheEndOfAnimation;
+ private boolean isDrawingOnePointAtATime;
private AnimationListener animationListener;
private Paint underTextPaint;
private boolean isClearPorterDuffXfermodeEnabled = true;
+
+ private TouchDispatchDelegate mTouchDispatchDelegate;
public DrawView( Context context, AttributeSet attrs, int defStyle ) {
super( context, attrs, defStyle );
- initializeHandler();
- initUnderTextPaint();
+
+ if (attrs!=null) {
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.DrawView,
+ defStyle, 0);
+ TextPaint textPaint = new TextPaint();
+ textPaint.setColor(a.getColor(R.styleable.DrawView_textColor, Color.WHITE));
+ textPaint.setAntiAlias(a.getBoolean(R.styleable.DrawView_textAntiAlias, true));
+ textPaint.setTextSize(a.getDimension(R.styleable.DrawView_textSize, DEFAULT_FONT_SIZE));
+ textPaint.setShadowLayer(a.getFloat(R.styleable.DrawView_shadowLayerBlurRadius, 2.0f),
+ a.getFloat(R.styleable.DrawView_shadowLayerXOffset, 0),
+ a.getFloat(R.styleable.DrawView_shadowLayerYOffset, 2.0f),
+ a.getColor(R.styleable.DrawView_shadowLayerColor, Color.BLACK));
+
+ Drawable drawable = a.getDrawable(R.styleable.DrawView_drawable);
+ drawViewAdapter = new DefaultDrawViewAdapter(context, drawable, textPaint, null);
+
+ underTextPaint = new Paint();
+ underTextPaint.setColor( a.getColor(R.styleable.DrawView_underTextPaintColor, Color.DKGRAY));
+ underTextPaint.setAlpha( a.getInt(R.styleable.DrawView_underTextPaintAlpha, 150) );
+
+ isShowingAllPointsAtTheEndOfAnimation = a.getBoolean(R.styleable.DrawView_isShowingAllPointsAtTheEndOfAnimation, true);
+ isDrawingOnePointAtATime = a.getBoolean(R.styleable.DrawView_isDrawingOnePointAtATime, false);
+ delayBetweenPoints = a.getInt(R.styleable.DrawView_delayBetweenPoints, (int) DELAY_BETWEEN_POINTS);
+ handlerType = a.getInt(R.styleable.DrawView_handlerType, 1);
+ initializeHandler(handlerType);
+ a.recycle();
+ }
}
- public DrawView( Context context, AttributeSet attrs ) {
- super( context, attrs );
- initializeHandler();
- initUnderTextPaint();
+ public DrawView( Context context, AttributeSet attrs ) {
+ this(context, attrs, 0);
}
public DrawView( Context context ) {
- super( context );
- initializeHandler();
- initUnderTextPaint();
+ this(context, null);
}
@Override
@@ -71,7 +102,7 @@ public void onDraw( Canvas canvas ) {
if ( isAnimationTerminated() ) {
if ( isShowingAllPointsAtTheEndOfAnimation ) {
- for ( int index = 0; index < drawViewAdapter.getPointsCount(); index++ ) {
+ for ( int index = 0; index < getAdapterPointCount(); index++ ) {
drawPoint( index, canvas );
}
} else {
@@ -99,7 +130,7 @@ public void setUnderTextPaint( Paint underTextPaint ) {
}
public boolean isAnimationTerminated() {
- return currentPointPositionToDisplay >= getDrawViewAdapter().getPointsCount() - 1;
+ return currentPointPositionToDisplay >= getAdapterPointCount() - 1;
}
/**
@@ -119,13 +150,17 @@ public void resetAnimation() {
*/
public void terminateAnimation() {
handler.removeMessages( AnimatorHandler.ANIMATION_MESSAGE_ID );
- currentPointPositionToDisplay = getDrawViewAdapter().getPointsCount() - 1;
+ currentPointPositionToDisplay = getAdapterPointCount() - 1;
if ( animationListener != null ) {
animationListener.onAnimationEnd( null );
}
refreshDrawableState();
invalidate();
}
+
+ private int getAdapterPointCount() {
+ return drawViewAdapter == null ? 0 : drawViewAdapter.getPointsCount();
+ }
public void setDrawViewAdapter( DrawViewAdapter drawViewAdapter ) {
this.drawViewAdapter = drawViewAdapter;
@@ -134,6 +169,17 @@ public void setDrawViewAdapter( DrawViewAdapter drawViewAdapter ) {
public DrawViewAdapter getDrawViewAdapter() {
return drawViewAdapter;
}
+
+ /**
+ * Supply the data to the Adapter.
+ * @see DefaultDrawViewAdapter
+ * @param listPoints - the data.
+ */
+ public void setDrawViewAdapterLabeledPoints(List< LabeledPoint > listPoints) {
+ ((DefaultDrawViewAdapter) drawViewAdapter).setListPoints(listPoints);
+ if (currentPointPositionToDisplay > getAdapterPointCount() - 1) // almost the same as isAnimationTerminated()
+ terminateAnimation();
+ }
/**
* Sets the delay between animation of two points.
@@ -143,7 +189,8 @@ public DrawViewAdapter getDrawViewAdapter() {
*/
public void setDelayBetweenPoints( long delayBetweenPoints ) {
this.delayBetweenPoints = delayBetweenPoints;
- initializeHandler();
+ handlerType = 1;
+ initializeHandler(handlerType);
}
public long getDelayBetweenPoints() {
@@ -193,19 +240,19 @@ public AnimationListener getAnimationListener() {
return animationListener;
}
- private void initializeHandler() {
- handler = new AnimatorHandler( this, delayBetweenPoints );
- }
-
- private void initUnderTextPaint() {
- underTextPaint = new Paint();
- underTextPaint.setColor( getResources().getColor( android.R.color.darker_gray ) );
- underTextPaint.setAlpha( 150 );
+ private void initializeHandler(int type) {
+ handler = new AnimatorHandler( this, delayBetweenPoints ); // since there are no null checks for handler
+
+ if (type == 1) {
+ handler.sendEmptyMessageDelayed();
+ } else if (type == 2) {
+
+ }
}
private void showNextPoint() {
- if ( currentPointPositionToDisplay < getDrawViewAdapter().getPointsCount() - 1 ) {
+ if ( currentPointPositionToDisplay < getAdapterPointCount() - 1 ) {
currentPointPositionToDisplay++;
if ( isAnimationTerminated() && animationListener != null ) {
@@ -227,6 +274,7 @@ private void showNextPoint() {
protected void drawPoint( int position, Canvas canvas ) {
drawText( position, canvas );
drawDrawable( position, canvas );
+ updateContentDescription(position);
}
/**
@@ -308,7 +356,67 @@ protected void doUseClearPorterDuffXfermode( Canvas canvas, Drawable drawable )
}
}
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ if (ISAUTOMATEDTESTMODE)
+ event = putTouchEventInLabledPoint(event, drawViewAdapter.getDrawableAt( currentPointPositionToDisplay ));
+
+ if (isTouchHandler() && isTouchEventInLabeledPoint(event, drawViewAdapter.getDrawableAt( currentPointPositionToDisplay ))) {
+ if (event.getAction() == MotionEvent.ACTION_UP)
+ showNextPoint();
+ if (mTouchDispatchDelegate != null)
+ return mTouchDispatchDelegate.sendTouchEvent(event);
+ else
+ return true;
+ }
+
+ return super.dispatchTouchEvent(event);
+ }
+
/**
+ * Allow UiAutomator to know the text of the current point through content description.
+ * @param - position of the {@link LabeledPoint}.
+ */
+ private void updateContentDescription(int position) {
+ String text = drawViewAdapter.getTextAt(position);
+ setContentDescription(text);
+ }
+
+ private boolean isTouchHandler() {
+ return handlerType == 2 || isTouchDrivenAnimationHandler();
+ }
+
+ private boolean isTouchDrivenAnimationHandler() {
+ return handlerType == 3;
+ }
+
+ private boolean isTouchEventInLabeledPoint(MotionEvent event, Drawable drawable) {
+ float x = event.getX();
+ float y = event.getY();
+ int center_x = drawable.getBounds().centerX();
+ int center_y = drawable.getBounds().centerY();
+ int radius = drawable.getIntrinsicWidth() / 2 - 3;
+
+ //change < to <= to include points on circle
+ boolean result = Math.pow((x - center_x), 2) + Math.pow((y - center_y), 2) < Math.pow(radius, 2);
+ return result;
+ }
+
+ private MotionEvent putTouchEventInLabledPoint(MotionEvent event, Drawable drawable) {
+ event.setLocation(drawable.getBounds().centerX(), drawable.getBounds().centerY());
+ return event;
+ }
+
+ public TouchDispatchDelegate getTouchDispatchDelegate() {
+ return mTouchDispatchDelegate;
+ }
+
+ public void setTouchDispatchDelegate(TouchDispatchDelegate touchDispatchDelegate) {
+ if (!isTouchDrivenAnimationHandler())
+ this.mTouchDispatchDelegate = touchDispatchDelegate;
+ }
+
+ /**
* Animate the point to draw on screen.
*
* @author sni
@@ -323,7 +431,10 @@ private static final class AnimatorHandler extends Handler {
private AnimatorHandler( DrawView drawView, long delayBetweenPoints ) {
this.weakReference = new WeakReference< DrawView >( drawView );
this.delayBetweenPoints = delayBetweenPoints;
- sendEmptyMessageDelayed( AnimatorHandler.ANIMATION_MESSAGE_ID, delayBetweenPoints );
+ }
+
+ public void sendEmptyMessageDelayed() {
+ sendEmptyMessageDelayed( AnimatorHandler.ANIMATION_MESSAGE_ID, delayBetweenPoints );
}
@Override
diff --git a/robodemo-lib/src/com/octo/android/robodemo/DrawViewAdapter.java b/robodemo-lib/src/com/octo/android/robodemo/DrawViewAdapter.java
index dfef611..3afcdfa 100644
--- a/robodemo-lib/src/com/octo/android/robodemo/DrawViewAdapter.java
+++ b/robodemo-lib/src/com/octo/android/robodemo/DrawViewAdapter.java
@@ -50,5 +50,12 @@ public interface DrawViewAdapter {
* @return the {@link Layout} to use when rendering the text of the {@link LabeledPoint} at a given position.
*/
public Layout getTextLayoutAt( int position );
+
+ /**
+ * Get the text of the {@link LabeledPoint} at position.
+ * @param position - the position of the {@link LabeledPoint} to render.
+ * @return the text of the {@link LabeledPoint}.
+ */
+ public String getTextAt(int position);
}
\ No newline at end of file
diff --git a/robodemo-lib/src/com/octo/android/robodemo/LabeledPoint.java b/robodemo-lib/src/com/octo/android/robodemo/LabeledPoint.java
index 5b34347..73c01da 100644
--- a/robodemo-lib/src/com/octo/android/robodemo/LabeledPoint.java
+++ b/robodemo-lib/src/com/octo/android/robodemo/LabeledPoint.java
@@ -1,5 +1,7 @@
package com.octo.android.robodemo;
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
@@ -8,6 +10,7 @@
import android.os.Parcelable;
import android.view.Display;
import android.view.View;
+import android.view.View.OnLayoutChangeListener;
/**
* A pojo class that wraps all information needed to display a point on screen. {@link LabeledPoint} embed a position
@@ -16,17 +19,25 @@
* Activity to illustrate and the {@link DemoActivity}.
*
* @author sni
- *
+ * @author ericharlow
*/
public class LabeledPoint extends Point implements Parcelable {
/** The text associated to this point. */
private String text;
+
+ //experimental
+ private boolean usePreferredSize = false;
+ private int preferredWidth;
+ private int preferredHeight;
/**
* Creates an empty {@link LabeledPoint}.
*/
public LabeledPoint() {
+ text="";
+ x = -100;
+ y = -100;
}
/**
@@ -87,7 +98,7 @@ public LabeledPoint( Point src, String text ) {
* the view on which to center the point.
*/
public LabeledPoint( View v ) {
- this( v, 50, 50, null );
+ this( v, 50, 50, null, true);
}
/**
@@ -99,7 +110,7 @@ public LabeledPoint( View v ) {
* the new text of the point.
*/
public LabeledPoint( View v, String text ) {
- this( v, 50, 0, text );
+ this( v, 50, 50, text, true);
}
/**
@@ -115,7 +126,7 @@ public LabeledPoint( View v, String text ) {
*
*/
public LabeledPoint( View v, float widthPercent, float heightPercent ) {
- this( v, widthPercent, heightPercent, null );
+ this( v, widthPercent, heightPercent, null, true);
}
/**
@@ -131,14 +142,47 @@ public LabeledPoint( View v, float widthPercent, float heightPercent ) {
*
* @param text
* the new text of the point.
+ *
+ * @param preferredSize
+ * whether to use the size of the view for the drawable size.
*/
- public LabeledPoint( View v, float widthPercent, float heightPercent, String text ) {
- int[] location = new int[ 2 ];
- v.getLocationOnScreen( location );
- this.x = location[ 0 ] + Math.round( widthPercent * v.getMeasuredWidth() / 100 );
- this.y = location[ 1 ] + Math.round( heightPercent * v.getMeasuredHeight() / 100 );
+ public LabeledPoint( View v, final float widthPercent, final float heightPercent, String text, boolean preferredSize) {
+ if (v != null) {
+ usePreferredSize = preferredSize;
+ setMeasuredLocation(widthPercent, heightPercent, v);
+
+ v.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right,
+ int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ setMeasuredLocation(widthPercent, heightPercent, v);
+ if (usePreferredSize)
+ setPreferredSize(v.getMeasuredWidth(), v.getMeasuredHeight());
+ v.removeOnLayoutChangeListener(this);
+ }
+ });
+ }
setText( text );
}
+
+ protected void setPreferredSize(int measuredWidth, int measuredHeight) {
+ // use min because of DrawView.doUseClearPorterDuffXfermode uses canvas.drawCircle
+ int min = Math.min(measuredWidth, measuredHeight);
+ preferredWidth = min;
+ preferredHeight = min;
+ }
+
+ /**
+ * Creates a {@link LabeledPoint} positioned relatively to a given activity, with a given text.
+ * @param activity - the view on which to center the point.
+ * @param widthPercent - the percent of the view width at which to place the new point.
+ * @param heightPercent - the percent of the view height at which to place the new point.
+ * @param stringID - reference to the new text of the point.
+ */
+ public LabeledPoint(Activity activity, float widthPercent, float heightPercent, int stringID) {
+ this(activity, widthPercent, heightPercent, activity.getString(stringID));
+ }
/**
* Creates a {@link LabeledPoint} positioned relatively to a given activity, with a given text.
@@ -154,21 +198,12 @@ public LabeledPoint( View v, float widthPercent, float heightPercent, String tex
* @param text
* the new text of the point.
*/
- @SuppressWarnings("deprecation")
public LabeledPoint( Activity activity, float widthPercent, float heightPercent, String text ) {
Display display = activity.getWindowManager().getDefaultDisplay();
- int screenWidth = 0;
- int screenHeight = 0;
- if ( Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR2 ) {
- Point outSize = new Point();
- display.getSize( outSize );
- screenHeight = outSize.y;
- screenWidth = outSize.x;
- } else {
- screenWidth = display.getWidth();
- screenHeight = display.getHeight();
- }
+ int screenWidth = getScreenWidth(display);
+ int screenHeight = getScreenHeight(display);
+
x = (int) ( screenWidth * widthPercent );
y = (int) ( screenHeight * heightPercent );
setText( text );
@@ -189,6 +224,53 @@ public LabeledPoint( Activity activity, float widthPercent, float heightPercent,
public LabeledPoint( Activity activity, float widthPercent, float heightPercent ) {
this( activity, widthPercent, heightPercent, null );
}
+
+ /**
+ * Creates a {@link LabeledPoint} located at the center of a given view, with a given text.
+ * @param activity - the context for the view.
+ * @param referenceID - the resource id for the view.
+ * @param stringID - the resource id for the string.
+ */
+ public LabeledPoint(Activity activity, int referenceID, int stringID) {
+ this(activity.findViewById(referenceID), activity.getString(stringID));
+ }
+
+ /**
+ * Creates a {@link LabeledPoint} at a given location, with a null text.
+ *
+ * @param activity - the view on which to center the point.
+ * @param x - the new x coordinate of the point.
+ * @param y - the new y coordinate of the point.
+ * @param stringID - reference to the new text of the point.
+ */
+ public LabeledPoint(Activity activity, int x, int y, int stringID ) {
+ this( x, y, activity.getString(stringID) );
+ }
+
+ /**
+ * Creates a {@link LabeledPoint} positioned relatively to a given view, with a given text.
+ * @param activity - the context for the view.
+ * @param widthPercent - the percent of the view width at which to place the new point.
+ * @param heightPercent - the percent of the view height at which to place the new point.
+ * @param referenceID - the resource id for the view.
+ * @param stringID - the resource id for the string.
+ */
+ public LabeledPoint(Activity activity, float widthPercent, float heightPercent, int referenceID, int stringID) {
+ this(activity.findViewById(referenceID), widthPercent, heightPercent, activity.getString(stringID), true);
+ }
+
+ /**
+ * Creates a {@link LabeledPoint} positioned relatively to a given view, with a given text.
+ * @param activity - the context for the view.
+ * @param widthPercent - the percent of the view width at which to place the new point.
+ * @param heightPercent - the percent of the view height at which to place the new point.
+ * @param referenceID - the resource id for the view.
+ * @param stringID - the resource id for the string.
+ * @param preferredSize - use the size from the view for the size of the drawable.
+ */
+ public LabeledPoint(Activity activity, float widthPercent, float heightPercent, int referenceID, int stringID, boolean preferredSize) {
+ this(activity.findViewById(referenceID), widthPercent, heightPercent, activity.getString(stringID), preferredSize);
+ }
public String getText() {
return text;
@@ -208,6 +290,9 @@ public void setText( String text ) {
public void writeToParcel( Parcel out, int flags ) {
out.writeInt( x );
out.writeInt( y );
+ out.writeInt(preferredHeight);
+ out.writeInt(preferredWidth);
+ out.writeByte((byte) (usePreferredSize ? 1 : 0));
out.writeString( text );
}
@@ -218,7 +303,8 @@ public void writeToParcel( Parcel out, int flags ) {
/**
* Return a new point from the data in the specified parcel.
*/
- @Override
+ @SuppressLint("NewApi")
+ @Override
public LabeledPoint createFromParcel( Parcel in ) {
LabeledPoint r = new LabeledPoint();
r.readFromParcel( in );
@@ -241,10 +327,73 @@ public LabeledPoint[] newArray( int size ) {
* @param in
* The parcel to read the point's coordinates from
*/
- @Override
public void readFromParcel( Parcel in ) {
x = in.readInt();
y = in.readInt();
+ preferredHeight = in.readInt();
+ preferredWidth = in.readInt();
+ usePreferredSize = in.readByte() != 0;
text = in.readString();
}
+
+ private void setMeasuredLocation(final float widthPercent,
+ final float heightPercent, View v) {
+ int[] location = new int[ 2 ];
+ v.getLocationOnScreen( location );
+ x = location[ 0 ] + Math.round( widthPercent * v.getMeasuredWidth() / 100 );
+ y = location[ 1 ] + Math.round( heightPercent * v.getMeasuredHeight() / 100 );
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
+ @SuppressWarnings("deprecation")
+ private int getScreenWidth(Display display) {
+ int screenWidth;
+ if ( Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR2 ) {
+ Point outSize = new Point();
+ display.getSize( outSize );
+ screenWidth = outSize.x;
+ } else {
+ screenWidth = display.getWidth();
+ }
+ return screenWidth;
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
+ @SuppressWarnings("deprecation")
+ private int getScreenHeight(Display display) {
+ int screenHeight;
+ if ( Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR2 ) {
+ Point outSize = new Point();
+ display.getSize( outSize );
+ screenHeight = outSize.y;
+ } else {
+ screenHeight = display.getHeight();
+ }
+ return screenHeight;
+ }
+
+ //experimental
+ public int getPreferredWidth() {
+ return preferredWidth;
+ }
+
+ public void setPreferredWidth(int preferedWidth) {
+ this.preferredWidth = preferedWidth;
+ }
+
+ public int getPreferredHeight() {
+ return preferredHeight;
+ }
+
+ public void setPreferredHeight(int preferedHeight) {
+ this.preferredHeight = preferedHeight;
+ }
+
+ public boolean doUsePreferredSize() {
+ return usePreferredSize;
+ }
+
+ public void setUsePreferredSize(boolean usePreferredSize) {
+ this.usePreferredSize = usePreferredSize;
+ }
}
\ No newline at end of file
diff --git a/robodemo-lib/src/com/octo/android/robodemo/RoboDemo.java b/robodemo-lib/src/com/octo/android/robodemo/RoboDemo.java
index f18109c..bc28ba8 100644
--- a/robodemo-lib/src/com/octo/android/robodemo/RoboDemo.java
+++ b/robodemo-lib/src/com/octo/android/robodemo/RoboDemo.java
@@ -3,8 +3,11 @@
import java.util.ArrayList;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.preference.PreferenceManager;
public class RoboDemo {
@@ -52,4 +55,39 @@ public static boolean isNeverShowAgain( Activity caller, String demoActivityId )
public static boolean showAgain( Activity caller, String demoActivityId ) {
return caller.getSharedPreferences( SHARED_PREFERENCE_NAME, Activity.MODE_PRIVATE ).edit().remove( demoActivityId ).commit();
}
+
+ /**
+ * Should the Demo be shown.
+ * @param caller - the activity that is calling the demo. It will be used internally to get a {@link SharedPreferences}.
+ * @param demoActivityId - the id that will be used to store the information about showing the demo.
+ * @return true if the demo should be shown.
+ */
+ public static boolean shouldShowDemo(Activity caller, String demoActivityId) {
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(caller.getApplicationContext());
+ return settings.getBoolean( demoActivityId, false );
+ }
+
+ /**
+ * Set whether the demo should be shown.
+ * @param caller - the activity that is calling the demo. It will be used internally to get a {@link SharedPreferences}.
+ * @param demoActivityId - the id that will be used to store the information about showing the demo.
+ * @param value - the value for whether the demo should be shown.
+ */
+ public static void setShowDemo(Activity caller, String demoActivityId, boolean value) {
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(caller.getApplicationContext());
+ settings.edit().putBoolean(demoActivityId, value).commit();
+ }
+
+ /**
+ *
+ * @param caller - the activity that is calling the {@link DemoActivity}. Its {@link SharedPreferences} will be used
+ * internally by the {@link DemoActivity}.
+ * @param demoActivityId - the id that will be used to store the information about the 'never show again' checkbox.
+ * @param value
+ */
+ public static void setNeverShowAgain(Activity caller, String demoActivityId, boolean value) {
+ Editor editor = caller.getSharedPreferences( SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ).edit();
+ editor = editor.putBoolean( demoActivityId, value );
+ editor.commit();
+ }
}
diff --git a/robodemo-lib/src/com/octo/android/robodemo/TouchDispatchDelegate.java b/robodemo-lib/src/com/octo/android/robodemo/TouchDispatchDelegate.java
new file mode 100644
index 0000000..7f7cc02
--- /dev/null
+++ b/robodemo-lib/src/com/octo/android/robodemo/TouchDispatchDelegate.java
@@ -0,0 +1,18 @@
+package com.octo.android.robodemo;
+
+import android.view.MotionEvent;
+
+/**
+ * Provide a way for Touch Events to be passed to other views.
+ * @author ericharlow
+ */
+public interface TouchDispatchDelegate {
+
+ /**
+ * Send MotionEvents to a delegate.
+ * Intend to send from a Fragment's custom semitranslucent view to the Activity beneath the Fragment.
+ * @param event - The motion event to be dispatched.
+ * @return True if the event was handled by the view, false otherwise.
+ */
+ public boolean sendTouchEvent(MotionEvent event);
+}
diff --git a/robodemo-sample/AndroidManifest.xml b/robodemo-sample/AndroidManifest.xml
index 57abe71..886f879 100644
--- a/robodemo-sample/AndroidManifest.xml
+++ b/robodemo-sample/AndroidManifest.xml
@@ -4,7 +4,7 @@
android:versionName="1.0" >