Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/***********************************************************************************************************************
*
* blueBill Mobile - open source birdwatching
* ==========================================
*
* Copyright (C) 2009, 2010 by Tidalwave s.a.s. (http://www.tidalwave.it)
* http://bluebill.tidalwave.it/mobile/
*
***********************************************************************************************************************
*
* 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.
*
***********************************************************************************************************************
*
* $Id: ControlFlow.java,v 67dcb737b74e 2010/06/09 01:20:14 fabrizio $
*
**********************************************************************************************************************/
package it.tidalwave.mobile.android.ui;
import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.openide.util.Lookup;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import it.tidalwave.util.Parameters;
import it.tidalwave.util.logging.Logger;
import java.util.ArrayList;
import java.util.List;
/***********************************************************************************************************************
*
* @author Fabrizio Giudici
* @version $Id: $
*
**********************************************************************************************************************/
public abstract class ControlFlow
{
private static final String CLASS = ControlFlow.class.getName();
private static final Logger logger = Logger.getLogger(CLASS);
/** Flags nextStep() to use the standard Intent mechanism for finding the next Activity, instead of relying of
* the registered sequences. It's still important to use nextStep() instead of Activity.startActivity() in
* order to propagate the ControlFlow class name and requestCode. */
public final static Object USE_INTENT_FILTER = "USE_INTENT_FILTER";
private static int nextRequestCode = 1000;
/* package */ final static String CONTROL_FLOW_CLASS_NAME = ControlFlow.class.getName() + ".controlFlowClassName";
private final static Map> REQUEST_CODE_TO_CONTROL_FLOW_MAP = new HashMap>();
private final static Map, Integer> CONTROL_FLOW_MAP_TO_REQUEST_CODE = new HashMap, Integer>();
private final Map, List> transitionMap =
new HashMap, List>();
private Class extends Activity> initialActivity;
protected Activity activity;
private final int requestCode;
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
public static interface Condition
{
public boolean compute (@Nonnull Object ... args);
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
private static class ConditionAndActivityPair
{
@Nonnull
protected final Condition condition;
@Nonnull
protected final Class extends Activity> activityClass;
public ConditionAndActivityPair (final @Nonnull Condition condition,
final @Nonnull Class extends Activity> activityClass)
{
this.condition = condition;
this.activityClass = activityClass;
}
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
public class FlowBuilder
{
private final Class extends Activity> from;
private Condition condition;
protected FlowBuilder (final @Nonnull Class extends Activity> from)
{
this.from = from;
}
@Nonnull
public FlowBuilder completes()
{
return completesWith(TRUE);
}
@Nonnull
public synchronized FlowBuilder completesWith (final @Nonnull Condition condition)
{
this.condition = condition;
return this;
}
@Nonnull
public FlowBuilder completesWithout (final @Nonnull Condition condition)
{
return completesWith(not(condition));
}
public void thenForwardTo (final @Nonnull Class extends Activity> to)
{
List pairs = transitionMap.get(from);
if (pairs == null)
{
pairs = new ArrayList();
transitionMap.put(from, pairs);
}
pairs.add(new ConditionAndActivityPair(condition, to));
}
private final Condition TRUE = new Condition()
{
public boolean compute (final @Nonnull Object... args)
{
return true;
}
};
@Nonnull
private Condition not (final @Nonnull Condition condition)
{
return new Condition()
{
public boolean compute (final @Nonnull Object... args)
{
return !condition.compute(args);
}
};
}
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
// TODO: I'd like to have the requestCode auto-initialized, but I'm not sure it always works. What about the
// activity gets unloaded, thus we lose the CONTROL_FLOW_MAP_TO_REQUEST_CODE? When the ControlFlow is used again
// it would recreate a requestCode, but we don't have a guarantee that ControlFlow subclasses are reinstantiated
// in the same order, so they'd get different requestCodes. Must understand better when classes are unloaded to
// understand whether it can happen or not.
protected ControlFlow()
{
synchronized (CONTROL_FLOW_MAP_TO_REQUEST_CODE)
{
final Class extends ControlFlow> clazz = getClass();
if (CONTROL_FLOW_MAP_TO_REQUEST_CODE.containsKey(clazz))
{
requestCode = CONTROL_FLOW_MAP_TO_REQUEST_CODE.get(clazz);
}
else
{
requestCode = nextRequestCode++;
REQUEST_CODE_TO_CONTROL_FLOW_MAP.put(requestCode, clazz);
CONTROL_FLOW_MAP_TO_REQUEST_CODE.put(clazz, requestCode);
logger.info("%s registered to requestCode: %d", clazz, requestCode);
}
}
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
protected void startFrom (final @Nonnull Class extends Activity> initialActivity)
{
this.initialActivity = initialActivity;
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Nonnull
protected FlowBuilder when (final @Nonnull Class extends Activity> initialActivity)
{
return new FlowBuilder(initialActivity);
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
public int getRequestCode()
{
return requestCode;
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Nonnull
public Class extends Activity> getInitialActivity()
{
return initialActivity;
}
/*******************************************************************************************************************
*
* Starts a new {@link ControlFlow}.
*
* @param controlFlowClass the {@link ControlFlow} to start
*
******************************************************************************************************************/
public final void start (final @Nonnull Class extends ControlFlow> controlFlowClass)
{
try
{
logger.info("startActivity(%s) - %s", controlFlowClass, activity);
final ControlFlow controlFlow = controlFlowClass.newInstance();
final Class extends Activity> initialActivity = controlFlow.getInitialActivity();
final int requestCode = controlFlow.getRequestCode();
logger.info(">>>> initialActivity: %s, requestCode: %d", initialActivity, requestCode);
final Intent intent = new Intent(getContext(), initialActivity);
intent.putExtra(CONTROL_FLOW_CLASS_NAME, controlFlowClass.getName());
activity.startActivityForResult(intent, requestCode);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Nonnull
public static ControlFlow forActivity (final @Nonnull Activity activity)
{
return new ControlFlowLazyProxy(activity);
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Nonnull
/* package */ static ControlFlow forActivity2 (final @Nonnull Activity activity)
{
Parameters.checkNonNull(activity, "activity");
try
{
final String className = activity.getIntent().getStringExtra(CONTROL_FLOW_CLASS_NAME);
assert className != null : "Sorry, I've lost the CONTROL_FLOW_CLASS_NAME!";
final ControlFlow controlFlow = (ControlFlow)Class.forName(className).newInstance();
controlFlow.activity = activity;
return controlFlow;
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
public static int findRequestCode (final @Nonnull Class extends ControlFlow> controlFlowClass)
{
if (!CONTROL_FLOW_MAP_TO_REQUEST_CODE.containsKey(controlFlowClass))
{
try // try to register it
{
controlFlowClass.newInstance();
}
catch (InstantiationException e)
{
throw new RuntimeException(e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
}
if (!CONTROL_FLOW_MAP_TO_REQUEST_CODE.containsKey(controlFlowClass))
{
throw new RuntimeException("ControlFlow not registered: " + controlFlowClass);
}
return CONTROL_FLOW_MAP_TO_REQUEST_CODE.get(controlFlowClass);
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
public void toNextStep (final @Nonnull Object ... args)
{
toNextStep(new Intent(), args);
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
public void toNextStep (final @Nonnull Intent intent, final @Nonnull Object ... args)
{
logger.info("toNextStep(%s, %s)", intent, Arrays.toString(args));
if (activity == null)
{
throw new IllegalStateException("ControlFlow must have been retrevied by ControlFlow.for_()");
}
if (Arrays.asList(args).contains(USE_INTENT_FILTER))
{
logger.info(">>>> using the intent for starting the next Activity...");
final Intent intentDecorator = new Intent(intent);
intentDecorator.putExtra(CONTROL_FLOW_CLASS_NAME, getClass().getName());
activity.startActivityForResult(intentDecorator, requestCode);
return;
}
Class extends Activity> nextActivityClass = null;
final ComponentName componentName = intent.getComponent();
if (componentName == null)
{
final List pairs = transitionMap.get(activity.getClass());
if (pairs != null)
{
for (final ConditionAndActivityPair pair : pairs)
{
if (pair.condition.compute(args))
{
nextActivityClass = pair.activityClass;
break;
}
}
}
}
else
{
final String explicitNextActivityClassName = componentName.getClassName();
logger.info(">>>> explicit next activity: %s", explicitNextActivityClassName);
try
{
nextActivityClass = (Class extends Activity>)Class.forName(explicitNextActivityClassName);
}
catch (ClassNotFoundException e2)
{
throw new RuntimeException(e2);
}
}
if (nextActivityClass == null)
{
logger.info(">>>> it was the final step, calling finish()...");
activity.setResult(Activity.RESULT_OK);
activity.finish();
}
else
{
logger.info(">>>> next Activity is: %s", nextActivityClass);
final Intent intentDecorator = new Intent(intent);
intentDecorator.setClass(getContext(), nextActivityClass);
intentDecorator.putExtra(CONTROL_FLOW_CLASS_NAME, getClass().getName());
activity.startActivityForResult(intentDecorator, requestCode);
}
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Nonnull
private static Context getContext()
{
return Lookup.getDefault().lookup(Context.class);
}
}