hasPagePosition(@IntRange(from = 0) final int position) {
52 | return new ViewPagerPositionMatcher(is(position));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
15 |
20 |
21 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/material-stepper/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | 48dp
19 | 10dp
20 | 24dp
21 |
22 | 8dp
23 |
24 | 2dp
25 | 8dp
26 | 4dp
27 |
28 | 96dp
29 | 3dp
30 | 14sp
31 | 6dp
32 | 24dp
33 | 14sp
34 |
35 |
36 | 72dp
37 | 72dp
38 | 1dp
39 | 24dp
40 | 12sp
41 | 14sp
42 | 12sp
43 | 8dp
44 |
45 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/activity_stepper_feedback.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/espresso-material-stepper/src/main/java/com/stepstone/stepper/test/idling/CustomViewPagerListener.java:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.test.idling;
2 |
3 |
4 | import android.support.annotation.Nullable;
5 | import android.support.test.espresso.IdlingResource;
6 | import android.support.v4.view.ViewPager;
7 |
8 | /**
9 | * View pager listener that serves as Espresso's {@link IdlingResource} and notifies the
10 | * registered callback when the view pager gets to STATE_IDLE state.
11 | *
12 | * A copy of {@code android.support.test.espresso.contrib.ViewPagerActions.CustomViewPagerListener}.
13 | */
14 | public final class CustomViewPagerListener
15 | implements ViewPager.OnPageChangeListener, IdlingResource {
16 |
17 | private int mCurrState = ViewPager.SCROLL_STATE_IDLE;
18 |
19 | @Nullable
20 | private IdlingResource.ResourceCallback mCallback;
21 |
22 | public boolean mNeedsIdle = false;
23 |
24 | @Override
25 | public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
26 | mCallback = resourceCallback;
27 | }
28 |
29 | @Override
30 | public String getName() {
31 | return "View pager listener";
32 | }
33 |
34 | @Override
35 | public boolean isIdleNow() {
36 | if (!mNeedsIdle) {
37 | return true;
38 | } else {
39 | return mCurrState == ViewPager.SCROLL_STATE_IDLE;
40 | }
41 | }
42 |
43 | @Override
44 | public void onPageSelected(int position) {
45 | if (mCurrState == ViewPager.SCROLL_STATE_IDLE) {
46 | if (mCallback != null) {
47 | mCallback.onTransitionToIdle();
48 | }
49 | }
50 | }
51 |
52 | @Override
53 | public void onPageScrollStateChanged(int state) {
54 | mCurrState = state;
55 | if (mCurrState == ViewPager.SCROLL_STATE_IDLE) {
56 | if (mCallback != null) {
57 | mCallback.onTransitionToIdle();
58 | }
59 | }
60 | }
61 |
62 | @Override
63 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/stepstone/stepper/sample/adapter/SampleStepAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.sample.adapter
2 |
3 | import android.content.Context
4 | import android.support.annotation.IntRange
5 | import android.util.SparseArray
6 | import android.view.View
7 | import android.view.ViewGroup
8 |
9 | import com.stepstone.stepper.Step
10 | import com.stepstone.stepper.adapter.AbstractStepAdapter
11 | import com.stepstone.stepper.sample.R
12 | import com.stepstone.stepper.sample.step.view.StepViewSample
13 | import com.stepstone.stepper.viewmodel.StepViewModel
14 |
15 | /**
16 | * A naive implementation of [AbstractStepAdapter].
17 | * This does not keep the view data on rotation, etc.
18 | * It also keeps a reference to the created views.
19 | */
20 | class SampleStepAdapter(context: Context) : AbstractStepAdapter(context) {
21 |
22 | private val pages = SparseArray()
23 |
24 | override fun createStep(position: Int): StepViewSample {
25 | return StepViewSample(context)
26 | }
27 |
28 | override fun getViewModel(@IntRange(from = 0) position: Int): StepViewModel {
29 | return StepViewModel.Builder(context)
30 | .setTitle(R.string.tab_title)
31 | .create()
32 | }
33 |
34 | override fun getCount(): Int {
35 | return 3
36 | }
37 |
38 | override fun findStep(position: Int): Step? {
39 | return if (pages.size() > 0) pages.get(position) else null
40 | }
41 |
42 | override fun instantiateItem(container: ViewGroup, position: Int): View {
43 | var step: Step? = pages.get(position)
44 | if (step == null) {
45 | step = createStep(position)
46 | pages.put(position, step)
47 | }
48 |
49 | val stepView = step as View
50 | container.addView(stepView)
51 |
52 | return stepView
53 | }
54 |
55 | override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
56 | container.removeView(`object` as View)
57 | }
58 |
59 | override fun isViewFromObject(view: View, `object`: Any): Boolean {
60 | return view === `object`
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ms_button_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
21 | -
22 |
27 |
28 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/stepstone/stepper/sample/test/spoon/Chmod.java:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Square, Inc.
2 | package com.stepstone.stepper.sample.test.spoon;
3 |
4 | import android.annotation.SuppressLint;
5 | import android.os.Build;
6 |
7 | import java.io.File;
8 | import java.io.IOException;
9 |
10 | abstract class Chmod {
11 | private static final Chmod INSTANCE;
12 |
13 | static {
14 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
15 | INSTANCE = new Java6Chmod();
16 | } else {
17 | INSTANCE = new Java5Chmod();
18 | }
19 | }
20 |
21 | static void chmodPlusR(File file) {
22 | INSTANCE.plusR(file);
23 | }
24 |
25 | static void chmodPlusRWX(File file) {
26 | INSTANCE.plusRWX(file);
27 | }
28 |
29 | protected abstract void plusR(File file);
30 |
31 | protected abstract void plusRWX(File file);
32 |
33 | private static class Java5Chmod extends Chmod {
34 | @Override
35 | protected void plusR(File file) {
36 | try {
37 | Runtime.getRuntime().exec(new String[]{"chmod", "644", file.getAbsolutePath()});
38 | } catch (IOException e) {
39 | throw new RuntimeException(e);
40 | }
41 | }
42 |
43 | @Override
44 | protected void plusRWX(File file) {
45 | try {
46 | Runtime.getRuntime().exec(new String[]{"chmod", "777", file.getAbsolutePath()});
47 | } catch (IOException e) {
48 | throw new RuntimeException(e);
49 | }
50 | }
51 | }
52 |
53 | @SuppressWarnings("ResultOfMethodCallIgnored")
54 | @SuppressLint({"SetWorldReadable", "SetWorldWritable"})
55 | private static class Java6Chmod extends Chmod {
56 |
57 | @Override
58 | protected void plusR(File file) {
59 | file.setReadable(true, false);
60 | }
61 |
62 | @Override
63 | protected void plusRWX(File file) {
64 | file.setReadable(true, false);
65 | file.setWritable(true, false);
66 | file.setExecutable(true, false);
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/stepstone/stepper/sample/step/fragment/PassDataBetweenStepsSecondStepFragment.kt:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2017 StepStone Services
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package com.stepstone.stepper.sample.step.fragment
18 |
19 | import android.content.Context
20 | import android.widget.TextView
21 |
22 | import com.stepstone.stepper.Step
23 | import com.stepstone.stepper.VerificationError
24 | import com.stepstone.stepper.sample.DataManager
25 | import com.stepstone.stepper.sample.R
26 |
27 | import butterknife.BindView
28 |
29 | internal class PassDataBetweenStepsSecondStepFragment : ButterKnifeFragment(), Step {
30 |
31 | companion object {
32 |
33 | fun newInstance(): PassDataBetweenStepsSecondStepFragment {
34 | return PassDataBetweenStepsSecondStepFragment()
35 | }
36 | }
37 |
38 | @BindView(R.id.stepContent)
39 | lateinit var stepContent: TextView
40 |
41 | lateinit var dataManager: DataManager
42 |
43 | override fun onAttach(context: Context?) {
44 | super.onAttach(context)
45 | if (context is DataManager) {
46 | dataManager = context
47 | } else {
48 | throw IllegalStateException("Activity must implement DataManager interface!")
49 | }
50 | }
51 |
52 | override fun verifyStep(): VerificationError? {
53 | return null
54 | }
55 |
56 | override fun onSelected() {
57 | stepContent.text = "Entered text: ${dataManager.getData()}\n"
58 | }
59 |
60 | override fun onError(error: VerificationError) {}
61 |
62 | override val layoutResId: Int
63 | get() = R.layout.fragment_with_text_content
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/StepperFeedbackType.java:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.internal.feedback;
2 |
3 |
4 | import android.support.annotation.NonNull;
5 |
6 |
7 | /**
8 | * An interface to be implemented by all support stepper feedback types.
9 | * It contains methods which allow to show feedback for the duration of some executed operation.
10 | *
11 | * @author Piotr Zawadzki
12 | */
13 | public interface StepperFeedbackType {
14 |
15 | /**
16 | * No changes during operation.
17 | */
18 | int NONE = 1;
19 |
20 | /**
21 | * Show a progress message instead of the tabs during operation.
22 | * @see TabsStepperFeedbackType
23 | */
24 | int TABS = 1 << 1;
25 |
26 | /**
27 | * Shows a progress bar on top of the steps' content.
28 | * @see ContentProgressStepperFeedbackType
29 | */
30 | int CONTENT_PROGRESS = 1 << 2;
31 |
32 | /**
33 | * Disables the buttons in the bottom navigation during operation.
34 | * @see DisabledBottomNavigationStepperFeedbackType
35 | */
36 | int DISABLED_BOTTOM_NAVIGATION = 1 << 3;
37 |
38 | /**
39 | * Disables content interaction during operation i.e. stops step views from receiving touch events.
40 | * @see DisabledContentInteractionStepperFeedbackType
41 | */
42 | int DISABLED_CONTENT_INTERACTION = 1 << 4;
43 |
44 | /**
45 | * Partially fades the content out during operation.
46 | * @see ContentFadeStepperFeedbackType
47 | */
48 | int CONTENT_FADE = 1 << 5;
49 |
50 | /**
51 | * Shows a dimmed overlay over the content during operation.
52 | * @see ContentOverlayStepperFeedbackType
53 | */
54 | int CONTENT_OVERLAY = 1 << 6;
55 |
56 | int PROGRESS_ANIMATION_DURATION = 200;
57 |
58 | /**
59 | * Shows a progress indicator. This does not have to be a progress bar and it depends on chosen stepper feedback types.
60 | * @param progressMessage optional progress message if supported by the selected types
61 | */
62 | void showProgress(@NonNull String progressMessage);
63 |
64 | /**
65 | * Hides the progress indicator.
66 | */
67 | void hideProgress();
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/ContentFadeStepperFeedbackType.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2017 StepStone Services
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package com.stepstone.stepper.internal.feedback;
18 |
19 | import android.support.annotation.FloatRange;
20 | import android.support.annotation.NonNull;
21 | import android.support.annotation.RestrictTo;
22 | import android.view.View;
23 |
24 | import com.stepstone.stepper.R;
25 | import com.stepstone.stepper.StepperLayout;
26 |
27 | import static android.support.annotation.RestrictTo.Scope.LIBRARY;
28 | import static com.stepstone.stepper.internal.util.AnimationUtil.ALPHA_OPAQUE;
29 |
30 | /**
31 | * Feedback stepper type which partially fades the content out.
32 | */
33 | @RestrictTo(LIBRARY)
34 | public class ContentFadeStepperFeedbackType implements StepperFeedbackType {
35 |
36 | @NonNull
37 | private final View mPager;
38 |
39 | @FloatRange(from = 0.0f, to = 1.0f)
40 | private final float mFadeOutAlpha;
41 |
42 | public ContentFadeStepperFeedbackType(@NonNull StepperLayout stepperLayout) {
43 | mPager = stepperLayout.findViewById(R.id.ms_stepPager);
44 | mFadeOutAlpha = stepperLayout.getContentFadeAlpha();
45 | }
46 |
47 | @Override
48 | public void showProgress(@NonNull String progressMessage) {
49 | mPager.animate()
50 | .alpha(mFadeOutAlpha)
51 | .setDuration(PROGRESS_ANIMATION_DURATION);
52 | }
53 |
54 | @Override
55 | public void hideProgress() {
56 | mPager.animate()
57 | .alpha(ALPHA_OPAQUE)
58 | .setDuration(PROGRESS_ANIMATION_DURATION);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/StepperFeedbackTypeComposite.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2017 StepStone Services
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package com.stepstone.stepper.internal.feedback;
18 |
19 | import android.support.annotation.NonNull;
20 | import android.support.annotation.RestrictTo;
21 |
22 | import java.util.ArrayList;
23 | import java.util.List;
24 |
25 | import static android.support.annotation.RestrictTo.Scope.LIBRARY;
26 |
27 | /**
28 | * A stepper feedback type which is a composition of other feedback type, which allows to select only a group of feedback types.
29 | * See Stepper feedback section in https://material.io/guidelines/components/steppers.html#steppers-types-of-steppers
30 | */
31 | @RestrictTo(LIBRARY)
32 | public class StepperFeedbackTypeComposite implements StepperFeedbackType {
33 |
34 | @NonNull
35 | private List mChildren = new ArrayList<>();
36 |
37 | @Override
38 | public void showProgress(@NonNull String progressMessage) {
39 | for (StepperFeedbackType child : mChildren) {
40 | child.showProgress(progressMessage);
41 | }
42 | }
43 |
44 | @Override
45 | public void hideProgress() {
46 | for (StepperFeedbackType child : mChildren) {
47 | child.hideProgress();
48 | }
49 | }
50 |
51 | /**
52 | * Adds a child component to this composite.
53 | * @param component child to add
54 | */
55 | public void addComponent(@NonNull StepperFeedbackType component) {
56 | mChildren.add(component);
57 | }
58 |
59 | public List getChildComponents() {
60 | return mChildren;
61 | }
62 | }
--------------------------------------------------------------------------------
/material-stepper/src/main/java/com/stepstone/stepper/internal/type/StepperTypeFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016 StepStone Services
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package com.stepstone.stepper.internal.type;
18 |
19 | import android.support.annotation.RestrictTo;
20 | import android.util.Log;
21 |
22 | import com.stepstone.stepper.StepperLayout;
23 |
24 | import static android.support.annotation.RestrictTo.Scope.LIBRARY;
25 |
26 | /**
27 | * Factory class for creating stepper types.
28 | */
29 | @RestrictTo(LIBRARY)
30 | public class StepperTypeFactory {
31 |
32 | private static final String TAG = StepperTypeFactory.class.getSimpleName();
33 |
34 | /**
35 | * Creates a stepper type for provided arguments.
36 | * @param stepType step type, one of attrs - ms_stepperType
37 | * @param stepperLayout stepper layout to use with this stepper type
38 | * @return a stepper type
39 | */
40 | public static AbstractStepperType createType(int stepType, StepperLayout stepperLayout) {
41 | switch (stepType) {
42 | case AbstractStepperType.DOTS:
43 | return new DotsStepperType(stepperLayout);
44 | case AbstractStepperType.PROGRESS_BAR:
45 | return new ProgressBarStepperType(stepperLayout);
46 | case AbstractStepperType.TABS:
47 | return new TabsStepperType(stepperLayout);
48 | case AbstractStepperType.NONE:
49 | return new NoneStepperType(stepperLayout);
50 | default:
51 | Log.e(TAG, "Unsupported type: " + stepType);
52 | throw new IllegalArgumentException("Unsupported type: " + stepType);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/material-stepper/src/test/java/com/stepstone/stepper/internal/feedback/ContentFadeStepperFeedbackTypeTest.kt:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.internal.feedback
2 |
3 | import com.stepstone.stepper.R
4 | import com.stepstone.stepper.StepperLayout
5 | import com.stepstone.stepper.internal.util.AnimationUtil
6 | import com.stepstone.stepper.test.TYPE_TABS
7 | import com.stepstone.stepper.test.assertion.StepperLayoutAssert
8 | import com.stepstone.stepper.test.createStepperLayoutInActivity
9 | import com.stepstone.stepper.test.runner.StepperRobolectricTestRunner
10 | import org.junit.Before
11 | import org.junit.Test
12 | import org.junit.runner.RunWith
13 | import org.robolectric.Robolectric
14 |
15 | /**
16 | * @author Piotr Zawadzki
17 | */
18 | @RunWith(StepperRobolectricTestRunner::class)
19 | class ContentFadeStepperFeedbackTypeTest {
20 |
21 | companion object {
22 | val PROGRESS_MESSAGE = "loading..loading."
23 | val CONTENT_FADE_ALPHA = 0.1f
24 | }
25 |
26 | lateinit var stepperLayout: StepperLayout
27 |
28 | lateinit var feedbackType: ContentFadeStepperFeedbackType
29 |
30 | @Before
31 | fun setUp() {
32 | val attributeSet = Robolectric.buildAttributeSet()
33 | .addAttribute(R.attr.ms_stepperType, TYPE_TABS)
34 | .addAttribute(R.attr.ms_stepperFeedback_contentFadeAlpha, CONTENT_FADE_ALPHA.toString())
35 | .build()
36 | stepperLayout = createStepperLayoutInActivity(attributeSet)
37 | feedbackType = ContentFadeStepperFeedbackType(stepperLayout)
38 | }
39 |
40 | @Test
41 | fun `Should fade content out when showing progress`() {
42 | //when
43 | feedbackType.showProgress(PROGRESS_MESSAGE)
44 |
45 | //then
46 | assertStepperLayout()
47 | .pagerHasAlpha(CONTENT_FADE_ALPHA)
48 | }
49 |
50 | @Test
51 | fun `Should fade content in when hiding progress`() {
52 | //when
53 | feedbackType.hideProgress()
54 |
55 | //then
56 | assertStepperLayout()
57 | .pagerHasAlpha(AnimationUtil.ALPHA_OPAQUE)
58 | }
59 |
60 | fun assertStepperLayout(): StepperLayoutAssert {
61 | return StepperLayoutAssert.assertThat(stepperLayout)
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/stepstone/stepper/sample/DelayedTransitionStepperActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016 StepStone Services
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package com.stepstone.stepper.sample
18 |
19 | import android.os.Bundle
20 | import android.support.v7.app.AppCompatActivity
21 |
22 | import com.stepstone.stepper.StepperLayout
23 | import com.stepstone.stepper.sample.adapter.DelayedTransitionFragmentStepAdapter
24 |
25 | import butterknife.BindView
26 | import butterknife.ButterKnife
27 |
28 | class DelayedTransitionStepperActivity : AppCompatActivity() {
29 |
30 | companion object {
31 |
32 | private const val CURRENT_STEP_POSITION_KEY = "position"
33 | }
34 |
35 | @BindView(R.id.stepperLayout)
36 | lateinit var stepperLayout: StepperLayout
37 |
38 | override fun onCreate(savedInstanceState: Bundle?) {
39 | super.onCreate(savedInstanceState)
40 | title = "Stepper sample"
41 |
42 | setContentView(R.layout.activity_delayed_transition)
43 | ButterKnife.bind(this)
44 | val startingStepPosition = savedInstanceState?.getInt(CURRENT_STEP_POSITION_KEY) ?: 0
45 | stepperLayout.setAdapter(DelayedTransitionFragmentStepAdapter(supportFragmentManager, this), startingStepPosition)
46 | }
47 |
48 | override fun onSaveInstanceState(outState: Bundle) {
49 | outState.putInt(CURRENT_STEP_POSITION_KEY, stepperLayout.currentStepPosition)
50 | super.onSaveInstanceState(outState)
51 | }
52 |
53 | override fun onBackPressed() {
54 | val currentStepPosition = stepperLayout.currentStepPosition
55 | if (currentStepPosition > 0) {
56 | stepperLayout.onBackClicked()
57 | } else {
58 | finish()
59 | }
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/config/quality/quality.gradle:
--------------------------------------------------------------------------------
1 | /**
2 | * Set up Checkstyle, Findbugs and PMD to perform extensive code analysis.
3 | *
4 | * Gradle tasks added:
5 | * - checkstyle
6 | * - findbugs
7 | * - pmd
8 | *
9 | * The three tasks above are added as dependencies of the check task so running check will
10 | * run all of them.
11 | */
12 |
13 | apply plugin: 'checkstyle'
14 | apply plugin: 'findbugs'
15 | apply plugin: 'pmd'
16 |
17 | dependencies {
18 | checkstyle 'com.puppycrawl.tools:checkstyle:6.11.1'
19 | }
20 |
21 | def qualityConfigDir = "$project.rootDir/config/quality"
22 | def reportsDir = "$project.buildDir/reports"
23 |
24 | task checkstyle(type: Checkstyle, group: 'Verification', description: 'Runs code style checks') {
25 | configFile file("$qualityConfigDir/checkstyle/checkstyle-config.xml")
26 | source 'src/main'
27 | include '**/*.java'
28 |
29 | reports {
30 | xml.enabled = true
31 | xml {
32 | destination file("$reportsDir/checkstyle/checkstyle.xml")
33 | }
34 | }
35 |
36 | classpath = files( )
37 | }
38 |
39 | task findbugs(type: FindBugs,
40 | group: 'Verification',
41 | description: 'Inspect java bytecode for bugs',
42 | dependsOn: ['compileDebugSources','compileReleaseSources']) {
43 |
44 | ignoreFailures = false
45 | effort = "max"
46 | reportLevel = "high"
47 | excludeFilter = new File("$qualityConfigDir/findbugs/android-exclude-filter.xml")
48 | classes = files("$project.rootDir/material-stepper/build/intermediates/classes")
49 |
50 | source 'src/main'
51 | include '**/*.java'
52 | exclude '**/gen/**'
53 |
54 | reports {
55 | xml.enabled = false
56 | html.enabled = true
57 | xml {
58 | destination file("$reportsDir/findbugs/findbugs.xml")
59 | }
60 | }
61 |
62 | classpath = files()
63 | }
64 |
65 |
66 | task pmd(type: Pmd, group: 'Verification', description: 'Inspect sourcecode for bugs') {
67 | ruleSetFiles = files("$qualityConfigDir/pmd/pmd-ruleset.xml")
68 | ignoreFailures = false
69 | ruleSets = []
70 |
71 | source 'src/main'
72 | include '**/*.java'
73 | exclude '**/gen/**'
74 |
75 | reports {
76 | xml.enabled = true
77 | html.enabled = true
78 | xml {
79 | destination file("$reportsDir/pmd/pmd.xml")
80 | }
81 | html {
82 | destination file("$reportsDir/pmd/pmd.html")
83 | }
84 | }
85 | }
--------------------------------------------------------------------------------
/material-stepper/src/main/java/com/stepstone/stepper/internal/feedback/ContentOverlayStepperFeedbackType.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2017 StepStone Services
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package com.stepstone.stepper.internal.feedback;
18 |
19 | import android.support.annotation.NonNull;
20 | import android.support.annotation.RestrictTo;
21 | import android.view.View;
22 |
23 | import com.stepstone.stepper.R;
24 | import com.stepstone.stepper.StepperLayout;
25 |
26 | import static android.support.annotation.RestrictTo.Scope.LIBRARY;
27 | import static com.stepstone.stepper.internal.util.AnimationUtil.ALPHA_INVISIBLE;
28 | import static com.stepstone.stepper.internal.util.AnimationUtil.ALPHA_OPAQUE;
29 |
30 | /**
31 | * Feedback stepper type which shows a dimmed overlay over the content.
32 | */
33 | @RestrictTo(LIBRARY)
34 | public class ContentOverlayStepperFeedbackType implements StepperFeedbackType {
35 |
36 | @NonNull
37 | private final View mOverlayView;
38 |
39 | public ContentOverlayStepperFeedbackType(@NonNull StepperLayout stepperLayout) {
40 | mOverlayView = stepperLayout.findViewById(R.id.ms_stepPagerOverlay);
41 | mOverlayView.setVisibility(View.VISIBLE);
42 | mOverlayView.setAlpha(ALPHA_INVISIBLE);
43 | final int contentOverlayBackground = stepperLayout.getContentOverlayBackground();
44 | if (contentOverlayBackground != 0) {
45 | mOverlayView.setBackgroundResource(contentOverlayBackground);
46 | }
47 | }
48 |
49 | @Override
50 | public void showProgress(@NonNull String progressMessage) {
51 | mOverlayView.animate()
52 | .alpha(ALPHA_OPAQUE)
53 | .setDuration(PROGRESS_ANIMATION_DURATION);
54 | }
55 |
56 | @Override
57 | public void hideProgress() {
58 | mOverlayView.animate()
59 | .alpha(ALPHA_INVISIBLE)
60 | .setDuration(PROGRESS_ANIMATION_DURATION);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/material-stepper/src/test/java/com/stepstone/stepper/internal/feedback/StepperFeedbackTypeCompositeTest.kt:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.internal.feedback
2 |
3 | import com.nhaarman.mockito_kotlin.mock
4 | import com.nhaarman.mockito_kotlin.verify
5 | import com.stepstone.stepper.test.assertion.StepperFeedbackTypeCompositeAssert.Companion.assertThat
6 | import org.junit.Test
7 |
8 | /**
9 | * @author Piotr Zawadzki
10 | */
11 | class StepperFeedbackTypeCompositeTest {
12 |
13 | companion object {
14 | val PROGRESS_MESSAGE = "loading..."
15 | }
16 |
17 | val mockChild: StepperFeedbackType = mock {}
18 |
19 | val mockSecondChild: StepperFeedbackType = mock {}
20 |
21 | val composite: StepperFeedbackTypeComposite = StepperFeedbackTypeComposite()
22 |
23 | @Test
24 | fun `Should have no child components by default`() {
25 | assertThat(composite)
26 | .hasNoChildComponents()
27 | }
28 |
29 | @Test
30 | fun `Should be able add child components`() {
31 | //when
32 | addChildren()
33 |
34 | //then
35 | assertThat(composite)
36 | .hasXChildComponents(2)
37 | .hasChildComponent(mockChild)
38 | .hasChildComponent(mockSecondChild)
39 | }
40 |
41 | @Test
42 | fun `Should propagate showing progress to child components if available`() {
43 | //given
44 | addChildren()
45 |
46 | //when
47 | composite.showProgress(PROGRESS_MESSAGE)
48 |
49 | //then
50 | verify(mockChild).showProgress(PROGRESS_MESSAGE)
51 | verify(mockSecondChild).showProgress(PROGRESS_MESSAGE)
52 | }
53 |
54 | @Test
55 | fun `Should not propagate showing progress to child components if no children added`() {
56 | composite.showProgress(PROGRESS_MESSAGE)
57 | }
58 |
59 | @Test
60 | fun `Should propagate hide progress to child components if available`() {
61 | //given
62 | addChildren()
63 |
64 | //when
65 | composite.hideProgress()
66 |
67 | //then
68 | verify(mockChild).hideProgress()
69 | verify(mockSecondChild).hideProgress()
70 | }
71 |
72 | @Test
73 | fun `Should not propagate hide progress to child components if no children added`() {
74 | composite.hideProgress()
75 | }
76 |
77 | private fun addChildren() {
78 | composite.addComponent(mockChild)
79 | composite.addComponent(mockSecondChild)
80 | }
81 | }
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/stepstone/stepper/sample/AbstractActivityTest.java:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.sample;
2 |
3 | import android.app.Activity;
4 | import android.support.annotation.NonNull;
5 | import android.support.test.espresso.intent.rule.IntentsTestRule;
6 |
7 | import org.junit.Rule;
8 |
9 | import java.lang.reflect.ParameterizedType;
10 | import java.lang.reflect.Type;
11 | import java.util.Locale;
12 |
13 | /**
14 | * Base test class.
15 | *
16 | * @author Piotr Zawadzki
17 | */
18 | public abstract class AbstractActivityTest {
19 |
20 | @Rule
21 | public IntentsTestRule intentsTestRule = new IntentsTestRule<>(getStartingActivityClass());
22 |
23 | @NonNull
24 | protected String getScreenshotTag(int position, @NonNull String title) {
25 | return String.format(Locale.ENGLISH, "%02d", position) + ". " + title;
26 | }
27 |
28 | /**
29 | * Gets the class from the generic type.
30 | * Source
31 | *
32 | * @return the class from the generic type.
33 | */
34 | @SuppressWarnings("unchecked")
35 | private Class getStartingActivityClass() {
36 | return (Class)
37 | getParameterizedType()
38 | .getActualTypeArguments()[0];
39 | }
40 |
41 | private ParameterizedType getParameterizedType() {
42 | ParameterizedType parameterizedType;
43 | Class> clazz = getClass();
44 | while (clazz != null) {
45 | try {
46 | parameterizedType = getGenericSuperclass(clazz);
47 | Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
48 | if (actualTypeArguments.length > 0
49 | && Activity.class.isAssignableFrom((Class>) actualTypeArguments[0])) {
50 | return parameterizedType;
51 | } else {
52 | clazz = clazz.getSuperclass();
53 | }
54 | } catch (ClassCastException ex) {
55 | clazz = clazz.getSuperclass();
56 | }
57 | }
58 | throw new IllegalStateException("Activity test must contain a type argument which is the tested Activity class");
59 | }
60 |
61 | private ParameterizedType getGenericSuperclass(Class> aClass) {
62 | return (ParameterizedType) aClass.getGenericSuperclass();
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/bintrayv1.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.jfrog.bintray'
2 |
3 | version = libraryVersion
4 |
5 | if (project.hasProperty("android")) { // Android libraries
6 | task sourcesJar(type: Jar) {
7 | classifier = 'sources'
8 | from android.sourceSets.main.java.srcDirs
9 | }
10 |
11 | task javadoc(type: Javadoc) {
12 | source = android.sourceSets.main.java.srcDirs
13 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
14 | }
15 | } else { // Java libraries
16 | task sourcesJar(type: Jar, dependsOn: classes) {
17 | classifier = 'sources'
18 | from sourceSets.main.allSource
19 | }
20 | }
21 |
22 | task javadocJar(type: Jar, dependsOn: javadoc) {
23 | classifier = 'javadoc'
24 | from javadoc.destinationDir
25 | }
26 |
27 | artifacts {
28 | archives javadocJar
29 | archives sourcesJar
30 | }
31 |
32 | /*
33 | *
34 | * Copyright 2016 StepStone Services
35 | *
36 | * Licensed under the Apache License, Version 2.0 (the "License");
37 | * you may not use this file except in compliance with the License.
38 | * You may obtain a copy of the License at
39 | *
40 | * http://www.apache.org/licenses/LICENSE-2.0
41 | *
42 | * Unless required by applicable law or agreed to in writing, software
43 | * distributed under the License is distributed on an "AS IS" BASIS,
44 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
45 | * See the License for the specific language governing permissions and
46 | * limitations under the License.
47 | *
48 | */
49 |
50 | // Bintray
51 | Properties properties = new Properties()
52 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
53 |
54 | bintray {
55 | user = properties.getProperty("bintray.user")
56 | key = properties.getProperty("bintray.apikey")
57 |
58 | configurations = ['archives']
59 | pkg {
60 | repo = bintrayRepo
61 | name = bintrayName
62 | desc = libraryDescription
63 | websiteUrl = siteUrl
64 | vcsUrl = gitUrl
65 | licenses = allLicenses
66 | publish = true
67 | publicDownloadNumbers = true
68 | version {
69 | desc = libraryDescription
70 | /* gpg {
71 | sign = true //Determines whether to GPG sign the files. The default is false
72 | passphrase = properties.getProperty("bintray.gpg.password")
73 | //Optional. The passphrase for GPG signing'
74 | }*/
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/material-stepper/src/test/java/com/stepstone/stepper/test/assertion/StepViewModelAssert.kt:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.test.assertion
2 |
3 | import com.stepstone.stepper.viewmodel.StepViewModel
4 | import org.assertj.core.api.AbstractAssert
5 | import org.junit.Assert.assertEquals
6 |
7 | /**
8 | * @author Piotr Zawadzki
9 | */
10 | class StepViewModelAssert constructor(actual: StepViewModel) : AbstractAssert(actual, StepViewModelAssert::class.java) {
11 |
12 | companion object {
13 |
14 | fun assertThat(actual: StepViewModel): StepViewModelAssert {
15 | return StepViewModelAssert(actual)
16 | }
17 |
18 | }
19 |
20 | fun hasTitle(title: CharSequence?): StepViewModelAssert {
21 | assertEquals("Incorrect title!", title, actual.title)
22 | return this
23 | }
24 |
25 | fun hasSubtitle(subtitle: CharSequence?): StepViewModelAssert {
26 | assertEquals("Incorrect subtitle!", subtitle, actual.subtitle)
27 | return this
28 | }
29 |
30 | fun hasEndButtonLabel(endButtonLabel: CharSequence?): StepViewModelAssert {
31 | assertEquals("Incorrect label for the Complete/Next button!", endButtonLabel, actual.endButtonLabel)
32 | return this
33 | }
34 |
35 | fun hasBackButtonLabel(backButtonLabel: CharSequence?): StepViewModelAssert {
36 | assertEquals("Incorrect label for the Back button!", backButtonLabel, actual.backButtonLabel)
37 | return this
38 | }
39 |
40 | fun hasNextButtonEndDrawableResId(nextButtonEndDrawableResId: Int): StepViewModelAssert {
41 | assertEquals("Incorrect drawable resource id for the Next button!", nextButtonEndDrawableResId, actual.nextButtonEndDrawableResId)
42 | return this
43 | }
44 |
45 | fun hasBackButtonStartDrawableResId(backButtonStartDrawableResId: Int): StepViewModelAssert {
46 | assertEquals("Incorrect drawable resource id for the Back button!", backButtonStartDrawableResId, actual.backButtonStartDrawableResId)
47 | return this
48 | }
49 |
50 | fun hasEndButtonVisible(endButtonVisible: Boolean): StepViewModelAssert {
51 | assertEquals("Incorrect Next/Complete button visibility!", endButtonVisible, actual.isEndButtonVisible)
52 | return this
53 | }
54 |
55 | fun hasBackButtonVisible(backButtonVisible: Boolean): StepViewModelAssert {
56 | assertEquals("Incorrect Back button visibility!", backButtonVisible, actual.isBackButtonVisible)
57 | return this
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/material-stepper/src/main/java/com/stepstone/stepper/internal/widget/StepViewPager.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016 StepStone Services
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package com.stepstone.stepper.internal.widget;
18 |
19 | import android.content.Context;
20 | import android.support.annotation.RestrictTo;
21 | import android.support.v4.view.ViewPager;
22 | import android.util.AttributeSet;
23 | import android.view.MotionEvent;
24 |
25 | import com.stepstone.stepper.internal.widget.pagetransformer.StepPageTransformerFactory;
26 |
27 | import static android.support.annotation.RestrictTo.Scope.LIBRARY;
28 |
29 | /**
30 | * A non-swipeable viewpager with RTL support.
31 | * Source
32 | */
33 | @RestrictTo(LIBRARY)
34 | public class StepViewPager extends ViewPager {
35 |
36 | private boolean mBlockTouchEventsFromChildrenEnabled;
37 |
38 | public StepViewPager(Context context) {
39 | this(context, null);
40 | }
41 |
42 | public StepViewPager(Context context, AttributeSet attrs) {
43 | super(context, attrs);
44 | setPageTransformer(false, StepPageTransformerFactory.createPageTransformer(context));
45 | }
46 |
47 | @Override
48 | public boolean onInterceptTouchEvent(MotionEvent event) {
49 | // Never allow swiping to switch between pages
50 | return mBlockTouchEventsFromChildrenEnabled;
51 | }
52 |
53 | @Override
54 | public boolean onTouchEvent(MotionEvent event) {
55 | // Never allow swiping to switch between pages
56 | return mBlockTouchEventsFromChildrenEnabled;
57 | }
58 |
59 | /**
60 | * @param blockTouchEventsFromChildrenEnabled true if children should not receive touch events
61 | */
62 | public void setBlockTouchEventsFromChildrenEnabled(boolean blockTouchEventsFromChildrenEnabled) {
63 | this.mBlockTouchEventsFromChildrenEnabled = blockTouchEventsFromChildrenEnabled;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/stepstone/stepper/sample/NoFragmentsActivity.kt:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.sample
2 |
3 | import android.os.Bundle
4 | import android.support.v7.app.AppCompatActivity
5 | import android.view.View
6 | import android.widget.Toast
7 |
8 | import com.stepstone.stepper.StepperLayout
9 | import com.stepstone.stepper.VerificationError
10 | import com.stepstone.stepper.sample.adapter.SampleStepAdapter
11 |
12 | import butterknife.BindView
13 | import butterknife.ButterKnife
14 |
15 | class NoFragmentsActivity : AppCompatActivity(), StepperLayout.StepperListener, OnNavigationBarListener {
16 |
17 | companion object {
18 |
19 | private const val CURRENT_STEP_POSITION_KEY = "position"
20 | }
21 |
22 | @BindView(R.id.stepperLayout)
23 | lateinit var stepperLayout: StepperLayout
24 |
25 | override fun onCreate(savedInstanceState: Bundle?) {
26 | super.onCreate(savedInstanceState)
27 | setContentView(R.layout.activity_no_frag)
28 | ButterKnife.bind(this)
29 | val startingStepPosition = savedInstanceState?.getInt(CURRENT_STEP_POSITION_KEY) ?: 0
30 | val sampleStepAdapter = SampleStepAdapter(this)
31 | stepperLayout.setAdapter(sampleStepAdapter, startingStepPosition)
32 | stepperLayout.setListener(this)
33 | }
34 |
35 | override fun onBackPressed() {
36 | val currentStepPosition = stepperLayout.currentStepPosition
37 | if (currentStepPosition > 0) {
38 | stepperLayout.onBackClicked()
39 | } else {
40 | finish()
41 | }
42 | }
43 |
44 | override fun onSaveInstanceState(outState: Bundle) {
45 | outState.putInt(CURRENT_STEP_POSITION_KEY, stepperLayout.currentStepPosition)
46 | super.onSaveInstanceState(outState)
47 | }
48 |
49 | override fun onCompleted(completeButton: View) {
50 | Toast.makeText(this, "onCompleted!", Toast.LENGTH_SHORT).show()
51 | }
52 |
53 | override fun onError(verificationError: VerificationError) {
54 | Toast.makeText(this, "onError! -> " + verificationError.errorMessage, Toast.LENGTH_SHORT).show()
55 | }
56 |
57 | override fun onStepSelected(newStepPosition: Int) {
58 | Toast.makeText(this, "onStepSelected! -> " + newStepPosition, Toast.LENGTH_SHORT).show()
59 | }
60 |
61 | override fun onReturn() {
62 | finish()
63 | }
64 |
65 | override fun onChangeEndButtonsEnabled(enabled: Boolean) {
66 | stepperLayout.setNextButtonVerificationFailed(!enabled)
67 | stepperLayout.setCompleteButtonVerificationFailed(!enabled)
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/stepstone/stepper/sample/DefaultDotsActivityTest.java:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.sample;
2 |
3 | import android.support.test.filters.LargeTest;
4 |
5 | import com.stepstone.stepper.sample.test.action.SpoonScreenshotAction;
6 |
7 | import org.junit.Test;
8 |
9 | import static android.support.test.espresso.Espresso.onView;
10 | import static android.support.test.espresso.action.ViewActions.doubleClick;
11 | import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
12 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
13 | import static com.stepstone.stepper.sample.test.matcher.CommonMatchers.checkCompleteButtonShown;
14 | import static com.stepstone.stepper.sample.test.matcher.CommonMatchers.checkCurrentStepIs;
15 | import static com.stepstone.stepper.test.StepperNavigationActions.clickNext;
16 | import static org.hamcrest.Matchers.allOf;
17 |
18 | /**
19 | * Performs tests on a dotted stepper i.e. the one with {@code ms_stepperType="dots"}.
20 | *
21 | * @author Piotr Zawadzki
22 | */
23 | @LargeTest
24 | public class DefaultDotsActivityTest extends AbstractActivityTest {
25 |
26 | @Test
27 | public void shouldStayOnTheFirstStepWhenVerificationFails() {
28 | //when
29 | onView(withId(R.id.stepperLayout)).perform(clickNext());
30 |
31 | //then
32 | checkCurrentStepIs(0);
33 | SpoonScreenshotAction.perform(getScreenshotTag(1, "Verification failure test"));
34 | }
35 |
36 | @Test
37 | public void shouldGoToTheNextStepWhenVerificationSucceeds() {
38 | //given
39 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
40 |
41 | //when
42 | onView(withId(R.id.stepperLayout)).perform(clickNext());
43 |
44 | //then
45 | checkCurrentStepIs(1);
46 | SpoonScreenshotAction.perform(getScreenshotTag(2, "Verification success test"));
47 | }
48 |
49 | @Test
50 | public void shouldShowCompleteButtonOnTheLastStep() {
51 | //given
52 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
53 | onView(withId(R.id.stepperLayout)).perform(clickNext());
54 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
55 |
56 | //when
57 | onView(withId(R.id.stepperLayout)).perform(clickNext());
58 |
59 | //then
60 | checkCurrentStepIs(2);
61 | checkCompleteButtonShown();
62 | SpoonScreenshotAction.perform(getScreenshotTag(3, "Last step test"));
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/stepstone/stepper/sample/DefaultNoneActivityTest.java:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.sample;
2 |
3 | import android.support.test.filters.LargeTest;
4 |
5 | import com.stepstone.stepper.sample.test.action.SpoonScreenshotAction;
6 |
7 | import org.junit.Test;
8 |
9 | import static android.support.test.espresso.Espresso.onView;
10 | import static android.support.test.espresso.action.ViewActions.doubleClick;
11 | import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
12 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
13 | import static com.stepstone.stepper.sample.test.matcher.CommonMatchers.checkCompleteButtonShown;
14 | import static com.stepstone.stepper.sample.test.matcher.CommonMatchers.checkCurrentStepIs;
15 | import static com.stepstone.stepper.test.StepperNavigationActions.clickNext;
16 | import static org.hamcrest.Matchers.allOf;
17 |
18 | /**
19 | * Performs tests on a 'none' stepper i.e. the one with {@code ms_stepperType="none"}.
20 | *
21 | * @author Piotr Zawadzki
22 | */
23 | @LargeTest
24 | public class DefaultNoneActivityTest extends AbstractActivityTest {
25 |
26 | @Test
27 | public void shouldStayOnTheFirstStepWhenVerificationFails() {
28 | //when
29 | onView(withId(R.id.stepperLayout)).perform(clickNext());
30 |
31 | //then
32 | checkCurrentStepIs(0);
33 | SpoonScreenshotAction.perform(getScreenshotTag(1, "Verification failure test"));
34 | }
35 |
36 | @Test
37 | public void shouldGoToTheNextStepWhenVerificationSucceeds() {
38 | //given
39 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
40 |
41 | //when
42 | onView(withId(R.id.stepperLayout)).perform(clickNext());
43 |
44 | //then
45 | checkCurrentStepIs(1);
46 | SpoonScreenshotAction.perform(getScreenshotTag(2, "Verification success test"));
47 | }
48 |
49 | @Test
50 | public void shouldShowCompleteButtonOnTheLastStep() {
51 | //given
52 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
53 | onView(withId(R.id.stepperLayout)).perform(clickNext());
54 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
55 |
56 | //when
57 | onView(withId(R.id.stepperLayout)).perform(clickNext());
58 |
59 | //then
60 | checkCurrentStepIs(2);
61 | checkCompleteButtonShown();
62 | SpoonScreenshotAction.perform(getScreenshotTag(3, "Last step test"));
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/stepstone/stepper/sample/step/view/StepViewSample.kt:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.sample.step.view
2 |
3 | import android.content.Context
4 | import android.text.Html
5 | import android.view.LayoutInflater
6 | import android.view.animation.AnimationUtils
7 | import android.widget.Button
8 | import android.widget.FrameLayout
9 | import com.stepstone.stepper.Step
10 | import com.stepstone.stepper.VerificationError
11 | import com.stepstone.stepper.sample.OnNavigationBarListener
12 | import com.stepstone.stepper.sample.R
13 |
14 | /**
15 | * Created by leonardo on 18/12/16.
16 | */
17 |
18 | class StepViewSample(context: Context) : FrameLayout(context), Step {
19 |
20 | companion object {
21 |
22 | private const val TAP_THRESHOLD = 2
23 | }
24 |
25 | private var i = 0
26 |
27 | private var onNavigationBarListener: OnNavigationBarListener? = null
28 |
29 | private var button: Button? = null
30 |
31 | init {
32 | init(context)
33 | }
34 |
35 | override fun onAttachedToWindow() {
36 | super.onAttachedToWindow()
37 | val c = context
38 | if (c is OnNavigationBarListener) {
39 | this.onNavigationBarListener = c
40 | }
41 | }
42 |
43 | override fun onDetachedFromWindow() {
44 | super.onDetachedFromWindow()
45 | this.onNavigationBarListener = null
46 | }
47 |
48 | @Suppress("DEPRECATION")
49 | private fun init(context: Context) {
50 | val v = LayoutInflater.from(context).inflate(R.layout.fragment_step, this, true)
51 | button = v.findViewById(R.id.button) as Button
52 |
53 | updateNavigationBar()
54 |
55 | button = v.findViewById(R.id.button) as Button
56 | button?.text = Html.fromHtml("Taps: $i")
57 | button?.setOnClickListener {
58 | button?.text = Html.fromHtml("Taps: ${++i}")
59 | updateNavigationBar()
60 | }
61 | }
62 |
63 | private val isAboveThreshold: Boolean
64 | get() = i >= TAP_THRESHOLD
65 |
66 | override fun verifyStep(): VerificationError? {
67 | return if (isAboveThreshold) null else VerificationError("Click ${TAP_THRESHOLD - i} more times!")
68 | }
69 |
70 | private fun updateNavigationBar() {
71 | onNavigationBarListener?.onChangeEndButtonsEnabled(isAboveThreshold)
72 | }
73 |
74 | override fun onSelected() {
75 | updateNavigationBar()
76 | }
77 |
78 | override fun onError(error: VerificationError) {
79 | button?.startAnimation(AnimationUtils.loadAnimation(context, R.anim.shake_error))
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/stepstone/stepper/sample/StyledDotsActivityTest.java:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.sample;
2 |
3 | import android.support.test.filters.LargeTest;
4 |
5 | import com.stepstone.stepper.sample.test.action.SpoonScreenshotAction;
6 |
7 | import org.junit.Test;
8 |
9 | import static android.support.test.espresso.Espresso.onView;
10 | import static android.support.test.espresso.action.ViewActions.doubleClick;
11 | import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
12 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
13 | import static com.stepstone.stepper.sample.test.matcher.CommonMatchers.checkCompleteButtonShown;
14 | import static com.stepstone.stepper.sample.test.matcher.CommonMatchers.checkCurrentStepIs;
15 | import static com.stepstone.stepper.test.StepperNavigationActions.clickNext;
16 | import static org.hamcrest.Matchers.allOf;
17 |
18 | /**
19 | * Performs tests on a styled dotted stepper i.e. the one with {@code ms_stepperType="dots"}.
20 | *
21 | * @author Piotr Zawadzki
22 | */
23 | @LargeTest
24 | public class StyledDotsActivityTest extends AbstractActivityTest {
25 |
26 | @Test
27 | public void shouldStayOnTheFirstStepWhenVerificationFails() {
28 | //when
29 | onView(withId(R.id.stepperLayout)).perform(clickNext());
30 |
31 | //then
32 | checkCurrentStepIs(0);
33 | SpoonScreenshotAction.perform(getScreenshotTag(1, "Verification failure test"));
34 | }
35 |
36 | @Test
37 | public void shouldGoToTheNextStepWhenVerificationSucceeds() {
38 | //given
39 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
40 |
41 | //when
42 | onView(withId(R.id.stepperLayout)).perform(clickNext());
43 |
44 | //then
45 | checkCurrentStepIs(1);
46 | SpoonScreenshotAction.perform(getScreenshotTag(2, "Verification success test"));
47 | }
48 |
49 | @Test
50 | public void shouldShowCompleteButtonOnTheLastStep() {
51 | //given
52 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
53 | onView(withId(R.id.stepperLayout)).perform(clickNext());
54 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
55 |
56 | //when
57 | onView(withId(R.id.stepperLayout)).perform(clickNext());
58 |
59 | //then
60 | checkCurrentStepIs(2);
61 | checkCompleteButtonShown();
62 | SpoonScreenshotAction.perform(getScreenshotTag(3, "Last step test"));
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/stepstone/stepper/sample/ThemedDotsActivityTest.java:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.sample;
2 |
3 | import android.support.test.filters.LargeTest;
4 |
5 | import com.stepstone.stepper.sample.test.action.SpoonScreenshotAction;
6 |
7 | import org.junit.Test;
8 |
9 | import static android.support.test.espresso.Espresso.onView;
10 | import static android.support.test.espresso.action.ViewActions.doubleClick;
11 | import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
12 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
13 | import static com.stepstone.stepper.sample.test.matcher.CommonMatchers.checkCompleteButtonShown;
14 | import static com.stepstone.stepper.sample.test.matcher.CommonMatchers.checkCurrentStepIs;
15 | import static com.stepstone.stepper.test.StepperNavigationActions.clickNext;
16 | import static org.hamcrest.Matchers.allOf;
17 |
18 | /**
19 | * Performs tests on a themed dotted stepper i.e. the one with {@code ms_stepperType="dots"}.
20 | *
21 | * @author Piotr Zawadzki
22 | */
23 | @LargeTest
24 | public class ThemedDotsActivityTest extends AbstractActivityTest {
25 |
26 | @Test
27 | public void shouldStayOnTheFirstStepWhenVerificationFails() {
28 | //when
29 | onView(withId(R.id.stepperLayout)).perform(clickNext());
30 |
31 | //then
32 | checkCurrentStepIs(0);
33 | SpoonScreenshotAction.perform(getScreenshotTag(1, "Verification failure test"));
34 | }
35 |
36 | @Test
37 | public void shouldGoToTheNextStepWhenVerificationSucceeds() {
38 | //given
39 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
40 |
41 | //when
42 | onView(withId(R.id.stepperLayout)).perform(clickNext());
43 |
44 | //then
45 | checkCurrentStepIs(1);
46 | SpoonScreenshotAction.perform(getScreenshotTag(2, "Verification success test"));
47 | }
48 |
49 | @Test
50 | public void shouldShowCompleteButtonOnTheLastStep() {
51 | //given
52 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
53 | onView(withId(R.id.stepperLayout)).perform(clickNext());
54 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
55 |
56 | //when
57 | onView(withId(R.id.stepperLayout)).perform(clickNext());
58 |
59 | //then
60 | checkCurrentStepIs(2);
61 | checkCompleteButtonShown();
62 | SpoonScreenshotAction.perform(getScreenshotTag(3, "Last step test"));
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/material-stepper/src/main/java/com/stepstone/stepper/internal/type/ProgressBarStepperType.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2016 StepStone Services
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package com.stepstone.stepper.internal.type;
18 |
19 | import android.support.annotation.NonNull;
20 | import android.support.annotation.RestrictTo;
21 | import android.view.View;
22 |
23 | import com.stepstone.stepper.R;
24 | import com.stepstone.stepper.StepperLayout;
25 | import com.stepstone.stepper.adapter.StepAdapter;
26 | import com.stepstone.stepper.internal.widget.ColorableProgressBar;
27 |
28 | import static android.support.annotation.RestrictTo.Scope.LIBRARY;
29 |
30 | /**
31 | * Stepper type which displays a mobile step progress bar.
32 | */
33 | @RestrictTo(LIBRARY)
34 | public class ProgressBarStepperType extends AbstractStepperType {
35 |
36 | private final ColorableProgressBar mProgressBar;
37 |
38 | public ProgressBarStepperType(StepperLayout stepperLayout) {
39 | super(stepperLayout);
40 | mProgressBar = (ColorableProgressBar) stepperLayout.findViewById(R.id.ms_stepProgressBar);
41 | mProgressBar.setProgressColor(getSelectedColor());
42 | mProgressBar.setProgressBackgroundColor(getUnselectedColor());
43 | if (stepperLayout.isInEditMode()) {
44 | mProgressBar.setVisibility(View.VISIBLE);
45 | mProgressBar.setProgressCompat(1, false);
46 | mProgressBar.setMax(3);
47 | }
48 | }
49 |
50 | /**
51 | * {@inheritDoc}
52 | */
53 | @Override
54 | public void onStepSelected(int newStepPosition, boolean userTriggeredChange) {
55 | mProgressBar.setProgressCompat(newStepPosition + 1, userTriggeredChange);
56 | }
57 |
58 | /**
59 | * {@inheritDoc}
60 | */
61 | @Override
62 | public void onNewAdapter(@NonNull StepAdapter stepAdapter) {
63 | super.onNewAdapter(stepAdapter);
64 | final int stepCount = stepAdapter.getCount();
65 | mProgressBar.setMax(stepAdapter.getCount());
66 | mProgressBar.setVisibility(stepCount > 1 ? View.VISIBLE : View.GONE);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/stepstone/stepper/sample/DefaultProgressBarActivityTest.java:
--------------------------------------------------------------------------------
1 | package com.stepstone.stepper.sample;
2 |
3 | import android.support.test.filters.LargeTest;
4 |
5 | import com.stepstone.stepper.sample.test.action.SpoonScreenshotAction;
6 |
7 | import org.junit.Test;
8 |
9 | import static android.support.test.espresso.Espresso.onView;
10 | import static android.support.test.espresso.action.ViewActions.doubleClick;
11 | import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
12 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
13 | import static com.stepstone.stepper.sample.test.matcher.CommonMatchers.checkCompleteButtonShown;
14 | import static com.stepstone.stepper.sample.test.matcher.CommonMatchers.checkCurrentStepIs;
15 | import static com.stepstone.stepper.test.StepperNavigationActions.clickNext;
16 | import static org.hamcrest.Matchers.allOf;
17 |
18 | /**
19 | * Performs tests on a progress bar stepper i.e. the one with {@code ms_stepperType="progress_bar"}.
20 | *
21 | * @author Piotr Zawadzki
22 | */
23 | @LargeTest
24 | public class DefaultProgressBarActivityTest extends AbstractActivityTest {
25 |
26 | @Test
27 | public void shouldStayOnTheFirstStepWhenVerificationFails() {
28 | //when
29 | onView(withId(R.id.stepperLayout)).perform(clickNext());
30 |
31 | //then
32 | checkCurrentStepIs(0);
33 | SpoonScreenshotAction.perform(getScreenshotTag(1, "Verification failure test"));
34 | }
35 |
36 | @Test
37 | public void shouldGoToTheNextStepWhenVerificationSucceeds() {
38 | //given
39 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
40 |
41 | //when
42 | onView(withId(R.id.stepperLayout)).perform(clickNext());
43 |
44 | //then
45 | checkCurrentStepIs(1);
46 | SpoonScreenshotAction.perform(getScreenshotTag(2, "Verification success test"));
47 | }
48 |
49 | @Test
50 | public void shouldShowCompleteButtonOnTheLastStep() {
51 | //given
52 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
53 | onView(withId(R.id.stepperLayout)).perform(clickNext());
54 | onView(allOf(withId(R.id.button), isCompletelyDisplayed())).perform(doubleClick());
55 |
56 | //when
57 | onView(withId(R.id.stepperLayout)).perform(clickNext());
58 |
59 | //then
60 | checkCurrentStepIs(2);
61 | checkCompleteButtonShown();
62 | SpoonScreenshotAction.perform(getScreenshotTag(3, "Last step test"));
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------