navigation added

This commit is contained in:
Gavriil Sitnikov 2016-03-09 01:15:32 +03:00
parent 165c9232e6
commit c1b12ea706
8 changed files with 597 additions and 82 deletions

View File

@ -51,7 +51,7 @@ import rx.subjects.PublishSubject;
* TODO: fill description
*/
@SuppressWarnings({"PMD.GodClass", "PMD.TooManyMethods"})
public abstract class AbstractBaseActivity extends AppCompatActivity
public abstract class AbstractBaseActivity extends BaseActivity
implements FragmentManager.OnBackStackChangedListener,
OnFragmentStartedListener {
@ -67,8 +67,6 @@ public abstract class AbstractBaseActivity extends AppCompatActivity
private String requestedPermission;
private final PublishSubject<PermissionState> requestPermissionsEvent = PublishSubject.create();
private final Handler postHandler = new Handler();
/* Returns id of main fragments container where navigation-node fragments should be */
protected int getFragmentContainerId() {
throw new UnsupportedOperationException("Implement getFragmentContainerId method to use fragment managing");
@ -322,24 +320,6 @@ public abstract class AbstractBaseActivity extends AppCompatActivity
}
}
@Override
protected void onDestroy() {
super.onDestroy();
postHandler.removeCallbacksAndMessages(null);
}
/* Hides device keyboard */
public void hideSoftInput() {
if (getCurrentFocus() != null) {
final InputMethodManager inputManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
final View mainFragmentContainer = findViewById(getFragmentContainerId());
if (mainFragmentContainer != null) {
mainFragmentContainer.requestFocus();
}
}
}
public void popBackStackToTopFragment() {
final FragmentManager fragmentManager = getSupportFragmentManager();
final int stackSize = fragmentManager.getBackStackEntryCount();
@ -353,13 +333,6 @@ public abstract class AbstractBaseActivity extends AppCompatActivity
fragmentManager.popBackStackImmediate(currentFragmentName, 0);
}
/* Shows device keyboard */
public void showSoftInput(@NonNull final View view) {
view.requestFocus();
final InputMethodManager inputManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
}
@Nullable
public Typeface getToolbarTitleTypeface(@NonNull final AbstractBaseActivity activity) {
return null;

View File

@ -0,0 +1,39 @@
package ru.touchin.roboswag.components.navigation;
import android.app.Activity;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
/**
* Created by Gavriil Sitnikov on 08/03/2016.
* TODO: fill description
*/
public class BaseActivity extends AppCompatActivity {
/**
* Hides device keyboard that is showing over {@link Activity}.
* Do not use it if keyboard is over {@link android.app.Dialog} - it won't work as they have different {@link Activity#getWindow()}.
*/
public void hideSoftInput() {
if (getCurrentFocus() == null) {
return;
}
final InputMethodManager inputManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
getWindow().getDecorView().requestFocus();
}
/**
* Shows device keyboard over {@link Activity} and focuses {@link View}.
* Do not use it if keyboard is over {@link android.app.Dialog} - it won't work as they have different {@link Activity#getWindow()}.
* @param view View to get focus for input from keyboard.
*/
public void showSoftInput(@NonNull final View view) {
view.requestFocus();
final InputMethodManager inputManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
}
}

View File

@ -0,0 +1,216 @@
/*
* 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 ru.touchin.roboswag.core.log.Lc;
import rx.functions.Func1;
/**
* Created by Gavriil Sitnikov on 07/03/2016.
* Navigation which is controlling using {@link android.support.v4.app.FragmentManager} as controller.
*/
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;
}
@NonNull
public FragmentManager getFragmentManager() {
return fragmentManager;
}
@NonNull
public Context getContext() {
return context;
}
@SuppressLint("CommitTransaction")
protected void addToStack(@NonNull final Class<? extends Fragment> fragmentClass,
@Nullable final Fragment targetFragment,
@Nullable final Bundle args,
@Nullable final String backStackTag,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
if (fragmentManager.isDestroyed()) {
Lc.assertion("FragmentManager is destroyed");
return;
}
final Fragment fragment = Fragment.instantiate(context, fragmentClass.getName(), args);
if (targetFragment != null) {
fragment.setTargetFragment(targetFragment, 0);
}
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
.replace(containerViewId, fragment, null)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(backStackTag);
if (transactionSetup != null) {
transactionSetup.call(fragmentTransaction).commit();
} else {
fragmentTransaction.commit();
}
}
public boolean back() {
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStack();
return true;
}
return false;
}
public boolean backTo(@NonNull final Func1<FragmentManager.BackStackEntry, Boolean> condition) {
final int stackSize = fragmentManager.getBackStackEntryCount();
Integer id = null;
for (int i = stackSize - 2; i >= 0; i--) {
final FragmentManager.BackStackEntry backStackEntry = fragmentManager.getBackStackEntryAt(i);
id = backStackEntry.getId();
if (condition.call(backStackEntry)) {
break;
}
}
if (id != null) {
fragmentManager.popBackStackImmediate(id, 0);
return true;
}
return false;
}
public boolean up() {
return backTo(backStackEntry -> backStackEntry.getName().endsWith(TOP_FRAGMENT_TAG_MARK));
}
public void push(@NonNull final Class<? extends Fragment> fragmentClass) {
addToStack(fragmentClass, null, null, null, null);
}
public void push(@NonNull final Class<? extends Fragment> fragmentClass,
@NonNull final Bundle args) {
addToStack(fragmentClass, null, args, null, null);
}
public void push(@NonNull final Class<? extends Fragment> fragmentClass,
@NonNull final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(fragmentClass, null, null, null, transactionSetup);
}
public void push(@NonNull final Class<? extends Fragment> fragmentClass,
@Nullable final Bundle args,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(fragmentClass, null, args, null, transactionSetup);
}
public void pushForResult(@NonNull final Class<? extends Fragment> fragmentClass,
@NonNull final Fragment targetFragment) {
addToStack(fragmentClass, targetFragment, null, fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, null);
}
public void pushForResult(@NonNull final Class<? extends Fragment> fragmentClass,
@NonNull final Fragment targetFragment,
@NonNull final Bundle args) {
addToStack(fragmentClass, targetFragment, args, fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, null);
}
public void pushForResult(@NonNull final Class<? extends Fragment> fragmentClass,
@NonNull final Fragment targetFragment,
@NonNull final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(fragmentClass, targetFragment, null, fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, transactionSetup);
}
public void pushForResult(@NonNull final Class<? extends Fragment> fragmentClass,
@NonNull final Fragment targetFragment,
@Nullable final Bundle args,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(fragmentClass, targetFragment, args, fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, transactionSetup);
}
public void setAsTop(@NonNull final Class<? extends Fragment> fragmentClass) {
addToStack(fragmentClass, null, null, fragmentClass.getName() + ';' + TOP_FRAGMENT_TAG_MARK, null);
}
public void setAsTop(@NonNull final Class<? extends Fragment> fragmentClass,
@NonNull final Bundle args) {
addToStack(fragmentClass, null, args, fragmentClass.getName() + ';' + TOP_FRAGMENT_TAG_MARK, null);
}
public void setAsTop(@NonNull final Class<? extends Fragment> fragmentClass,
@NonNull final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(fragmentClass, null, null, fragmentClass.getName() + ';' + TOP_FRAGMENT_TAG_MARK, transactionSetup);
}
public void setAsTop(@NonNull final Class<? extends Fragment> fragmentClass,
@Nullable final Bundle args,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(fragmentClass, null, args, fragmentClass.getName() + ';' + TOP_FRAGMENT_TAG_MARK, transactionSetup);
}
public void setInitial(@NonNull final Class<? extends Fragment> fragmentClass) {
setInitial(fragmentClass, null, null);
}
public void setInitial(@NonNull final Class<? extends Fragment> fragmentClass,
@NonNull final Bundle args) {
setInitial(fragmentClass, args, null);
}
public void setInitial(@NonNull final Class<? extends Fragment> fragmentClass,
@NonNull final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
setInitial(fragmentClass, null, transactionSetup);
}
public void setInitial(@NonNull final Class<? extends Fragment> fragmentClass,
@Nullable final Bundle args,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
if (fragmentManager.isDestroyed()) {
Lc.assertion("FragmentManager is destroyed");
return;
}
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
setAsTop(fragmentClass, args, transactionSetup);
}
}

