org.ioc.commons.impl.android.flowcontrol.placecontroller.AndroidPlaceController Maven / Gradle / Ivy
package org.ioc.commons.impl.android.flowcontrol.placecontroller;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import org.ioc.commons.flowcontrol.activitymapper.IsActivityManager;
import org.ioc.commons.flowcontrol.eventbus.EventBusBinder;
import org.ioc.commons.flowcontrol.placecontroller.HasPlaceData;
import org.ioc.commons.flowcontrol.placecontroller.IsPlace;
import org.ioc.commons.flowcontrol.placecontroller.PlaceController;
import org.ioc.commons.impl.android.flowcontrol.activitymapper.Place;
import org.ioc.commons.impl.android.flowcontrol.activitymapper.Place.Type;
import org.ioc.commons.impl.android.flowcontrol.eventbus.AndroidEventBusImpl;
import org.ioc.commons.ui.IsWidget;
import org.ioc.commons.utils.Format;
import org.ioc.commons.utils.FormatterLogger;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentManager.BackStackEntry;
import android.support.v4.app.FragmentTransaction;
import android.view.HasFrameLayout;
import android.widget.FrameLayout;
import android.widget.Toast;
public class AndroidPlaceController implements PlaceController {
private static final FormatterLogger logger = FormatterLogger.getLogger(AndroidPlaceController.class);
private static final String PLACE_KEY = "PLACE";
public interface PlaceMapper {
> Place
getPlace(P place, Object... params);
}
private static boolean strictMode = true;
private final LinkedList forwardPlaces = new LinkedList();
private Place extends Enum extends IsPlace>> firstPlace;
private AndroidEventBusImpl eventBus;
private final PlaceMapper mapper;
private final Context context;
private boolean firstPlaceHandled;
public AndroidPlaceController(Context context, PlaceMapper placeMapper) {
this.context = context;
this.mapper = placeMapper;
init();
}
private void init() {
this.eventBus = new AndroidEventBusImpl(context.getApplicationContext(), true);
}
@Override
public > void initPlaceController(P firstPlaceId, Object... params) {
Place
firstPlace = this.mapper.getPlace(firstPlaceId, params);
assert (firstPlace != null) : "The default cannot be null";
this.firstPlace = firstPlace;
}
@Override
public
> void initPlaceController(boolean handleCurrentPlaceNow, P firstPlaceId,
Object... params) {
this.initPlaceController(firstPlaceId, params);
if (handleCurrentPlaceNow) {
this.handleCurrentPlace();
}
}
@Override
public
> void goTo(P placeId, Object... params) {
goTo(true, placeId, params);
}
@Override
public
> void goTo(boolean addToHistoryStack, P placeId, Object... params) {
Place
place = this.mapper.getPlace(placeId, params);
goTo(addToHistoryStack, place);
}
@Override
public void goTo(HasPlaceData placeData) {
goTo(true, placeData);
}
@Override
public void goTo(boolean addToHistoryStack, HasPlaceData placeData) {
if (placeData == null) {
throw new NullPointerException("Cannot go to a null place");
}
if (!(placeData instanceof Place>)) {
throw new IllegalArgumentException(
Format.substitute("placeData parameter should be an instance of {0} but found {1}", Place.class,
placeData.getClass()));
}
// if (context == null) {
// IsActivityManager activityManager =
// this.activityManagerProvider.getActivityManager();
// IsWidget display = activityManager.getDisplay();
//
// throw new IllegalArgumentException(Format.substitute(
// "display should be an instance of {0} or {1} but found {2}",
// Context.class, HasContext.class,
// display != null ? display.getClass() : "-- null --"));
// }
Place> place = (Place>) placeData;
goTo(context, place, addToHistoryStack);
}
// private Context getContext() {
// IsActivityManager activityManager =
// this.activityManagerProvider.getActivityManager();
// IsWidget display = activityManager.getDisplay();
//
// Context context = null;
//
// if (display instanceof HasContext) {
// context = ((HasContext) display).getContext();
// } else if (display instanceof Context) {
// context = (Context) display;
// }
// return context;
// }
protected void goTo(Context context, Place> place, boolean addToHistoryStack) {
Enum extends IsPlace> placeId = place.getPlaceId();
switch (place.getType()) {
case ACTIVITY_PLACE:
Class extends Activity> toActivity = place.getActivity();
startActivity(placeId, context, toActivity, place.getForResultRequestCode(), place.getExtras());
break;
case FRAGMENT_PLACE:
showFragment(placeId, place.getFragment(), context, place.equals(firstPlace), addToHistoryStack);
break;
case INTENT_PLACE:
startActivity(placeId, context, place.getIntent(), place.getForResultRequestCode());
break;
}
}
protected
> void startActivity(P placeId, Context context,
Class extends Activity> toActivity, Integer forResultRequestCode, Bundle extras) {
Intent i = new Intent(context, toActivity);
if (extras != null) {
i.putExtras(extras);
}
startActivity(placeId, context, i, forResultRequestCode);
}
protected
> void startActivity(P placeId, Context context, Intent i,
Integer forResultRequestCode) {
i.putExtra(PLACE_KEY, placeId.ordinal());
startActivity(placeId, context, i, true, forResultRequestCode);
}
protected
> void showFragment(final P placeId, final Fragment fragment,
Context context, final boolean firstPlace, final boolean addToHistoryStack) {
IsActivityManager activityManager = ensureIsActivityManager(context);
IsWidget display = activityManager.getDisplay();
Object view = display.asWidget();
if (view instanceof HasFrameLayout) {
view = ((HasFrameLayout) view).getFrameLayout();
if (view == null) {
throw new IllegalArgumentException(Format.substitute("Required frame layout instance but found null"));
}
}
if (!(view instanceof FrameLayout)) {
throw new IllegalArgumentException(Format.substitute(
"Required {0} as display for controlling fragment places but found {1}",
FrameLayout.class.getSimpleName(), view != null ? view.getClass() : "-- null --"));
}
final FrameLayout frameLayout = (FrameLayout) view;
if (context instanceof FragmentActivity) {
final FragmentActivity host = (FragmentActivity) context;
if (host.isDestroyed()) {
throw new IllegalArgumentException("Invalid destroyed activity as context");
}
host.runOnUiThread(new Runnable() {
@Override
public void run() {
String activeTag = getActiveFragmentTag(host);
// Fragment visibleFragment = getVisibleFragment(host);
if (Objects.equals(activeTag, getTag(placeId))) {
// if (Objects.equals(visibleFragment, fragment)) {
return;
}
frameLayout.removeAllViews();
if (firstPlace && !firstPlaceHandled) {
// // Check that the activity is using the layout
// version
// with
// // the fragment_container FrameLayout
// if (findViewById(R.id.fragment_container) != null) {
//
// However, if we're being restored from a previous
// state,
// then we don't need to do anything and should return
// or
// else
// we could end up with overlapping fragments.
// if (savedInstanceState != null) {
// return;
// }
host.getSupportFragmentManager().beginTransaction()
.add(frameLayout.getId(), fragment, getTag(placeId)).commitAllowingStateLoss();
} else {
FragmentTransaction tx = host.getSupportFragmentManager().beginTransaction()
.replace(frameLayout.getId(), fragment, getTag(placeId));
if (addToHistoryStack) {
tx.addToBackStack(getTag(placeId));
}
tx.commitAllowingStateLoss();
}
logBackStack(host, false);
eventBus.fireEvent(Event.PlaceChanged, placeId);
}
});
} else {
throw new IllegalArgumentException(Format.substitute(
"Required context which extends from {0} or {1} for controlling fragment places",
FragmentActivity.class.getSimpleName(), Activity.class.getSimpleName()));
}
}
private IsActivityManager ensureIsActivityManager(Context context) {
if (!(context instanceof IsActivityManager)) {
throw new IllegalArgumentException(Format.substitute(
"Required context which implements {0} for controlling fragment places",
IsActivityManager.class.getSimpleName()));
}
IsActivityManager activityManager = (IsActivityManager) context;
return activityManager;
}
protected
> void showFragment(final P placeId,
final android.app.Fragment fragment, Context context, final boolean firstPlace,
final boolean addToHistoryStack) {
IsActivityManager activityManager = ensureIsActivityManager(context);
IsWidget display = activityManager.getDisplay();
Object view = display.asWidget();
if (!(view instanceof FrameLayout)) {
throw new IllegalArgumentException(Format.substitute(
"Required {0} as display for controlling fragment places", FrameLayout.class.getSimpleName()));
}
final FrameLayout frameLayout = (FrameLayout) display;
if (context instanceof Activity) {
final Activity host = (Activity) context;
host.runOnUiThread(new Runnable() {
@Override
public void run() {
String activeTag = getActiveFragmentTag(host);
// Fragment visibleFragment = getVisibleFragment(host);
if (Objects.equals(activeTag, getTag(placeId))) {
// if (Objects.equals(visibleFragment, fragment)) {
return;
}
frameLayout.removeAllViews();
if (firstPlace && !firstPlaceHandled) {
host.getFragmentManager().beginTransaction()
.add(frameLayout.getId(), fragment, getTag(placeId)).commitAllowingStateLoss();
} else {
android.app.FragmentTransaction tx = host.getFragmentManager().beginTransaction()
.replace(frameLayout.getId(), fragment, getTag(placeId));
if (addToHistoryStack) {
tx.addToBackStack(getTag(placeId));
}
tx.commitAllowingStateLoss();
}
logBackStack(host, false);
eventBus.fireEvent(Event.PlaceChanged, placeId);
}
});
} else {
throw new IllegalArgumentException(Format.substitute(
"Required context which extends from {0} or {1} for controlling fragment places",
FragmentActivity.class.getSimpleName(), Activity.class.getSimpleName()));
}
}
private
> void startActivity(P placeId, Context context, Intent intent,
boolean clearForwardPlaces, Integer forResultRequestCode) {
if (forResultRequestCode == null) {
context.startActivity(intent);
} else {
if (!(context instanceof Activity)) {
throw new IllegalArgumentException("Cannot start an activity when Context is not another Activity");
}
Activity activity = (Activity) context;
activity.startActivityForResult(intent, forResultRequestCode);
}
this.eventBus.fireEvent(Event.PlaceChanged, placeId);
if (clearForwardPlaces) {
forwardPlaces.clear();
}
}
@Override
public void goBack() {
forwardPlaces.add(this.getCurrentPlaceData());
Activity ca = getActivity();
if (ca == null) {
throw new IllegalArgumentException("Required context activity but found null");
}
ca.onBackPressed();
}
private Activity getActivity() {
if (context != null && !(context instanceof Activity)) {
throw new IllegalArgumentException("Cannot start an activity when Context is not another Activity. Found "
+ context);
}
Activity activity = (Activity) context;
return activity;
}
@Override
public void goForward() {
if (!forwardPlaces.isEmpty()) {
HasPlaceData placeData = forwardPlaces.removeLast();
assert (placeData instanceof Place>) : "place parameter should be an instance of " + Place.class;
Place> place = (Place>) placeData;
goTo(place);
}
}
@Override
public void appendPlaceInHistory(HasPlaceData place) {
forwardPlaces.addFirst(place);
}
@SuppressLint("Assert")
@Override
public HasPlaceData getCurrentPlaceData() {
Activity where = getActivity();
assert (!strictMode || (where == null) || (where instanceof HasPlaceData) || (where instanceof FragmentActivity)) : "There is no possible to introduce a Place which does not inherit from HasPlaceData in strit mode. Disable strict mode setting AndroidPlaceController.setStrictMode(false). Where="
+ where;
if (where instanceof FragmentActivity) {
// String curFragmentTag = getActiveFragmentTag((FragmentActivity)
// where);
// if (curFragmentTag != null) {
// Fragment fragment = ((FragmentActivity)
// where).getSupportFragmentManager().findFragmentByTag(
// curFragmentTag);
Fragment fragment = getVisibleFragment((FragmentActivity) where);
if (fragment != null) {
String tag = fragment.getTag();
try {
int sep = tag != null ? tag.lastIndexOf('.') : -1;
String className = tag.substring(0, sep);
String enumValue = tag.substring(sep + 1);
Class enumClazz = Class.forName(className);
@SuppressWarnings("unchecked")
Enum curPlace = Enum.valueOf(enumClazz, enumValue);
return new Place>(curPlace, fragment);
} catch (Exception e) {
if (this.firstPlace != null) {
logger.warn(e, "Unknown current place: '{0}'. Known places from: {1}", tag, this.firstPlace
.getPlaceId().getClass());
} else {
logger.warn(e, "Unknown current place: '{0}'. Unknown valid places.", tag);
}
/*
* Unknown place
*/
return null;
}
} else {
return null;
}
}
return (where instanceof HasPlaceData) ? (HasPlaceData) where : null;
}
private Fragment getVisibleFragment(FragmentActivity fa) {
List fragments = fa.getSupportFragmentManager().getFragments();
if (fragments != null) {
for (int i = fragments.size() - 1; i >= 0; i--) {
Fragment f = fragments.get(i);
if (f != null && f.getUserVisibleHint()) {
return f;
}
}
}
return null;
}
private String getActiveFragmentTag(FragmentActivity fa) {
if (fa.getSupportFragmentManager().getBackStackEntryCount() == 0) {
return null;
}
String tag = fa.getSupportFragmentManager()
.getBackStackEntryAt(fa.getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
return tag;
}
private String getActiveFragmentTag(Activity fa) {
if (fa.getFragmentManager().getBackStackEntryCount() == 0) {
return null;
}
String tag = fa.getFragmentManager().getBackStackEntryAt(fa.getFragmentManager().getBackStackEntryCount() - 1)
.getName();
return tag;
}
private String getPreviousFragmentTag(FragmentActivity fa) {
if (fa.getSupportFragmentManager().getBackStackEntryCount() < 2) {
return null;
}
String tag = fa.getSupportFragmentManager()
.getBackStackEntryAt(fa.getSupportFragmentManager().getBackStackEntryCount() - 2).getName();
return tag;
}
public void logBackStack(FragmentActivity fa, boolean toast) {
if (fa.getSupportFragmentManager().getBackStackEntryCount() == 0) {
logger.debug("Empty backstack");
}
for (int i = 0; i < fa.getSupportFragmentManager().getBackStackEntryCount(); i++) {
BackStackEntry entry = fa.getSupportFragmentManager().getBackStackEntryAt(i);
String message = Format.substitute("Backstack Pos: {0} Id: {1} Name: {2}", i, entry.getId(),
entry.getName());
logger.debug(message);
if (toast) {
Toast.makeText(fa, message, Toast.LENGTH_SHORT).show();
}
}
}
public void logBackStack(Activity fa, boolean toast) {
if (fa.getFragmentManager().getBackStackEntryCount() == 0) {
logger.debug("Empty backstack");
}
for (int i = 0; i < fa.getFragmentManager().getBackStackEntryCount(); i++) {
android.app.FragmentManager.BackStackEntry entry = fa.getFragmentManager().getBackStackEntryAt(i);
String message = Format.substitute("Backstack Pos: {0} Id: {1} Name: {2}", i, entry.getId(),
entry.getName());
logger.debug(message);
if (toast) {
Toast.makeText(fa, message, Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void handleCurrentPlace() {
logger.entering("handleCurrentPlace");
logger.debug("Handling curren place...");
Activity ca = getActivity();
// if (!this.firstPlaceHandled
// && this.firstPlace != null
// && ((ca == null && this.firstPlace.getType() != Type.FRAGMENT_PLACE)
// || (this.firstPlace.getType() == Type.FRAGMENT_PLACE && ca instanceof
// FragmentActivity))) {
// this.firstPlaceHandled = true;
// logger.debug("Going to first place {0} at the first time",
// firstPlace);
// this.goTo(firstPlace);
// } else {
HasPlaceData cpd = getCurrentPlaceData();
if (cpd != null) {
logger.debug("Notifying detected placed changed", firstPlace);
this.eventBus.fireEvent(Event.PlaceChanged, cpd.getPlaceId());
} else if (this.firstPlace != null
&& (this.firstPlace.getType() == Type.FRAGMENT_PLACE && ca instanceof FragmentActivity)) {
/*
* When current place is null, is a fragment, and the current place
* (the current place) is not set, we go to the main fragment (It
* happens when activity has been created again and the previous
* fragment has not been saved)
*/
logger.debug("Going to first place {0} again", firstPlace);
this.firstPlaceHandled = true;
this.goTo(firstPlace);
}
// }
logger.exiting("handleCurrentPlace");
}
@Override
public void notifyPlaceChanged() {
HasPlaceData cpd = getCurrentPlaceData();
if (cpd != null) {
this.eventBus.fireEvent(Event.PlaceChanged, cpd.getPlaceId());
}
}
@Override
public > HasPlaceData getPlaceData(P placeId, Object... params) {
Place
androidPlace = this.mapper.getPlace(placeId, params);
return (HasPlaceData) androidPlace;
}
@Override
public EventBusBinder eventbus() {
return this.eventBus;
}
@Override
public HasPlaceData getFirstPlace() {
return this.firstPlace;
}
protected boolean isFirstPlace(HasPlaceData placeData) {
return this.firstPlace != null && this.firstPlace.equals(placeData);
}
@Override
public boolean isCurrentPlaceFirstPlace() {
return this.isFirstPlace(this.getCurrentPlaceData());
}
/*
* (non-Javadoc)
*
* @see
* org.ioc.commons.flowcontrol.placecontroller.PlaceController#currentPlaceAsUrl
* ()
*/
@Override
public String currentPlaceAsUrl() {
Activity ca = getActivity();
if (ca == null) {
throw new IllegalArgumentException("Required context activity but found null");
}
Intent in = ca != null ? ca.getIntent() : null;
Uri data = in != null ? in.getData() : null;
return data != null ? data.toString() : "";
}
@Override
public > String getPlaceAsUrl(P placeId, Object... params) {
Activity ca = getActivity();
if (ca == null) {
throw new IllegalArgumentException("Required context activity but found null");
}
Place
place = this.mapper.getPlace(placeId, params);
Intent in = null;
Bundle extras = place.getExtras();
switch (place.getType()) {
case ACTIVITY_PLACE:
in = new Intent(ca, place.getActivity());
break;
case FRAGMENT_PLACE:
in = new Intent(ca, place.getFragment().getActivity() != null ? place.getFragment().getActivity()
.getClass() : place.getFragment().getClass());
break;
default:
throw new IllegalArgumentException("Unknow type " + place.getType());
}
in.putExtra(PLACE_KEY, placeId.ordinal());
if (extras != null) {
in.putExtras(extras);
}
Uri data = in != null ? in.getData() : null;
return data != null ? data.toString() : "";
}
@Override
public String getPlaceAsUrl(HasPlaceData placeData) {
assert (placeData instanceof Intent) : "place parameter should be an instance of " + Intent.class;
// Activity ca = currentActivityProvider.getCurrentActivity();
Intent in = (Intent) placeData;
Uri data = in != null ? in.getData() : null;
return data != null ? data.toString() : "";
}
public static boolean isStrictMode() {
return strictMode;
}
public static void setStrictMode(boolean strictMode) {
AndroidPlaceController.strictMode = strictMode;
}
@Override
public boolean removeCurrentPlaceFromHistory() {
Activity where = getActivity();
if (where instanceof FragmentActivity) {
FragmentManager sfm = ((FragmentActivity) where).getSupportFragmentManager();
return sfm.popBackStackImmediate();
// return true;
}
return false;
}
@Override
public
> void goBackOrTo(P placeId, Object... params) {
Activity where = getActivity();
String prevTag = null;
if (where instanceof FragmentActivity) {
prevTag = getPreviousFragmentTag((FragmentActivity) where);
}
if (!Objects.equals(prevTag, getTag(placeId))) {
goTo(placeId, params);
} else {
goBack();
}
}
protected
> String getTag(final P placeId) {
return placeId.getClass().getName() + "." + placeId.name();
}
}