Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Popping can also cause back navigation between tabs when current stack is depleted #98

Merged
merged 4 commits into from
Nov 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import android.view.MenuItem;

import com.ncapdevi.fragnav.FragNavController;
import com.ncapdevi.fragnav.FragNavSwitchController;
import com.ncapdevi.fragnav.FragNavTransactionOptions;
import com.ncapdevi.fragnav.tabhistory.FragNavTabHistoryController;
import com.ncapdevi.sample.R;
import com.ncapdevi.sample.fragments.BaseFragment;
import com.ncapdevi.sample.fragments.FavoritesFragment;
Expand All @@ -32,15 +35,25 @@ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(com.ncapdevi.sample.R.layout.activity_bottom_tabs);

BottomBar mBottomBar = findViewById(R.id.bottomBar);
mBottomBar.selectTabAtPosition(INDEX_NEARBY);
final BottomBar bottomBar = findViewById(R.id.bottomBar);
boolean initial = savedInstanceState == null;
if (initial) {
bottomBar.selectTabAtPosition(INDEX_NEARBY);
}
mNavController = FragNavController.newBuilder(savedInstanceState, getSupportFragmentManager(), R.id.container)
.transactionListener(this)
.rootFragmentListener(this, 5)
.popStrategy(FragNavTabHistoryController.UNIQUE_TAB_HISTORY)
.switchController(new FragNavSwitchController() {
@Override
public void switchTab(int index, FragNavTransactionOptions transactionOptions) {
bottomBar.selectTabAtPosition(index);
}
})
.build();


mBottomBar.setOnTabSelectListener(new OnTabSelectListener() {
bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
@Override
public void onTabSelected(@IdRes int tabId) {
switch (tabId) {
Expand All @@ -61,9 +74,9 @@ public void onTabSelected(@IdRes int tabId) {
break;
}
}
});
}, initial);