View File

@ -0,0 +1,51 @@
package ru.touchin.roboswag.components.navigation;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.io.Serializable;
/**
* Created by Gavriil Sitnikov on 07/03/2016.
* TODO: fill description
*/
public class SimpleViewControllerFragment<TState extends Serializable, TLogicBridge, TActivity extends ViewControllerActivity<TLogicBridge>>
extends ViewControllerFragment<TState, TLogicBridge, TActivity> {
private static final String VIEW_CONTROLLER_CLASS_EXTRA = "VIEW_CONTROLLER_CLASS_EXTRA";
/**
* Creates {@link Bundle} which will store state and {@link ViewController}'s class.
*
* @param viewControllerClass Class of {@link ViewController} which will be instantiated inside this fragment.
* @param state State to use into {@link ViewController}.
* @return Returns {@link Bundle} with state inside.
*/
@NonNull
public static Bundle createState(@NonNull Class<? extends ViewController> viewControllerClass,
@Nullable final Serializable state) {
final Bundle result = ViewControllerFragment.createState(state);
result.putSerializable(VIEW_CONTROLLER_CLASS_EXTRA, viewControllerClass);
return result;
}
private Class<? extends ViewController<TLogicBridge, TActivity,
? extends ViewControllerFragment<TState, TLogicBridge, TActivity>>> viewControllerClass;
@NonNull
@Override
protected Class<? extends ViewController<TLogicBridge, TActivity,
? extends ViewControllerFragment<TState, TLogicBridge, TActivity>>> getViewControllerClass() {
return viewControllerClass;
}
@SuppressWarnings("unchecked")
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewControllerClass = (Class<? extends ViewController<TLogicBridge, TActivity,
? extends ViewControllerFragment<TState, TLogicBridge, TActivity>>>) getArguments().getSerializable(VIEW_CONTROLLER_CLASS_EXTRA);
}
}

