From 5f6152050b6148851acbeddbfd1db6ea36495ad6 Mon Sep 17 00:00:00 2001 From: Denis Karmyshakov Date: Mon, 19 Mar 2018 12:29:25 +0300 Subject: [PATCH] Navigation refactor, DelegationListAdapter fix --- .../components/adapters/DelegatesManager.kt | 11 +- .../adapters/DelegationListAdapter.kt | 10 +- .../navigation/FragmentNavigation.java | 433 ------------------ .../navigation/FragmentNavigation.kt | 261 +++++++++++ .../fragments/ViewControllerFragment.java | 22 +- .../navigation/fragments/ViewFragment.java | 5 +- .../ViewControllerNavigation.kt | 84 ++-- 7 files changed, 318 insertions(+), 508 deletions(-) delete mode 100644 src/main/java/ru/touchin/roboswag/components/navigation/FragmentNavigation.java create mode 100644 src/main/java/ru/touchin/roboswag/components/navigation/FragmentNavigation.kt diff --git a/src/main/java/ru/touchin/roboswag/components/adapters/DelegatesManager.kt b/src/main/java/ru/touchin/roboswag/components/adapters/DelegatesManager.kt index bb70eb3..1da1498 100644 --- a/src/main/java/ru/touchin/roboswag/components/adapters/DelegatesManager.kt +++ b/src/main/java/ru/touchin/roboswag/components/adapters/DelegatesManager.kt @@ -18,7 +18,7 @@ class DelegatesManager { return delegate.itemViewType } } - throw IllegalStateException("Delegate not found") + throw IllegalStateException("Delegate not found for adapterPosition: $adapterPosition") } fun getItemId(items: List<*>, adapterPosition: Int, collectionPosition: Int): Long { @@ -33,19 +33,12 @@ class DelegatesManager { delegate.onBindViewHolder(holder, items, adapterPosition, collectionPosition, payloads) } - /** - * Adds [ItemAdapterDelegate] to adapter. - * - * @param delegate Delegate to add. - */ - fun addDelegate(delegate: ItemAdapterDelegate<*, *>) = delegates.put(delegate.itemViewType, delegate) - /** * Adds [PositionAdapterDelegate] to adapter. * * @param delegate Delegate to add. */ - fun addDelegate(delegate: PositionAdapterDelegate<*>) = delegates.put(delegate.itemViewType, delegate) + fun addDelegate(delegate: AdapterDelegate<*>) = delegates.put(delegate.itemViewType, delegate) /** * Removes [AdapterDelegate] from adapter. diff --git a/src/main/java/ru/touchin/roboswag/components/adapters/DelegationListAdapter.kt b/src/main/java/ru/touchin/roboswag/components/adapters/DelegationListAdapter.kt index 24e56fb..edf4707 100644 --- a/src/main/java/ru/touchin/roboswag/components/adapters/DelegationListAdapter.kt +++ b/src/main/java/ru/touchin/roboswag/components/adapters/DelegationListAdapter.kt @@ -41,18 +41,18 @@ open class DelegationListAdapter(diffCallback: DiffUtil.ItemCallback) = delegatesManager.addDelegate(delegate) + fun addDelegate(delegate: AdapterDelegate<*>) = delegatesManager.addDelegate(delegate) /** - * Adds [PositionAdapterDelegate] to adapter. + * Removes [AdapterDelegate] from adapter. * - * @param delegate Delegate to add. + * @param delegate Delegate to remove. */ - fun addDelegate(delegate: PositionAdapterDelegate<*>) = delegatesManager.addDelegate(delegate) + fun removeDelegate(delegate: AdapterDelegate<*>) = delegatesManager.removeDelegate(delegate) /** * Submits a new list to be diffed, and displayed. diff --git a/src/main/java/ru/touchin/roboswag/components/navigation/FragmentNavigation.java b/src/main/java/ru/touchin/roboswag/components/navigation/FragmentNavigation.java deleted file mode 100644 index dad6ee8..0000000 --- a/src/main/java/ru/touchin/roboswag/components/navigation/FragmentNavigation.java +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) - * - * This file is part of RoboSwag library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package ru.touchin.roboswag.components.navigation; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.os.Bundle; -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.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.view.MenuItem; - -import io.reactivex.functions.Function; -import ru.touchin.roboswag.core.log.Lc; - -/** - * Created by Gavriil Sitnikov on 07/03/2016. - * Navigation which is controlling fragments on activity using {@link android.support.v4.app.FragmentManager}. - * Basically there are 4 main actions to add fragments to activity. - * 1) {@link #setInitial} means to set fragment on top and remove all previously added fragments from stack; - * 2) {@link #push} means to simply add fragment on top of the stack; - * 3) {@link #setAsTop} means to push fragment on top of the stack with specific {@link #TOP_FRAGMENT_TAG_MARK} tag. - * It is useful to realize up/back navigation: if {@link #up()} method will be called then stack will go to nearest fragment with TOP tag. - * If {@link #back()} method will be called then stack will go to previous fragment. - * Usually such logic using to set as top fragments from sidebar and show hamburger when some of them appeared; - * 4) {@link #pushForResult} means to push fragment with target fragment. It is also adding {@link #WITH_TARGET_FRAGMENT_TAG_MARK} tag. - * Also if such up/back navigation logic is not OK then {@link #backTo(Function)} method could be used with any condition to back to. - * In that case in any stack-change method it is allowed to setup fragment transactions. - */ -public class FragmentNavigation { - - protected static final String TOP_FRAGMENT_TAG_MARK = "TOP_FRAGMENT"; - protected static final String WITH_TARGET_FRAGMENT_TAG_MARK = "FRAGMENT_WITH_TARGET"; - - @NonNull - private final Context context; - @NonNull - private final FragmentManager fragmentManager; - @IdRes - private final int containerViewId; - - public FragmentNavigation(@NonNull final Context context, @NonNull final FragmentManager fragmentManager, @IdRes final int containerViewId) { - this.context = context; - this.fragmentManager = fragmentManager; - this.containerViewId = containerViewId; - } - - /** - * Returns {@link Context} that is using to instantiate fragments. - * - * @return {@link Context}. - */ - @NonNull - public Context getContext() { - return context; - } - - /** - * Returns {@link FragmentManager} using for navigation. - * - * @return {@link FragmentManager}. - */ - @NonNull - public FragmentManager getFragmentManager() { - return fragmentManager; - } - - /** - * Returns if last fragment in stack is top (added by {@link #setAsTop} or {@link #setInitial}) like fragment from sidebar menu. - * - * @return True if last fragment on stack has TOP_FRAGMENT_TAG_MARK. - */ - public boolean isCurrentFragmentTop() { - if (fragmentManager.getBackStackEntryCount() == 0) { - return true; - } - - final String topFragmentTag = fragmentManager - .getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1) - .getName(); - return topFragmentTag != null && topFragmentTag.contains(TOP_FRAGMENT_TAG_MARK); - } - - /** - * Allowed to react on {@link android.app.Activity}'s menu item selection. - * - * @param item Selected menu item; - * @return True if reaction fired. - */ - @SuppressLint("InlinedApi") - //InlinedApi: it is ok as android.R.id.home contains in latest SDK - public boolean onOptionsItemSelected(@NonNull final MenuItem item) { - return item.getItemId() == android.R.id.home && back(); - } - - /** - * Base method which is adding fragment to stack. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param targetFragment Target fragment to be set as {@link Fragment#getTargetFragment()} of instantiated {@link Fragment}; - * @param addToStack Flag to add this transaction to the back stack; - * @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment}; - * @param backStackTag Tag of {@link Fragment} in back stack; - * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. - */ - @SuppressLint("CommitTransaction") - //CommitTransaction: it is ok as we could setup transaction before commit - protected void addToStack(@NonNull final Class fragmentClass, - @Nullable final Fragment targetFragment, - final boolean addToStack, - @Nullable final Bundle args, - @Nullable final String backStackTag, - @Nullable final Function transactionSetup) { - if (fragmentManager.isDestroyed()) { - Lc.assertion("FragmentManager is destroyed"); - return; - } - - final Fragment fragment = Fragment.instantiate(context, fragmentClass.getName(), args); - if (targetFragment != null) { - if (fragmentManager != targetFragment.getFragmentManager()) { - Lc.assertion("FragmentManager of target is differ then of creating fragment. Target will be lost after restoring activity. " - + targetFragment.getFragmentManager() + " != " + fragmentManager); - } - fragment.setTargetFragment(targetFragment, 0); - } - - final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction() - .replace(containerViewId, fragment, null); - if (addToStack) { - fragmentTransaction.addToBackStack(backStackTag); - } - if (fragmentManager.getBackStackEntryCount() != 0) { - fragmentTransaction.setTransition(getDefaultTransition()); - } - if (transactionSetup != null) { - try { - transactionSetup.apply(fragmentTransaction).commit(); - } catch (final Exception exception) { - Lc.assertion(exception); - fragmentTransaction.commit(); - } - } else { - fragmentTransaction.commit(); - } - } - - /** - * Returns default transition animation. - * - * @return {@link FragmentTransaction#TRANSIT_FRAGMENT_OPEN}. - */ - protected int getDefaultTransition() { - return FragmentTransaction.TRANSIT_FRAGMENT_OPEN; - } - - /** - * Simply calls {@link FragmentManager#popBackStack()}. - * - * @return True if it have back to some entry in stack. - */ - public boolean back() { - if (fragmentManager.getBackStackEntryCount() > 1) { - fragmentManager.popBackStack(); - return true; - } - return false; - } - - /** - * Backs to fragment which back stack's entry satisfy to specific condition. - * - * @param condition Condition of back stack entry to be satisfied; - * @return True if it have back to some entry in stack. - */ - public boolean backTo(@NonNull final Function condition) { - final int stackSize = fragmentManager.getBackStackEntryCount(); - Integer id = null; - try { - for (int i = stackSize - 2; i >= 0; i--) { - final FragmentManager.BackStackEntry backStackEntry = fragmentManager.getBackStackEntryAt(i); - id = backStackEntry.getId(); - if (condition.apply(backStackEntry)) { - break; - } - } - } catch (final Exception exception) { - Lc.assertion(exception); - return false; - } - if (id != null) { - fragmentManager.popBackStack(id, 0); - return true; - } - return false; - } - - /** - * Backs to fragment with specific {@link #TOP_FRAGMENT_TAG_MARK} tag. - * This tag is adding if fragment added to stack via {@link #setInitial} or {@link #setAsTop(Class)} methods. - * It can be used to create simple up/back navigation. - * - * @return True if it have back to some entry in stack. - */ - @SuppressWarnings("PMD.ShortMethodName") - //ShortMethodName: it is ok because method name is good! - public boolean up() { - return backTo(backStackEntry -> - backStackEntry.getName() != null && backStackEntry.getName().endsWith(TOP_FRAGMENT_TAG_MARK)); - } - - /** - * Pushes {@link Fragment} on top of stack. - * - * @param fragmentClass Class of {@link Fragment} to instantiate. - */ - public void push(@NonNull final Class fragmentClass) { - addToStack(fragmentClass, null, true, null, null, null); - } - - /** - * Pushes {@link Fragment} on top of stack with specific arguments. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment}. - */ - public void push(@NonNull final Class fragmentClass, - @NonNull final Bundle args) { - addToStack(fragmentClass, null, true, args, null, null); - } - - /** - * Pushes {@link Fragment} on top of stack with specific transaction setup. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. - */ - public void push(@NonNull final Class fragmentClass, - @NonNull final Function transactionSetup) { - addToStack(fragmentClass, null, true, null, null, transactionSetup); - } - - /** - * Pushes {@link Fragment} on top of stack with specific arguments and transaction setup. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment}; - * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. - */ - public void push(@NonNull final Class fragmentClass, - @Nullable final Bundle args, - @Nullable final Function transactionSetup) { - addToStack(fragmentClass, null, true, args, null, transactionSetup); - } - - /** - * Pushes {@link Fragment} on top of stack with specific target fragment. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param targetFragment Target fragment to be set as {@link Fragment#getTargetFragment()} of instantiated {@link Fragment}. - */ - public void pushForResult(@NonNull final Class fragmentClass, - @NonNull final Fragment targetFragment) { - addToStack(fragmentClass, targetFragment, true, null, fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, null); - } - - /** - * Pushes {@link Fragment} on top of stack with specific target fragment and arguments. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param targetFragment Target fragment to be set as {@link Fragment#getTargetFragment()} of instantiated {@link Fragment}; - * @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment}. - */ - public void pushForResult(@NonNull final Class fragmentClass, - @NonNull final Fragment targetFragment, - @NonNull final Bundle args) { - addToStack(fragmentClass, targetFragment, true, args, fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, null); - } - - /** - * Pushes {@link Fragment} on top of stack with specific target fragment and transaction setup. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param targetFragment Target fragment to be set as {@link Fragment#getTargetFragment()} of instantiated {@link Fragment}; - * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. - */ - public void pushForResult(@NonNull final Class fragmentClass, - @NonNull final Fragment targetFragment, - @NonNull final Function transactionSetup) { - addToStack(fragmentClass, targetFragment, true, null, fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, transactionSetup); - } - - /** - * Pushes {@link Fragment} on top of stack with specific target fragment, arguments and transaction setup. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param targetFragment Target fragment to be set as {@link Fragment#getTargetFragment()} of instantiated {@link Fragment}; - * @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment}; - * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. - */ - public void pushForResult(@NonNull final Class fragmentClass, - @NonNull final Fragment targetFragment, - @Nullable final Bundle args, - @Nullable final Function transactionSetup) { - addToStack(fragmentClass, targetFragment, true, args, fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, transactionSetup); - } - - /** - * Pushes {@link Fragment} on top of stack with {@link #TOP_FRAGMENT_TAG_MARK} tag used for simple up/back navigation. - * - * @param fragmentClass Class of {@link Fragment} to instantiate. - */ - public void setAsTop(@NonNull final Class fragmentClass) { - addToStack(fragmentClass, null, true, null, fragmentClass.getName() + ';' + TOP_FRAGMENT_TAG_MARK, null); - } - - /** - * Pushes {@link Fragment} on top of stack with specific arguments and with {@link #TOP_FRAGMENT_TAG_MARK} tag used for simple up/back navigation. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment}. - */ - public void setAsTop(@NonNull final Class fragmentClass, - @NonNull final Bundle args) { - addToStack(fragmentClass, null, true, args, fragmentClass.getName() + ';' + TOP_FRAGMENT_TAG_MARK, null); - } - - /** - * Pushes {@link Fragment} on top of stack with specific transaction setup - * and with {@link #TOP_FRAGMENT_TAG_MARK} tag used for simple up/back navigation. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. - */ - public void setAsTop(@NonNull final Class fragmentClass, - @NonNull final Function transactionSetup) { - addToStack(fragmentClass, null, true, null, fragmentClass.getName() + ';' + TOP_FRAGMENT_TAG_MARK, transactionSetup); - } - - /** - * Pushes {@link Fragment} on top of stack with specific transaction setup, arguments - * and with {@link #TOP_FRAGMENT_TAG_MARK} tag used for simple up/back navigation. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment}; - * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. - */ - public void setAsTop(@NonNull final Class fragmentClass, - @Nullable final Bundle args, - @Nullable final Function transactionSetup) { - addToStack(fragmentClass, null, true, args, fragmentClass.getName() + ';' + TOP_FRAGMENT_TAG_MARK, transactionSetup); - } - - /** - * Pops all {@link Fragment}s and places new initial {@link Fragment} on top of stack. - * - * @param fragmentClass Class of {@link Fragment} to instantiate. - */ - public void setInitial(@NonNull final Class fragmentClass) { - setInitial(fragmentClass, null, null); - } - - /** - * Pops all {@link Fragment}s and places new initial {@link Fragment} on top of stack with specific arguments. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment}. - */ - public void setInitial(@NonNull final Class fragmentClass, - @NonNull final Bundle args) { - setInitial(fragmentClass, args, null); - } - - /** - * Pops all {@link Fragment}s and places new initial {@link Fragment} on top of stack with specific transaction setup. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. - */ - public void setInitial(@NonNull final Class fragmentClass, - @NonNull final Function transactionSetup) { - setInitial(fragmentClass, null, transactionSetup); - } - - /** - * Pops all {@link Fragment}s and places new initial {@link Fragment} on top of stack with specific transaction setup and arguments. - * - * @param fragmentClass Class of {@link Fragment} to instantiate; - * @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment}; - * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. - */ - public void setInitial(@NonNull final Class fragmentClass, - @Nullable final Bundle args, - @Nullable final Function transactionSetup) { - beforeSetInitialActions(); - setAsTop(fragmentClass, args, transactionSetup); - } - - /** - * Method calls every time before initial {@link Fragment} will be placed. - */ - protected void beforeSetInitialActions() { - if (fragmentManager.isDestroyed()) { - Lc.assertion("FragmentManager is destroyed"); - return; - } - - if (fragmentManager.getBackStackEntryCount() > 0) { - fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); - } - } - -} diff --git a/src/main/java/ru/touchin/roboswag/components/navigation/FragmentNavigation.kt b/src/main/java/ru/touchin/roboswag/components/navigation/FragmentNavigation.kt new file mode 100644 index 0000000..fe63d34 --- /dev/null +++ b/src/main/java/ru/touchin/roboswag/components/navigation/FragmentNavigation.kt @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) + * + * This file is part of RoboSwag library. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ru.touchin.roboswag.components.navigation + +import android.content.Context +import android.os.Bundle +import android.support.annotation.IdRes +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentManager +import android.support.v4.app.FragmentTransaction +import android.view.MenuItem + +import ru.touchin.roboswag.core.log.Lc + +/** + * Created by Gavriil Sitnikov on 07/03/2016. + * Navigation which is controlling fragments on activity using [android.support.v4.app.FragmentManager]. + * Basically there are 4 main actions to add fragments to activity. + * 1) [.setInitial] means to set fragment on top and remove all previously added fragments from stack; + * 2) [.push] means to simply add fragment on top of the stack; + * 3) [.setAsTop] means to push fragment on top of the stack with specific [.TOP_FRAGMENT_TAG_MARK] tag. + * It is useful to realize up/back navigation: if [.up] method will be called then stack will go to nearest fragment with TOP tag. + * If [.back] method will be called then stack will go to previous fragment. + * Usually such logic using to set as top fragments from sidebar and show hamburger when some of them appeared; + * 4) [.pushForResult] means to push fragment with target fragment. It is also adding [.WITH_TARGET_FRAGMENT_TAG_MARK] tag. + * Also if such up/back navigation logic is not OK then [.backTo] method could be used with any condition to back to. + * In that case in any stack-change method it is allowed to setup fragment transactions. + */ +open class FragmentNavigation( + private val context: Context, + private val fragmentManager: FragmentManager, + @IdRes private val containerViewId: Int, + private val transition: Int = FragmentTransaction.TRANSIT_FRAGMENT_OPEN +) { + + companion object { + const val TOP_FRAGMENT_TAG_MARK = "TOP_FRAGMENT" + const val WITH_TARGET_FRAGMENT_TAG_MARK = "FRAGMENT_WITH_TARGET" + } + + /** + * Returns if last fragment in stack is top (added by [.setAsTop] or [.setInitial]) like fragment from sidebar menu. + * + * @return True if last fragment on stack has TOP_FRAGMENT_TAG_MARK. + */ + fun isCurrentFragmentTop(): Boolean { + if (fragmentManager.backStackEntryCount == 0) { + return true + } + val topFragmentTag = fragmentManager + .getBackStackEntryAt(fragmentManager.backStackEntryCount - 1) + .name + return topFragmentTag != null && topFragmentTag.contains(TOP_FRAGMENT_TAG_MARK) + } + + /** + * Allowed to react on [android.app.Activity]'s menu item selection. + * + * @param item Selected menu item; + * @return True if reaction fired. + */ + fun onOptionsItemSelected(item: MenuItem): Boolean = item.itemId == android.R.id.home && back() + + /** + * Base method which is adding fragment to stack. + * + * @param fragmentClass Class of [Fragment] to instantiate; + * @param targetFragment Target fragment to be set as [Fragment.getTargetFragment] of instantiated [Fragment]; + * @param addToStack Flag to add this transaction to the back stack; + * @param args Bundle to be set as [Fragment.getArguments] of instantiated [Fragment]; + * @param backStackTag Tag of [Fragment] in back stack; + * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. + */ + fun addToStack( + fragmentClass: Class, + targetFragment: Fragment?, + targetRequestCode: Int, + addToStack: Boolean, + args: Bundle?, + backStackTag: String?, + transactionSetup: ((FragmentTransaction) -> Unit)? + ) { + if (fragmentManager.isDestroyed) { + Lc.assertion("FragmentManager is destroyed") + return + } + + val fragment = Fragment.instantiate(context, fragmentClass.name, args) + if (targetFragment != null) { + if (fragmentManager !== targetFragment.fragmentManager) { + Lc.assertion("FragmentManager of target is differ then of creating fragment. Target will be lost after restoring activity. " + + targetFragment.fragmentManager + " != " + fragmentManager) + } + fragment.setTargetFragment(targetFragment, targetRequestCode) + } + + val fragmentTransaction = fragmentManager.beginTransaction() + .replace(containerViewId, fragment, null) + if (addToStack) { + fragmentTransaction.addToBackStack(backStackTag) + } + if (fragmentManager.backStackEntryCount != 0) { + fragmentTransaction.setTransition(transition) + } + transactionSetup?.invoke(fragmentTransaction) + fragmentTransaction.commit() + } + + /** + * Simply calls [FragmentManager.popBackStack]. + * + * @return True if it have back to some entry in stack. + */ + fun back(): Boolean { + if (fragmentManager.backStackEntryCount > 1) { + fragmentManager.popBackStack() + return true + } + return false + } + + /** + * Backs to fragment which back stack's entry satisfy to specific condition. + * + * @param condition Condition of back stack entry to be satisfied; + * @return True if it have back to some entry in stack. + */ + fun backTo(condition: (FragmentManager.BackStackEntry) -> Boolean): Boolean { + val stackSize = fragmentManager.backStackEntryCount + var id: Int? = null + for (i in stackSize - 2 downTo 0) { + val backStackEntry = fragmentManager.getBackStackEntryAt(i) + if (condition(backStackEntry)) { + id = backStackEntry.id + break + } + } + if (id != null) { + fragmentManager.popBackStack(id, 0) + return true + } + return false + } + + /** + * Backs to fragment with specific [.TOP_FRAGMENT_TAG_MARK] tag. + * This tag is adding if fragment added to stack via [.setInitial] or [.setAsTop] methods. + * It can be used to create simple up/back navigation. + * + * @return True if it have back to some entry in stack. + */ + //ShortMethodName: it is ok because method name is good! + fun up(): Boolean = backTo { backStackEntry -> backStackEntry.name != null && backStackEntry.name.endsWith(TOP_FRAGMENT_TAG_MARK) } + + /** + * Pushes [Fragment] on top of stack with specific arguments and transaction setup. + * + * @param fragmentClass Class of [Fragment] to instantiate; + * @param args Bundle to be set as [Fragment.getArguments] of instantiated [Fragment]; + * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. + */ + fun push( + fragmentClass: Class, + args: Bundle? = null, + addToStack: Boolean = true, + transactionSetup: ((FragmentTransaction) -> Unit)? = null + ) { + addToStack(fragmentClass, null, 0, addToStack, args, null, transactionSetup) + } + + /** + * Pushes [Fragment] on top of stack with specific target fragment, arguments and transaction setup. + * + * @param fragmentClass Class of [Fragment] to instantiate; + * @param targetFragment Target fragment to be set as [Fragment.getTargetFragment] of instantiated [Fragment]; + * @param args Bundle to be set as [Fragment.getArguments] of instantiated [Fragment]; + * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. + */ + fun pushForResult( + fragmentClass: Class, + targetFragment: Fragment, + targetRequestCode: Int, + args: Bundle? = null, + transactionSetup: ((FragmentTransaction) -> Unit)? = null + ) { + addToStack( + fragmentClass, + targetFragment, + targetRequestCode, + true, + args, + fragmentClass.name + ';'.toString() + WITH_TARGET_FRAGMENT_TAG_MARK, + transactionSetup + ) + } + + /** + * Pushes [Fragment] on top of stack with specific transaction setup, arguments + * and with [.TOP_FRAGMENT_TAG_MARK] tag used for simple up/back navigation. + * + * @param fragmentClass Class of [Fragment] to instantiate; + * @param args Bundle to be set as [Fragment.getArguments] of instantiated [Fragment]; + * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. + */ + fun setAsTop( + fragmentClass: Class, + args: Bundle? = null, + transactionSetup: ((FragmentTransaction) -> Unit)? = null + ) { + addToStack(fragmentClass, null, 0, true, args, fragmentClass.name + ';'.toString() + TOP_FRAGMENT_TAG_MARK, transactionSetup) + } + + /** + * Pops all [Fragment]s and places new initial [Fragment] on top of stack with specific transaction setup and arguments. + * + * @param fragmentClass Class of [Fragment] to instantiate; + * @param args Bundle to be set as [Fragment.getArguments] of instantiated [Fragment]; + * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info. + */ + @JvmOverloads + fun setInitial( + fragmentClass: Class, + args: Bundle? = null, + transactionSetup: ((FragmentTransaction) -> Unit)? = null + ) { + beforeSetInitialActions() + setAsTop(fragmentClass, args, transactionSetup) + } + + /** + * Method calls every time before initial [Fragment] will be placed. + */ + protected fun beforeSetInitialActions() { + if (fragmentManager.isDestroyed) { + Lc.assertion("FragmentManager is destroyed") + return + } + + if (fragmentManager.backStackEntryCount > 0) { + fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) + } + } + +} diff --git a/src/main/java/ru/touchin/roboswag/components/navigation/fragments/ViewControllerFragment.java b/src/main/java/ru/touchin/roboswag/components/navigation/fragments/ViewControllerFragment.java index a63a0be..91e4c87 100644 --- a/src/main/java/ru/touchin/roboswag/components/navigation/fragments/ViewControllerFragment.java +++ b/src/main/java/ru/touchin/roboswag/components/navigation/fragments/ViewControllerFragment.java @@ -107,6 +107,8 @@ public class ViewControllerFragment, TState>> viewControllerClass; private TState state; + @Nullable + private ActivityResult pendingActivityResult; /** * Returns specific {@link Parcelable} which contains state of fragment and it's {@link ViewController}. @@ -192,6 +194,10 @@ public class ViewControllerFragment extends Fragment implements OnFragmentStartedListener { private boolean appeared; - private boolean started; /** * Returns if fragment have parent fragment. @@ -111,7 +111,6 @@ public abstract class ViewFragment extends F @Override public void onStart() { super.onStart(); - started = true; callMethodAfterInstantiation(this::onStart); } @@ -168,6 +167,7 @@ public abstract class ViewFragment extends F public void setMenuVisibility(final boolean menuVisible) { super.setMenuVisibility(menuVisible); if (getBaseActivity() != null && getView() != null) { + final boolean started = getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED); if (!appeared && menuVisible && started) { onAppear(getView(), getBaseActivity()); } @@ -209,7 +209,6 @@ public abstract class ViewFragment extends F @Deprecated @Override public void onStop() { - started = false; callMethodAfterInstantiation(this::onStop); super.onStop(); } diff --git a/src/main/java/ru/touchin/roboswag/components/navigation/viewcontrollers/ViewControllerNavigation.kt b/src/main/java/ru/touchin/roboswag/components/navigation/viewcontrollers/ViewControllerNavigation.kt index ba67da9..dde03c9 100644 --- a/src/main/java/ru/touchin/roboswag/components/navigation/viewcontrollers/ViewControllerNavigation.kt +++ b/src/main/java/ru/touchin/roboswag/components/navigation/viewcontrollers/ViewControllerNavigation.kt @@ -27,7 +27,6 @@ import android.support.v4.app.FragmentActivity import android.support.v4.app.FragmentManager import android.support.v4.app.FragmentTransaction -import io.reactivex.functions.Function import ru.touchin.roboswag.components.navigation.FragmentNavigation import ru.touchin.roboswag.components.navigation.fragments.ViewControllerFragment @@ -41,38 +40,34 @@ import ru.touchin.roboswag.components.navigation.fragments.ViewControllerFragmen open class ViewControllerNavigation( context: Context, fragmentManager: FragmentManager, - @IdRes containerViewId: Int -) : FragmentNavigation(context, fragmentManager, containerViewId) { + @IdRes containerViewId: Int, + transition: Int = FragmentTransaction.TRANSIT_FRAGMENT_OPEN +) : FragmentNavigation(context, fragmentManager, containerViewId, transition) { /** * Pushes [ViewController] on top of stack with specific [ViewControllerFragment.getState] and with specific transaction setup. * * @param viewControllerClass Class of [ViewController] to be pushed; * @param state [Parcelable] of [ViewController]'s fragment; + * @param addToStack Flag to add this transaction to the back stack; * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info; * @param TState Type of state of fragment. */ fun pushViewController( viewControllerClass: Class, TState>>, state: TState, - transactionSetup: Function? = null + addToStack: Boolean = true, + transactionSetup: ((FragmentTransaction) -> Unit)? = null ) { - addViewControllerToStack(viewControllerClass, null, true, state, null, transactionSetup) - } - - /** - * Pushes [ViewController] without adding to stack and with specific [ViewControllerFragment.getState]. - * - * @param viewControllerClass Class of [ViewController] to be pushed; - * @param state [Parcelable] of [ViewController]'s fragment; - * @param TState Type of state of fragment. - */ - fun pushSingleViewController( - viewControllerClass: Class, TState>>, - state: TState, - transactionSetup: Function? = null - ) { - addViewControllerToStack(viewControllerClass, null, false, state, null, transactionSetup) + addToStack( + ViewControllerFragment::class.java, + null, + 0, + addToStack, + ViewControllerFragment.args(viewControllerClass, state), + null, + transactionSetup + ) } /** @@ -90,13 +85,15 @@ open class ViewControllerNavigation( viewControllerClass: Class, TState>>, state: TState, targetFragment: TTargetFragment, - transactionSetup: Function? = null + targetRequestCode: Int, + transactionSetup: ((FragmentTransaction) -> Unit)? = null ) { - addViewControllerToStack( - viewControllerClass, + addToStack( + ViewControllerFragment::class.java, targetFragment, + targetRequestCode, true, - state, + ViewControllerFragment.args(viewControllerClass, state), viewControllerClass.name + ';'.toString() + WITH_TARGET_FRAGMENT_TAG_MARK, transactionSetup ) @@ -114,13 +111,14 @@ open class ViewControllerNavigation( fun setViewControllerAsTop( viewControllerClass: Class, TState>>, state: TState, - transactionSetup: Function? = null + transactionSetup: ((FragmentTransaction) -> Unit)? = null ) { - addViewControllerToStack( - viewControllerClass, + addToStack( + ViewControllerFragment::class.java, null, + 0, true, - state, + ViewControllerFragment.args(viewControllerClass, state), viewControllerClass.name + ';'.toString() + TOP_FRAGMENT_TAG_MARK, transactionSetup ) @@ -138,38 +136,10 @@ open class ViewControllerNavigation( fun setInitialViewController( viewControllerClass: Class, TState>>, state: TState, - transactionSetup: Function? = null + transactionSetup: ((FragmentTransaction) -> Unit)? = null ) { beforeSetInitialActions() setViewControllerAsTop(viewControllerClass, state, transactionSetup) } - /** - * Base method to push stateful [ViewControllerFragment] to stack. - * - * @param viewControllerClass Class of [ViewController] to be pushed; - * @param targetFragment [ViewControllerFragment] to be set as target; - * @param state [Parcelable] of [ViewController]'s fragment; - * @param backStackTag Tag of [ViewControllerFragment] in back stack; - * @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info; - * @param TState Type of state of fragment. - */ - protected fun addViewControllerToStack( - viewControllerClass: Class, TState>>, - targetFragment: Fragment?, - addToStack: Boolean, - state: TState, - backStackTag: String?, - transactionSetup: Function? - ) { - addToStack( - ViewControllerFragment::class.java, - targetFragment, - addToStack, - ViewControllerFragment.args(viewControllerClass, state), - backStackTag, - transactionSetup - ) - } - }