Java tutorial
/******************************************************************************* * Copyright (c) 2016 Francisco Gonzalez-Armijo Ridigos * <p> * 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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * 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.kuassivi.october.util; import com.kuassivi.october.annotation.PerActivity; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.AnimRes; import android.support.annotation.IdRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import java.util.List; import javax.inject.Inject; /** * Utility for a navigation flow through the application for both Activities and Fragments. * <p> * {@link Navigator} does not finish an Activity, you should do it manual or use custom flags. */ @PerActivity public class Navigator { /** * The current loaded Activity. */ @NonNull private Activity activity; /** * The current loaded Fragment if exists. */ @Nullable private Fragment fragment; /** * The current fragment animations provided. */ private int[] animations = new int[0]; /** * If loaded with Dagger, it injects automatically the current Activity in this Navigator * instance. * * @param activity The current loaded Activity. */ @Inject public Navigator(@NonNull Activity activity) { this.activity = activity; } /** * Starts a new Activity without params. * * @param clazz Activity class * @param <T> Activity type */ public <T extends Activity> void start(@NonNull Class<T> clazz) { start(activity, clazz, -1, null); } /** * Starts a new Activity with flags params. * * @param clazz Activity class * @param flags Activity flags * @param <T> Activity type */ public <T extends Activity> void start(@NonNull Class<T> clazz, int flags) { start(activity, clazz, flags, null); } /** * Starts a new Activity with {@link Bundle} params. * * @param clazz Activity class * @param extras Activity Bundle * @param <T> Activity type */ public <T extends Activity> void start(@NonNull Class<T> clazz, Bundle extras) { start(activity, clazz, -1, extras); } /** * Starts a new Activity with flags and {@link Bundle} params. * * @param clazz Activity class * @param flags Activity flags * @param extras Activity Bundle * @param <T> Activity type */ public <T extends Activity> void start(@NonNull Class<T> clazz, int flags, Bundle extras) { start(activity, clazz, flags, extras); } /** * Starts a new Activity with flags and {@link Bundle} params. * * @param activity Activity context * @param clazz Activity class * @param flags Activity flags * @param extras Activity Bundle * @param <T> Activity type */ public <T extends Activity> void start(@NonNull Context activity, @NonNull Class<T> clazz, int flags, Bundle extras) { if (activity != null) { Intent intent = new Intent(activity, clazz); if (flags > 0) { intent.addFlags(flags); } if (extras != null) { intent.putExtras(extras); } activity.startActivity(intent); } } /** * Sets the fragment transition animations. * * @param enter animation transition * @param exit animation transition */ public void setFragmentAnimations(@AnimRes int enter, @AnimRes int exit) { animations = new int[2]; animations[0] = enter; animations[1] = exit; } /** * Sets the fragment transition animations. * * @param enter animation transition * @param exit animation transition * @param popEnter animation transition * @param popExit animation transition */ public void setFragmentAnimations(@AnimRes int enter, @AnimRes int exit, @AnimRes int popEnter, @AnimRes int popExit) { animations = new int[4]; animations[0] = enter; animations[1] = exit; animations[2] = popEnter; animations[3] = popExit; } /** * Replaces a Fragment from the Fragment Container. * * @param resourceId id for the fragment container * @param fragment Fragment to be loaded in the container * @param animate whether it must be animated */ public void load(@IdRes int resourceId, @NonNull Fragment fragment, boolean animate) { FragmentTransaction fragmentTransaction = prepareFragment(resourceId, fragment, animate, null); if (fragmentTransaction != null) { fragmentTransaction.commit(); } } /** * Replaces a Fragment from the Fragment Container. * * @param resourceId id for the fragment container * @param fragment Fragment to be loaded in the container */ public void load(@IdRes int resourceId, @NonNull Fragment fragment) { FragmentTransaction fragmentTransaction = prepareFragment(resourceId, fragment, false, null); if (fragmentTransaction != null) { fragmentTransaction.commit(); } } /** * Prepares a Fragment to be committed. * * @param resourceId id for the fragment container * @param fragment Fragment to be loaded on the container * @param animate whether it must be animated * @param addToBackStack the BackStack string tag */ @Nullable private FragmentTransaction prepareFragment(@IdRes int resourceId, @NonNull Fragment fragment, boolean animate, String addToBackStack) { if (resourceId <= 0) { throw new RuntimeException("You must supply a resource id for the Fragment Container"); } if (!(activity instanceof FragmentActivity)) { throw new RuntimeException("The activity must inherits from FragmentActivity or AppCompatActivity"); } FragmentActivity fragmentActivity = (FragmentActivity) activity; FragmentManager fragmentManager = fragmentActivity.getSupportFragmentManager(); /* * The method isFinishing() prevents to load a Fragment * when the activity is going to be killed or destroyed. */ if ((fragmentManager == null || fragmentActivity.isFinishing())) { return null; } @SuppressLint("CommitTransaction") FragmentTransaction ft = fragmentManager.beginTransaction(); if (animate && animations.length > 0) { if (animations.length == 2) { ft.setCustomAnimations(animations[0], animations[1]); } else { ft.setCustomAnimations(animations[0], animations[1], animations[2], animations[3]); } } ft.addToBackStack(addToBackStack); this.fragment = fragment; return ft.replace(resourceId, fragment); } /** * Pops back to the specified Fragment class existing in the BackStack. * <p> * It will detach and destroy forwarded Fragments. * * @param clazz The fragment class */ public void popToFragment(@NonNull Class<?> clazz) { popToFragment(clazz, null, false); } /** * Pops back with some specific fragment arguments. * * @param clazz The fragment class * @param args Arguments to update into the Fragment * @param clearArguments if true clear the current Fragment arguments */ public void popToFragment(@NonNull Class<?> clazz, Bundle args, boolean clearArguments) { if (!(activity instanceof FragmentActivity)) { throw new RuntimeException("The activity must inherits from FragmentActivity or AppCompatActivity"); } FragmentActivity fragmentActivity = (FragmentActivity) activity; FragmentManager fragmentManager = fragmentActivity.getSupportFragmentManager(); /* * The method isFinishing() prevents to load a Fragment * when the activity is going to be killed or destroyed. */ if (fragmentManager == null || fragmentActivity.isFinishing()) { return; } List<Fragment> fragments = fragmentManager.getFragments(); int index; for (index = 0; index < fragments.size(); index++) { Fragment frag = fragments.get(index); if (frag.getClass().equals(clazz)) { if (args != null && args.size() > 0 && frag.getArguments() == null) { throw new IllegalArgumentException("Fragment constructor must call super()"); } if (frag.getArguments() != null && args != null) { if (clearArguments) { frag.getArguments().clear(); } frag.getArguments().putAll(args); } this.fragment = frag; popToStack(index + 1); break; } } } /** * Pops back to a specific BackStack position. * <p> * It will detach and destroy forwarded Fragments. * * @param index index in the back stack entry */ public void popToStack(int index) { if (!(activity instanceof FragmentActivity)) { throw new RuntimeException("The activity must inherits from FragmentActivity or AppCompatActivity"); } FragmentActivity fragmentActivity = (FragmentActivity) activity; FragmentManager fragmentManager = fragmentActivity.getSupportFragmentManager(); /* * The method isFinishing() prevents to load a Fragment * when the activity is going to be killed or destroyed. */ if (fragmentManager == null || fragmentActivity.isFinishing()) { return; } final int firstFragmentCount = 1; int backStackCount = fragmentManager.getBackStackEntryCount(); if (backStackCount <= firstFragmentCount) { fragmentActivity.finish(); } else { if (index > 0) { List<Fragment> fragments = fragmentManager.getFragments(); this.fragment = fragments.get(index - 1); fragmentManager.popBackStackImmediate(index, FragmentManager.POP_BACK_STACK_INCLUSIVE); } } } /** * Returns the current attached Fragment if it exists. * * @return the current attached Fragment */ @Nullable public Fragment getFragment() { return fragment; } }