View File

@ -20,22 +20,27 @@
package ru.touchin.roboswag.components.navigation;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import java.io.Serializable;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
/**
* Created by Gavriil Sitnikov on 21/10/2015.
* Class to control view of specific fragment, activity and application by logic bridge.
* [phase 1]
*/
public class ViewController<TState extends Serializable,
TLogicBridge,
TActivity extends AppCompatActivity,
TFragment extends ViewControllerFragment<TState, TLogicBridge, TActivity>> {
public class ViewController<TLogicBridge,
TActivity extends ViewControllerActivity<TLogicBridge>,
TFragment extends ViewControllerFragment<?, TLogicBridge, TActivity>> {
private static final String SUPPORT_FRAGMENT_VIEW_STATE_EXTRA = "android:view_state";
@NonNull
private final TLogicBridge logicBridge;
@ -45,13 +50,35 @@ public class ViewController<TState extends Serializable,
private final TFragment fragment;
@NonNull
private final ViewGroup container;
@NonNull
private final Subscription savedStateSubscription;
public ViewController(@NonNull final CreationContext<TState, TLogicBridge, TActivity, TFragment> creationContext,
public ViewController(@NonNull final CreationContext<TLogicBridge, TActivity, TFragment> creationContext,
@Nullable final Bundle savedInstanceState) {
this.logicBridge = creationContext.logicBridge;
this.activity = creationContext.activity;
this.fragment = creationContext.fragment;
this.container = creationContext.container;
savedStateSubscription = getRestoreSavedStateObservable(creationContext, savedInstanceState)
.filter(savedState -> savedState != null)
.first()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onRestoreSavedState);
}
/**
* Sets {@link Observable} which will be used to get a moment when controller should restore it's state.
* It will be waits for first non-null {@link Bundle} that contains saved state.
*
* @param creationContext Context passed into {@link ViewController} constructor.
* @param savedInstanceState Saved state of {@link ViewController}.
* @return {@link Observable} to get restore time to.
*/
@NonNull
protected Observable<Bundle> getRestoreSavedStateObservable(@NonNull final CreationContext<TLogicBridge, TActivity, TFragment> creationContext,
@Nullable final Bundle savedInstanceState) {
return Observable.just(savedInstanceState);
}
/**
@ -67,7 +94,7 @@ public class ViewController<TState extends Serializable,
/**
* Returns view's activity.
*
* @return Returns activity.
* @return Returns activity;
*/
@NonNull
public TActivity getActivity() {
@ -87,24 +114,40 @@ public class ViewController<TState extends Serializable,
/**
* Returns view instantiated in {@link #getFragment} fragment attached to {@link #getActivity} activity.
*
* @return Returns view.
* @return Returns view;
*/
@NonNull
public ViewGroup getContainer() {
public View getContainer() {
return container;
}
public void onDestroy() {
//do nothing
/**
* Called when savedInstanceState is ready to be restored.
*
* @param savedInstanceState Saved state.
*/
protected void onRestoreSavedState(@NonNull final Bundle savedInstanceState) {
final SparseArray<Parcelable> viewStates = savedInstanceState.getSparseParcelableArray(SUPPORT_FRAGMENT_VIEW_STATE_EXTRA);
if (viewStates != null) {
container.restoreHierarchyState(viewStates);
}
}
public void onSaveInstanceState(@NonNull final Bundle savedInstanceState) {
// do nothing
}
public void onDestroy() {
savedStateSubscription.unsubscribe();
}
/**
* Class to simplify constructor override.
*/
public static class CreationContext<TState extends Serializable,
TLogicBridge,
TActivity extends AppCompatActivity,
TFragment extends ViewControllerFragment<TState, TLogicBridge, TActivity>> {
public static class CreationContext<TLogicBridge,
TActivity extends ViewControllerActivity<TLogicBridge>,
TFragment extends ViewControllerFragment<?, TLogicBridge, TActivity>> {
@NonNull
private final TLogicBridge logicBridge;

View File

@ -0,0 +1,35 @@
package ru.touchin.roboswag.components.navigation;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import ru.touchin.roboswag.components.services.LogicService;
import ru.touchin.roboswag.core.utils.android.RxAndroidUtils;
import rx.Observable;
/**
* Created by Gavriil Sitnikov on 07/03/2016.
* TODO: fill description
*/
public abstract class ViewControllerActivity<TLogicBridge> extends BaseActivity {
/**
* It should return specific Service class where from this fragment should get interface to logic.
*
* @return Returns class of specific LogicService.
*/
@NonNull
protected abstract Class<? extends LogicService<TLogicBridge>> getLogicServiceClass();
/**
* Returns {@link Observable} which will connect to {@link LogicService} and get object of {@link TLogicBridge} type from it.
*
* @return {@link Observable} which will provide changes of object of type {@link TLogicBridge};
*/
@NonNull
public Observable<TLogicBridge> observeLogicBridge() {
return RxAndroidUtils.observeService(this, getLogicServiceClass())
.map(service -> service != null ? service.getLogicBridge() : null);
}
}

View File

@ -23,7 +23,6 @@ import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
@ -33,21 +32,21 @@ import android.widget.FrameLayout;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import ru.touchin.roboswag.components.services.LogicService;
import ru.touchin.roboswag.core.log.Lc;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
import ru.touchin.roboswag.core.utils.android.RxAndroidUtils;
import rx.Observable;
import rx.Scheduler;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.exceptions.OnErrorThrowable;
import rx.subjects.BehaviorSubject;
/**
* Created by Gavriil Sitnikov on 21/10/2015.
* Fragment that creates {@link ViewController} between {@link #onViewCreated} and {@link #onDestroyView}.
* [phase 1]
* Fragment instantiated in specific activity of {@link TActivity} type that is holding {@link ViewController} inside.
*/
public abstract class ViewControllerFragment<TState extends Serializable, TLogicBridge, TActivity extends AppCompatActivity>
public abstract class ViewControllerFragment<TState extends Serializable, TLogicBridge, TActivity extends ViewControllerActivity<TLogicBridge>>
extends ViewFragment<TActivity> {
private static final String VIEW_CONTROLLER_STATE_EXTRA = "VIEW_CONTROLLER_STATE_EXTRA";
@ -58,6 +57,7 @@ public abstract class ViewControllerFragment<TState extends Serializable, TLogic
* @param state State to use into ViewController.
* @return Returns bundle with state inside.
*/
@NonNull
public static Bundle createState(@Nullable final Serializable state) {
final Bundle result = new Bundle();
result.putSerializable(VIEW_CONTROLLER_STATE_EXTRA, state);
@ -69,6 +69,7 @@ public abstract class ViewControllerFragment<TState extends Serializable, TLogic
private final Scheduler backgroundScheduler = RxAndroidUtils.createLooperScheduler();
@Nullable
private ViewController viewController;
@Nullable
private Subscription viewControllerSubscription;
private TState state;
@ -82,31 +83,23 @@ public abstract class ViewControllerFragment<TState extends Serializable, TLogic
return state;
}
/**
* It should return specific Service class where from this fragment should get interface to logic.
*
* @return Returns class of specific LogicService.
*/
@NonNull
protected abstract Class<? extends LogicService<TLogicBridge>> getLogicServiceClass();
/**
* It should return specific ViewController class to control instantiated view by logic bridge after activity creation.
*
* @return Returns class of specific ViewController.
*/
@NonNull
protected abstract Class<? extends ViewController<TState, TLogicBridge, TActivity,
protected abstract Class<? extends ViewController<TLogicBridge, TActivity,
? extends ViewControllerFragment<TState, TLogicBridge, TActivity>>> getViewControllerClass();
// need throwable for app stability
@SuppressWarnings({"PMD.AvoidCatchingThrowable", "unchecked"})
@SuppressWarnings("unchecked")
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getContext() == null) {
Lc.assertion("Context is null in onCreate");
return;
}
state = savedInstanceState != null
@ -114,26 +107,26 @@ public abstract class ViewControllerFragment<TState extends Serializable, TLogic
: (getArguments() != null ? (TState) getArguments().getSerializable(VIEW_CONTROLLER_STATE_EXTRA) : null);
viewControllerSubscription = Observable
.combineLatest(RxAndroidUtils.observeService(getContext(), getLogicServiceClass())
.map(service -> service != null ? service.getLogicBridge() : null)
.combineLatest(activitySubject
.switchMap(activity -> activity != null ? activity.observeLogicBridge() : Observable.just(null))
.distinctUntilChanged()
.observeOn(backgroundScheduler),
activitySubject.distinctUntilChanged().observeOn(backgroundScheduler),
viewSubject.distinctUntilChanged().observeOn(backgroundScheduler),
(logicBridge, activity, view) -> {
if (activity == null || view == null || logicBridge == null) {
if (logicBridge == null || activity == null || view == null) {
return null;
}
final ViewController.CreationContext<? extends Serializable, TLogicBridge, TActivity,
final ViewController.CreationContext<TLogicBridge, TActivity,
? extends ViewControllerFragment<TState, TLogicBridge, TActivity>> creationContext
= new ViewController.CreationContext<>(logicBridge, activity, this, view.first);
if (getViewControllerClass().getConstructors().length > 1) {
Lc.assertion("There should be single constructor for " + getViewControllerClass());
return null;
if (getViewControllerClass().getConstructors().length != 1) {
throw OnErrorThrowable
.from(new ShouldNotHappenException("There should be single constructor for " + getViewControllerClass()));
}
final Constructor<?> constructor = getViewControllerClass().getConstructors()[0];
try {
final Constructor<?> constructor = getViewControllerClass().getConstructors()[0];
switch (constructor.getParameterTypes().length) {
case 2:
return (ViewController) constructor.newInstance(creationContext, view.second);
@ -143,13 +136,12 @@ public abstract class ViewControllerFragment<TState extends Serializable, TLogic
Lc.assertion("Wrong constructor parameters count: " + constructor.getParameterTypes().length);
return null;
}
} catch (final Throwable throwable) {
Lc.assertion(throwable);
return null;
} catch (final Exception exception) {
throw OnErrorThrowable.from(exception);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onViewControllerChanged);
.subscribe(this::onViewControllerChanged, Lc::assertion);
}
@Deprecated
@ -181,22 +173,23 @@ public abstract class ViewControllerFragment<TState extends Serializable, TLogic
if (this.viewController == null) {
return;
}
if (getView() instanceof PlaceholderView) {
((PlaceholderView) getView()).removeAllViews();
((PlaceholderView) getView())
.addView(this.viewController.getContainer(), ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
} else {
if (getView() == null || !(getView() instanceof PlaceholderView)) {
Lc.assertion("View of fragment should be PlaceholderView");
}
((PlaceholderView) getView()).removeAllViews();
((PlaceholderView) getView())
.addView(this.viewController.getContainer(), ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
viewController.getActivity().supportInvalidateOptionsMenu();
}
@Override
public void onSaveInstanceState(@NonNull final Bundle stateToSave) {
super.onSaveInstanceState(stateToSave);
public void onSaveInstanceState(@NonNull final Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
if (viewController != null) {
stateToSave.putSerializable(VIEW_CONTROLLER_STATE_EXTRA, state);
viewController.onSaveInstanceState(savedInstanceState);
savedInstanceState.putSerializable(VIEW_CONTROLLER_STATE_EXTRA, state);
} else if (getArguments() != null && getArguments().containsKey(VIEW_CONTROLLER_STATE_EXTRA)) {
stateToSave.putSerializable(VIEW_CONTROLLER_STATE_EXTRA, getArguments().getSerializable(VIEW_CONTROLLER_STATE_EXTRA));
savedInstanceState.putSerializable(VIEW_CONTROLLER_STATE_EXTRA, getArguments().getSerializable(VIEW_CONTROLLER_STATE_EXTRA));
}
}
@ -208,7 +201,10 @@ public abstract class ViewControllerFragment<TState extends Serializable, TLogic
@Override
public void onDestroy() {
viewControllerSubscription.unsubscribe();
if (viewControllerSubscription != null) {
viewControllerSubscription.unsubscribe();
viewControllerSubscription = null;
}
super.onDestroy();
}

View File

@ -0,0 +1,162 @@
package ru.touchin.roboswag.components.navigation;
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 java.io.Serializable;
import ru.touchin.roboswag.core.log.Lc;
import rx.functions.Func1;
/**
* Created by Gavriil Sitnikov on 07/03/2016.
* TODO: fill description
*/
public class ViewControllerNavigation<TLogicBridge> extends FragmentNavigation {
public ViewControllerNavigation(@NonNull final ViewControllerActivity<TLogicBridge> context,
@NonNull final FragmentManager fragmentManager,
@IdRes final int containerViewId) {
super(context, fragmentManager, containerViewId);
}
public <TState extends Serializable> void push(@NonNull final Class<? extends ViewControllerFragment<TState, TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>>> fragmentClass,
@NonNull final TState state) {
addToStack(fragmentClass, null, ViewControllerFragment.createState(state), null, null);
}
public <TState extends Serializable> void push(@NonNull final Class<? extends ViewControllerFragment<TState, TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>>> fragmentClass,
@Nullable final TState state,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(fragmentClass, null, ViewControllerFragment.createState(state), null, transactionSetup);
}
public <TState extends Serializable> void pushForResult(@NonNull final Class<? extends ViewControllerFragment<TState, TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>>> fragmentClass,
@NonNull final Fragment targetFragment,
@NonNull final TState state) {
addToStack(fragmentClass, targetFragment, ViewControllerFragment.createState(state), fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, null);
}
public <TState extends Serializable> void pushForResult(@NonNull final Class<? extends ViewControllerFragment<TState, TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>>> fragmentClass,
@NonNull final Fragment targetFragment,
@Nullable final TState state,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(fragmentClass, targetFragment, ViewControllerFragment.createState(state), fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, transactionSetup);
}
public <TState extends Serializable> void setAsTop(@NonNull final Class<? extends ViewControllerFragment<TState, TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>>> fragmentClass,
@NonNull final TState state) {
setAsTop(fragmentClass, ViewControllerFragment.createState(state), null);
}
public <TState extends Serializable> void setAsTop(@NonNull final Class<? extends ViewControllerFragment<TState, TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>>> fragmentClass,
@Nullable final TState state,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
setAsTop(fragmentClass, ViewControllerFragment.createState(state), transactionSetup);
}
public <TState extends Serializable> void setInitial(@NonNull final Class<? extends ViewControllerFragment<TState, TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>>> fragmentClass,
@NonNull final TState state) {
setInitial(fragmentClass, ViewControllerFragment.createState(state), null);
}
public <TState extends Serializable> void setInitial(@NonNull final Class<? extends ViewControllerFragment<TState, TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>>> fragmentClass,
@Nullable final TState state,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
setInitial(fragmentClass, ViewControllerFragment.createState(state), transactionSetup);
}
public <TState extends Serializable> void pushViewController(@NonNull final Class<? extends ViewController<TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>,
? extends SimpleViewControllerFragment<TState, TLogicBridge, ? extends ViewControllerActivity<TLogicBridge>>>> viewControllerClass,
@NonNull final TState state) {
addViewControllerToStack(viewControllerClass, null, state, null, null);
}
public <TState extends Serializable> void pushViewController(@NonNull final Class<? extends ViewController<TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>,
? extends SimpleViewControllerFragment<TState, TLogicBridge, ? extends ViewControllerActivity<TLogicBridge>>>> viewControllerClass,
@Nullable final TState state,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
addViewControllerToStack(viewControllerClass, null, state, null, transactionSetup);
}
public <TState extends Serializable> void pushViewControllerForResult(@NonNull final Class<? extends ViewController<TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>,
? extends SimpleViewControllerFragment<TState, TLogicBridge, ? extends ViewControllerActivity<TLogicBridge>>>> viewControllerClass,
@NonNull final Fragment targetFragment,
@NonNull final TState state) {
addViewControllerToStack(viewControllerClass, targetFragment, state, viewControllerClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, null);
}
public <TState extends Serializable> void pushViewControllerForResult(@NonNull final Class<? extends ViewController<TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>,
? extends SimpleViewControllerFragment<TState, TLogicBridge, ? extends ViewControllerActivity<TLogicBridge>>>> viewControllerClass,
@NonNull final Fragment targetFragment,
@Nullable final TState state,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
addViewControllerToStack(viewControllerClass, targetFragment, state, viewControllerClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, transactionSetup);
}
public <TState extends Serializable> void setViewControllerAsTop(@NonNull final Class<? extends ViewController<TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>,
? extends SimpleViewControllerFragment<TState, TLogicBridge, ? extends ViewControllerActivity<TLogicBridge>>>> viewControllerClass,
@NonNull final TState state) {
addViewControllerToStack(viewControllerClass, null, state, viewControllerClass.getName() + ' ' + TOP_FRAGMENT_TAG_MARK, null);
}
public <TState extends Serializable> void setViewControllerAsTop(@NonNull final Class<? extends ViewController<TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>,
? extends SimpleViewControllerFragment<TState, TLogicBridge, ? extends ViewControllerActivity<TLogicBridge>>>> viewControllerClass,
@Nullable final TState state,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
addViewControllerToStack(viewControllerClass, null, state, viewControllerClass.getName() + ' ' + TOP_FRAGMENT_TAG_MARK, transactionSetup);
}
public <TState extends Serializable> void setInitialViewController(@NonNull final Class<? extends ViewController<TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>,
? extends SimpleViewControllerFragment<TState, TLogicBridge, ? extends ViewControllerActivity<TLogicBridge>>>> viewControllerClass,
@NonNull final TState state) {
setInitialViewController(viewControllerClass, state, null);
}
public <TState extends Serializable> void setInitialViewController(@NonNull final Class<? extends ViewController<TLogicBridge,
? extends ViewControllerActivity<TLogicBridge>,
? extends SimpleViewControllerFragment<TState, TLogicBridge, ? extends ViewControllerActivity<TLogicBridge>>>> viewControllerClass,
@Nullable final TState state,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
if (getFragmentManager().isDestroyed()) {
Lc.assertion("FragmentManager is destroyed");
return;
}
if (getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
setViewControllerAsTop(viewControllerClass, state, transactionSetup);
}
protected <TState extends Serializable> void addViewControllerToStack(
@NonNull final Class<? extends ViewController<TLogicBridge, ? extends ViewControllerActivity<TLogicBridge>, ?>> viewControllerClass,
@Nullable final Fragment targetFragment,
@Nullable final TState state,
@Nullable final String backStackTag,
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(SimpleViewControllerFragment.class, targetFragment,
SimpleViewControllerFragment.createState(viewControllerClass, state), backStackTag, transactionSetup);
}
}