diff --git a/android-app-tests/android-app-tests.iml b/android-app-tests/android-app-tests.iml index d50c06a6..ad249d00 100644 --- a/android-app-tests/android-app-tests.iml +++ b/android-app-tests/android-app-tests.iml @@ -85,12 +85,12 @@ - + @@ -98,6 +98,7 @@ + diff --git a/android-app/android-app.iml b/android-app/android-app.iml index c7441375..cecc8e29 100644 --- a/android-app/android-app.iml +++ b/android-app/android-app.iml @@ -95,12 +95,13 @@ + + - diff --git a/android-app/build.gradle b/android-app/build.gradle index 065466b9..d16b9d37 100644 --- a/android-app/build.gradle +++ b/android-app/build.gradle @@ -69,7 +69,8 @@ dependencies { compile('org.solovyev:jscl:1.0.8') { exclude(module: 'xercesImpl') } - compile 'org.solovyev.android:checkout:0.6.0@aar' + compile 'org.solovyev.android:checkout:0.6.1@aar' + compile 'org.solovyev.android:material:0.1.2@aar' compile 'com.google.android.gms:play-services-ads:6.5.87@aar' compile 'com.google.android.gms:play-services-base:6.5.87@aar' compile 'com.melnykov:floatingactionbutton:1.1.0' diff --git a/android-app/src/main/AndroidManifest.xml b/android-app/src/main/AndroidManifest.xml index 886987cb..b7776c9d 100644 --- a/android-app/src/main/AndroidManifest.xml +++ b/android-app/src/main/AndroidManifest.xml @@ -60,7 +60,7 @@ - + @@ -71,7 +71,7 @@ - + diff --git a/android-app/src/main/java/com/viewpagerindicator/CirclePageIndicator.java b/android-app/src/main/java/com/viewpagerindicator/CirclePageIndicator.java new file mode 100644 index 00000000..c5666391 --- /dev/null +++ b/android-app/src/main/java/com/viewpagerindicator/CirclePageIndicator.java @@ -0,0 +1,556 @@ +/* + * Copyright (C) 2011 Patrik Akerfeldt + * Copyright (C) 2011 Jake Wharton + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.viewpagerindicator; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.ViewConfigurationCompat; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import org.solovyev.android.calculator.R; + +import static android.graphics.Paint.ANTI_ALIAS_FLAG; +import static android.widget.LinearLayout.HORIZONTAL; +import static android.widget.LinearLayout.VERTICAL; + +/** + * Draws circles (one for each view). The current view position is filled and + * others are only stroked. + */ +public class CirclePageIndicator extends View implements PageIndicator { + private static final int INVALID_POINTER = -1; + + private float mRadius; + private final Paint mPaintPageFill = new Paint(ANTI_ALIAS_FLAG); + private final Paint mPaintStroke = new Paint(ANTI_ALIAS_FLAG); + private final Paint mPaintFill = new Paint(ANTI_ALIAS_FLAG); + private ViewPager mViewPager; + private ViewPager.OnPageChangeListener mListener; + private int mCurrentPage; + private int mSnapPage; + private float mPageOffset; + private int mScrollState; + private int mOrientation; + private boolean mCentered; + private boolean mSnap; + + private int mTouchSlop; + private float mLastMotionX = -1; + private int mActivePointerId = INVALID_POINTER; + private boolean mIsDragging; + + + public CirclePageIndicator(Context context) { + this(context, null); + } + + public CirclePageIndicator(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.vpiCirclePageIndicatorStyle); + } + + public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + if (isInEditMode()) return; + + //Load defaults from resources + final Resources res = getResources(); + final int defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color); + final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color); + final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation); + final int defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color); + final float defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width); + final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius); + final boolean defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered); + final boolean defaultSnap = res.getBoolean(R.bool.default_circle_indicator_snap); + + //Retrieve styles attributes + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, 0); + + mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered); + mOrientation = a.getInt(R.styleable.CirclePageIndicator_android_orientation, defaultOrientation); + mPaintPageFill.setStyle(Style.FILL); + mPaintPageFill.setColor(a.getColor(R.styleable.CirclePageIndicator_pageColor, defaultPageColor)); + mPaintStroke.setStyle(Style.STROKE); + mPaintStroke.setColor(a.getColor(R.styleable.CirclePageIndicator_strokeColor, defaultStrokeColor)); + mPaintStroke.setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth)); + mPaintFill.setStyle(Style.FILL); + mPaintFill.setColor(a.getColor(R.styleable.CirclePageIndicator_fillColor, defaultFillColor)); + mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius); + mSnap = a.getBoolean(R.styleable.CirclePageIndicator_snap, defaultSnap); + + Drawable background = a.getDrawable(R.styleable.CirclePageIndicator_android_background); + if (background != null) { + setBackgroundDrawable(background); + } + + a.recycle(); + + final ViewConfiguration configuration = ViewConfiguration.get(context); + mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); + } + + + public void setCentered(boolean centered) { + mCentered = centered; + invalidate(); + } + + public boolean isCentered() { + return mCentered; + } + + public void setPageColor(int pageColor) { + mPaintPageFill.setColor(pageColor); + invalidate(); + } + + public int getPageColor() { + return mPaintPageFill.getColor(); + } + + public void setFillColor(int fillColor) { + mPaintFill.setColor(fillColor); + invalidate(); + } + + public int getFillColor() { + return mPaintFill.getColor(); + } + + public void setOrientation(int orientation) { + switch (orientation) { + case HORIZONTAL: + case VERTICAL: + mOrientation = orientation; + requestLayout(); + break; + + default: + throw new IllegalArgumentException("Orientation must be either HORIZONTAL or VERTICAL."); + } + } + + public int getOrientation() { + return mOrientation; + } + + public void setStrokeColor(int strokeColor) { + mPaintStroke.setColor(strokeColor); + invalidate(); + } + + public int getStrokeColor() { + return mPaintStroke.getColor(); + } + + public void setStrokeWidth(float strokeWidth) { + mPaintStroke.setStrokeWidth(strokeWidth); + invalidate(); + } + + public float getStrokeWidth() { + return mPaintStroke.getStrokeWidth(); + } + + public void setRadius(float radius) { + mRadius = radius; + invalidate(); + } + + public float getRadius() { + return mRadius; + } + + public void setSnap(boolean snap) { + mSnap = snap; + invalidate(); + } + + public boolean isSnap() { + return mSnap; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (mViewPager == null) { + return; + } + final int count = mViewPager.getAdapter().getCount(); + if (count == 0) { + return; + } + + if (mCurrentPage >= count) { + setCurrentItem(count - 1); + return; + } + + int longSize; + int longPaddingBefore; + int longPaddingAfter; + int shortPaddingBefore; + if (mOrientation == HORIZONTAL) { + longSize = getWidth(); + longPaddingBefore = getPaddingLeft(); + longPaddingAfter = getPaddingRight(); + shortPaddingBefore = getPaddingTop(); + } else { + longSize = getHeight(); + longPaddingBefore = getPaddingTop(); + longPaddingAfter = getPaddingBottom(); + shortPaddingBefore = getPaddingLeft(); + } + + final float threeRadius = mRadius * 3; + final float shortOffset = shortPaddingBefore + mRadius; + float longOffset = longPaddingBefore + mRadius; + if (mCentered) { + longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f) - ((count * threeRadius) / 2.0f); + } + + float dX; + float dY; + + float pageFillRadius = mRadius; + if (mPaintStroke.getStrokeWidth() > 0) { + pageFillRadius -= mPaintStroke.getStrokeWidth() / 2.0f; + } + + //Draw stroked circles + for (int iLoop = 0; iLoop < count; iLoop++) { + float drawLong = longOffset + (iLoop * threeRadius); + if (mOrientation == HORIZONTAL) { + dX = drawLong; + dY = shortOffset; + } else { + dX = shortOffset; + dY = drawLong; + } + // Only paint fill if not completely transparent + if (mPaintPageFill.getAlpha() > 0) { + canvas.drawCircle(dX, dY, pageFillRadius, mPaintPageFill); + } + + // Only paint stroke if a stroke width was non-zero + if (pageFillRadius != mRadius) { + canvas.drawCircle(dX, dY, mRadius, mPaintStroke); + } + } + + //Draw the filled circle according to the current scroll + float cx = (mSnap ? mSnapPage : mCurrentPage) * threeRadius; + if (!mSnap) { + cx += mPageOffset * threeRadius; + } + if (mOrientation == HORIZONTAL) { + dX = longOffset + cx; + dY = shortOffset; + } else { + dX = shortOffset; + dY = longOffset + cx; + } + canvas.drawCircle(dX, dY, mRadius, mPaintFill); + } + + public boolean onTouchEvent(MotionEvent ev) { + if (super.onTouchEvent(ev)) { + return true; + } + if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) { + return false; + } + + final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; + switch (action) { + case MotionEvent.ACTION_DOWN: + mActivePointerId = MotionEventCompat.getPointerId(ev, 0); + mLastMotionX = ev.getX(); + break; + + case MotionEvent.ACTION_MOVE: { + final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); + final float x = MotionEventCompat.getX(ev, activePointerIndex); + final float deltaX = x - mLastMotionX; + + if (!mIsDragging) { + if (Math.abs(deltaX) > mTouchSlop) { + mIsDragging = true; + } + } + + if (mIsDragging) { + mLastMotionX = x; + if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) { + mViewPager.fakeDragBy(deltaX); + } + } + + break; + } + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + if (!mIsDragging) { + final int count = mViewPager.getAdapter().getCount(); + final int width = getWidth(); + final float halfWidth = width / 2f; + final float sixthWidth = width / 6f; + + if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) { + if (action != MotionEvent.ACTION_CANCEL) { + mViewPager.setCurrentItem(mCurrentPage - 1); + } + return true; + } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) { + if (action != MotionEvent.ACTION_CANCEL) { + mViewPager.setCurrentItem(mCurrentPage + 1); + } + return true; + } + } + + mIsDragging = false; + mActivePointerId = INVALID_POINTER; + if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag(); + break; + + case MotionEventCompat.ACTION_POINTER_DOWN: { + final int index = MotionEventCompat.getActionIndex(ev); + mLastMotionX = MotionEventCompat.getX(ev, index); + mActivePointerId = MotionEventCompat.getPointerId(ev, index); + break; + } + + case MotionEventCompat.ACTION_POINTER_UP: + final int pointerIndex = MotionEventCompat.getActionIndex(ev); + final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); + if (pointerId == mActivePointerId) { + final int newPointerIndex = pointerIndex == 0 ? 1 : 0; + mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); + } + mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId)); + break; + } + + return true; + } + + @Override + public void setViewPager(ViewPager view) { + if (mViewPager == view) { + return; + } + if (mViewPager != null) { + mViewPager.setOnPageChangeListener(null); + } + if (view.getAdapter() == null) { + throw new IllegalStateException("ViewPager does not have adapter instance."); + } + mViewPager = view; + mViewPager.setOnPageChangeListener(this); + invalidate(); + } + + @Override + public void setViewPager(ViewPager view, int initialPosition) { + setViewPager(view); + setCurrentItem(initialPosition); + } + + @Override + public void setCurrentItem(int item) { + if (mViewPager == null) { + throw new IllegalStateException("ViewPager has not been bound."); + } + mViewPager.setCurrentItem(item); + mCurrentPage = item; + invalidate(); + } + + @Override + public void notifyDataSetChanged() { + invalidate(); + } + + @Override + public void onPageScrollStateChanged(int state) { + mScrollState = state; + + if (mListener != null) { + mListener.onPageScrollStateChanged(state); + } + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + mCurrentPage = position; + mPageOffset = positionOffset; + invalidate(); + + if (mListener != null) { + mListener.onPageScrolled(position, positionOffset, positionOffsetPixels); + } + } + + @Override + public void onPageSelected(int position) { + if (mSnap || mScrollState == ViewPager.SCROLL_STATE_IDLE) { + mCurrentPage = position; + mSnapPage = position; + invalidate(); + } + + if (mListener != null) { + mListener.onPageSelected(position); + } + } + + @Override + public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { + mListener = listener; + } + + /* + * (non-Javadoc) + * + * @see android.view.View#onMeasure(int, int) + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mOrientation == HORIZONTAL) { + setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec)); + } else { + setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec)); + } + } + + /** + * Determines the width of this view + * + * @param measureSpec + * A measureSpec packed into an int + * @return The width of the view, honoring constraints from measureSpec + */ + private int measureLong(int measureSpec) { + int result; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + + if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) { + //We were told how big to be + result = specSize; + } else { + //Calculate the width according the views count + final int count = mViewPager.getAdapter().getCount(); + result = (int)(getPaddingLeft() + getPaddingRight() + + (count * 2 * mRadius) + (count - 1) * mRadius + 1); + //Respect AT_MOST value if that was what is called for by measureSpec + if (specMode == MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + } + return result; + } + + /** + * Determines the height of this view + * + * @param measureSpec + * A measureSpec packed into an int + * @return The height of the view, honoring constraints from measureSpec + */ + private int measureShort(int measureSpec) { + int result; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + + if (specMode == MeasureSpec.EXACTLY) { + //We were told how big to be + result = specSize; + } else { + //Measure the height + result = (int)(2 * mRadius + getPaddingTop() + getPaddingBottom() + 1); + //Respect AT_MOST value if that was what is called for by measureSpec + if (specMode == MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + } + return result; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + SavedState savedState = (SavedState)state; + super.onRestoreInstanceState(savedState.getSuperState()); + mCurrentPage = savedState.currentPage; + mSnapPage = savedState.currentPage; + requestLayout(); + } + + @Override + public Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + SavedState savedState = new SavedState(superState); + savedState.currentPage = mCurrentPage; + return savedState; + } + + static class SavedState extends BaseSavedState { + int currentPage; + + public SavedState(Parcelable superState) { + super(superState); + } + + private SavedState(Parcel in) { + super(in); + currentPage = in.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(currentPage); + } + + @SuppressWarnings("UnusedDeclaration") + public static final Creator CREATOR = new Creator() { + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } +} diff --git a/android-app/src/main/java/com/viewpagerindicator/PageIndicator.java b/android-app/src/main/java/com/viewpagerindicator/PageIndicator.java new file mode 100644 index 00000000..c08c00ae --- /dev/null +++ b/android-app/src/main/java/com/viewpagerindicator/PageIndicator.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2011 Patrik Akerfeldt + * Copyright (C) 2011 Jake Wharton + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.viewpagerindicator; + +import android.support.v4.view.ViewPager; + +/** + * A PageIndicator is responsible to show an visual indicator on the total views + * number and the current visible view. + */ +public interface PageIndicator extends ViewPager.OnPageChangeListener { + /** + * Bind the indicator to a ViewPager. + * + * @param view + */ + void setViewPager(ViewPager view); + + /** + * Bind the indicator to a ViewPager. + * + * @param view + * @param initialPosition + */ + void setViewPager(ViewPager view, int initialPosition); + + /** + *

Set the current page of both the ViewPager and indicator.

+ * + *

This must be used if you need to set the page before + * the views are drawn on screen (e.g., default start page).

+ * + * @param item + */ + void setCurrentItem(int item); + + /** + * Set a page change listener which will receive forwarded events. + * + * @param listener + */ + void setOnPageChangeListener(ViewPager.OnPageChangeListener listener); + + /** + * Notify the indicator that the fragment list has changed. + */ + void notifyDataSetChanged(); +} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/wizard/CalculatorWizardActivity.java b/android-app/src/main/java/org/solovyev/android/calculator/wizard/CalculatorWizardActivity.java deleted file mode 100644 index 8c8796b2..00000000 --- a/android-app/src/main/java/org/solovyev/android/calculator/wizard/CalculatorWizardActivity.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2013 serso aka se.solovyev - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * Contact details - * - * Email: se.solovyev@gmail.com - * Site: http://se.solovyev.org - */ - -package org.solovyev.android.calculator.wizard; - -import org.solovyev.android.calculator.CalculatorApplication; -import org.solovyev.android.calculator.R; -import org.solovyev.android.wizard.BaseWizardActivity; -import org.solovyev.android.wizard.Wizards; - -import javax.annotation.Nonnull; - -public final class CalculatorWizardActivity extends BaseWizardActivity { - - @Nonnull - private Wizards wizards; - - public CalculatorWizardActivity() { - super(R.layout.cpp_wizard); - this.wizards = CalculatorApplication.getInstance().getWizards(); - } - - @Nonnull - public Wizards getWizards() { - return wizards; - } - - public void setWizards(@Nonnull Wizards wizards) { - this.wizards = wizards; - } -} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/wizard/CalculatorWizards.java b/android-app/src/main/java/org/solovyev/android/calculator/wizard/CalculatorWizards.java index 4df8c546..2983ccc6 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/wizard/CalculatorWizards.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/wizard/CalculatorWizards.java @@ -27,7 +27,7 @@ public class CalculatorWizards implements Wizards { @Nonnull @Override public Class getActivityClassName() { - return CalculatorWizardActivity.class; + return WizardActivity.class; } @Nonnull diff --git a/android-app/src/main/java/org/solovyev/android/calculator/wizard/WelcomeWizardStep.java b/android-app/src/main/java/org/solovyev/android/calculator/wizard/WelcomeWizardStep.java index c209e02d..3961107c 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/wizard/WelcomeWizardStep.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/wizard/WelcomeWizardStep.java @@ -23,22 +23,23 @@ package org.solovyev.android.calculator.wizard; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import org.solovyev.android.calculator.R; -/** - * User: serso - * Date: 6/16/13 - * Time: 9:44 PM - */ -public final class WelcomeWizardStep extends Fragment { +public final class WelcomeWizardStep extends WizardFragment { + + @Override + protected int getViewResId() { + return R.layout.cpp_wizard_step_welcome; + } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.cpp_wizard_step_welcome, null); + final View view = super.onCreateView(inflater, container, savedInstanceState); + setupNextButton(R.string.wizard_start); + setupPrevButton(R.string.wizard_skip); + return view; } } diff --git a/android-app/src/main/java/org/solovyev/android/calculator/wizard/WizardActivity.java b/android-app/src/main/java/org/solovyev/android/calculator/wizard/WizardActivity.java new file mode 100644 index 00000000..ced1e682 --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/wizard/WizardActivity.java @@ -0,0 +1,139 @@ +package org.solovyev.android.calculator.wizard; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import com.viewpagerindicator.PageIndicator; +import org.solovyev.android.calculator.BaseActivity; +import org.solovyev.android.calculator.CalculatorApplication; +import org.solovyev.android.calculator.R; +import org.solovyev.android.wizard.*; + +import javax.annotation.Nonnull; + +public class WizardActivity extends BaseActivity implements WizardsAware { + @Nonnull + private final WizardUi wizardUi = new WizardUi(this, this, 0); + + @Nonnull + private ViewPager pager; + + @Nonnull + private PagerAdapter pagerAdapter; + + public WizardActivity() { + super(R.layout.cpp_activity_wizard); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + wizardUi.onCreate(savedInstanceState); + getSupportActionBar().hide(); + final ListWizardFlow flow = (ListWizardFlow) wizardUi.getFlow(); + + pager = (ViewPager) findViewById(R.id.pager); + pagerAdapter = new WizardPagerAdapter(flow, getSupportFragmentManager()); + pager.setAdapter(pagerAdapter); + final PageIndicator titleIndicator = (PageIndicator) findViewById(R.id.pager_indicator); + titleIndicator.setViewPager(pager); + titleIndicator.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + @Override + public void onPageSelected(int position) { + wizardUi.setStep(flow.getStepAt(position)); + } + }); + + if (savedInstanceState == null) { + final int position = flow.getPositionFor(wizardUi.getStep()); + pager.setCurrentItem(position); + } + } + + @Override + public void onBackPressed() { + if (pager.getCurrentItem() == 0) { + super.onBackPressed(); + } else { + pager.setCurrentItem(pager.getCurrentItem() - 1); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + wizardUi.onSaveInstanceState(outState); + } + + @Override + protected void onPause() { + super.onPause(); + wizardUi.onPause(); + } + + @Nonnull + @Override + public Wizards getWizards() { + return CalculatorApplication.getInstance().getWizards(); + } + + public void finishWizardAbruptly() { + wizardUi.finishWizardAbruptly(); + finish(); + } + + public void finishWizard() { + wizardUi.finishWizard(); + finish(); + } + + public boolean canGoNext() { + final int position = pager.getCurrentItem(); + return position != pagerAdapter.getCount() - 1; + } + + public boolean canGoPrev() { + final int position = pager.getCurrentItem(); + return position != 0; + } + + public void goNext() { + final int position = pager.getCurrentItem(); + if (position < pagerAdapter.getCount() - 1) { + pager.setCurrentItem(position + 1, true); + } + } + + public void goPrev() { + final int position = pager.getCurrentItem(); + if (position > 0) { + pager.setCurrentItem(position - 1, true); + } + } + + private class WizardPagerAdapter extends FragmentStatePagerAdapter { + @Nonnull + private final ListWizardFlow flow; + + public WizardPagerAdapter(@Nonnull ListWizardFlow flow, @Nonnull FragmentManager fm) { + super(fm); + this.flow = flow; + } + + @Override + public Fragment getItem(int position) { + final WizardStep step = flow.getStepAt(position); + final String className = step.getFragmentClass().getName(); + final Bundle args = step.getFragmentArgs(); + return Fragment.instantiate(WizardActivity.this, className, args); + } + + @Override + public int getCount() { + return flow.getCount(); + } + } +} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/wizard/WizardFragment.java b/android-app/src/main/java/org/solovyev/android/calculator/wizard/WizardFragment.java new file mode 100644 index 00000000..73e7e899 --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/wizard/WizardFragment.java @@ -0,0 +1,72 @@ +package org.solovyev.android.calculator.wizard; + +import android.os.Bundle; +import android.support.annotation.LayoutRes; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import org.solovyev.android.calculator.R; + +import javax.annotation.Nullable; + +public abstract class WizardFragment extends Fragment implements View.OnClickListener { + + @Nullable + private TextView nextButton; + + @Nullable + private TextView prevButton; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.fragment_wizard, container, false); + + final ViewGroup content = (ViewGroup) view.findViewById(R.id.wizard_content); + inflater.inflate(getViewResId(), content, true); + + nextButton = (TextView) view.findViewById(R.id.wizard_next); + if (nextButton != null) { + nextButton.setOnClickListener(this); + } + prevButton = (TextView) view.findViewById(R.id.wizard_prev); + if (prevButton != null) { + prevButton.setOnClickListener(this); + } + + return view; + } + + protected final void setupNextButton(int textResId) { + assert nextButton != null; + nextButton.setText(textResId); + } + + protected final void setupPrevButton(int textResId) { + assert prevButton != null; + prevButton.setText(textResId); + } + + @LayoutRes + protected abstract int getViewResId(); + + @Override + public void onClick(View v) { + final int id = v.getId(); + final WizardActivity activity = (WizardActivity) getActivity(); + if (id == R.id.wizard_next) { + if (activity.canGoNext()) { + activity.goNext(); + } else { + activity.finishWizard(); + } + } else if (id == R.id.wizard_prev) { + if (activity.canGoPrev()) { + activity.goPrev(); + } else { + activity.finishWizardAbruptly(); + } + } + } +} diff --git a/android-app/src/main/java/org/solovyev/android/wizard/BaseWizardActivity.java b/android-app/src/main/java/org/solovyev/android/wizard/BaseWizardActivity.java deleted file mode 100644 index ca393151..00000000 --- a/android-app/src/main/java/org/solovyev/android/wizard/BaseWizardActivity.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.solovyev.android.wizard; - -import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; - -import javax.annotation.Nonnull; - -public abstract class BaseWizardActivity extends ActionBarActivity implements WizardsAware, FinishWizardConfirmationDialog.Listener { - - @Nonnull - private WizardUi ui; - - protected BaseWizardActivity(int layoutResId) { - ui = new WizardUi(this, this, layoutResId); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - ui.onCreate(savedInstanceState); - } - - @Override - protected void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); - ui.onSaveInstanceState(out); - } - - @Override - public void onPause() { - super.onPause(); - ui.onPause(); - } - - @Override - public void onBackPressed() { - ui.onBackPressed(); - } - - @Override - public void finishWizardAbruptly() { - ui.finishWizardAbruptly(); - } -} diff --git a/android-app/src/main/java/org/solovyev/android/wizard/ListWizardFlow.java b/android-app/src/main/java/org/solovyev/android/wizard/ListWizardFlow.java index d7699c5b..5177a0d5 100644 --- a/android-app/src/main/java/org/solovyev/android/wizard/ListWizardFlow.java +++ b/android-app/src/main/java/org/solovyev/android/wizard/ListWizardFlow.java @@ -22,33 +22,28 @@ package org.solovyev.android.wizard; -import org.solovyev.common.JPredicate; - import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.List; -import static org.solovyev.common.collections.Collections.find; - public final class ListWizardFlow implements WizardFlow { @Nonnull - private final List steps; + private final List steps; - public ListWizardFlow(@Nonnull List steps) { + public ListWizardFlow(@Nonnull List steps) { this.steps = steps; } @Nullable @Override public WizardStep getStepByName(@Nonnull final String name) { - return find(steps, new JPredicate() { - @Override - public boolean apply(@Nullable WizardStep step) { - assert step != null; - return step.getName().equals(name); + for (WizardStep step : steps) { + if (step.getName().equals(name)) { + return step; } - }); + } + return null; } @Nullable @@ -78,4 +73,24 @@ public final class ListWizardFlow implements WizardFlow { public WizardStep getFirstStep() { return steps.get(0); } + + @Nonnull + public WizardStep getStepAt(int position) { + return steps.get(position); + } + + public int getPositionFor(@Nonnull WizardStep step) { + for (int i = 0; i < steps.size(); i++) { + if (steps.get(i).equals(step)) { + return i; + } + + } + + return -1; + } + + public int getCount() { + return steps.size(); + } } diff --git a/android-app/src/main/java/org/solovyev/android/wizard/WizardUi.java b/android-app/src/main/java/org/solovyev/android/wizard/WizardUi.java index 8dbac768..306b452c 100644 --- a/android-app/src/main/java/org/solovyev/android/wizard/WizardUi.java +++ b/android-app/src/main/java/org/solovyev/android/wizard/WizardUi.java @@ -6,63 +6,25 @@ import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.view.View; -import android.widget.Button; -import org.solovyev.android.calculator.R; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import static android.view.View.GONE; -import static android.view.View.VISIBLE; - -public class WizardUi { - - /* - ********************************************************************** - * - * CONSTANTS - * - ********************************************************************** - */ +public class WizardUi { private static final String FLOW = "flow"; private static final String STEP = "step"; - /* - ********************************************************************** - * - * FIELDS - * - ********************************************************************** - */ - - private WizardStep step; - - private Wizard wizard; - - /* - ********************************************************************** - * - * VIEWS - * - ********************************************************************** - */ - - @Nullable - private View prevButton; + protected WizardStep step; + protected Wizard wizard; @Nonnull - private Button nextButton; + protected final A activity; @Nonnull - private final A activity; + protected final WizardsAware wizardsAware; - @Nonnull - private final WizardsAware wizardsAware; - - private final int layoutResId; + protected final int layoutResId; public WizardUi(@Nonnull A activity, @Nonnull WizardsAware wizardsAware, int layoutResId) { this.activity = activity; @@ -70,22 +32,14 @@ public class WizardUi + + + + + + \ No newline at end of file diff --git a/android-app/src/main/res/layout/cpp_activity_wizard.xml b/android-app/src/main/res/layout/cpp_activity_wizard.xml new file mode 100644 index 00000000..d76948ad --- /dev/null +++ b/android-app/src/main/res/layout/cpp_activity_wizard.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/android-app/src/main/res/layout/cpp_wizard_step_welcome.xml b/android-app/src/main/res/layout/cpp_wizard_step_welcome.xml index be126b5b..14418ffe 100644 --- a/android-app/src/main/res/layout/cpp_wizard_step_welcome.xml +++ b/android-app/src/main/res/layout/cpp_wizard_step_welcome.xml @@ -22,9 +22,13 @@ ~ Site: http://se.solovyev.org --> - + diff --git a/android-app/src/main/res/layout/fragment_wizard.xml b/android-app/src/main/res/layout/fragment_wizard.xml new file mode 100644 index 00000000..91d2b263 --- /dev/null +++ b/android-app/src/main/res/layout/fragment_wizard.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/android-app/src/main/res/layout/fragment_wizard_buttons.xml b/android-app/src/main/res/layout/fragment_wizard_buttons.xml new file mode 100644 index 00000000..3eca995f --- /dev/null +++ b/android-app/src/main/res/layout/fragment_wizard_buttons.xml @@ -0,0 +1,21 @@ + + + + +