mBottomBar.setOnTabReselectListener(new OnTabReselectListener() {
bottomBar.setOnTabReselectListener(new OnTabReselectListener() {
@Override
public void onTabReSelected(@IdRes int tabId) {
mNavController.clearStack();
Expand All @@ -74,9 +87,7 @@ public void onTabReSelected(@IdRes int tabId) {

@Override
public void onBackPressed() {
if (!mNavController.isRootFragment()) {
mNavController.popFragment();
} else {
if (!mNavController.popFragment()) {
super.onBackPressed();
}
}
Expand Down
102 changes: 91 additions & 11 deletions frag-nav/src/main/java/com/ncapdevi/fragnav/FragNavController.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
import android.support.v4.util.Pair;
import android.view.View;

import com.ncapdevi.fragnav.tabhistory.CurrentTabHistoryController;
import com.ncapdevi.fragnav.tabhistory.FragNavTabHistoryController;
import com.ncapdevi.fragnav.tabhistory.UniqueTabHistoryController;
import com.ncapdevi.fragnav.tabhistory.UnlimitedTabHistoryController;

import org.json.JSONArray;

import java.lang.annotation.Retention;
Expand All @@ -21,6 +26,10 @@
import java.util.List;
import java.util.Stack;

import static com.ncapdevi.fragnav.tabhistory.FragNavTabHistoryController.CURRENT_TAB;
import static com.ncapdevi.fragnav.tabhistory.FragNavTabHistoryController.UNIQUE_TAB_HISTORY;
import static com.ncapdevi.fragnav.tabhistory.FragNavTabHistoryController.UNLIMITED_TAB_HISTORY;

/**
* The class is used to manage navigation through multiple stacks of fragments, as well as coordinate
* fragments that may appear on screen
Expand Down Expand Up @@ -83,6 +92,9 @@ public class FragNavController {
@Nullable
private TransactionListener mTransactionListener;
private boolean mExecutingTransaction;
private FragNavTabHistoryController mFragNavTabHistoryController;
@FragNavTabHistoryController.PopStrategy
private final int mPopStrategy;

//region Construction and setup

Expand All @@ -94,6 +106,24 @@ private FragNavController(Builder builder, @Nullable Bundle savedInstanceState)
mTransactionListener = builder.mTransactionListener;
mDefaultTransactionOptions = builder.mDefaultTransactionOptions;
mSelectedTabIndex = builder.mSelectedTabIndex;
mPopStrategy = builder.mPopStrategy;

DefaultFragNavPopController fragNavPopController = new DefaultFragNavPopController();
switch (mPopStrategy) {
case CURRENT_TAB:
mFragNavTabHistoryController = new CurrentTabHistoryController(fragNavPopController);
break;
case UNIQUE_TAB_HISTORY:
mFragNavTabHistoryController = new UniqueTabHistoryController(fragNavPopController,
builder.fragNavSwitchController);
break;
case UNLIMITED_TAB_HISTORY:
mFragNavTabHistoryController = new UnlimitedTabHistoryController(fragNavPopController,
builder.fragNavSwitchController);
break;
}

mFragNavTabHistoryController.switchTab(mSelectedTabIndex);

//Attempt to restore from bundle, if not, initialize
if (!restoreFromBundle(savedInstanceState, builder.mRootFragments)) {
Expand All @@ -107,6 +137,8 @@ private FragNavController(Builder builder, @Nullable Bundle savedInstanceState)
}

initialize(builder.mSelectedTabIndex);
} else {
mFragNavTabHistoryController.restoreFromBundle(savedInstanceState);
}
}

Expand Down Expand Up @@ -157,6 +189,10 @@ public void initialize(@TabIndex int index) {
* @throws IndexOutOfBoundsException Thrown if trying to switch to an index outside given range
*/
public void switchTab(@TabIndex int index, @Nullable FragNavTransactionOptions transactionOptions) throws IndexOutOfBoundsException {
switchTabInternal(index, transactionOptions);
}

private void switchTabInternal(@TabIndex int index, @Nullable FragNavTransactionOptions transactionOptions) throws IndexOutOfBoundsException {
//Check to make sure the tab is within range
if (index >= mFragmentStacks.size()) {
throw new IndexOutOfBoundsException("Can't switch to a tab that hasn't been initialized, " +
Expand All @@ -165,6 +201,7 @@ public void switchTab(@TabIndex int index, @Nullable FragNavTransactionOptions t
}
if (mSelectedTabIndex != index) {
mSelectedTabIndex = index;
mFragNavTabHistoryController.switchTab(index);

FragmentTransaction ft = createTransactionWithOptions(transactionOptions, false);

Expand All @@ -185,9 +222,9 @@ public void switchTab(@TabIndex int index, @Nullable FragNavTransactionOptions t
}
}


mCurrentFrag = fragment;
if (mTransactionListener != null) {

mTransactionListener.onTabTransaction(getCurrentFrag(), mSelectedTabIndex);
}
}
Expand Down Expand Up @@ -241,25 +278,30 @@ public void pushFragment(@Nullable Fragment fragment) {
*
* @param transactionOptions Transaction options to be displayed
*/
public void popFragment(@Nullable FragNavTransactionOptions transactionOptions) throws UnsupportedOperationException {
popFragments(1, transactionOptions);
public boolean popFragment(@Nullable FragNavTransactionOptions transactionOptions) throws UnsupportedOperationException {
return popFragments(1, transactionOptions);
}

/**
* Pop the current fragment from the current tab
*/
public void popFragment() throws UnsupportedOperationException {
popFragment(null);
public boolean popFragment() throws UnsupportedOperationException {
return popFragment(null);
}

/**
* Pop the current stack until a given tag is found. If the tag is not found, the stack will popFragment until it is at
* the root fragment
*
* @param transactionOptions Transaction options to be displayed
* @return true if any any fragment has been popped
*/
public void popFragments(int popDepth, @Nullable FragNavTransactionOptions transactionOptions) throws UnsupportedOperationException {
if (isRootFragment()) {
public boolean popFragments(int popDepth, @Nullable FragNavTransactionOptions transactionOptions) throws UnsupportedOperationException {
return mFragNavTabHistoryController.popFragments(popDepth, transactionOptions);
}

private int tryPopFragmentsFromCurrentStack(int popDepth, @Nullable FragNavTransactionOptions transactionOptions) throws UnsupportedOperationException {
if (mPopStrategy == CURRENT_TAB && isRootFragment()) {
throw new UnsupportedOperationException(
"You can not popFragment the rootFragment. If you need to change this fragment, use replaceFragment(fragment)");
} else if (popDepth < 1) {
Expand All @@ -269,9 +311,10 @@ public void popFragments(int popDepth, @Nullable FragNavTransactionOptions trans
}

//If our popDepth is big enough that it would just clear the stack, then call that.
if (popDepth >= mFragmentStacks.get(mSelectedTabIndex).size() - 1) {
int poppableSize = mFragmentStacks.get(mSelectedTabIndex).size() - 1;
if (popDepth >= poppableSize) {
clearStack(transactionOptions);
return;
return poppableSize;
}

Fragment fragment;
Expand Down Expand Up @@ -316,6 +359,7 @@ public void popFragments(int popDepth, @Nullable FragNavTransactionOptions trans
if (mTransactionListener != null) {
mTransactionListener.onFragmentTransaction(getCurrentFrag(), TransactionType.POP);
}
return popDepth;
}

/**
Expand Down Expand Up @@ -697,7 +741,6 @@ private FragmentTransaction createTransactionWithOptions(@Nullable FragNavTransa

/**
* Helper function to commit fragment transaction with transaction option - allowStateLoss
* k
*
* @param fragmentTransaction
* @param transactionOptions
Expand Down Expand Up @@ -734,7 +777,9 @@ public int getSize() {
@CheckResult
@Nullable
public Stack<Fragment> getStack(@TabIndex int index) {
if (index == NO_TAB) return null;
if (index == NO_TAB) {
return null;
}
if (index >= mFragmentStacks.size()) {
throw new IndexOutOfBoundsException("Can't get an index that's larger than we've setup");
}
Expand Down Expand Up @@ -826,6 +871,8 @@ public void onSaveInstanceState(@NonNull Bundle outState) {
} catch (Throwable t) {
// Nothing we can do
}

mFragNavTabHistoryController.onSaveInstanceState(outState);
}

/**
Expand Down Expand Up @@ -937,6 +984,13 @@ public interface TransactionListener {
void onFragmentTransaction(Fragment fragment, TransactionType transactionType);
}

public class DefaultFragNavPopController implements com.ncapdevi.fragnav.FragNavPopController {
@Override
public int tryPopFragments(int popDepth, FragNavTransactionOptions transactionOptions) throws UnsupportedOperationException {
return FragNavController.this.tryPopFragmentsFromCurrentStack(popDepth, transactionOptions);
}
}

public static final class Builder {
private final int mContainerId;
private FragmentManager mFragmentManager;
Expand All @@ -946,8 +1000,14 @@ public static final class Builder {
private TransactionListener mTransactionListener;
private FragNavTransactionOptions mDefaultTransactionOptions;
private int mNumberOfTabs = 0;

@FragNavTabHistoryController.PopStrategy
private int mPopStrategy = CURRENT_TAB;
private List<Fragment> mRootFragments;
private Bundle mSavedInstanceState;

@Nullable
private FragNavSwitchController fragNavSwitchController;

public Builder(@Nullable Bundle savedInstanceState, FragmentManager mFragmentManager, int mContainerId) {
this.mSavedInstanceState = savedInstanceState;
Expand Down Expand Up @@ -1018,10 +1078,30 @@ public Builder transactionListener(TransactionListener val) {
return this;
}

/**
* @param popStrategy Switch between different approaches of handling tab history while popping fragments on current tab
*/
public Builder popStrategy(@FragNavTabHistoryController.PopStrategy int popStrategy) {
mPopStrategy = popStrategy;
return this;
}

/**
* @param fragNavSwitchController Handles switch requests
*/
public Builder switchController(FragNavSwitchController fragNavSwitchController) {
this.fragNavSwitchController = fragNavSwitchController;
return this;
}

public FragNavController build() {
if (mRootFragmentListener == null && mRootFragments == null) {
throw new IndexOutOfBoundsException("Either a root fragment(s) needs to be set, or a fragment listener");
}
if ((mPopStrategy == UNIQUE_TAB_HISTORY || mPopStrategy == UNLIMITED_TAB_HISTORY) && fragNavSwitchController == null) {
throw new IllegalStateException(
"Switch handler needs to be set for unique or unlimited tab history strategy");
}
return new FragNavController(this, mSavedInstanceState);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.ncapdevi.fragnav;

public interface FragNavPopController {
int tryPopFragments(int popDepth, FragNavTransactionOptions transactionOptions) throws UnsupportedOperationException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.ncapdevi.fragnav;

public interface FragNavSwitchController {
void switchTab(@FragNavController.TabIndex int index, FragNavTransactionOptions transactionOptions);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.ncapdevi.fragnav.tabhistory;

import com.ncapdevi.fragnav.FragNavPopController;

abstract class BaseFragNavTabHistoryController implements FragNavTabHistoryController {
FragNavPopController fragNavPopController;

BaseFragNavTabHistoryController(FragNavPopController fragNavPopController) {
this.fragNavPopController = fragNavPopController;
}
}
Loading