diff --git a/library/src/main/java/com/heinrichreimersoftware/materialintro/app/IntroActivity.java b/library/src/main/java/com/heinrichreimersoftware/materialintro/app/IntroActivity.java index 9d2827f..f506772 100644 --- a/library/src/main/java/com/heinrichreimersoftware/materialintro/app/IntroActivity.java +++ b/library/src/main/java/com/heinrichreimersoftware/materialintro/app/IntroActivity.java @@ -39,6 +39,11 @@ @SuppressLint("Registered") public class IntroActivity extends AppCompatActivity { + private static final String KEY_CURRENT_ITEM = + "com.heinrichreimersoftware.materialintro.app.IntroActivity.KEY_CURRENT_ITEM"; + + private static final String KEY_SLIDES = + "com.heinrichreimersoftware.materialintro.app.IntroActivity.KEY_SLIDES"; private final ArgbEvaluator evaluator = new ArgbEvaluator(); private FrameLayout frame; @@ -68,6 +73,10 @@ protected void onCreate(Bundle savedInstanceState) { setFullscreenFlags(fullscreen); } + if (savedInstanceState != null && savedInstanceState.containsKey(KEY_CURRENT_ITEM)) { + position = savedInstanceState.getInt(KEY_CURRENT_ITEM, position); + } + setContentView(R.layout.activity_intro); findViews(); } @@ -82,12 +91,12 @@ protected void onPostCreate(Bundle savedInstanceState) { updateButtonSkipDrawable(); frame.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { updateViewPositions(); v.removeOnLayoutChangeListener(this); } }); - lockSwipeIfNeeded(); } @Override @@ -96,6 +105,12 @@ protected void onResume() { setFullscreenFlags(fullscreen); } + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(KEY_CURRENT_ITEM, pager.getCurrentItem()); + } + @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { @@ -133,10 +148,12 @@ private void findViews(){ buttonNext = findViewById(R.id.mi_button_next); buttonSkip = findViewById(R.id.mi_button_skip); - adapter = new SlideAdapter(getSupportFragmentManager()); + FragmentManager fragmentManager = getSupportFragmentManager(); + adapter = new SlideAdapter(fragmentManager); pager.setAdapter(adapter); pager.addOnPageChangeListener(listener); + pager.setCurrentItem(position); pagerIndicator.setViewPager(pager); @@ -533,7 +550,7 @@ protected boolean isEmpty() { } protected int getCount() { - return adapter.getCount(); + return adapter == null ? 0 : adapter.getCount(); } protected int lastIndexOfSlide(Object object) { @@ -571,6 +588,11 @@ public void onPageScrolled(int position, float positionOffset, int positionOffse IntroActivity.this.position = position; IntroActivity.this.positionOffset = positionOffset; + //Lock while scrolling a slide near its edges to lock (uncommon) multiple page swipes + if (Math.abs(positionOffset) < 0.1f) { + lockSwipeIfNeeded(); + } + updateBackground(); updateViewPositions(); updateFullscreen(); diff --git a/library/src/main/java/com/heinrichreimersoftware/materialintro/app/SlideFragment.java b/library/src/main/java/com/heinrichreimersoftware/materialintro/app/SlideFragment.java index 2091b41..36081ba 100644 --- a/library/src/main/java/com/heinrichreimersoftware/materialintro/app/SlideFragment.java +++ b/library/src/main/java/com/heinrichreimersoftware/materialintro/app/SlideFragment.java @@ -12,7 +12,7 @@ public boolean canGoBackward() { return true; } - protected void updateNavigation() { + public void updateNavigation() { if (getActivity() instanceof IntroActivity) { ((IntroActivity) getActivity()).lockSwipeIfNeeded(); } diff --git a/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/FragmentSlide.java b/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/FragmentSlide.java index 49c9ee1..089d1aa 100644 --- a/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/FragmentSlide.java +++ b/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/FragmentSlide.java @@ -5,6 +5,7 @@ import android.support.annotation.ColorRes; import android.support.annotation.LayoutRes; import android.support.annotation.StyleRes; +import android.support.v4.app.Fragment; import android.support.v7.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; @@ -12,9 +13,9 @@ import com.heinrichreimersoftware.materialintro.app.SlideFragment; -public class FragmentSlide extends Slide{ +public class FragmentSlide extends RestorableSlide { - private final android.support.v4.app.Fragment fragment; + private Fragment fragment; @ColorRes private final int background; @ColorRes @@ -33,10 +34,15 @@ private FragmentSlide(Builder builder) { } @Override - public android.support.v4.app.Fragment getFragment() { + public Fragment getFragment() { return fragment; } + @Override + public void setFragment(Fragment fragment) { + this.fragment = fragment; + } + @Override public int getBackground() { return background; @@ -49,22 +55,22 @@ public int getBackgroundDark() { @Override public boolean canGoForward() { - if (fragment instanceof SlideFragment) { - return ((SlideFragment) fragment).canGoForward(); + if (getFragment() instanceof SlideFragment) { + return ((SlideFragment) getFragment()).canGoForward(); } return canGoForward; } @Override public boolean canGoBackward() { - if (fragment instanceof SlideFragment) { - return ((SlideFragment) fragment).canGoBackward(); + if (getFragment() instanceof SlideFragment) { + return ((SlideFragment) getFragment()).canGoBackward(); } return canGoBackward; } public static class Builder{ - private android.support.v4.app.Fragment fragment; + private Fragment fragment; @ColorRes private int background; @ColorRes @@ -74,18 +80,18 @@ public static class Builder{ private boolean canGoBackward = true; - public Builder fragment(android.support.v4.app.Fragment fragment) { + public Builder fragment(Fragment fragment) { this.fragment = fragment; return this; } public Builder fragment(@LayoutRes int layoutRes, @StyleRes int themeRes) { - this.fragment = Fragment.newInstance(layoutRes, themeRes); + this.fragment = FragmentSlideFragment.newInstance(layoutRes, themeRes); return this; } public Builder fragment(@LayoutRes int layoutRes) { - this.fragment = Fragment.newInstance(layoutRes); + this.fragment = FragmentSlideFragment.newInstance(layoutRes); return this; } @@ -116,17 +122,17 @@ public FragmentSlide build(){ } } - public static class Fragment extends android.support.v4.app.Fragment{ + public static class FragmentSlideFragment extends Fragment { private static final String ARGUMENT_LAYOUT_RES = "com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_LAYOUT_RES"; private static final String ARGUMENT_THEME_RES = "com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_THEME_RES"; - public Fragment() { + public FragmentSlideFragment() { } - public static Fragment newInstance(@LayoutRes int layoutRes, @StyleRes int themeRes) { - Fragment fragment = new Fragment(); + public static FragmentSlideFragment newInstance(@LayoutRes int layoutRes, @StyleRes int themeRes) { + FragmentSlideFragment fragment = new FragmentSlideFragment(); Bundle arguments = new Bundle(); arguments.putInt(ARGUMENT_LAYOUT_RES, layoutRes); @@ -136,7 +142,7 @@ public static Fragment newInstance(@LayoutRes int layoutRes, @StyleRes int theme return fragment; } - public static Fragment newInstance(@LayoutRes int layoutRes) { + public static FragmentSlideFragment newInstance(@LayoutRes int layoutRes) { return newInstance(layoutRes, 0); } diff --git a/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/RestorableSlide.java b/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/RestorableSlide.java new file mode 100644 index 0000000..5f79638 --- /dev/null +++ b/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/RestorableSlide.java @@ -0,0 +1,7 @@ +package com.heinrichreimersoftware.materialintro.slide; + +import android.support.v4.app.Fragment; + +public abstract class RestorableSlide extends Slide { + public abstract void setFragment(Fragment fragment); +} diff --git a/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/SimpleSlide.java b/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/SimpleSlide.java index 7eb736b..5d520bc 100644 --- a/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/SimpleSlide.java +++ b/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/SimpleSlide.java @@ -1,5 +1,6 @@ package com.heinrichreimersoftware.materialintro.slide; +import android.content.Context; import android.content.pm.PackageManager; import android.content.res.ColorStateList; import android.os.Bundle; @@ -8,12 +9,13 @@ import android.support.annotation.DrawableRes; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.v4.app.ActivityCompat; +import android.support.v4.app.Fragment; import android.support.v4.content.ContextCompat; import android.support.v4.graphics.ColorUtils; import android.support.v4.view.ViewCompat; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -27,11 +29,11 @@ import java.util.ArrayList; import java.util.List; -public class SimpleSlide extends Slide { +public class SimpleSlide extends RestorableSlide { - private static final int PERMISSIONS_REQUEST_CODE = 34; + private static final int DEFAULT_PERMISSIONS_REQUEST_CODE = 34; //Random number - private final Fragment fragment; + private Fragment fragment; @ColorRes private final int background; @@ -44,9 +46,10 @@ public class SimpleSlide extends Slide { private final boolean canGoBackward; private SimpleSlide(Builder builder) { - fragment = Fragment.newInstance(builder.title, builder.titleRes, builder.description, - builder.descriptionRes, builder.imageRes, builder.backgroundRes, - builder.backgroundDarkRes, builder.layoutRes, builder.permissions); + fragment = SimpleSlideFragment.newInstance(builder.title, builder.titleRes, + builder.description, builder.descriptionRes, builder.imageRes, + builder.backgroundRes, builder.backgroundDarkRes, builder.layoutRes, + builder.permissions, builder.permissionsRequestCode); background = builder.backgroundRes; backgroundDark = builder.backgroundDarkRes; canGoForward = builder.canGoForward; @@ -58,6 +61,11 @@ public Fragment getFragment() { return fragment; } + @Override + public void setFragment(Fragment fragment) { + this.fragment = fragment; + } + @Override public int getBackground() { return background; @@ -70,8 +78,8 @@ public int getBackgroundDark() { @Override public boolean canGoForward() { - if (fragment != null) { - return canGoForward && fragment.canGoForward(); + if (fragment instanceof SlideFragment) { + return canGoForward && ((SlideFragment) fragment).canGoForward(); } return canGoForward; } @@ -84,32 +92,24 @@ public boolean canGoBackward() { public static class Builder { @ColorRes private int backgroundRes = 0; - @ColorRes private int backgroundDarkRes = 0; - private String title = null; - @StringRes private int titleRes = 0; - private String description = null; - @StringRes private int descriptionRes = 0; - @DrawableRes private int imageRes = 0; - @LayoutRes private int layoutRes = R.layout.fragment_simple_slide; - private boolean canGoForward = true; - private boolean canGoBackward = true; - private String[] permissions = null; + private int permissionsRequestCode = DEFAULT_PERMISSIONS_REQUEST_CODE; + public Builder background(@ColorRes int backgroundRes) { this.backgroundRes = backgroundRes; return this; @@ -180,52 +180,52 @@ public Builder permission(String permission) { return this; } + public Builder permissionsRequestCode(int permissionsRequestCode) { + this.permissionsRequestCode = permissionsRequestCode; + return this; + } + public SimpleSlide build() { return new SimpleSlide(this); } } - public static class Fragment extends SlideFragment { + public static class SimpleSlideFragment extends SlideFragment { private static final String ARGUMENT_TITLE = "com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_TITLE"; - private static final String ARGUMENT_TITLE_RES = "com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_TITLE_RES"; - private static final String ARGUMENT_DESCRIPTION = "com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_DESCRIPTION"; - private static final String ARGUMENT_DESCRIPTION_RES = "com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_DESCRIPTION_RES"; - private static final String ARGUMENT_IMAGE_RES = "com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_IMAGE_RES"; - private static final String ARGUMENT_BACKGROUND_RES = "com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_BACKGROUND_RES"; - private static final String ARGUMENT_BACKGROUND_DARK_RES = "com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_BACKGROUND_DARK_RES"; - private static final String ARGUMENT_LAYOUT_RES = "com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_LAYOUT_RES"; - private static final String ARGUMENT_PERMISSIONS = "com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_PERMISSIONS"; + private static final String ARGUMENT_PERMISSIONS_REQUEST_CODE = + "com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_PERMISSIONS_REQUEST_CODE"; + private Button buttonGrantPermissions; - private boolean permissionsGranted = true; + private String[] permissions = null; - public Fragment() { + public SimpleSlideFragment() { } - public static Fragment newInstance(String title, @StringRes int titleRes, + public static SimpleSlideFragment newInstance(String title, @StringRes int titleRes, String description, @StringRes int descriptionRes, @DrawableRes int imageRes, @ColorRes int background, @ColorRes int backgroundDark, @LayoutRes int layout, - String[] permissions) { - Fragment fragment = new Fragment(); + String[] permissions, int permissionsRequestCode) { + SimpleSlideFragment fragment = new SimpleSlideFragment(); Bundle arguments = new Bundle(); arguments.putString(ARGUMENT_TITLE, title); @@ -237,13 +237,34 @@ public static Fragment newInstance(String title, @StringRes int titleRes, arguments.putInt(ARGUMENT_BACKGROUND_DARK_RES, backgroundDark); arguments.putInt(ARGUMENT_LAYOUT_RES, layout); arguments.putStringArray(ARGUMENT_PERMISSIONS, permissions); + arguments.putInt(ARGUMENT_PERMISSIONS_REQUEST_CODE, permissionsRequestCode); fragment.setArguments(arguments); - fragment.permissionsGranted = permissions == null || permissions.length <= 0; - return fragment; } + @Override + public void onAttach(Context context) { + super.onAttach(context); + Bundle arguments = getArguments(); + String[] newPermissions = arguments.getStringArray(ARGUMENT_PERMISSIONS); + updatePermissions(newPermissions != null ? newPermissions : permissions); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + } + + @Override + public void onResume() { + super.onResume(); + //Lock scroll for the case that users revoke accepted permission settings while in the intro + updatePermissions(); + updateNavigation(); + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -264,7 +285,6 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, int imageRes = arguments.getInt(ARGUMENT_IMAGE_RES, 0); int backgroundRes = arguments.getInt(ARGUMENT_BACKGROUND_RES, 0); int backgroundDarkRes = arguments.getInt(ARGUMENT_BACKGROUND_DARK_RES, 0); - String[] permissions = arguments.getStringArray(ARGUMENT_PERMISSIONS); //Title if (titleView != null) { @@ -333,23 +353,26 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, buttonGrantPermissions.setTextColor(textColorPrimary); } - updatePermissions(permissions); - return fragment; } - private void updatePermissions(String[] permissions) { - Log.d("SimpleSlide", "Updating permissions..."); - if (permissions != null) { + private void updatePermissions() { + updatePermissions(permissions); + } + + private synchronized void updatePermissions(@Nullable String[] newPermissions) { + if (newPermissions != null) { final List permissionsNotGranted = new ArrayList<>(); - for (String permission : permissions) { - if (ContextCompat.checkSelfPermission(getActivity(), permission) != - PackageManager.PERMISSION_GRANTED) { + for (String permission : newPermissions) { + if (ContextCompat.checkSelfPermission(getActivity(), + permission) != PackageManager.PERMISSION_GRANTED) { permissionsNotGranted.add(permission); } } if (permissionsNotGranted.size() > 0) { + this.permissions = permissionsNotGranted.toArray( + new String[permissionsNotGranted.size()]); if (buttonGrantPermissions != null) { buttonGrantPermissions.setVisibility(View.VISIBLE); buttonGrantPermissions.setText(getResources().getQuantityText( @@ -357,40 +380,52 @@ private void updatePermissions(String[] permissions) { buttonGrantPermissions.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - ActivityCompat.requestPermissions(getActivity(), - permissionsNotGranted.toArray( - new String[permissionsNotGranted.size()]), - PERMISSIONS_REQUEST_CODE); + int permissionsRequestCode = getArguments() == null ? DEFAULT_PERMISSIONS_REQUEST_CODE : + getArguments().getInt(ARGUMENT_PERMISSIONS_REQUEST_CODE, + DEFAULT_PERMISSIONS_REQUEST_CODE); + ActivityCompat.requestPermissions(getActivity(), permissions, + permissionsRequestCode); } }); } } else { + this.permissions = null; if (buttonGrantPermissions != null) { buttonGrantPermissions.setVisibility(View.GONE); } - permissionsGranted = true; - updateNavigation(); } } else { + this.permissions = null; if (buttonGrantPermissions != null) { buttonGrantPermissions.setVisibility(View.GONE); } - permissionsGranted = true; - updateNavigation(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - if (requestCode == PERMISSIONS_REQUEST_CODE) { - updatePermissions(permissions); + int permissionsRequestCode = getArguments() == null ? DEFAULT_PERMISSIONS_REQUEST_CODE : + getArguments().getInt(ARGUMENT_PERMISSIONS_REQUEST_CODE, + DEFAULT_PERMISSIONS_REQUEST_CODE); + if (requestCode == permissionsRequestCode) { + updatePermissions(); + updateNavigation(); } } @Override - public boolean canGoForward() { - return permissionsGranted; + public synchronized boolean canGoForward() { + final List permissionsNotGranted = new ArrayList<>(); + if (permissions != null) { + for (String permission : permissions) { + if (getActivity() == null || ContextCompat.checkSelfPermission(getActivity(), + permission) != PackageManager.PERMISSION_GRANTED) { + permissionsNotGranted.add(permission); + } + } + } + return permissionsNotGranted.size() <= 0; } } } diff --git a/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/Slide.java b/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/Slide.java index f62e71e..21901c0 100644 --- a/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/Slide.java +++ b/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/Slide.java @@ -4,12 +4,14 @@ import android.support.v4.app.Fragment; public abstract class Slide { + public abstract Fragment getFragment(); + @ColorRes public abstract int getBackground(); @ColorRes public int getBackgroundDark(){ - return 0; + return android.R.color.black; } public boolean canGoForward() { diff --git a/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/SlideAdapter.java b/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/SlideAdapter.java index a5368ec..0092bbb 100644 --- a/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/SlideAdapter.java +++ b/library/src/main/java/com/heinrichreimersoftware/materialintro/slide/SlideAdapter.java @@ -5,6 +5,9 @@ import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; +import android.view.ViewGroup; + +import com.heinrichreimersoftware.materialintro.app.SlideFragment; import java.util.ArrayList; import java.util.Collection; @@ -71,6 +74,20 @@ public Fragment getItem(int position) { return data.get(position).getFragment(); } + @Override + public Object instantiateItem(ViewGroup container, int position) { + Fragment instantiatedFragment = (Fragment) super.instantiateItem(container, position); + Slide slide = data.get(position); + if (slide instanceof RestorableSlide) { + //Load old fragment from fragment manager + ((RestorableSlide) slide).setFragment(instantiatedFragment); + data.set(position, slide); + if (instantiatedFragment instanceof SlideFragment) + ((SlideFragment) instantiatedFragment).updateNavigation(); + } + return instantiatedFragment; + } + @ColorRes public int getBackground(int position) { return data.get(position).getBackground(); diff --git a/library/src/main/java/com/heinrichreimersoftware/materialintro/view/SwipeBlockableViewPager.java b/library/src/main/java/com/heinrichreimersoftware/materialintro/view/SwipeBlockableViewPager.java index 95f2fce..1f0e2fc 100644 --- a/library/src/main/java/com/heinrichreimersoftware/materialintro/view/SwipeBlockableViewPager.java +++ b/library/src/main/java/com/heinrichreimersoftware/materialintro/view/SwipeBlockableViewPager.java @@ -25,9 +25,9 @@ public class SwipeBlockableViewPager extends ViewPager { private float initialX; - private boolean swipeRightEnabled = false; + private boolean swipeRightEnabled = true; - private boolean swipeLeftEnabled = false; + private boolean swipeLeftEnabled = true; public SwipeBlockableViewPager(Context context) { super(context);