package com.androidexperiments.landmarker.widget; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.content.SharedPreferences; import android.util.AttributeSet; import android.util.TypedValue; import android.view.animation.OvershootInterpolator; import android.widget.FrameLayout; import android.widget.TextView; import com.google.creativelabs.androidexperiments.typecompass.R; import com.androidexperiments.landmarker.data.NearbyPlace; import java.util.ArrayList; import butterknife.ButterKnife; import butterknife.InjectView; import butterknife.OnClick; import de.greenrobot.event.EventBus; /** * view that takes big text and little guy */ public class DirectionalTextView extends FrameLayout { private static final String TAG = DirectionalTextView.class.getSimpleName(); /** * largest amount allowed to move across screen with easing, larger amounts, like * when south jumps from last to first, will just be translated and not eased */ private static final float MAX_JUMP = 1920.f; /** * Name of prefs object we are using. * TODO - if shared prefs used anywhere else, extract and put into constants file */ private static final String NAME_SHARED_PREFS = "LandmarkerPrefs"; /** * Key for SharedPrefs storage of whether we are in metric mode or not */ private static final String KEY_IS_METRIC = "key_is_metric"; @InjectView(R.id.dtv_main_text) TextView mMainText; @InjectView(R.id.dtv_marker_view) TextView mMarkerText; @InjectView(R.id.dtv_distance_text) TextView mDistanceText; private float mTranslationX = 0.f; private float mGotoX = 0.f; private float mLastX = 0.f; private ArrayList<NearbyPlace> mPlaces; private NearbyPlace mCurrentPlace; private float mDistanceInKilometers; private float mDistanceInMiles; private boolean mIsMetric = true; public DirectionalTextView(Context context) { super(context); } public DirectionalTextView(Context context, AttributeSet attrs) { super(context, attrs); } public DirectionalTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onFinishInflate() { super.onFinishInflate(); ButterKnife.inject(this, this); //restore saved metric settings SharedPreferences prefs = getContext().getSharedPreferences(NAME_SHARED_PREFS, Context.MODE_PRIVATE); mIsMetric = prefs.getBoolean(KEY_IS_METRIC, true); setDistanceText(); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); EventBus.getDefault().register(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); EventBus.getDefault().unregister(this); } public void setDir(String dir) { mMarkerText.setText(dir); } public void setPlaces(ArrayList<NearbyPlace> places) { mPlaces = places; if(mPlaces.size() > 0) setPlace(mPlaces.get(0)); else setEmptyPlace(); } @OnClick(R.id.dtv_distance_text) public void onDistanceClicked() { mIsMetric = !mIsMetric; SharedPreferences prefs = getContext().getSharedPreferences(NAME_SHARED_PREFS, Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(KEY_IS_METRIC, mIsMetric); editor.apply(); EventBus.getDefault().post(new OnDistanceUnitsChangedEvent(mIsMetric)); } public void onEvent(OnDistanceUnitsChangedEvent event) { mIsMetric = event.isMetric; setDistanceText(); } public void setDistance(float distance) { mDistanceInKilometers = distance / 1000; mDistanceInMiles = getMiles(distance); setDistanceText(); } private void setDistanceText() { if(mIsMetric) mDistanceText.setText(String.format("%.1f", mDistanceInKilometers) + " km"); else mDistanceText.setText(String.format("%.1f", mDistanceInMiles) + " mi"); } private void setEmptyPlace() { this.setText(""); mMainText.setTextColor(0xaaffffff); mMainText.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen.directional_tv_main_text_size_small)); } private void setPlace(NearbyPlace place) { mCurrentPlace = place; this.setText(place.getName()); this.setDistance(place.getDistance()); } public void setText(String name) { mMainText.setText(name); } private float getMiles(float meters) { return meters*0.000621371192f; } public NearbyPlace getCurrentPlace() { return mCurrentPlace; } @Override public void setTranslationY(float translationY) { mMainText.setTranslationY(translationY); } private void setRandomPlace() { int size = mPlaces.size(); if(size > 0) setPlace(mPlaces.get((int)Math.floor(Math.random() * size))); } /** * move the view the proper amount * @param offset -1 to 1 will be displaying on screen somehow * @param viewWidth total width of each view to mulitply by */ public void setTranslation(float offset, int viewWidth) { mGotoX = (offset * viewWidth); if(Math.abs(mGotoX - mLastX) > MAX_JUMP) { mTranslationX = mGotoX; this.setTranslationX(mTranslationX); } mLastX = mGotoX; } public void springUp() { final ObjectAnimator anim = ObjectAnimator.ofFloat(mMainText, "translationY", mMainText.getTranslationY(), 0.f); anim.setInterpolator(new OvershootInterpolator(12.f)); anim.setDuration(300); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { if(animation.getAnimatedFraction() > 2.0f) { setRandomPlace(); anim.removeUpdateListener(this); } } }); anim.start(); ValueAnimator distance = ValueAnimator.ofFloat(mDistanceText.getAlpha(), 1.f); distance.setInterpolator(new OvershootInterpolator(12.f)); distance.setDuration(300); distance.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float frac = (float)animation.getAnimatedValue(); mDistanceText.setAlpha(frac); mDistanceText.setScaleX(frac); mDistanceText.setScaleY(frac); } }); distance.start(); } public void returnToPosition() { ObjectAnimator anim = ObjectAnimator.ofFloat(mMainText, "translationY", mMainText.getTranslationY(), 0.f); anim.setDuration(250); anim.start(); ValueAnimator distance = ValueAnimator.ofFloat(mDistanceText.getAlpha(), 1.f); distance.setDuration(250); distance.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float frac = (float) animation.getAnimatedValue(); mDistanceText.setAlpha(frac); mDistanceText.setScaleX(frac); mDistanceText.setScaleY(frac); } }); distance.start(); } public void drawView() { mTranslationX += (mGotoX - mTranslationX) * 0.12f; this.setTranslationX((int) mTranslationX); } public String getDir() { return mMarkerText.getText().toString(); } public void updatePostition(float percent) { int TOTAL_Y_MOVEMENT = -480; float HALF_PI = (float) Math.PI / 2.f; float v = (float)Math.sin(percent * HALF_PI) * -(TOTAL_Y_MOVEMENT / 2); this.setTranslationY(v); float reverse = 1.f - percent; mDistanceText.setAlpha(reverse); mDistanceText.setScaleX(reverse); mDistanceText.setScaleY(reverse); } public static class OnDistanceUnitsChangedEvent { public boolean isMetric; public OnDistanceUnitsChangedEvent(boolean isMetric) { this.isMetric = isMetric; } } }