view controllers fixed. now working

This commit is contained in:
Gavriil Sitnikov 2016-02-20 03:56:25 +03:00
parent a0b9326ab2
commit 4df5862967
8 changed files with 196 additions and 81 deletions

View File

@ -168,7 +168,7 @@ public abstract class AbstractBaseActivity extends AppCompatActivity
}
@Override
public void onFragmentStarted(@NonNull final AbstractBaseFragment fragment) {
public void onFragmentStarted(@NonNull final Fragment fragment) {
hideSoftInput();
}

View File

@ -106,7 +106,7 @@ public abstract class AbstractBaseFragment<TViewController extends AbstractBaseF
protected abstract TViewController createViewController(@NonNull final View view, @Nullable final Bundle savedInstanceState);
@Override
public void onFragmentStarted(@NonNull final AbstractBaseFragment fragment) {
public void onFragmentStarted(@NonNull final Fragment fragment) {
//do nothing
}

View File

@ -19,6 +19,9 @@
package ru.touchin.roboswag.components.navigation;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
/**
* Created by Gavriil Sitnikov on 08/10/2014.
* Base interface to listen fragment changing
@ -26,5 +29,6 @@ package ru.touchin.roboswag.components.navigation;
public interface OnFragmentStartedListener {
/* Raises by fragment to notify that it is started */
void onFragmentStarted(AbstractBaseFragment fragment);
void onFragmentStarted(@NonNull Fragment fragment);
}

View File

@ -1,11 +1,3 @@
package ru.touchin.roboswag.components.navigation;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
@ -25,14 +17,28 @@ import android.view.View;
*
*/
package ru.touchin.roboswag.components.navigation;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.ViewGroup;
import java.io.Serializable;
/**
* 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<TLogicBridge,
public class ViewController<TState extends Serializable,
TLogicBridge,
TActivity extends AppCompatActivity,
TFragment extends ViewControllerFragment<TLogicBridge, TActivity>> {
@NonNull
private final TState state;
@NonNull
private final TLogicBridge logicBridge;
@NonNull
@ -40,16 +46,25 @@ public class ViewController<TLogicBridge,
@NonNull
private final TFragment fragment;
@NonNull
private final View view;
private final ViewGroup container;
//not completed yet
@SuppressWarnings("PMD.UnusedFormalParameter")
public ViewController(@NonNull final CreationContext<TLogicBridge, TActivity, TFragment> creationContext,
public ViewController(@NonNull final CreationContext<TState, TLogicBridge, TActivity, TFragment> creationContext,
@Nullable final Bundle savedInstanceState) {
this.state = creationContext.state;
this.logicBridge = creationContext.logicBridge;
this.activity = creationContext.activity;
this.fragment = creationContext.fragment;
this.view = creationContext.view;
this.container = creationContext.container;
}
/**
* Returns specific object which contains state of ViewController.
*
* @return Object of TState type.
*/
@NonNull
public TState getState() {
return state;
}
/**
@ -65,7 +80,7 @@ public class ViewController<TLogicBridge,
/**
* Returns view's activity.
*
* @return Returns activity;
* @return Returns activity.
*/
@NonNull
public TActivity getActivity() {
@ -85,11 +100,11 @@ public class ViewController<TLogicBridge,
/**
* Returns view instantiated in {@link #getFragment} fragment attached to {@link #getActivity} activity.
*
* @return Returns view;
* @return Returns view.
*/
@NonNull
public View getView() {
return view;
public ViewGroup getContainer() {
return container;
}
public void onDestroy() {
@ -99,10 +114,13 @@ public class ViewController<TLogicBridge,
/**
* Class to simplify constructor override.
*/
public static class CreationContext<TLogicBridge,
public static class CreationContext<TState extends Serializable,
TLogicBridge,
TActivity extends AppCompatActivity,
TFragment extends ViewControllerFragment<TLogicBridge, TActivity>> {
@NonNull
private final TState state;
@NonNull
private final TLogicBridge logicBridge;
@NonNull
@ -110,16 +128,18 @@ public class ViewController<TLogicBridge,
@NonNull
private final TFragment fragment;
@NonNull
private final View view;
private final ViewGroup container;
public CreationContext(@NonNull final TLogicBridge logicBridge,
public CreationContext(@NonNull final TState state,
@NonNull final TLogicBridge logicBridge,
@NonNull final TActivity activity,
@NonNull final TFragment fragment,
@NonNull final View view) {
@NonNull final ViewGroup container) {
this.state = state;
this.logicBridge = logicBridge;
this.activity = activity;
this.fragment = fragment;
this.view = view;
this.container = container;
}
}

View File

@ -19,32 +19,55 @@
package ru.touchin.roboswag.components.navigation;
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;
import android.view.ViewGroup;
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.android.RxAndroidUtils;
import rx.Observable;
import rx.Scheduler;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.subjects.BehaviorSubject;
/**
* Created by Gavriil Sitnikov on 21/10/2015.
* TODO: fill description
* Fragment that creates {@link ViewController} between {@link #onViewCreated} and {@link #onDestroyView}.
* [phase 1]
*/
public abstract class ViewControllerFragment<TLogicBridge, TActivity extends AppCompatActivity> extends ViewFragment<TActivity> {
private static final String VIEW_CONTROLLER_STATE_EXTRA = "VIEW_CONTROLLER_STATE_EXTRA";
/**
* Creates {@link Bundle} which will store state.
*
* @param state State to use into ViewController.
* @return Returns bundle with state inside.
*/
public static Bundle createState(@NonNull final Serializable state) {
final Bundle result = new Bundle();
result.putSerializable(VIEW_CONTROLLER_STATE_EXTRA, state);
return result;
}
private final BehaviorSubject<TActivity> activitySubject = BehaviorSubject.create();
private final BehaviorSubject<Pair<View, Bundle>> viewSubject = BehaviorSubject.create();
private final BehaviorSubject<Pair<ViewGroup, Bundle>> viewSubject = BehaviorSubject.create();
private final Scheduler backgroundScheduler = RxAndroidUtils.createLooperScheduler();
@Nullable
private ViewController viewController;
@Nullable
private Subscription viewControllerSubscription;
/**
@ -53,7 +76,7 @@ public abstract class ViewControllerFragment<TLogicBridge, TActivity extends App
* @return Returns class of specific LogicService.
*/
@NonNull
protected abstract Class<LogicService<TLogicBridge>> getLogicServiceClass();
protected abstract Class<? extends LogicService<TLogicBridge>> getLogicServiceClass();
/**
* It should return specific ViewController class to control instantiated view by logic bridge after activity creation.
@ -61,7 +84,7 @@ public abstract class ViewControllerFragment<TLogicBridge, TActivity extends App
* @return Returns class of specific ViewController.
*/
@NonNull
protected abstract Class<ViewController<TLogicBridge, TActivity,
protected abstract Class<? extends ViewController<? extends Serializable, TLogicBridge, TActivity,
? extends ViewControllerFragment<TLogicBridge, TActivity>>> getViewControllerClass();
// need throwable for app stability
@ -70,39 +93,73 @@ public abstract class ViewControllerFragment<TLogicBridge, TActivity extends App
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getContext() != null) {
viewControllerSubscription = Observable.combineLatest(activitySubject.distinctUntilChanged(), viewSubject.distinctUntilChanged(),
RxAndroidUtils.observeService(getContext(), getLogicServiceClass())
.map(service -> service != null ? service.getLogicBridge() : null)
.distinctUntilChanged(),
(activity, view, logicBridge) -> {
if (activity == null || view == null || logicBridge == null) {
return null;
}
final ViewController.CreationContext<TLogicBridge, TActivity,
? extends ViewControllerFragment<TLogicBridge, TActivity>> creationContext
= new ViewController.CreationContext<>(logicBridge, activity, this, view.first);
if (getViewControllerClass().getConstructors().length == 1) {
try {
return (ViewController) getViewControllerClass().getConstructors()[0].newInstance(creationContext, view.second);
} catch (Throwable throwable) {
Lc.assertion(throwable);
}
} else {
Lc.assertion("There should be single constructor for " + getViewControllerClass());
}
return null;
}).subscribe(this::onViewControllerChanged);
} else {
Lc.assertion("Context is null in onCreate.");
if (getContext() == null) {
Lc.assertion("Context is null in onCreate");
}
viewControllerSubscription = Observable
.combineLatest(Observable.<Serializable>create(subscriber -> {
final Serializable state = savedInstanceState != null
? savedInstanceState.getSerializable(VIEW_CONTROLLER_STATE_EXTRA)
: (getArguments() != null ? getArguments().getSerializable(VIEW_CONTROLLER_STATE_EXTRA) : null);
if (state != null) {
subscriber.onNext(state);
} else {
Lc.assertion("State should be stored into arguments or savedInstanceState");
}
subscriber.onCompleted();
}).subscribeOn(backgroundScheduler),
RxAndroidUtils.observeService(getContext(), getLogicServiceClass())
.map(service -> service != null ? service.getLogicBridge() : null)
.distinctUntilChanged()
.observeOn(backgroundScheduler),
activitySubject.distinctUntilChanged().observeOn(backgroundScheduler),
viewSubject.distinctUntilChanged().observeOn(backgroundScheduler),
(state, logicBridge, activity, view) -> {
if (activity == null || view == null || logicBridge == null) {
return null;
}
final ViewController.CreationContext<? extends Serializable, TLogicBridge, TActivity,
? extends ViewControllerFragment<TLogicBridge, TActivity>> creationContext
= new ViewController.CreationContext<>(state, logicBridge, activity, this, view.first);
if (getViewControllerClass().getConstructors().length > 1) {
Lc.assertion("There should be single constructor for " + getViewControllerClass());
return null;
}
try {
final Constructor<?> constructor = getViewControllerClass().getConstructors()[0];
switch (constructor.getParameterTypes().length) {
case 2:
return (ViewController) constructor.newInstance(creationContext, view.second);
case 3:
return (ViewController) constructor.newInstance(this, creationContext, view.second);
default:
Lc.assertion("Wrong constructor parameters count: " + constructor.getParameterTypes().length);
return null;
}
} catch (final Throwable throwable) {
Lc.assertion(throwable);
return null;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onViewControllerChanged);
}
@Deprecated
@NonNull
@Override
public View onCreateView(@NonNull final LayoutInflater inflater,
@Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) {
return new PlaceholderView(inflater.getContext());
}
@Override
public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) {
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//TODO...
viewSubject.onNext(new Pair<>(new FrameLayout(view.getContext()), savedInstanceState));
}
@Override
@ -116,6 +173,26 @@ public abstract class ViewControllerFragment<TLogicBridge, TActivity extends App
this.viewController.onDestroy();
}
this.viewController = viewController;
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 {
Lc.assertion("View of fragment should be PlaceholderView");
}
}
@Override
public void onSaveInstanceState(@NonNull final Bundle stateToSave) {
super.onSaveInstanceState(stateToSave);
if (viewController != null) {
stateToSave.putSerializable(VIEW_CONTROLLER_STATE_EXTRA, viewController.getState());
} else if (getArguments() != null && getArguments().containsKey(VIEW_CONTROLLER_STATE_EXTRA)) {
stateToSave.putSerializable(VIEW_CONTROLLER_STATE_EXTRA, getArguments().getSerializable(VIEW_CONTROLLER_STATE_EXTRA));
}
}
@Override
@ -126,10 +203,7 @@ public abstract class ViewControllerFragment<TLogicBridge, TActivity extends App
@Override
public void onDestroy() {
if (viewControllerSubscription != null) {
viewControllerSubscription.unsubscribe();
viewControllerSubscription = null;
}
viewControllerSubscription.unsubscribe();
super.onDestroy();
}
@ -139,4 +213,12 @@ public abstract class ViewControllerFragment<TLogicBridge, TActivity extends App
super.onDetach();
}
private static class PlaceholderView extends FrameLayout {
public PlaceholderView(@NonNull final Context context) {
super(context);
}
}
}

View File

@ -29,12 +29,12 @@ import android.view.View;
import android.view.ViewGroup;
import ru.touchin.roboswag.core.log.Lc;
import rx.functions.Action2;
/**
* Created by Gavriil Sitnikov on 21/10/2015.
* Fragment that have specific activity as a parent and can't be background.
* [phase 1]
*/
public abstract class ViewFragment<TActivity extends AppCompatActivity> extends Fragment {
@ -67,24 +67,23 @@ public abstract class ViewFragment<TActivity extends AppCompatActivity> extends
}
}
@Deprecated
@NonNull
@Override
public View onCreateView(@NonNull final LayoutInflater inflater,
@Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) {
throw new IllegalStateException("Method onCreateView() should be override.");
throw new IllegalStateException("Method onCreateView() should be overridden");
}
@Deprecated
@Override
public void onActivityCreated(@Nullable final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getView() != null && getBaseActivity() != null) {
onActivityCreated(getView(), getBaseActivity(), savedInstanceState);
} else {
if (getView() == null || getBaseActivity() == null) {
Lc.assertion("View and activity shouldn't be null");
return;
}
onActivityCreated(getView(), getBaseActivity(), savedInstanceState);
}
/**
@ -99,11 +98,11 @@ public abstract class ViewFragment<TActivity extends AppCompatActivity> extends
}
private void callMethodAfterInstantiation(@NonNull final Action2<View, TActivity> action) {
if (getView() != null && getBaseActivity() != null) {
action.call(getView(), getBaseActivity());
} else {
if (getView() == null || getBaseActivity() == null) {
Lc.assertion("View and activity shouldn't be null");
return;
}
action.call(getView(), getBaseActivity());
}
@Deprecated
@ -120,7 +119,11 @@ public abstract class ViewFragment<TActivity extends AppCompatActivity> extends
* @param activity Activity which fragment attached to.
*/
protected void onStart(@NonNull final View view, @NonNull final TActivity activity) {
//do nothing
if (getParentFragment() instanceof OnFragmentStartedListener) {
((OnFragmentStartedListener) getParentFragment()).onFragmentStarted(this);
} else if (activity instanceof OnFragmentStartedListener) {
((OnFragmentStartedListener) activity).onFragmentStarted(this);
}
}
@Deprecated
@ -177,11 +180,11 @@ public abstract class ViewFragment<TActivity extends AppCompatActivity> extends
@Deprecated
@Override
public void onDestroyView() {
if (getView() != null) {
onDestroyView(getView());
} else {
if (getView() == null) {
Lc.assertion("View shouldn't be null");
return;
}
onDestroyView(getView());
super.onDestroyView();
}

View File

@ -10,18 +10,24 @@ import ru.touchin.roboswag.core.utils.android.ServiceBinder;
* Created by Gavriil Sitnikov on 10/01/2016.
* Service which holds interface to all application's logic objects and methods.
* Any part of application should interact with some part of logic via this interface.
* If it is Service, Activity, Fragment or iew then it should bind itself to that service first then get service and logic bridge from IBinder.
* If it is Service, Activity, Fragment or view then it should bind itself to that service first then get service and logic bridge from IBinder.
* If it is BroadcastReceiver then it should start service which can bind to that service or just send some intent to that service.
* [phase 1]
*/
public abstract class LogicService<TLogicBridge> extends IntentService {
private final TLogicBridge logicBridge;
protected LogicService(final String name) {
super(name);
protected LogicService() {
super("LogicService");
this.logicBridge = createLogicBridge();
}
@Override
protected void onHandleIntent(final Intent intent) {
// do nothing
}
/**
* Creates object which will provide all logic methods and objects of application.
* Any other activity, fragment or service should bind to that service to get logic bridge and start interact with application's logic.

View File

@ -34,7 +34,7 @@ import java.util.List;
import java.util.Map;
/**
* Created by Gavriil Sitnikov on 18/07/2014. [phase 1]
* Created by Gavriil Sitnikov on 18/07/2014.
* Manager for typefaces stored in assets 'fonts' folder.
*/
public final class Typefaces {