Use binding in EditOptions and TourGuide of DroidFishApp

This commit is contained in:
Ebrahim Byagowi 2019-04-23 00:04:16 +04:30 committed by Peter Osterlund
parent 80e8183757
commit a912458ffb
9 changed files with 431 additions and 348 deletions

@ -50,6 +50,8 @@ android {
sourceCompatibility = '1.8' sourceCompatibility = '1.8'
targetCompatibility = '1.8' targetCompatibility = '1.8'
} }
dataBinding.enabled = true
} }
dependencies { dependencies {

@ -18,13 +18,6 @@
package org.petero.droidfish.activities; package org.petero.droidfish.activities;
import java.util.Locale;
import java.util.TreeMap;
import org.petero.droidfish.R;
import org.petero.droidfish.Util;
import org.petero.droidfish.engine.UCIOptions;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -35,20 +28,27 @@ import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener; import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Button; import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.ToggleButton;
/** Edit UCI options. */ import org.petero.droidfish.R;
import org.petero.droidfish.Util;
import org.petero.droidfish.databinding.UciOptionButtonBinding;
import org.petero.droidfish.databinding.UciOptionCheckBinding;
import org.petero.droidfish.databinding.UciOptionComboBinding;
import org.petero.droidfish.databinding.UciOptionSpinBinding;
import org.petero.droidfish.databinding.UciOptionStringBinding;
import org.petero.droidfish.engine.UCIOptions;
import java.util.Locale;
import java.util.TreeMap;
/**
* Edit UCI options.
*/
public class EditOptions extends Activity { public class EditOptions extends Activity {
private UCIOptions uciOpts = null; private UCIOptions uciOpts = null;
private String engineName = ""; private String engineName = "";
@ -61,8 +61,8 @@ public class EditOptions extends Activity {
Util.setFullScreenMode(this, settings); Util.setFullScreenMode(this, settings);
Intent i = getIntent(); Intent i = getIntent();
uciOpts = (UCIOptions)i.getSerializableExtra("org.petero.droidfish.ucioptions"); uciOpts = (UCIOptions) i.getSerializableExtra("org.petero.droidfish.ucioptions");
engineName = (String)i.getSerializableExtra("org.petero.droidfish.enginename"); engineName = (String) i.getSerializableExtra("org.petero.droidfish.enginename");
if (uciOpts != null) { if (uciOpts != null) {
initUI(); initUI();
} else { } else {
@ -102,28 +102,29 @@ public class EditOptions extends Activity {
continue; continue;
switch (o.type) { switch (o.type) {
case CHECK: { case CHECK: {
View v = View.inflate(this, R.layout.uci_option_check, null); UciOptionCheckBinding binding = UciOptionCheckBinding.inflate(getLayoutInflater(), null, false);
CheckBox checkBox = v.findViewById(R.id.eo_value); binding.eoValue.setText(o.name);
checkBox.setText(o.name); final UCIOptions.CheckOption co = (UCIOptions.CheckOption) o;
final UCIOptions.CheckOption co = (UCIOptions.CheckOption)o; binding.eoValue.setChecked(co.value);
checkBox.setChecked(co.value); binding.eoValue.setOnCheckedChangeListener((buttonView, isChecked) -> co.set(isChecked));
checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> co.set(isChecked)); content.addView(binding.getRoot());
content.addView(v);
break; break;
} }
case SPIN: { case SPIN: {
View v = View.inflate(this, R.layout.uci_option_spin, null); UciOptionSpinBinding binding = UciOptionSpinBinding.inflate(getLayoutInflater(), null, false);
TextView label = v.findViewById(R.id.eo_label); final UCIOptions.SpinOption so = (UCIOptions.SpinOption) o;
EditText value = v.findViewById(R.id.eo_value);
final UCIOptions.SpinOption so = (UCIOptions.SpinOption)o;
String labelText = String.format(Locale.US, "%s (%d\u2013%d)", so.name, so.minValue, so.maxValue); String labelText = String.format(Locale.US, "%s (%d\u2013%d)", so.name, so.minValue, so.maxValue);
label.setText(labelText); binding.eoLabel.setText(labelText);
value.setText(so.getStringValue()); binding.eoValue.setText(so.getStringValue());
if (so.minValue >= 0) if (so.minValue >= 0)
value.setInputType(android.text.InputType.TYPE_CLASS_NUMBER); binding.eoValue.setInputType(android.text.InputType.TYPE_CLASS_NUMBER);
value.addTextChangedListener(new TextWatcher() { binding.eoValue.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) { } public void onTextChanged(CharSequence s, int start, int before, int count) {
public void beforeTextChanged(CharSequence s, int start, int count, int after) { } }
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override @Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
try { try {
@ -138,60 +139,61 @@ public class EditOptions extends Activity {
} }
} }
}); });
content.addView(v); content.addView(binding.getRoot());
break; break;
} }
case COMBO: { case COMBO: {
View v = View.inflate(this, R.layout.uci_option_combo, null); UciOptionComboBinding binding = UciOptionComboBinding.inflate(getLayoutInflater(), null, false);
TextView label = v.findViewById(R.id.eo_label); binding.eoLabel.setText(o.name);
Spinner value = v.findViewById(R.id.eo_value); final UCIOptions.ComboOption co = (UCIOptions.ComboOption) o;
label.setText(o.name);
final UCIOptions.ComboOption co = (UCIOptions.ComboOption)o;
ArrayAdapter<CharSequence> adapter = ArrayAdapter<CharSequence> adapter =
new ArrayAdapter<CharSequence>(this, android.R.layout.simple_spinner_item, new ArrayAdapter<>(this, android.R.layout.simple_spinner_item,
co.allowedValues); co.allowedValues);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
value.setAdapter(adapter); binding.eoValue.setAdapter(adapter);
value.setSelection(adapter.getPosition(co.value)); binding.eoValue.setSelection(adapter.getPosition(co.value));
value.setOnItemSelectedListener(new OnItemSelectedListener() { binding.eoValue.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> av, View view, int position, long id) { public void onItemSelected(AdapterView<?> av, View view, int position, long id) {
if ((position >= 0) && (position < co.allowedValues.length)) if ((position >= 0) && (position < co.allowedValues.length))
co.set(co.allowedValues[position]); co.set(co.allowedValues[position]);
} }
public void onNothingSelected(AdapterView<?> arg0) { }
public void onNothingSelected(AdapterView<?> arg0) {
}
}); });
content.addView(v); content.addView(binding.getRoot());
break; break;
} }
case BUTTON: { case BUTTON: {
View v = View.inflate(this, R.layout.uci_option_button, null); UciOptionButtonBinding binding = UciOptionButtonBinding.inflate(getLayoutInflater(), null, false);
ToggleButton button = v.findViewById(R.id.eo_label); final UCIOptions.ButtonOption bo = (UCIOptions.ButtonOption) o;
final UCIOptions.ButtonOption bo = (UCIOptions.ButtonOption)o;
bo.trigger = false; bo.trigger = false;
button.setText(o.name); binding.eoLabel.setText(o.name);
button.setTextOn(o.name); binding.eoLabel.setTextOn(o.name);
button.setTextOff(o.name); binding.eoLabel.setTextOff(o.name);
button.setOnCheckedChangeListener((buttonView, isChecked) -> bo.trigger = isChecked); binding.eoLabel.setOnCheckedChangeListener((buttonView, isChecked) -> bo.trigger = isChecked);
content.addView(v); content.addView(binding.getRoot());
break; break;
} }
case STRING: { case STRING: {
View v = View.inflate(this, R.layout.uci_option_string, null); UciOptionStringBinding binding = UciOptionStringBinding.inflate(getLayoutInflater(), null, false);
TextView label = v.findViewById(R.id.eo_label); binding.eoLabel.setText(String.format("%s ", o.name));
EditText value = v.findViewById(R.id.eo_value); final UCIOptions.StringOption so = (UCIOptions.StringOption) o;
label.setText(o.name + " "); binding.eoValue.setText(so.value);
final UCIOptions.StringOption so = (UCIOptions.StringOption)o; binding.eoValue.addTextChangedListener(new TextWatcher() {
value.setText(so.value); public void onTextChanged(CharSequence s, int start, int before, int count) {
value.addTextChangedListener(new TextWatcher() { }
public void onTextChanged(CharSequence s, int start, int before, int count) { }
public void beforeTextChanged(CharSequence s, int start, int count, int after) { } public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override @Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
so.set(s.toString()); so.set(s.toString());
} }
}); });
content.addView(v); content.addView(binding.getRoot());
break; break;
} }
} }
@ -218,25 +220,25 @@ public class EditOptions extends Activity {
continue; continue;
switch (o.type) { switch (o.type) {
case CHECK: { case CHECK: {
UCIOptions.CheckOption co = (UCIOptions.CheckOption)o; UCIOptions.CheckOption co = (UCIOptions.CheckOption) o;
if (co.set(co.defaultValue)) if (co.set(co.defaultValue))
modified = true; modified = true;
break; break;
} }
case SPIN: { case SPIN: {
UCIOptions.SpinOption so = (UCIOptions.SpinOption)o; UCIOptions.SpinOption so = (UCIOptions.SpinOption) o;
if (so.set(so.defaultValue)) if (so.set(so.defaultValue))
modified = true; modified = true;
break; break;
} }
case COMBO: { case COMBO: {
UCIOptions.ComboOption co = (UCIOptions.ComboOption)o; UCIOptions.ComboOption co = (UCIOptions.ComboOption) o;
if (co.set(co.defaultValue)) if (co.set(co.defaultValue))
modified = true; modified = true;
break; break;
} }
case STRING: { case STRING: {
UCIOptions.StringOption so = (UCIOptions.StringOption)o; UCIOptions.StringOption so = (UCIOptions.StringOption) o;
if (so.set(so.defaultValue)) if (so.set(so.defaultValue))
modified = true; modified = true;
break; break;
@ -258,7 +260,7 @@ public class EditOptions extends Activity {
UCIOptions.OptionBase o = uciOpts.getOption(name); UCIOptions.OptionBase o = uciOpts.getOption(name);
if (o != null) { if (o != null) {
if (o instanceof UCIOptions.ButtonOption) { if (o instanceof UCIOptions.ButtonOption) {
UCIOptions.ButtonOption bo = (UCIOptions.ButtonOption)o; UCIOptions.ButtonOption bo = (UCIOptions.ButtonOption) o;
if (bo.trigger) if (bo.trigger)
uciMap.put(name, ""); uciMap.put(name, "");
} else { } else {

@ -1,7 +1,5 @@
package tourguide.tourguide; package tourguide.tourguide;
import org.petero.droidfish.R;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorSet; import android.animation.AnimatorSet;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
@ -11,22 +9,24 @@ import android.graphics.Color;
import android.graphics.Point; import android.graphics.Point;
import android.util.Log; import android.util.Log;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewTreeObserver; import android.view.ViewTreeObserver;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView;
import net.i2p.android.ext.floatingactionbutton.FloatingActionButton; import net.i2p.android.ext.floatingactionbutton.FloatingActionButton;
import org.petero.droidfish.R;
import org.petero.droidfish.databinding.TooltipBinding;
/** /**
* Created by tanjunrong on 2/10/15. * Created by tanjunrong on 2/10/15.
*/ */
public class TourGuide { public class TourGuide {
/** /**
* This describes the animation techniques * This describes the animation techniques
* */ */
public enum Technique { public enum Technique {
Click, HorizontalLeft, HorizontalRight, VerticalUpward, VerticalDownward Click, HorizontalLeft, HorizontalRight, VerticalUpward, VerticalDownward
} }
@ -37,15 +37,16 @@ public class TourGuide {
public enum MotionType { public enum MotionType {
AllowAll, ClickOnly, SwipeOnly AllowAll, ClickOnly, SwipeOnly
} }
private Technique mTechnique; private Technique mTechnique;
private View mHighlightedView; private View mHighlightedView;
private Activity mActivity; private Activity mActivity;
private MotionType mMotionType; private MotionType mMotionType;
private FrameLayoutWithHole mFrameLayout; private FrameLayoutWithHole mFrameLayout;
private View mToolTipViewGroup; private TooltipBinding tooltipBinding;
public ToolTip mToolTip; ToolTip mToolTip;
public Pointer mPointer; Pointer mPointer;
public Overlay mOverlay; Overlay mOverlay;
private Sequence mSequence; private Sequence mSequence;
@ -56,17 +57,18 @@ public class TourGuide {
*************/ *************/
/* Static builder */ /* Static builder */
public static TourGuide init(Activity activity){ public static TourGuide init(Activity activity) {
return new TourGuide(activity); return new TourGuide(activity);
} }
/* Constructor */ /* Constructor */
public TourGuide(Activity activity){ public TourGuide(Activity activity) {
mActivity = activity; mActivity = activity;
} }
/** /**
* Setter for the animation to be used * Setter for the animation to be used
*
* @param technique Animation to be used * @param technique Animation to be used
* @return return TourGuide instance for chaining purpose * @return return TourGuide instance for chaining purpose
*/ */
@ -77,20 +79,22 @@ public class TourGuide {
/** /**
* Sets which motion type is motionType * Sets which motion type is motionType
*
* @param motionType * @param motionType
* @return return TourGuide instance for chaining purpose * @return return TourGuide instance for chaining purpose
*/ */
public TourGuide motionType(MotionType motionType){ public TourGuide motionType(MotionType motionType) {
mMotionType = motionType; mMotionType = motionType;
return this; return this;
} }
/** /**
* Sets the duration * Sets the duration
*
* @param view the view in which the tutorial button will be placed on top of * @param view the view in which the tutorial button will be placed on top of
* @return return TourGuide instance for chaining purpose * @return return TourGuide instance for chaining purpose
*/ */
public TourGuide playOn(View view){ public TourGuide playOn(View view) {
mHighlightedView = view; mHighlightedView = view;
setupView(); setupView();
return this; return this;
@ -98,43 +102,49 @@ public class TourGuide {
/** /**
* Sets the overlay * Sets the overlay
*
* @param overlay this overlay object should contain the attributes of the overlay, such as background color, animation, Style, etc * @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 * @return return TourGuide instance for chaining purpose
*/ */
public TourGuide setOverlay(Overlay overlay){ public TourGuide setOverlay(Overlay overlay) {
mOverlay = overlay; mOverlay = overlay;
return this; return this;
} }
/** /**
* Set the toolTip * 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 * @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 * @return return TourGuide instance for chaining purpose
*/ */
public TourGuide setToolTip(ToolTip toolTip){ public TourGuide setToolTip(ToolTip toolTip) {
mToolTip = toolTip; mToolTip = toolTip;
return this; return this;
} }
/** /**
* Set the Pointer * 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} * @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 * @return return TourGuide instance for chaining purpose
*/ */
public TourGuide setPointer(Pointer pointer){ public TourGuide setPointer(Pointer pointer) {
mPointer = pointer; mPointer = pointer;
return this; return this;
} }
/** /**
* Clean up the tutorial that is added to the activity * Clean up the tutorial that is added to the activity
*/ */
public void cleanUp(){ public void cleanUp() {
if (mFrameLayout != null) if (mFrameLayout != null)
mFrameLayout.cleanUp(); mFrameLayout.cleanUp();
if (mToolTipViewGroup!=null) { if (tooltipBinding != null) {
((ViewGroup) mActivity.getWindow().getDecorView()).removeView(mToolTipViewGroup); ((ViewGroup) mActivity.getWindow().getDecorView()).removeView(tooltipBinding.getRoot());
} }
} }
public TourGuide playLater(View view){ public TourGuide playLater(View view) {
mHighlightedView = view; mHighlightedView = view;
return this; return this;
} }
@ -143,16 +153,16 @@ public class TourGuide {
* Sequence related method * Sequence related method
**************************/ **************************/
public TourGuide playInSequence(Sequence sequence){ public TourGuide playInSequence(Sequence sequence) {
setSequence(sequence); setSequence(sequence);
next(); next();
return this; return this;
} }
public TourGuide setSequence(Sequence sequence){ public TourGuide setSequence(Sequence sequence) {
mSequence = sequence; mSequence = sequence;
mSequence.setParentTourGuide(this); mSequence.setParentTourGuide(this);
for (TourGuide tourGuide : sequence.mTourGuideArray){ for (TourGuide tourGuide : sequence.mTourGuideArray) {
if (tourGuide.mHighlightedView == null) { if (tourGuide.mHighlightedView == null) {
throw new NullPointerException("Please specify the view using 'playLater' method"); throw new NullPointerException("Please specify the view using 'playLater' method");
} }
@ -160,8 +170,8 @@ public class TourGuide {
return this; return this;
} }
public TourGuide next(){ public TourGuide next() {
if (mFrameLayout!=null) { if (mFrameLayout != null) {
cleanUp(); cleanUp();
} }
@ -179,52 +189,53 @@ public class TourGuide {
} }
/** /**
*
* @return FrameLayoutWithHole that is used as overlay * @return FrameLayoutWithHole that is used as overlay
*/ */
public FrameLayoutWithHole getOverlay(){ public FrameLayoutWithHole getOverlay() {
return mFrameLayout; return mFrameLayout;
} }
/** /**
*
* @return the ToolTip container View * @return the ToolTip container View
*/ */
public View getToolTip(){ public View getToolTip() {
return mToolTipViewGroup; return tooltipBinding.getRoot();
} }
/****** /******
* *
* Private methods * Private methods
* *
*******/ *******/
//TODO: move into Pointer //TODO: move into Pointer
private int getXBasedOnGravity(int width){ private int getXBasedOnGravity(int width) {
int [] pos = new int[2]; int[] pos = new int[2];
mHighlightedView.getLocationOnScreen(pos); mHighlightedView.getLocationOnScreen(pos);
int x = pos[0]; int x = pos[0];
if((mPointer.mGravity & Gravity.RIGHT) == Gravity.RIGHT){ if ((mPointer.mGravity & Gravity.RIGHT) == Gravity.RIGHT) {
return x+mHighlightedView.getWidth()-width; return x + mHighlightedView.getWidth() - width;
} else if ((mPointer.mGravity & Gravity.LEFT) == Gravity.LEFT) { } else if ((mPointer.mGravity & Gravity.LEFT) == Gravity.LEFT) {
return x; return x;
} else { // this is center } else { // this is center
return x+mHighlightedView.getWidth()/2-width/2; 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: 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 // TODO: throw exception if either mActivity, mDuration, mHighlightedView is null
checking(); checking();
final ViewTreeObserver viewTreeObserver = mHighlightedView.getViewTreeObserver(); final ViewTreeObserver viewTreeObserver = mHighlightedView.getViewTreeObserver();
@ -250,13 +261,15 @@ public class TourGuide {
} }
}); });
} }
private void checking(){
private void checking() {
// There is not check for tooltip because tooltip can be null, it means there no tooltip will be shown // There is not check for tooltip because tooltip can be null, it means there no tooltip will be shown
} }
private void handleDisableClicking(FrameLayoutWithHole frameLayoutWithHole){
private void handleDisableClicking(FrameLayoutWithHole frameLayoutWithHole) {
// 1. if user provides an overlay listener, use that as 1st priority // 1. if user provides an overlay listener, use that as 1st priority
if (mOverlay != null && mOverlay.mOnClickListener!=null) { if (mOverlay != null && mOverlay.mOnClickListener != null) {
frameLayoutWithHole.setClickable(true); frameLayoutWithHole.setClickable(true);
frameLayoutWithHole.setOnClickListener(mOverlay.mOnClickListener); frameLayoutWithHole.setOnClickListener(mOverlay.mOnClickListener);
} }
@ -266,60 +279,58 @@ public class TourGuide {
frameLayoutWithHole.setViewHole(mHighlightedView); frameLayoutWithHole.setViewHole(mHighlightedView);
frameLayoutWithHole.setSoundEffectsEnabled(false); frameLayoutWithHole.setSoundEffectsEnabled(false);
// do nothing, disabled. // do nothing, disabled.
frameLayoutWithHole.setOnClickListener(v -> {}); frameLayoutWithHole.setOnClickListener(v -> {
});
} }
} }
private void setupToolTip(){ private void setupToolTip() {
final FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); final FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
if (mToolTip != null) { if (mToolTip != null) {
/* inflate and get views */ /* inflate and get views */
ViewGroup parent = (ViewGroup) mActivity.getWindow().getDecorView(); ViewGroup parent = (ViewGroup) mActivity.getWindow().getDecorView();
LayoutInflater layoutInflater = mActivity.getLayoutInflater(); tooltipBinding = TooltipBinding.inflate(mActivity.getLayoutInflater(),
mToolTipViewGroup = layoutInflater.inflate(R.layout.tooltip, null); null, false);
View toolTipContainer = mToolTipViewGroup.findViewById(R.id.toolTip_container);
TextView toolTipTitleTV = mToolTipViewGroup.findViewById(R.id.title);
TextView toolTipDescriptionTV = mToolTipViewGroup.findViewById(R.id.description);
/* set tooltip attributes */ /* set tooltip attributes */
toolTipContainer.setBackgroundColor(mToolTip.mBackgroundColor); tooltipBinding.toolTipContainer.setBackgroundColor(mToolTip.mBackgroundColor);
if (mToolTip.mTitle == null){ if (mToolTip.mTitle == null) {
toolTipTitleTV.setVisibility(View.GONE); tooltipBinding.title.setVisibility(View.GONE);
} else { } else {
toolTipTitleTV.setText(mToolTip.mTitle); tooltipBinding.title.setText(mToolTip.mTitle);
} }
if (mToolTip.mDescription == null){ if (mToolTip.mDescription == null) {
toolTipDescriptionTV.setVisibility(View.GONE); tooltipBinding.description.setVisibility(View.GONE);
} else { } else {
toolTipDescriptionTV.setText(mToolTip.mDescription); tooltipBinding.description.setText(mToolTip.mDescription);
} }
mToolTipViewGroup.startAnimation(mToolTip.mEnterAnimation); tooltipBinding.getRoot().startAnimation(mToolTip.mEnterAnimation);
/* add setShadow if it's turned on */ /* add setShadow if it's turned on */
if (mToolTip.mShadow) { if (mToolTip.mShadow) {
mToolTipViewGroup.setBackgroundDrawable(mActivity.getResources().getDrawable(R.drawable.drop_shadow)); tooltipBinding.getRoot().setBackgroundDrawable(mActivity.getResources().getDrawable(R.drawable.drop_shadow));
} }
/* position and size calculation */ /* position and size calculation */
int [] pos = new int[2]; int[] pos = new int[2];
mHighlightedView.getLocationOnScreen(pos); mHighlightedView.getLocationOnScreen(pos);
int targetViewX = pos[0]; int targetViewX = pos[0];
final int targetViewY = pos[1]; final int targetViewY = pos[1];
// get measured size of tooltip // get measured size of tooltip
mToolTipViewGroup.measure(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); tooltipBinding.getRoot().measure(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
int toolTipMeasuredWidth = mToolTipViewGroup.getMeasuredWidth(); int toolTipMeasuredWidth = tooltipBinding.getRoot().getMeasuredWidth();
int toolTipMeasuredHeight = mToolTipViewGroup.getMeasuredHeight(); int toolTipMeasuredHeight = tooltipBinding.getRoot().getMeasuredHeight();
Point resultPoint = new Point(); // this holds the final position of tooltip Point resultPoint = new Point(); // this holds the final position of tooltip
float density = mActivity.getResources().getDisplayMetrics().density; float density = mActivity.getResources().getDisplayMetrics().density;
final float adjustment = 10 * density; //adjustment is that little overlapping area of tooltip and targeted button 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 // calculate x position, based on gravity, tooltipMeasuredWidth, parent max width, x position of target view, adjustment
if (toolTipMeasuredWidth > parent.getWidth()){ if (toolTipMeasuredWidth > parent.getWidth()) {
resultPoint.x = getXForTooTip(mToolTip.mGravity, parent.getWidth(), targetViewX, adjustment); resultPoint.x = getXForTooTip(mToolTip.mGravity, parent.getWidth(), targetViewX, adjustment);
} else { } else {
resultPoint.x = getXForTooTip(mToolTip.mGravity, toolTipMeasuredWidth, targetViewX, adjustment); resultPoint.x = getXForTooTip(mToolTip.mGravity, toolTipMeasuredWidth, targetViewX, adjustment);
@ -328,28 +339,28 @@ public class TourGuide {
resultPoint.y = getYForTooTip(mToolTip.mGravity, toolTipMeasuredHeight, targetViewY, adjustment); resultPoint.y = getYForTooTip(mToolTip.mGravity, toolTipMeasuredHeight, targetViewY, adjustment);
// add view to parent // add view to parent
// ((ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content)).addView(mToolTipViewGroup, layoutParams); // ((ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content)).addView(tooltipBinding, layoutParams);
parent.addView(mToolTipViewGroup, layoutParams); parent.addView(tooltipBinding.getRoot(), layoutParams);
// 1. width < screen check // 1. width < screen check
if (toolTipMeasuredWidth > parent.getWidth()){ if (toolTipMeasuredWidth > parent.getWidth()) {
mToolTipViewGroup.getLayoutParams().width = parent.getWidth(); tooltipBinding.getRoot().getLayoutParams().width = parent.getWidth();
toolTipMeasuredWidth = parent.getWidth(); toolTipMeasuredWidth = parent.getWidth();
} }
// 2. x left boundary check // 2. x left boundary check
if (resultPoint.x < 0){ if (resultPoint.x < 0) {
mToolTipViewGroup.getLayoutParams().width = toolTipMeasuredWidth + resultPoint.x; //since point.x is negative, use plus tooltipBinding.getRoot().getLayoutParams().width = toolTipMeasuredWidth + resultPoint.x; //since point.x is negative, use plus
resultPoint.x = 0; resultPoint.x = 0;
} }
// 3. x right boundary check // 3. x right boundary check
int tempRightX = resultPoint.x + toolTipMeasuredWidth; int tempRightX = resultPoint.x + toolTipMeasuredWidth;
if ( tempRightX > parent.getWidth()){ if (tempRightX > parent.getWidth()) {
mToolTipViewGroup.getLayoutParams().width = parent.getWidth() - resultPoint.x; //since point.x is negative, use plus tooltipBinding.getRoot().getLayoutParams().width = parent.getWidth() - resultPoint.x; //since point.x is negative, use plus
} }
// pass toolTip onClickListener into toolTipViewGroup // pass toolTip onClickListener into toolTipViewGroup
if (mToolTip.mOnClickListener!=null) { if (mToolTip.mOnClickListener != null) {
mToolTipViewGroup.setOnClickListener(mToolTip.mOnClickListener); tooltipBinding.getRoot().setOnClickListener(mToolTip.mOnClickListener);
} }
// TODO: no boundary check for height yet, this is a unlikely case though // TODO: no boundary check for height yet, this is a unlikely case though
@ -357,16 +368,16 @@ public class TourGuide {
// 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 // 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 // re-calculate height again once it's rendered
final ViewTreeObserver viewTreeObserver = mToolTipViewGroup.getViewTreeObserver(); final ViewTreeObserver viewTreeObserver = tooltipBinding.getRoot().getViewTreeObserver();
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override @Override
public void onGlobalLayout() { public void onGlobalLayout() {
mToolTipViewGroup.getViewTreeObserver().removeGlobalOnLayoutListener(this);// make sure this only run once tooltipBinding.getRoot().getViewTreeObserver().removeGlobalOnLayoutListener(this);// make sure this only run once
int fixedY; int fixedY;
int toolTipHeightAfterLayouted = mToolTipViewGroup.getHeight(); int toolTipHeightAfterLayouted = tooltipBinding.getRoot().getHeight();
fixedY = getYForTooTip(mToolTip.mGravity, toolTipHeightAfterLayouted, targetViewY, adjustment); fixedY = getYForTooTip(mToolTip.mGravity, toolTipHeightAfterLayouted, targetViewY, adjustment);
layoutParams.setMargins((int)mToolTipViewGroup.getX(),fixedY,0,0); layoutParams.setMargins((int) tooltipBinding.getRoot().getX(), fixedY, 0, 0);
} }
}); });
@ -376,25 +387,26 @@ public class TourGuide {
} }
private int getXForTooTip(int gravity, int toolTipMeasuredWidth, int targetViewX, float adjustment){ private int getXForTooTip(int gravity, int toolTipMeasuredWidth, int targetViewX, float adjustment) {
int x; int x;
if ((gravity & Gravity.LEFT) == Gravity.LEFT){ if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
x = targetViewX - toolTipMeasuredWidth + (int)adjustment; x = targetViewX - toolTipMeasuredWidth + (int) adjustment;
} else if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) { } else if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
x = targetViewX + mHighlightedView.getWidth() - (int)adjustment; x = targetViewX + mHighlightedView.getWidth() - (int) adjustment;
} else { } else {
x = targetViewX + mHighlightedView.getWidth() / 2 - toolTipMeasuredWidth / 2; x = targetViewX + mHighlightedView.getWidth() / 2 - toolTipMeasuredWidth / 2;
} }
return x; return x;
} }
private int getYForTooTip(int gravity, int toolTipMeasuredHeight, int targetViewY, float adjustment){
private int getYForTooTip(int gravity, int toolTipMeasuredHeight, int targetViewY, float adjustment) {
int y; int y;
if ((gravity & Gravity.TOP) == Gravity.TOP) { if ((gravity & Gravity.TOP) == Gravity.TOP) {
if (((gravity & Gravity.LEFT) == Gravity.LEFT) || ((gravity & Gravity.RIGHT) == Gravity.RIGHT)) { if (((gravity & Gravity.LEFT) == Gravity.LEFT) || ((gravity & Gravity.RIGHT) == Gravity.RIGHT)) {
y = targetViewY - toolTipMeasuredHeight + (int)adjustment; y = targetViewY - toolTipMeasuredHeight + (int) adjustment;
} else { } else {
y = targetViewY - toolTipMeasuredHeight - (int)adjustment; y = targetViewY - toolTipMeasuredHeight - (int) adjustment;
} }
} else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) { } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
if (((gravity & Gravity.LEFT) == Gravity.LEFT) || ((gravity & Gravity.RIGHT) == Gravity.RIGHT)) { if (((gravity & Gravity.LEFT) == Gravity.LEFT) || ((gravity & Gravity.RIGHT) == Gravity.RIGHT)) {
@ -412,12 +424,12 @@ public class TourGuide {
return y; return y;
} }
private FloatingActionButton setupAndAddFABToFrameLayout(final FrameLayoutWithHole frameLayoutWithHole){ private FloatingActionButton setupAndAddFABToFrameLayout(final FrameLayoutWithHole frameLayoutWithHole) {
// invisFab is invisible, and it's only used for getting the width and height // invisFab is invisible, and it's only used for getting the width and height
final FloatingActionButton invisFab = new FloatingActionButton(mActivity); final FloatingActionButton invisFab = new FloatingActionButton(mActivity);
invisFab.setSize(FloatingActionButton.SIZE_MINI); invisFab.setSize(FloatingActionButton.SIZE_MINI);
invisFab.setVisibility(View.INVISIBLE); invisFab.setVisibility(View.INVISIBLE);
((ViewGroup)mActivity.getWindow().getDecorView()).addView(invisFab); ((ViewGroup) mActivity.getWindow().getDecorView()).addView(invisFab);
// fab is the real fab that is going to be added // fab is the real fab that is going to be added
final FloatingActionButton fab = new FloatingActionButton(mActivity); final FloatingActionButton fab = new FloatingActionButton(mActivity);
@ -446,28 +458,37 @@ public class TourGuide {
return fab; return fab;
} }
private void setupFrameLayout(){ private void setupFrameLayout() {
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
ViewGroup contentArea = mActivity.getWindow().getDecorView().findViewById(android.R.id.content); ViewGroup contentArea = mActivity.getWindow().getDecorView().findViewById(android.R.id.content);
int [] pos = new int[2]; int[] pos = new int[2];
contentArea.getLocationOnScreen(pos); contentArea.getLocationOnScreen(pos);
// frameLayoutWithHole's coordinates are calculated taking full screen height into account // 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 // 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); layoutParams.setMargins(0, -pos[1], 0, 0);
contentArea.addView(mFrameLayout, layoutParams); contentArea.addView(mFrameLayout, layoutParams);
} }
private void performAnimationOn(final View view){ private void performAnimationOn(final View view) {
if (mTechnique != null && mTechnique == Technique.HorizontalLeft){ if (mTechnique != null && mTechnique == Technique.HorizontalLeft) {
final AnimatorSet animatorSet = new AnimatorSet(); final AnimatorSet animatorSet = new AnimatorSet();
final AnimatorSet animatorSet2 = new AnimatorSet(); final AnimatorSet animatorSet2 = new AnimatorSet();
Animator.AnimatorListener lis1 = new Animator.AnimatorListener() { Animator.AnimatorListener lis1 = new Animator.AnimatorListener() {
@Override public void onAnimationStart(Animator animator) {} @Override
@Override public void onAnimationCancel(Animator animator) {} public void onAnimationStart(Animator animator) {
@Override public void onAnimationRepeat(Animator animator) {} }
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
@Override @Override
public void onAnimationEnd(Animator animator) { public void onAnimationEnd(Animator animator) {
view.setScaleX(1f); view.setScaleX(1f);
@ -477,9 +498,18 @@ public class TourGuide {
} }
}; };
Animator.AnimatorListener lis2 = new Animator.AnimatorListener() { Animator.AnimatorListener lis2 = new Animator.AnimatorListener() {
@Override public void onAnimationStart(Animator animator) {} @Override
@Override public void onAnimationCancel(Animator animator) {} public void onAnimationStart(Animator animator) {
@Override public void onAnimationRepeat(Animator animator) {} }
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
@Override @Override
public void onAnimationEnd(Animator animator) { public void onAnimationEnd(Animator animator) {
view.setScaleX(1f); view.setScaleX(1f);
@ -493,7 +523,7 @@ public class TourGuide {
long scaleDownDuration = 800; long scaleDownDuration = 800;
long goLeftXDuration = 2000; long goLeftXDuration = 2000;
long fadeOutDuration = goLeftXDuration; long fadeOutDuration = goLeftXDuration;
float translationX = getScreenWidth()/2; float translationX = getScreenWidth() / 2;
final ValueAnimator fadeInAnim = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f); final ValueAnimator fadeInAnim = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);
fadeInAnim.setDuration(fadeInDuration); fadeInAnim.setDuration(fadeInDuration);
@ -532,19 +562,28 @@ public class TourGuide {
/* these animatorSets are kept track in FrameLayout, so that they can be cleaned up when FrameLayout is detached from window */ /* 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(animatorSet);
mFrameLayout.addAnimatorSet(animatorSet2); mFrameLayout.addAnimatorSet(animatorSet2);
} else if (mTechnique != null && mTechnique == Technique.HorizontalRight){ } else if (mTechnique != null && mTechnique == Technique.HorizontalRight) {
} else if (mTechnique != null && mTechnique == Technique.VerticalUpward){ } else if (mTechnique != null && mTechnique == Technique.VerticalUpward) {
} else if (mTechnique != null && mTechnique == Technique.VerticalDownward){ } else if (mTechnique != null && mTechnique == Technique.VerticalDownward) {
} else { // do click for default case } else { // do click for default case
final AnimatorSet animatorSet = new AnimatorSet(); final AnimatorSet animatorSet = new AnimatorSet();
final AnimatorSet animatorSet2 = new AnimatorSet(); final AnimatorSet animatorSet2 = new AnimatorSet();
Animator.AnimatorListener lis1 = new Animator.AnimatorListener() { Animator.AnimatorListener lis1 = new Animator.AnimatorListener() {
@Override public void onAnimationStart(Animator animator) {} @Override
@Override public void onAnimationCancel(Animator animator) {} public void onAnimationStart(Animator animator) {
@Override public void onAnimationRepeat(Animator animator) {} }
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
@Override @Override
public void onAnimationEnd(Animator animator) { public void onAnimationEnd(Animator animator) {
view.setScaleX(1f); view.setScaleX(1f);
@ -554,9 +593,18 @@ public class TourGuide {
} }
}; };
Animator.AnimatorListener lis2 = new Animator.AnimatorListener() { Animator.AnimatorListener lis2 = new Animator.AnimatorListener() {
@Override public void onAnimationStart(Animator animator) {} @Override
@Override public void onAnimationCancel(Animator animator) {} public void onAnimationStart(Animator animator) {
@Override public void onAnimationRepeat(Animator animator) {} }
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
@Override @Override
public void onAnimationEnd(Animator animator) { public void onAnimationEnd(Animator animator) {
view.setScaleX(1f); view.setScaleX(1f);
@ -621,8 +669,9 @@ public class TourGuide {
mFrameLayout.addAnimatorSet(animatorSet2); mFrameLayout.addAnimatorSet(animatorSet2);
} }
} }
private int getScreenWidth(){
if (mActivity!=null) { private int getScreenWidth() {
if (mActivity != null) {
return mActivity.getResources().getDisplayMetrics().widthPixels; return mActivity.getResources().getDisplayMetrics().widthPixels;
} else { } else {
return 0; return 0;

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -15,8 +17,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:background="#e74c3c" android:background="#e74c3c"
android:orientation="vertical"
android:gravity="center" android:gravity="center"
android:orientation="vertical"
android:padding="10dp"> android:padding="10dp">
<TextView <TextView
@ -37,4 +39,6 @@
android:textColor="@color/White" /> android:textColor="@color/White" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</layout>

@ -1,5 +1,9 @@
<ToggleButton xmlns:android="http://schemas.android.com/apk/res/android" <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<ToggleButton
android:id="@+id/eo_label" android:id="@+id/eo_label"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content" />
</ToggleButton>
</layout>

@ -1,5 +1,9 @@
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android" <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBox
android:id="@+id/eo_value" android:id="@+id/eo_value"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content" />
</CheckBox>
</layout>

@ -1,15 +1,21 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<TextView <TextView
android:id="@+id/eo_label" android:id="@+id/eo_label"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content" />
</TextView>
<Spinner <Spinner
android:id="@+id/eo_value" android:id="@+id/eo_value"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"> android:layout_weight="1" />
</Spinner>
</LinearLayout> </LinearLayout>
</layout>

@ -1,16 +1,22 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <?xml version="1.0" encoding="utf-8"?>
<layout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" > android:layout_height="fill_parent">
<TextView <TextView
android:id="@+id/eo_label" android:id="@+id/eo_label"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content" />
</TextView>
<EditText <EditText
android:id="@+id/eo_value" android:id="@+id/eo_value"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:inputType="numberSigned"> android:inputType="numberSigned" />
</EditText>
</LinearLayout> </LinearLayout>
</layout>

@ -1,16 +1,22 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" > android:layout_height="fill_parent">
<TextView <TextView
android:id="@+id/eo_label" android:id="@+id/eo_label"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content" />
</TextView>
<EditText <EditText
android:id="@+id/eo_value" android:id="@+id/eo_value"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:inputType="text"> android:inputType="text" />
</EditText>
</LinearLayout> </LinearLayout>
</layout>