diff --git a/README.md b/README.md index d0c165f..790b049 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Quoting the [documentation](https://www.google.com/design/spec/components/steppe ## Download (from JCenter) ```groovy -compile 'com.stepstone.stepper:material-stepper:3.1.0' +compile 'com.stepstone.stepper:material-stepper:3.2.0' ``` ## Supported steppers @@ -30,6 +30,8 @@ compile 'com.stepstone.stepper:material-stepper:3.1.0' - embedding the stepper anywhere in the view hierarchy and changing the stepper type for various device configurations, e.g. phone/tablet, portrait/landscape - step validation - use with Fragments or Views + - showing errors in tabs + - showing stepper feedback for ongoing operations (see [Stepper feedback](https://material.io/guidelines/components/steppers.html#steppers-types-of-steppers)) ## Getting started @@ -341,10 +343,60 @@ For an example of how to use it with views please see the sample app. ### Showing an error on tabs if step verification failed To show an error in the tabbed stepper if step verification fails you need to set `ms_showErrorStateEnabled` attribute to `true`. -

+

If you want to keep the error displayed when going back to the previous step you need to also set `ms_showErrorStateOnBackEnabled` to `true`. +### Stepper feedback +It is possible to show stepper feedback for ongoing operations (see [Stepper feedback](https://material.io/guidelines/components/steppers.html#steppers-types-of-steppers)). +To do so you firstly need to set ```ms_stepperFeedbackType``` to one or more of: +* ```tabs``` - shows a progress message instead of the tabs during operation, +* ```content``` - shows a progress bar on top of the steps' content and partially fades the content out during operation, +* ```disabled_bottom_navigation``` - disables the buttons in the bottom navigation during operation. +The default is ```none``` which does nothing. It is possible to use multiple flags together. + +After setting this to show the feedback you need to call ```StepperLayout#showProgress(@NonNull String progressMessage)``` +and do hide the progress indicator you need to call ```StepperLayout#hideProgress()```. + +

+ +E.g. +In layout: +```xml + + +``` + +and in BlockingStep: + +```java +public class StepperFeedbackStepFragment extends Fragment implements BlockingStep { + + //... + + @Override + @UiThread + public void onNextClicked(final StepperLayout.OnNextClickedCallback callback) { + callback.getStepperLayout().showProgress("Operation in progress, please wait..."); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + callback.goToNextStep(); + callback.getStepperLayout().hideProgress(); + } + }, 2000L); + } + + //... + +``` + ### Custom styling Basic styling can be done by choosing the active and inactive step colors. There are some additional properties which can be changed directly from StepperLayout's attributes e.g. the background of bottom navigation buttons (see StepperLayout attributes) @@ -356,28 +408,29 @@ See 'Custom StepperLayout theme' in the sample app for an example. For other examples, e.g. persisting state on rotation, displaying errors, changing whether the user can go to the next step, etc. check out the sample app. ## StepperLayout attributes -| Attribute name | Format | Description | -| --------------------------------|-------------------------------------------|-------------| -| *ms_stepperType* | one of `dots`, `progress_bar` or `tabs` | **REQUIRED:** Type of the stepper | -| *ms_backButtonColor* | color or reference | BACK button's text color | -| *ms_nextButtonColor* | color or reference | NEXT button's text color | -| *ms_completeButtonColor* | color or reference | COMPLETE button's text color | -| *ms_activeStepColor* | color or reference | Active step's color | -| *ms_inactiveStepColor* | color or reference | Inactive step's color | -| *ms_bottomNavigationBackground* | reference | Background of the bottom navigation | -| *ms_backButtonBackground* | reference | BACK button's background | -| *ms_nextButtonBackground* | reference | NEXT button's background | -| *ms_completeButtonBackground* | reference | COMPLETE button's background | -| *ms_backButtonText* | string or reference | BACK button's text | -| *ms_nextButtonText* | string or reference | NEXT button's text | -| *ms_completeButtonText* | string or reference | COMPLETE button's text | -| *ms_tabStepDividerWidth* | dimension or reference | The width of the horizontal tab divider used in tabs stepper type | -| *ms_showBackButtonOnFirstStep* | boolean | Flag indicating if the Back (Previous step) button should be shown on the first step. False by default. | -| *ms_errorColor* | color or reference | Error color in Tabs stepper | -| *ms_showErrorStateEnabled* | boolean | Flag indicating whether to show the error state. Only applicable for 'tabs' type. False by default. | -| *ms_showErrorStateOnBackEnabled*| boolean | Flag indicating whether to keep showing the error state when user moves back. Only applicable for 'tabs' type. False by default. | -| *ms_tabNavigationEnabled* | boolean | Flag indicating whether step navigation is possible by clicking on the tabs directly. Only applicable for 'tabs' type. True by default. | -| *ms_stepperLayoutTheme* | reference | Theme to use for even more custom styling of the stepper layout. It is recommended that it should extend @style/MSDefaultStepperLayoutTheme, which is the default theme used. | +| Attribute name | Format | Description | +| --------------------------------|---------------------------------------------------------------------|-------------| +| *ms_stepperType* | one of `dots`, `progress_bar` or `tabs` | **REQUIRED:** Type of the stepper | +| *ms_backButtonColor* | color or reference | BACK button's text color | +| *ms_nextButtonColor* | color or reference | NEXT button's text color | +| *ms_completeButtonColor* | color or reference | COMPLETE button's text color | +| *ms_activeStepColor* | color or reference | Active step's color | +| *ms_inactiveStepColor* | color or reference | Inactive step's color | +| *ms_bottomNavigationBackground* | reference | Background of the bottom navigation | +| *ms_backButtonBackground* | reference | BACK button's background | +| *ms_nextButtonBackground* | reference | NEXT button's background | +| *ms_completeButtonBackground* | reference | COMPLETE button's background | +| *ms_backButtonText* | string or reference | BACK button's text | +| *ms_nextButtonText* | string or reference | NEXT button's text | +| *ms_completeButtonText* | string or reference | COMPLETE button's text | +| *ms_tabStepDividerWidth* | dimension or reference | The width of the horizontal tab divider used in tabs stepper type | +| *ms_showBackButtonOnFirstStep* | boolean | Flag indicating if the Back (Previous step) button should be shown on the first step. False by default. | +| *ms_errorColor* | color or reference | Error color in Tabs stepper | +| *ms_showErrorStateEnabled* | boolean | Flag indicating whether to show the error state. Only applicable for 'tabs' type. False by default. | +| *ms_showErrorStateOnBackEnabled*| boolean | Flag indicating whether to keep showing the error state when user moves back. Only applicable for 'tabs' type. False by default. | +| *ms_tabNavigationEnabled* | boolean | Flag indicating whether step navigation is possible by clicking on the tabs directly. Only applicable for 'tabs' type. True by default. | +| *ms_stepperFeedbackType* | flag(s): `none` or `tabs`, `content` & `disabled_bottom_navigation` | Type(s) of stepper feedback. Can be a combination of `tabs`, `content` & `disabled_bottom_navigation`. Default is `none`.| +| *ms_stepperLayoutTheme* | reference | Theme to use for even more custom styling of the stepper layout. It is recommended that it should extend @style/MSDefaultStepperLayoutTheme, which is the default theme used. | ### StepperLayout style attributes A list of `ms_stepperLayoutTheme` attributes responsible for styling of StepperLayout's child views. @@ -390,8 +443,10 @@ A list of `ms_stepperLayoutTheme` attributes responsible for styling of StepperL | *ms_nextNavigationButtonStyle* | Used by ms_stepNextButton in layout/ms_stepper_layout | | *ms_completeNavigationButtonStyle*| Used by ms_stepCompleteButton in layout/ms_stepper_layout | | *ms_colorableProgressBarStyle* | Used by ms_stepProgressBar in layout/ms_stepper_layout | +| *ms_stepPagerProgressBarStyle* | Used by ms_stepPagerProgressBar in layout/ms_stepper_layout | | *ms_stepTabsScrollViewStyle* | Used by ms_stepTabsScrollView in layout/ms_tabs_container | | *ms_stepTabsInnerContainerStyle* | Used by ms_stepTabsInnerContainer in layout/ms_tabs_container | +| *ms_stepTabsProgressMessageStyle* | Used by ms_stepTabsProgressMessage in layout/ms_tabs_container| | *ms_stepTabContainerStyle* | Used in layout/ms_step_tab_container | | *ms_stepTabNumberStyle* | Used by ms_stepNumber in layout/ms_step_tab | | *ms_stepTabDoneIndicatorStyle* | Used by ms_stepDoneIndicator in layout/ms_step_tab | diff --git a/gifs/error-on-tabs.gif b/gifs/error-on-tabs.gif index fe4852f..86276c9 100644 Binary files a/gifs/error-on-tabs.gif and b/gifs/error-on-tabs.gif differ diff --git a/gifs/stepper-feedback.gif b/gifs/stepper-feedback.gif new file mode 100644 index 0000000..03fab00 Binary files /dev/null and b/gifs/stepper-feedback.gif differ diff --git a/gradle.properties b/gradle.properties index b02daa4..b0502bf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,4 +19,4 @@ POM_GROUP_ID=com.stepstone.stepper POM_ARTIFACT_ID=material-stepper -POM_VERSION=3.1.0 \ No newline at end of file +POM_VERSION=3.2.0 \ No newline at end of file diff --git a/material-stepper/src/main/java/com/stepstone/stepper/StepperLayout.java b/material-stepper/src/main/java/com/stepstone/stepper/StepperLayout.java index 9854324..8838eb2 100644 --- a/material-stepper/src/main/java/com/stepstone/stepper/StepperLayout.java +++ b/material-stepper/src/main/java/com/stepstone/stepper/StepperLayout.java @@ -43,6 +43,8 @@ import android.widget.LinearLayout; import com.stepstone.stepper.adapter.StepAdapter; +import com.stepstone.stepper.internal.feedback.StepperFeedbackType; +import com.stepstone.stepper.internal.feedback.StepperFeedbackTypeFactory; import com.stepstone.stepper.internal.type.AbstractStepperType; import com.stepstone.stepper.internal.type.StepperTypeFactory; import com.stepstone.stepper.internal.util.AnimationUtil; @@ -222,10 +224,14 @@ public void goToPrevStep() { private int mTypeIdentifier = AbstractStepperType.PROGRESS_BAR; + private int mFeedbackTypeMask = StepperFeedbackType.NONE; + private StepAdapter mStepAdapter; private AbstractStepperType mStepperType; + private StepperFeedbackType mStepperFeedbackType; + private int mCurrentStepPosition; private boolean mShowErrorStateEnabled; @@ -234,6 +240,8 @@ public void goToPrevStep() { private boolean mTabNavigationEnabled; + private boolean mInProgress; + @StyleRes private int mStepperLayoutTheme; @@ -243,7 +251,7 @@ public void goToPrevStep() { private OnClickListener mOnBackClickListener = new OnClickListener() { @Override public void onClick(View v) { - onPrevious(); + onBackClicked(); } }; @@ -370,6 +378,22 @@ public void proceed() { } } + /** + * To be called when the user wants to go to the previous step. + */ + public void onBackClicked() { + Step step = findCurrentStep(); + + updateErrorFlagWhenGoingBack(); + + OnBackClickedCallback onBackClickedCallback = new OnBackClickedCallback(); + if (step instanceof BlockingStep) { + ((BlockingStep) step).onBackClicked(onBackClickedCallback); + } else { + onBackClickedCallback.goToPrevStep(); + } + } + /** * Sets the current step to the one at the provided index. * This does not verify the current step. @@ -385,6 +409,11 @@ public void setCurrentStepPosition(int currentStepPosition) { onUpdate(currentStepPosition, true); } + /** + * Returns the position of the currently selected step. + * + * @return position of the currently selected step + */ public int getCurrentStepPosition() { return mCurrentStepPosition; } @@ -397,6 +426,18 @@ public void setCompleteButtonVerificationFailed(boolean verificationFailed) { mCompleteNavigationButton.setVerificationFailed(verificationFailed); } + public void setNextButtonEnabled(boolean enabled) { + mNextNavigationButton.setEnabled(enabled); + } + + public void setCompleteButtonEnabled(boolean enabled) { + mCompleteNavigationButton.setEnabled(enabled); + } + + public void setBackButtonEnabled(boolean enabled) { + mBackNavigationButton.setEnabled(enabled); + } + /** * Set whether when going backwards should clear the error state from the Tab. Default is false. * @@ -496,6 +537,40 @@ public void updateErrorFlag(boolean hasError) { mStepperType.setErrorFlag(mCurrentStepPosition, hasError); } + /** + * Shows a progress indicator. This does not have to be a progress bar and it depends on chosen stepper feedback types. + * @param progressMessage optional progress message if supported by the selected types + */ + public void showProgress(@NonNull String progressMessage) { + mInProgress = true; + mStepperFeedbackType.showProgress(progressMessage); + } + + /** + * Hides the progress indicator. + */ + public void hideProgress() { + mInProgress = false; + mStepperFeedbackType.hideProgress(); + } + + /** + * Checks if there's an ongoing operation i.e. if {@link #showProgress(String)} was called and not followed by {@link #hideProgress()} yet. + * @return true if in progress, false otherwise + */ + public boolean isInProgress() { + return mInProgress; + } + + /** + * Sets the mask for the stepper feedback type. + * @param feedbackTypeMask step feedback type mask, should contain one or more flags from {@link StepperFeedbackType} + */ + public void setFeedbackType(int feedbackTypeMask) { + mFeedbackTypeMask = feedbackTypeMask; + mStepperFeedbackType = StepperFeedbackTypeFactory.createType(mFeedbackTypeMask, this); + } + @SuppressWarnings("RestrictedApi") private void init(AttributeSet attrs, @AttrRes int defStyleAttr) { initDefaultValues(); @@ -523,6 +598,7 @@ public boolean onTouch(View view, MotionEvent motionEvent) { mTabsContainer.setVisibility(GONE); mStepperType = StepperTypeFactory.createType(mTypeIdentifier, this); + mStepperFeedbackType = StepperFeedbackTypeFactory.createType(mFeedbackTypeMask, this); } private void initNavigation() { @@ -645,7 +721,11 @@ private void extractValuesFromAttributes(AttributeSet attrs, @AttrRes int defSty mShowErrorStateEnabled = a.getBoolean(R.styleable.StepperLayout_ms_showErrorStateEnabled, mShowErrorStateEnabled); if (a.hasValue(R.styleable.StepperLayout_ms_stepperType)) { - mTypeIdentifier = a.getInt(R.styleable.StepperLayout_ms_stepperType, DEFAULT_TAB_DIVIDER_WIDTH); + mTypeIdentifier = a.getInt(R.styleable.StepperLayout_ms_stepperType, AbstractStepperType.PROGRESS_BAR); + } + + if (a.hasValue(R.styleable.StepperLayout_ms_stepperFeedbackType)) { + mFeedbackTypeMask = a.getInt(R.styleable.StepperLayout_ms_stepperFeedbackType, StepperFeedbackType.NONE); } mShowErrorStateOnBackEnabled = a.getBoolean(R.styleable.StepperLayout_ms_showErrorStateOnBack, false); @@ -678,19 +758,6 @@ private Step findCurrentStep() { return mStepAdapter.findStep(mCurrentStepPosition); } - private void onPrevious() { - Step step = findCurrentStep(); - - updateErrorFlagWhenGoingBack(); - - OnBackClickedCallback onBackClickedCallback = new OnBackClickedCallback(); - if (step instanceof BlockingStep) { - ((BlockingStep) step).onBackClicked(onBackClickedCallback); - } else { - onBackClickedCallback.goToPrevStep(); - } - } - private void updateErrorFlagWhenGoingBack() { updateErrorFlag(mShowErrorStateOnBackEnabled && mStepperType.getErrorAtPosition(mCurrentStepPosition)); } diff --git a/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/ContentStepperFeedbackType.java b/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/ContentStepperFeedbackType.java new file mode 100644 index 0000000..bccde25 --- /dev/null +++ b/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/ContentStepperFeedbackType.java @@ -0,0 +1,61 @@ +/* +Copyright 2017 StepStone Services + +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.stepstone.stepper.internal.feedback; + +import android.support.annotation.NonNull; +import android.support.annotation.RestrictTo; +import android.view.View; +import android.widget.ProgressBar; + +import com.stepstone.stepper.R; +import com.stepstone.stepper.StepperLayout; + +import static android.support.annotation.RestrictTo.Scope.LIBRARY; +import static com.stepstone.stepper.internal.util.AnimationUtil.ALPHA_HALF; +import static com.stepstone.stepper.internal.util.AnimationUtil.ALPHA_OPAQUE; + +/** + * Feedback stepper type which displays a progress bar on top of the steps' content and partially fades the content out. + */ +@RestrictTo(LIBRARY) +public class ContentStepperFeedbackType implements StepperFeedbackType { + + private final View mPager; + + private final ProgressBar mPagerProgressBar; + + public ContentStepperFeedbackType(@NonNull StepperLayout stepperLayout) { + mPager = stepperLayout.findViewById(R.id.ms_stepPager); + mPagerProgressBar = (ProgressBar) stepperLayout.findViewById(R.id.stepPagerProgressBar); + } + + @Override + public void showProgress(@NonNull String progressMessage) { + mPagerProgressBar.setVisibility(View.VISIBLE); + mPager.animate() + .alpha(ALPHA_HALF) + .setDuration(PROGRESS_ANIMATION_DURATION); + } + + @Override + public void hideProgress() { + mPagerProgressBar.setVisibility(View.GONE); + mPager.animate() + .alpha(ALPHA_OPAQUE) + .setDuration(PROGRESS_ANIMATION_DURATION); + } +} diff --git a/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/DisabledBottomNavigationStepperFeedbackType.java b/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/DisabledBottomNavigationStepperFeedbackType.java new file mode 100644 index 0000000..1dbf1cc --- /dev/null +++ b/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/DisabledBottomNavigationStepperFeedbackType.java @@ -0,0 +1,54 @@ +/* +Copyright 2017 StepStone Services + +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.stepstone.stepper.internal.feedback; + +import android.support.annotation.NonNull; +import android.support.annotation.RestrictTo; + +import com.stepstone.stepper.StepperLayout; + +import static android.support.annotation.RestrictTo.Scope.LIBRARY; + +/** + * Feedback stepper type which disabled the buttons in the bottom navigation when an operation is in progress. + */ +@RestrictTo(LIBRARY) +public class DisabledBottomNavigationStepperFeedbackType implements StepperFeedbackType { + + private StepperLayout mStepperLayout; + + public DisabledBottomNavigationStepperFeedbackType(@NonNull StepperLayout stepperLayout) { + mStepperLayout = stepperLayout; + } + + @Override + public void showProgress(@NonNull String progressMessage) { + setButtonsEnabled(false); + } + + @Override + public void hideProgress() { + setButtonsEnabled(true); + } + + private void setButtonsEnabled(boolean enabled) { + mStepperLayout.setNextButtonEnabled(enabled); + mStepperLayout.setCompleteButtonEnabled(enabled); + mStepperLayout.setBackButtonEnabled(enabled); + } + +} diff --git a/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/StepperFeedbackType.java b/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/StepperFeedbackType.java new file mode 100644 index 0000000..c05e889 --- /dev/null +++ b/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/StepperFeedbackType.java @@ -0,0 +1,51 @@ +package com.stepstone.stepper.internal.feedback; + + +import android.support.annotation.NonNull; + + +/** + * An interface to be implemented by all support stepper feedback types. + * It contains methods which allow to show feedback for the duration of some executed operation. + * + * @author Piotr Zawadzki + */ +public interface StepperFeedbackType { + + /** + * No changes during operation. + */ + int NONE = 1; + + /** + * Show a progress message instead of the tabs during operation. + * @see TabsStepperFeedbackType + */ + int TABS = 1 << 1; + + /** + * Shows a progress bar on top of the steps' content and partially fades the content out during operation. + * @see ContentStepperFeedbackType + */ + int CONTENT = 1 << 2; + + /** + * Disables the buttons in the bottom navigation during operation. + * @see DisabledBottomNavigationStepperFeedbackType + */ + int DISABLED_BOTTOM_NAVIGATION = 1 << 3; + + int PROGRESS_ANIMATION_DURATION = 200; + + /** + * Shows a progress indicator. This does not have to be a progress bar and it depends on chosen stepper feedback types. + * @param progressMessage optional progress message if supported by the selected types + */ + void showProgress(@NonNull String progressMessage); + + /** + * Hides the progress indicator. + */ + void hideProgress(); + +} diff --git a/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/StepperFeedbackTypeComposite.java b/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/StepperFeedbackTypeComposite.java new file mode 100644 index 0000000..0109a0f --- /dev/null +++ b/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/StepperFeedbackTypeComposite.java @@ -0,0 +1,58 @@ +/* +Copyright 2017 StepStone Services + +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.stepstone.stepper.internal.feedback; + +import android.support.annotation.NonNull; +import android.support.annotation.RestrictTo; + +import java.util.ArrayList; +import java.util.List; + +import static android.support.annotation.RestrictTo.Scope.LIBRARY; + +/** + * A stepper feedback type which is a composition of other feedback type, which allows to select only a group of feedback types. + * See Stepper feedback section in https://material.io/guidelines/components/steppers.html#steppers-types-of-steppers + */ +@RestrictTo(LIBRARY) +public class StepperFeedbackTypeComposite implements StepperFeedbackType { + + private List children = new ArrayList<>(); + + @Override + public void showProgress(@NonNull String progressMessage) { + for (StepperFeedbackType child : children) { + child.showProgress(progressMessage); + } + } + + @Override + public void hideProgress() { + for (StepperFeedbackType child : children) { + child.hideProgress(); + } + } + + /** + * Adds a child component to this composite. + * @param component child to add + */ + public void addComponent(StepperFeedbackType component) { + children.add(component); + } + +} \ No newline at end of file diff --git a/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/StepperFeedbackTypeFactory.java b/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/StepperFeedbackTypeFactory.java new file mode 100644 index 0000000..2cce30d --- /dev/null +++ b/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/StepperFeedbackTypeFactory.java @@ -0,0 +1,62 @@ +/* +Copyright 2017 StepStone Services + +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.stepstone.stepper.internal.feedback; + +import android.support.annotation.RestrictTo; + +import com.stepstone.stepper.StepperLayout; + +import static android.support.annotation.RestrictTo.Scope.LIBRARY; + +/** + * Factory class for creating feedback stepper types. + */ +@RestrictTo(LIBRARY) +public class StepperFeedbackTypeFactory { + + /** + * Creates a stepper feedback type for provided arguments. + * It can be a composition of several feedback types depending on the provided flags. + * + * @param feedbackTypeMask step feedback type mask, should contain one or more from {@link StepperFeedbackType} + * @param stepperLayout stepper layout to use with the chosen stepper feedback type(s) + * @return a stepper feedback type + */ + public static StepperFeedbackType createType(int feedbackTypeMask, StepperLayout stepperLayout) { + + StepperFeedbackTypeComposite stepperFeedbackTypeComposite = new StepperFeedbackTypeComposite(); + + if ((feedbackTypeMask & StepperFeedbackType.NONE) != 0) { + //Add no more components if NONE type is selected + return stepperFeedbackTypeComposite; + } + + if ((feedbackTypeMask & StepperFeedbackType.TABS) != 0) { + stepperFeedbackTypeComposite.addComponent(new TabsStepperFeedbackType(stepperLayout)); + } + + if ((feedbackTypeMask & StepperFeedbackType.CONTENT) != 0) { + stepperFeedbackTypeComposite.addComponent(new ContentStepperFeedbackType(stepperLayout)); + } + + if ((feedbackTypeMask & StepperFeedbackType.DISABLED_BOTTOM_NAVIGATION) != 0) { + stepperFeedbackTypeComposite.addComponent(new DisabledBottomNavigationStepperFeedbackType(stepperLayout)); + } + + return stepperFeedbackTypeComposite; + } +} diff --git a/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/TabsStepperFeedbackType.java b/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/TabsStepperFeedbackType.java new file mode 100644 index 0000000..d5a0ed0 --- /dev/null +++ b/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/TabsStepperFeedbackType.java @@ -0,0 +1,90 @@ +/* +Copyright 2017 StepStone Services + +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.stepstone.stepper.internal.feedback; + +import android.support.annotation.NonNull; +import android.support.annotation.RestrictTo; +import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.LinearInterpolator; +import android.widget.TextView; + +import com.stepstone.stepper.R; +import com.stepstone.stepper.StepperLayout; + +import static android.support.annotation.RestrictTo.Scope.LIBRARY; +import static com.stepstone.stepper.internal.util.AnimationUtil.ALPHA_INVISIBLE; +import static com.stepstone.stepper.internal.util.AnimationUtil.ALPHA_OPAQUE; + +/** + * Feedback stepper type which displays a progress message instead of the tabs. + */ +@RestrictTo(LIBRARY) +public class TabsStepperFeedbackType implements StepperFeedbackType { + + private final float mProgressMessageTranslationWhenHidden; + + private TextView mProgressMessageTextView; + + private View mTabs; + + private StepperLayout mStepperLayout; + + public TabsStepperFeedbackType(@NonNull StepperLayout stepperLayout) { + mProgressMessageTranslationWhenHidden = stepperLayout.getResources().getDimension(R.dimen.ms_progress_message_translation_when_hidden); + mProgressMessageTextView = (TextView) stepperLayout.findViewById(R.id.ms_stepTabsProgressMessage); + mTabs = stepperLayout.findViewById(R.id.ms_stepTabsScrollView); + mStepperLayout = stepperLayout; + mProgressMessageTextView.setVisibility(View.VISIBLE); + } + + @Override + public void showProgress(@NonNull String progressMessage) { + setTabNavigationEnabled(false); + mProgressMessageTextView.setText(progressMessage); + mProgressMessageTextView.animate() + .setStartDelay(PROGRESS_ANIMATION_DURATION) + .alpha(ALPHA_OPAQUE) + .translationY(0.0f) + .setDuration(PROGRESS_ANIMATION_DURATION); + mTabs.animate() + .alpha(ALPHA_INVISIBLE) + .setStartDelay(0) + .setInterpolator(new LinearInterpolator()) + .setDuration(PROGRESS_ANIMATION_DURATION); + } + + @Override + public void hideProgress() { + setTabNavigationEnabled(true); + + mProgressMessageTextView.animate() + .setStartDelay(0) + .alpha(ALPHA_INVISIBLE) + .translationY(mProgressMessageTranslationWhenHidden) + .setDuration(PROGRESS_ANIMATION_DURATION); + mTabs.animate() + .alpha(ALPHA_OPAQUE) + .setStartDelay(PROGRESS_ANIMATION_DURATION) + .setInterpolator(new AccelerateInterpolator()) + .setDuration(PROGRESS_ANIMATION_DURATION); + } + + private void setTabNavigationEnabled(boolean tabNavigationEnabled) { + mStepperLayout.setTabNavigationEnabled(tabNavigationEnabled); + } +} diff --git a/material-stepper/src/main/java/com/stepstone/stepper/internal/type/AbstractStepperType.java b/material-stepper/src/main/java/com/stepstone/stepper/internal/type/AbstractStepperType.java index 2281c96..02b0766 100644 --- a/material-stepper/src/main/java/com/stepstone/stepper/internal/type/AbstractStepperType.java +++ b/material-stepper/src/main/java/com/stepstone/stepper/internal/type/AbstractStepperType.java @@ -48,12 +48,12 @@ public abstract class AbstractStepperType { */ public static final int TABS = 0x03; - final StepperLayout stepperLayout; + final StepperLayout mStepperLayout; final SparseBooleanArray mStepErrors = new SparseBooleanArray(); public AbstractStepperType(StepperLayout stepperLayout) { - this.stepperLayout = stepperLayout; + this.mStepperLayout = stepperLayout; } /** @@ -93,12 +93,12 @@ public void onNewAdapter(@NonNull StepAdapter stepAdapter) { @ColorInt protected int getSelectedColor() { - return stepperLayout.getSelectedColor(); + return mStepperLayout.getSelectedColor(); } @ColorInt protected int getUnselectedColor() { - return stepperLayout.getUnselectedColor(); + return mStepperLayout.getUnselectedColor(); } } \ No newline at end of file diff --git a/material-stepper/src/main/java/com/stepstone/stepper/internal/type/TabsStepperType.java b/material-stepper/src/main/java/com/stepstone/stepper/internal/type/TabsStepperType.java index 00ca2f9..0755f20 100644 --- a/material-stepper/src/main/java/com/stepstone/stepper/internal/type/TabsStepperType.java +++ b/material-stepper/src/main/java/com/stepstone/stepper/internal/type/TabsStepperType.java @@ -18,6 +18,7 @@ import android.support.annotation.NonNull; import android.support.annotation.RestrictTo; +import android.util.SparseBooleanArray; import android.view.View; import com.stepstone.stepper.R; @@ -54,6 +55,7 @@ public TabsStepperType(StepperLayout stepperLayout) { if (stepperLayout.isInEditMode()) { mTabsContainer.setSteps(EDIT_MODE_STEP_TITLES); + mTabsContainer.updateSteps(0, new SparseBooleanArray()); mTabsContainer.setVisibility(View.VISIBLE); } } @@ -63,7 +65,7 @@ public TabsStepperType(StepperLayout stepperLayout) { */ @Override public void onStepSelected(int newStepPosition, boolean userTriggeredChange) { - if (!stepperLayout.isShowErrorStateEnabled()) { + if (!mStepperLayout.isShowErrorStateEnabled()) { mStepErrors.clear(); } mTabsContainer.updateSteps(newStepPosition, mStepErrors); diff --git a/material-stepper/src/main/java/com/stepstone/stepper/internal/util/AnimationUtil.java b/material-stepper/src/main/java/com/stepstone/stepper/internal/util/AnimationUtil.java index 6833e9f..0dfd3bb 100644 --- a/material-stepper/src/main/java/com/stepstone/stepper/internal/util/AnimationUtil.java +++ b/material-stepper/src/main/java/com/stepstone/stepper/internal/util/AnimationUtil.java @@ -22,6 +22,10 @@ public final class AnimationUtil { @IntDef({View.VISIBLE, View.INVISIBLE, View.GONE}) @interface Visibility {} + public static final float ALPHA_OPAQUE = 1.0f; + public static final float ALPHA_INVISIBLE = 0.0f; + public static final float ALPHA_HALF = 0.5f; + private static final int DEFAULT_DURATION = 300; private AnimationUtil() { @@ -37,7 +41,7 @@ private AnimationUtil() { public static void fadeViewVisibility(@NonNull final View view, @Visibility final int visibility, boolean animate) { ViewPropertyAnimator animator = view.animate(); animator.cancel(); - animator.alpha(visibility == View.VISIBLE ? 1 : 0) + animator.alpha(visibility == View.VISIBLE ? ALPHA_OPAQUE : ALPHA_INVISIBLE) .setDuration(animate ? DEFAULT_DURATION : 0) .setListener(new Animator.AnimatorListener() { @Override diff --git a/material-stepper/src/main/res/layout/ms_stepper_layout.xml b/material-stepper/src/main/res/layout/ms_stepper_layout.xml index 1074859..bd2d263 100644 --- a/material-stepper/src/main/res/layout/ms_stepper_layout.xml +++ b/material-stepper/src/main/res/layout/ms_stepper_layout.xml @@ -13,11 +13,24 @@ android:layout_width="match_parent" tools:style="@style/MSStepTabsContainer" /> - + android:layout_weight="1"> + + + + + + + + + + \ No newline at end of file diff --git a/material-stepper/src/main/res/values/attrs.xml b/material-stepper/src/main/res/values/attrs.xml index 2b624f5..7d11f34 100644 --- a/material-stepper/src/main/res/values/attrs.xml +++ b/material-stepper/src/main/res/values/attrs.xml @@ -67,6 +67,14 @@ limitations under the License. + + + + + + + + + + + + + + diff --git a/material-stepper/src/main/res/values/colors.xml b/material-stepper/src/main/res/values/colors.xml index 77c5e8c..debf4f2 100644 --- a/material-stepper/src/main/res/values/colors.xml +++ b/material-stepper/src/main/res/values/colors.xml @@ -16,6 +16,7 @@ limitations under the License. --> #000000 + #DD000000 #FFFFFF #2196f3 #BDBDBD diff --git a/material-stepper/src/main/res/values/dimens.xml b/material-stepper/src/main/res/values/dimens.xml index b58518d..19f36e1 100644 --- a/material-stepper/src/main/res/values/dimens.xml +++ b/material-stepper/src/main/res/values/dimens.xml @@ -31,6 +31,9 @@ limitations under the License. 3dp 8dp 14sp + 6dp + 24dp + 14sp 72dp diff --git a/material-stepper/src/main/res/values/styles.xml b/material-stepper/src/main/res/values/styles.xml index e20ecbf..dc3785b 100644 --- a/material-stepper/src/main/res/values/styles.xml +++ b/material-stepper/src/main/res/values/styles.xml @@ -105,6 +105,26 @@ limitations under the License. @drawable/ms_vertical_tab_divider + + + + \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index 53489d1..d343b8d 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -25,6 +25,8 @@ android { dependencies { compile project(':material-stepper') compile ("com.android.support:appcompat-v7:$androidSupportLibraryVersion") + compile ("com.android.support:recyclerview-v7:$androidSupportLibraryVersion") + compile ("com.android.support:design:$androidSupportLibraryVersion") compile ("com.jakewharton:butterknife:$butterknifeVersion") compile ("uk.co.chrisjenx:calligraphy:$calligraphyVersion") } diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 2b5af60..285a2d9 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -8,7 +8,8 @@ android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> - + @@ -30,6 +31,7 @@ + diff --git a/sample/src/main/java/com/stepstone/stepper/sample/AbstractStepperActivity.java b/sample/src/main/java/com/stepstone/stepper/sample/AbstractStepperActivity.java index 732f280..ee444bb 100644 --- a/sample/src/main/java/com/stepstone/stepper/sample/AbstractStepperActivity.java +++ b/sample/src/main/java/com/stepstone/stepper/sample/AbstractStepperActivity.java @@ -64,7 +64,7 @@ protected void onSaveInstanceState(Bundle outState) { public void onBackPressed() { final int currentStepPosition = mStepperLayout.getCurrentStepPosition(); if (currentStepPosition > 0) { - mStepperLayout.setCurrentStepPosition(currentStepPosition - 1); + mStepperLayout.onBackClicked(); } else { finish(); } diff --git a/sample/src/main/java/com/stepstone/stepper/sample/CustomNavigationButtonsActivity.java b/sample/src/main/java/com/stepstone/stepper/sample/CustomNavigationButtonsActivity.java index 7e0c328..3a04a1f 100644 --- a/sample/src/main/java/com/stepstone/stepper/sample/CustomNavigationButtonsActivity.java +++ b/sample/src/main/java/com/stepstone/stepper/sample/CustomNavigationButtonsActivity.java @@ -61,7 +61,7 @@ protected void onSaveInstanceState(Bundle outState) { public void onBackPressed() { final int currentStepPosition = mStepperLayout.getCurrentStepPosition(); if (currentStepPosition > 0) { - mStepperLayout.setCurrentStepPosition(currentStepPosition - 1); + mStepperLayout.onBackClicked(); } else { finish(); } diff --git a/sample/src/main/java/com/stepstone/stepper/sample/DelayedTransitionStepperActivity.java b/sample/src/main/java/com/stepstone/stepper/sample/DelayedTransitionStepperActivity.java index 20a5a49..28647bb 100644 --- a/sample/src/main/java/com/stepstone/stepper/sample/DelayedTransitionStepperActivity.java +++ b/sample/src/main/java/com/stepstone/stepper/sample/DelayedTransitionStepperActivity.java @@ -53,7 +53,7 @@ protected void onSaveInstanceState(Bundle outState) { public void onBackPressed() { final int currentStepPosition = mStepperLayout.getCurrentStepPosition(); if (currentStepPosition > 0) { - mStepperLayout.setCurrentStepPosition(currentStepPosition - 1); + mStepperLayout.onBackClicked(); } else { finish(); } diff --git a/sample/src/main/java/com/stepstone/stepper/sample/MainActivity.java b/sample/src/main/java/com/stepstone/stepper/sample/MainActivity.java index c6ad138..44a5c54 100644 --- a/sample/src/main/java/com/stepstone/stepper/sample/MainActivity.java +++ b/sample/src/main/java/com/stepstone/stepper/sample/MainActivity.java @@ -16,116 +16,139 @@ package com.stepstone.stepper.sample; +import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import java.util.Arrays; +import java.util.List; + +import butterknife.Bind; import butterknife.ButterKnife; -import butterknife.OnClick; public class MainActivity extends AppCompatActivity { + @Bind(R.id.list) + RecyclerView recyclerView; + + @Bind(R.id.toolbar) + Toolbar toolbar; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); - } - - @OnClick(R.id.defaultDots) - public void onDefaultDots(View view) { - startActivity(new Intent(this, DefaultDotsActivity.class)); - } - - @OnClick(R.id.styledDots) - public void onStyledDots(View view) { - startActivity(new Intent(this, StyledDotsActivity.class)); - } - - @OnClick(R.id.themedDots) - public void onThemedDots(View view) { - startActivity(new Intent(this, ThemedDotsActivity.class)); - } - - @OnClick(R.id.defaultProgressBar) - public void onDefaultProgressBar(View view) { - startActivity(new Intent(this, DefaultProgressBarActivity.class)); - } - - @OnClick(R.id.styledProgressBar) - public void onStyledProgressBar(View view) { - startActivity(new Intent(this, StyledProgressBarActivity.class)); - } - - @OnClick(R.id.defaultTabs) - public void onDefaultTabs(View view) { - startActivity(new Intent(this, DefaultTabsActivity.class)); - } - - @OnClick(R.id.styledTabs) - public void onStyledTabs(View view) { - startActivity(new Intent(this, StyledTabsActivity.class)); - } - - @OnClick(R.id.errorTabs) - public void onErrorTabs(View view) { - startActivity(new Intent(this, ShowErrorTabActivity.class)); - } - - @OnClick(R.id.errorColorTabs) - public void onCustomColorErrorTabs(View view) { - startActivity(new Intent(this, ShowErrorCustomColorTabActivity.class)); - } - - @OnClick(R.id.errorOnBackTabs) - public void onErrorOnBackTabs(View view) { - startActivity(new Intent(this, ShowErrorOnBackTabActivity.class)); - } - - @OnClick(R.id.combination) - public void onCombination(View view) { - startActivity(new Intent(this, CombinationActivity.class)); - } - - @OnClick(R.id.customPageTransformer) - public void onCustomPageTransformer(View view) { - startActivity(new Intent(this, CustomPageTransformerActivity.class)); - } - - @OnClick(R.id.delayedTransition) - public void onDelayedTransition(View view) { - startActivity(new Intent(this, DelayedTransitionStepperActivity.class)); - } - - @OnClick(R.id.customNavigationButtons) - public void onDifferentNextButtons(View view) { - startActivity(new Intent(this, CustomNavigationButtonsActivity.class)); - } - - @OnClick(R.id.showReturnButtonOnFirstStep) - public void onShowReturnButton(View view) { - startActivity(new Intent(this, ReturnButtonActivity.class)); - } - - @OnClick(R.id.noFragments) - public void onNoFrag(View view){ - startActivity(new Intent(this, NoFragmentsActivity.class)); - } - - @OnClick(R.id.proceedProgrammatically) - public void onProceedProgrammatically(View view){ - startActivity(new Intent(this, ProceedProgrammaticallyActivity.class)); - } - - @OnClick(R.id.disabledTabNavigation) - public void onDisabledTabNavigation(View view){ - startActivity(new Intent(this, DisabledTabNavigationActivity.class)); - } - @OnClick(R.id.customStepperLayoutTheme) - public void onCustomStepperLayoutThemeWithTabs(View view){ - startActivity(new Intent(this, CustomStepperLayoutThemeActivity.class)); + setSupportActionBar(toolbar); + + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.setAdapter(new SampleItemAdapter()); + } + + private class SampleItemAdapter extends RecyclerView.Adapter { + + @NonNull + private List items; + + private SampleItemAdapter() { + this.items = Arrays.asList( + new SampleItem(getString(R.string.default_dots), getString(R.string.default_dots_description), DefaultDotsActivity.class), + new SampleItem(getString(R.string.styled_dots), getString(R.string.styled_dots_description), StyledDotsActivity.class), + new SampleItem(getString(R.string.themed_dots), getString(R.string.themed_dots_description), ThemedDotsActivity.class), + new SampleItem(getString(R.string.default_progress_bar), getString(R.string.default_progress_bar_description), DefaultProgressBarActivity.class), + new SampleItem(getString(R.string.styled_progress_bar), getString(R.string.styled_progress_bar_description), StyledProgressBarActivity.class), + new SampleItem(getString(R.string.default_tabs), getString(R.string.default_tabs_description), DefaultTabsActivity.class), + new SampleItem(getString(R.string.styled_tabs), getString(R.string.styled_tabs_description), StyledTabsActivity.class), + new SampleItem(getString(R.string.error_tabs), getString(R.string.error_tabs_description), ShowErrorTabActivity.class), + new SampleItem(getString(R.string.error_color_tabs), getString(R.string.error_color_tabs_description), ShowErrorCustomColorTabActivity.class), + new SampleItem(getString(R.string.error_back_tabs), getString(R.string.error_back_tabs_description), ShowErrorOnBackTabActivity.class), + new SampleItem(getString(R.string.combination), getString(R.string.combination_description), CombinationActivity.class), + new SampleItem(getString(R.string.custom_page_transformer), getString(R.string.custom_page_transformer_description), CustomPageTransformerActivity.class), + new SampleItem(getString(R.string.delayed_transition), getString(R.string.delayed_transition_description), DelayedTransitionStepperActivity.class), + new SampleItem(getString(R.string.stepper_feedback), getString(R.string.stepper_feedback_description), StepperFeedbackActivity.class), + new SampleItem(getString(R.string.custom_navigation_buttons), getString(R.string.custom_navigation_buttons_description), CustomNavigationButtonsActivity.class), + new SampleItem(getString(R.string.show_back_button), getString(R.string.show_back_button_description), ReturnButtonActivity.class), + new SampleItem(getString(R.string.no_fragments), getString(R.string.no_fragments_description), NoFragmentsActivity.class), + new SampleItem(getString(R.string.proceed_programmatically), getString(R.string.proceed_programmatically_description), ProceedProgrammaticallyActivity.class), + new SampleItem(getString(R.string.disabled_tab_navigation), getString(R.string.disabled_tab_navigation_description), DisabledTabNavigationActivity.class), + new SampleItem(getString(R.string.custom_stepperlayout_theme), getString(R.string.custom_stepperlayout_theme_description), CustomStepperLayoutThemeActivity.class) + ); + } + + @Override + public SampleItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_sample_info, parent, false); + return new SampleItemViewHolder(view); + } + + @Override + public void onBindViewHolder(SampleItemViewHolder holder, int position) { + holder.bindItem(items.get(position)); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemCount() { + return items.size(); + } + } + + static class SampleItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + + @Bind(R.id.item_sample_title) + TextView title; + + @Bind(R.id.item_sample_subtitle) + TextView subtitle; + + private SampleItem item; + + private SampleItemViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + itemView.setOnClickListener(this); + } + + private void bindItem(SampleItem item) { + this.item = item; + title.setText(item.title); + subtitle.setText(item.subtitle); + } + + @Override + public void onClick(View v) { + Context context = v.getContext(); + context.startActivity(new Intent(context, item.activityClass)); + } + } + + private static class SampleItem { + + private String title; + + private String subtitle; + + private Class activityClass; + + private SampleItem(String title, String subtitle, Class activityClass) { + this.title = title; + this.subtitle = subtitle; + this.activityClass = activityClass; + } } } diff --git a/sample/src/main/java/com/stepstone/stepper/sample/NoFragmentsActivity.java b/sample/src/main/java/com/stepstone/stepper/sample/NoFragmentsActivity.java index 9e7f7e8..f3ddd46 100644 --- a/sample/src/main/java/com/stepstone/stepper/sample/NoFragmentsActivity.java +++ b/sample/src/main/java/com/stepstone/stepper/sample/NoFragmentsActivity.java @@ -34,7 +34,7 @@ protected void onCreate(Bundle savedInstanceState) { public void onBackPressed() { final int currentStepPosition = mStepperLayout.getCurrentStepPosition(); if (currentStepPosition > 0) { - mStepperLayout.setCurrentStepPosition(currentStepPosition - 1); + mStepperLayout.onBackClicked(); } else { finish(); } diff --git a/sample/src/main/java/com/stepstone/stepper/sample/ProceedProgrammaticallyActivity.java b/sample/src/main/java/com/stepstone/stepper/sample/ProceedProgrammaticallyActivity.java index ab8a5e1..028f3f6 100644 --- a/sample/src/main/java/com/stepstone/stepper/sample/ProceedProgrammaticallyActivity.java +++ b/sample/src/main/java/com/stepstone/stepper/sample/ProceedProgrammaticallyActivity.java @@ -57,7 +57,7 @@ protected void onSaveInstanceState(Bundle outState) { public void onBackPressed() { final int currentStepPosition = mStepperLayout.getCurrentStepPosition(); if (currentStepPosition > 0) { - mStepperLayout.setCurrentStepPosition(currentStepPosition - 1); + mStepperLayout.onBackClicked(); } else { finish(); } diff --git a/sample/src/main/java/com/stepstone/stepper/sample/StepperFeedbackActivity.java b/sample/src/main/java/com/stepstone/stepper/sample/StepperFeedbackActivity.java new file mode 100644 index 0000000..97d6a4a --- /dev/null +++ b/sample/src/main/java/com/stepstone/stepper/sample/StepperFeedbackActivity.java @@ -0,0 +1,113 @@ +/* +Copyright 2017 StepStone Services + +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.stepstone.stepper.sample; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; + +import com.stepstone.stepper.StepperLayout; +import com.stepstone.stepper.internal.feedback.StepperFeedbackType; +import com.stepstone.stepper.sample.adapter.StepperFeedbackFragmentStepAdapter; + +import butterknife.Bind; +import butterknife.ButterKnife; + +public class StepperFeedbackActivity extends AppCompatActivity { + + private static final String CURRENT_STEP_POSITION_KEY = "position"; + + @Bind(R.id.stepperLayout) + StepperLayout mStepperLayout; + + private Menu mMenu; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setTitle("Stepper sample"); + + setContentView(R.layout.activity_stepper_feedback); + ButterKnife.bind(this); + int startingStepPosition = savedInstanceState != null ? savedInstanceState.getInt(CURRENT_STEP_POSITION_KEY) : 0; + mStepperLayout.setAdapter(new StepperFeedbackFragmentStepAdapter(getSupportFragmentManager(), this), startingStepPosition); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + outState.putInt(CURRENT_STEP_POSITION_KEY, mStepperLayout.getCurrentStepPosition()); + super.onSaveInstanceState(outState); + } + + @Override + public void onBackPressed() { + final int currentStepPosition = mStepperLayout.getCurrentStepPosition(); + if (currentStepPosition > 0) { + //do nothing when operation is in progress + if (!mStepperLayout.isInProgress()) { + mStepperLayout.onBackClicked(); + } + } else { + finish(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.activity_stepper_feedback, menu); + mMenu = menu; + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int itemId = item.getItemId(); + + if (!mStepperLayout.isInProgress() + && (itemId == R.id.menu_feedback_content || itemId == R.id.menu_feedback_tabs || itemId == R.id.menu_feedback_nav)) { + toggleItem(item); + boolean tabsEnabled = mMenu.findItem(R.id.menu_feedback_tabs).isChecked(); + boolean contentEnabled = mMenu.findItem(R.id.menu_feedback_content).isChecked(); + boolean disablingBottomNavigationEnabled = mMenu.findItem(R.id.menu_feedback_nav).isChecked(); + + int feedbackMask = 0; + if (tabsEnabled) { + feedbackMask |= StepperFeedbackType.TABS; + } + if (contentEnabled) { + feedbackMask |= StepperFeedbackType.CONTENT; + } + if (disablingBottomNavigationEnabled) { + feedbackMask |= StepperFeedbackType.DISABLED_BOTTOM_NAVIGATION; + } + if (feedbackMask == 0) { + feedbackMask = StepperFeedbackType.NONE; + } + mStepperLayout.setFeedbackType(feedbackMask); + return true; + } + + return super.onOptionsItemSelected(item); + } + + private void toggleItem(MenuItem item) { + item.setChecked(!item.isChecked()); + } + + +} diff --git a/sample/src/main/java/com/stepstone/stepper/sample/adapter/StepperFeedbackFragmentStepAdapter.java b/sample/src/main/java/com/stepstone/stepper/sample/adapter/StepperFeedbackFragmentStepAdapter.java new file mode 100644 index 0000000..cae1b4e --- /dev/null +++ b/sample/src/main/java/com/stepstone/stepper/sample/adapter/StepperFeedbackFragmentStepAdapter.java @@ -0,0 +1,37 @@ +package com.stepstone.stepper.sample.adapter; + +import android.content.Context; +import android.support.annotation.IntRange; +import android.support.annotation.NonNull; +import android.support.v4.app.FragmentManager; + +import com.stepstone.stepper.Step; +import com.stepstone.stepper.adapter.AbstractFragmentStepAdapter; +import com.stepstone.stepper.sample.R; +import com.stepstone.stepper.sample.step.fragment.StepperFeedbackStepFragment; +import com.stepstone.stepper.viewmodel.StepViewModel; + +public class StepperFeedbackFragmentStepAdapter extends AbstractFragmentStepAdapter { + + public StepperFeedbackFragmentStepAdapter(@NonNull FragmentManager fm, @NonNull Context context) { + super(fm, context); + } + + @Override + public Step createStep(int position) { + return StepperFeedbackStepFragment.newInstance(position); + } + + @NonNull + @Override + public StepViewModel getViewModel(@IntRange(from = 0) int position) { + return new StepViewModel.Builder(context) + .setTitle(R.string.tab_title) + .create(); + } + + @Override + public int getCount() { + return 3; + } +} \ No newline at end of file diff --git a/sample/src/main/java/com/stepstone/stepper/sample/step/fragment/StepperFeedbackStepFragment.java b/sample/src/main/java/com/stepstone/stepper/sample/step/fragment/StepperFeedbackStepFragment.java new file mode 100644 index 0000000..9fec69f --- /dev/null +++ b/sample/src/main/java/com/stepstone/stepper/sample/step/fragment/StepperFeedbackStepFragment.java @@ -0,0 +1,96 @@ +/* +Copyright 2017 StepStone Services + +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.stepstone.stepper.sample.step.fragment; + +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.UiThread; +import android.view.View; +import android.widget.TextView; + +import com.stepstone.stepper.BlockingStep; +import com.stepstone.stepper.StepperLayout; +import com.stepstone.stepper.VerificationError; +import com.stepstone.stepper.sample.R; + +import butterknife.Bind; + +public class StepperFeedbackStepFragment extends ButterKnifeFragment implements BlockingStep { + + private static final String STEP_POSITION = "step_position"; + + public static StepperFeedbackStepFragment newInstance(int stepPosition) { + StepperFeedbackStepFragment fragment = new StepperFeedbackStepFragment(); + Bundle arguments = new Bundle(); + arguments.putInt(STEP_POSITION, stepPosition); + fragment.setArguments(arguments); + return fragment; + } + + @Bind(R.id.stepContent) + TextView stepContent; + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + stepContent.setText("Step content #" + getArguments().getInt(STEP_POSITION) + "\n" + getString(R.string.lorem_ipsum)); + } + + @Override + public VerificationError verifyStep() { + return null; + } + + @Override + public void onSelected() { + } + + @Override + public void onError(@NonNull VerificationError error) { + } + + @Override + @UiThread + public void onNextClicked(final StepperLayout.OnNextClickedCallback callback) { + callback.getStepperLayout().showProgress("Operation in progress, please wait..."); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + callback.goToNextStep(); + callback.getStepperLayout().hideProgress(); + } + }, 2000L); + } + + @Override + public void onCompleteClicked(StepperLayout.OnCompleteClickedCallback callback) { + callback.complete(); + } + + @Override + @UiThread + public void onBackClicked(StepperLayout.OnBackClickedCallback callback) { + callback.goToPrevStep(); + } + + @Override + protected int getLayoutResId() { + return R.layout.fragment_step_stepper_feedback; + } +} diff --git a/sample/src/main/res/color/ms_disabling_button_text_color_selector.xml b/sample/src/main/res/color/ms_disabling_button_text_color_selector.xml new file mode 100644 index 0000000..0d57fbf --- /dev/null +++ b/sample/src/main/res/color/ms_disabling_button_text_color_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index a8f7741..40bf388 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -1,134 +1,47 @@ - + android:orientation="vertical" + tools:context=".MainActivity"> - - -