mirror of https://github.com/tasks/tasks
A/B Testing and Feature flipping infrastructure. AB testing of new user experience.
parent
58fe374d20
commit
7c364a4ff7
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:paddingLeft="4dip"
|
||||
android:paddingRight="4dip"
|
||||
android:background="@drawable/astrid_com_gradient"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logo"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="110dip"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/astrid_com_logo" />
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="20dip"
|
||||
android:text="@string/DLG_loading" />
|
||||
</LinearLayout>
|
@ -0,0 +1,60 @@
|
||||
package com.todoroo.astrid.service;
|
||||
|
||||
@SuppressWarnings("nls")
|
||||
public class StatisticsConstants {
|
||||
public static final String EULA_ACCEPTED = "eula-accepted";
|
||||
public static final String ACTFM_TAG_COMMENT = "actfm-tag-comment";
|
||||
public static final String ACTFM_TASK_COMMENT = "actfm-task-comment";
|
||||
public static final String ACTFM_NEW_USER = "actfm-new-user";
|
||||
public static final String TASK_COMPLETED_V2 = "task-completed-v2";
|
||||
public static final String USER_FIRST_TASK = "user-first-task";
|
||||
public static final String LOST_TASKS_RESTORED = "lost-tasks-restored";
|
||||
public static final String CREATE_TASK = "create-task";
|
||||
public static final String EDIT_TASK = "edit-task";
|
||||
public static final String ACTFM_TASK_CREATED = "actfm-task-created";
|
||||
public static final String ACTFM_TASK_COMPLETED = "actfm-task-completed";
|
||||
public static final String ACTFM_LOGIN_GL_SUCCESS = "actfm-login-gl-success";
|
||||
public static final String ACTFM_LOGIN_PW = "actfm-login-pw";
|
||||
public static final String ACTFM_SIGNUP_PW = "actfm-signup-pw";
|
||||
public static final String ACTFM_LOGIN_GL_START = "actfm-login-gl-start";
|
||||
public static final String ACTFM_LOGIN_FB = "actfm-login-fb";
|
||||
public static final String LAUNCH_FROM_NOTIFICATION = "launch-from-notification";
|
||||
public static final String LAUNCH_FROM_OTHER = "launch-from-other";
|
||||
public static final String LAUNCH_FROM_PPW = "launch-from-ppw";
|
||||
public static final String LAUNCH_FROM_WIDGET = "launch-from-widget";
|
||||
public static final String LAUNCH_FROM_C2DM = "launch-from-c2dm";
|
||||
public static final String LOCALE_EDIT_ALERTS_NO_PLUGIN = "locale-edit-alerts-no-plugin";
|
||||
public static final String LOCALE_EDIT_ALERTS = "locale-edit-alerts";
|
||||
public static final String WIDGET_CONFIG = "widget-config";
|
||||
public static final String FILTER_LIST = "filter-list";
|
||||
public static final String FILTER_SEARCH = "filter-search";
|
||||
public static final String TLA_MENU_LISTS = "tla-menu-lists";
|
||||
public static final String TLA_MENU_ADDONS = "tla-menu-addons";
|
||||
public static final String TLA_MENU_SETTINGS = "tla-menu-settings";
|
||||
public static final String TLA_MENU_SORT = "tla-menu-sort";
|
||||
public static final String TLA_MENU_SYNC = "tla-menu-sync";
|
||||
public static final String TLA_MENU_HELP = "tla-menu-help";
|
||||
public static final String V2_TASK_REPEAT = "v2-task-repeat";
|
||||
public static final String TASK_COMPLETED_INBOX = "task-completed-inbox";
|
||||
public static final String TASK_COMPLETED_FILTER = "task-completed-filter";
|
||||
public static final String TASK_COMPLETED_NOTIFICATION = "task-completed-notification";
|
||||
public static final String ACTFM_SYNC_FINISHED = "actfm-sync-finished";
|
||||
public static final String GTASKS_SYNC_FINISHED = "gtasks-sync-finished";
|
||||
public static final String PDV_SYNC_FINISHED = "pdv-sync-finished";
|
||||
public static final String TASK_CREATED_TASKLIST = "task-created-tasklist";
|
||||
public static final String ACTFM_LOGIN_SHOW = "actfm-login-show";
|
||||
public static final String ACTFM_TASK_SHARED = "actfm-task-shared";
|
||||
public static final String PRODUCTEEV_LOGIN = "producteev-login";
|
||||
public static final String PRODUCTEEV_SIGNUP = "producteev-signup";
|
||||
public static final String TASK_SNOOZE = "task-snooze";
|
||||
public static final String TASK_COMPLETED_API2 = "task-completed-api2";
|
||||
public static final String TIMER_START = "timer-start";
|
||||
public static final String TIMER_STOP = "timer-stop";
|
||||
public static final String GTASKS_TASK_COMPLETED = "gtasks-task-completed";
|
||||
public static final String PDV_TASK_COMPLETED = "pdv-task-completed";
|
||||
public static final String CREATE_CALENDAR_EVENT = "create-calendar-event";
|
||||
public static final String REPEAT_TASK_CREATE = "repeat-task-create";
|
||||
public static final String ACTFM_LIST_SHARED = "actfm-list-shared";
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package com.todoroo.astrid.service.abtesting;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.service.StatisticsService;
|
||||
|
||||
/**
|
||||
* Helper class to facilitate A/B testing by randomly choosing an option
|
||||
* based on probabilities that can be supplied from local defaults
|
||||
* @author Sam Bosley <sam@astrid.com>
|
||||
*
|
||||
*/
|
||||
public class ABChooser {
|
||||
|
||||
public static final int NO_OPTION = -1;
|
||||
|
||||
@Autowired
|
||||
private ABOptions abOptions;
|
||||
|
||||
private final Random random;
|
||||
|
||||
public ABChooser() {
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
random = new Random();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the choice for the specified feature if already made,
|
||||
* or chooses one randomly from the distribution of feature probabilities
|
||||
* if not.
|
||||
*
|
||||
* The statistics session needs to be open here in order to collect statistics
|
||||
*
|
||||
* @param optionKey - the preference key string of the option (defined in ABOptions)
|
||||
* @return
|
||||
*/
|
||||
public int getChoiceForOption(String optionKey) {
|
||||
int pref = readChoiceForOption(optionKey);
|
||||
if (pref > NO_OPTION) return pref;
|
||||
|
||||
int chosen = NO_OPTION;
|
||||
if (abOptions.isValidKey(optionKey)) {
|
||||
int[] optionProbs = abOptions.getProbsForKey(optionKey);
|
||||
chosen = chooseOption(optionProbs);
|
||||
setChoiceForOption(optionKey, chosen);
|
||||
|
||||
StatisticsService.reportEvent(abOptions.getDescriptionForOption(optionKey, chosen)); // Session should be open
|
||||
}
|
||||
return chosen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chosen option if set or NO_OPTION if unset
|
||||
* @param optionKey
|
||||
* @return
|
||||
*/
|
||||
public static int readChoiceForOption(String optionKey) {
|
||||
return Preferences.getInt(optionKey, NO_OPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the choice of an A/B feature in the preferences. Useful for
|
||||
* the feature flipper (can manually override previous choice)
|
||||
* @param optionKey
|
||||
* @param choiceIndex
|
||||
*/
|
||||
public void setChoiceForOption(String optionKey, int choiceIndex) {
|
||||
if (abOptions.isValidKey(optionKey))
|
||||
Preferences.setInt(optionKey, choiceIndex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method to choose an option from an int[] corresponding to the
|
||||
* relative weights of each option. Returns the index of the chosen option.
|
||||
*/
|
||||
private int chooseOption(int[] optionProbs) {
|
||||
int sum = 0;
|
||||
for (int opt : optionProbs) // Compute sum
|
||||
sum += opt;
|
||||
|
||||
double rand = random.nextDouble() * sum; // Get uniformly distributed double between [0, sum)
|
||||
sum = 0;
|
||||
for (int i = 0; i < optionProbs.length; i++) {
|
||||
sum += optionProbs[i];
|
||||
if (rand <= sum) return i;
|
||||
}
|
||||
return optionProbs.length - 1;
|
||||
}
|
||||
}
|
@ -0,0 +1,196 @@
|
||||
package com.todoroo.astrid.service.abtesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import com.todoroo.astrid.service.StatisticsConstants;
|
||||
|
||||
/**
|
||||
* Helper class to define options with their probabilities and descriptions
|
||||
* @author Sam Bosley <sam@astrid.com>
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("nls")
|
||||
public class ABOptions {
|
||||
|
||||
public ABOptions() {
|
||||
bundles = new HashMap<String, ABOptionBundle>();
|
||||
events = new HashMap<String, List<String>>();
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the integer array of weighted probabilities for an option key
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public synchronized int[] getProbsForKey(String key) {
|
||||
if (bundles.containsKey(key)) {
|
||||
ABOptionBundle bundle = bundles.get(key);
|
||||
return bundle.weightedProbs;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the weighted probability array for a given key. Returns true
|
||||
* on success, false if they key doesn't exist or if the array is the wrong
|
||||
* length.
|
||||
* @param key
|
||||
* @param newProbs
|
||||
* @return
|
||||
*/
|
||||
public synchronized boolean setProbsForKey(String key, int[] newProbs) {
|
||||
if (bundles.containsKey(newProbs)) {
|
||||
ABOptionBundle bundle = bundles.get(key);
|
||||
if (bundle.descriptions == null || newProbs.length == bundle.descriptions.length) {
|
||||
bundle.weightedProbs = newProbs;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string array of option descriptions for an option key
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public String[] getDescriptionsForKey(String key) {
|
||||
if (bundles.containsKey(key)) {
|
||||
ABOptionBundle bundle = bundles.get(key);
|
||||
return bundle.descriptions;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description for a particular choice of the given option
|
||||
* @param key
|
||||
* @param optionIndex
|
||||
* @return
|
||||
*/
|
||||
public String getDescriptionForOption(String key, int optionIndex) {
|
||||
if (bundles.containsKey(key)) {
|
||||
ABOptionBundle bundle = bundles.get(key);
|
||||
if (bundle.descriptions != null && optionIndex < bundle.descriptions.length) {
|
||||
return bundle.descriptions[optionIndex];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps keys (i.e. preference key identifiers) to feature weights and descriptions
|
||||
*/
|
||||
private final HashMap<String, ABOptionBundle> bundles;
|
||||
private final HashMap<String, List<String>> events; // maps events to lists of interested keys
|
||||
|
||||
private static class ABOptionBundle {
|
||||
public int[] weightedProbs;
|
||||
public String[] descriptions;
|
||||
|
||||
public ABOptionBundle(int[] weightedProbs, String[] descriptions) {
|
||||
this.weightedProbs = weightedProbs;
|
||||
this.descriptions = descriptions;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isValidKey(String key) {
|
||||
return bundles.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a localytics attribute array for the specified event.
|
||||
* @param event
|
||||
* @return
|
||||
*/
|
||||
public String[] getLocalyticsAttributeArrayForEvent(String event) {
|
||||
ArrayList<String> attributes = new ArrayList<String>();
|
||||
List<String> interestedKeys = events.get(event);
|
||||
if (interestedKeys != null)
|
||||
for (String key : interestedKeys) {
|
||||
// Get choice if exists and add to array
|
||||
if (isValidKey(key)) {
|
||||
ABOptionBundle bundle = bundles.get(key);
|
||||
int choice = ABChooser.readChoiceForOption(key);
|
||||
if (choice != ABChooser.NO_OPTION &&
|
||||
bundle.descriptions != null && choice < bundle.descriptions.length) {
|
||||
attributes.add(key);
|
||||
attributes.add(getDescriptionForOption(key, choice));
|
||||
}
|
||||
}
|
||||
}
|
||||
return attributes.toArray(new String[attributes.size()]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A/B testing options are defined below according to the following spec:
|
||||
*
|
||||
* @param optionKey = "<key>"
|
||||
* --This key is used to identify the option in the application and in the preferences
|
||||
*
|
||||
* @param probs = { int, int, ... }
|
||||
* --The different choices in an option correspond to an index in the probability array.
|
||||
* Probabilities are expressed as integers to easily define relative weights. For example,
|
||||
* the array { 1, 2 } would mean option 0 would happen one time for every two occurrences of option 1
|
||||
*
|
||||
* (optional)
|
||||
* @param descriptions = { "...", "...", ... }
|
||||
* --A string description of each option. Useful for tagging events. The index of
|
||||
* each description should correspond to the events location in the probability array
|
||||
* (i.e. the arrays should be the same length if this one exists)
|
||||
*
|
||||
* (optional)
|
||||
* @param relevantEvents = { "...", "...", ... }
|
||||
* --An arbitrary length list of relevant localytics events. When events are
|
||||
* tagged from StatisticsService, they will be appended with attributes
|
||||
* that have that event in this array
|
||||
*/
|
||||
private void addOption(String optionKey, int[] probs, String[] descriptions, String[] relevantEvents) {
|
||||
ABOptionBundle bundle = new ABOptionBundle(probs, descriptions);
|
||||
bundles.put(optionKey, bundle);
|
||||
|
||||
if (relevantEvents != null) {
|
||||
for (String event : relevantEvents) {
|
||||
List<String> interestedKeys = events.get(event);
|
||||
if (interestedKeys == null) {
|
||||
interestedKeys = new ArrayList<String>();
|
||||
events.put(event, interestedKeys);
|
||||
}
|
||||
interestedKeys.add(optionKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initialize() { // Set up
|
||||
addOption(AB_OPTION_FIRST_ACTIVITY, AB_OPTION_FIRST_ACTIVITY_PROBS,
|
||||
AB_OPTION_FIRST_ACTIVITY_DESCRIPTIONS, AB_OPTION_FIRST_ACTIVITY_RELEVANT_EVENTS);
|
||||
|
||||
addOption(AB_OPTION_WELCOME_LOGIN, AB_OPTION_WELCOME_LOGIN_PROBS,
|
||||
AB_OPTION_WELCOME_LOGIN_DESCRIPTIONS, AB_OPTION_WELCOME_LOGIN_RELEVANT_EVENTS);
|
||||
}
|
||||
|
||||
|
||||
public static final String AB_OPTION_FIRST_ACTIVITY = "ab_first_activity";
|
||||
private static final int[] AB_OPTION_FIRST_ACTIVITY_PROBS = { 1, 1 };
|
||||
private static final String[] AB_OPTION_FIRST_ACTIVITY_DESCRIPTIONS = { "ab-show-tasks-first", "ab-show-lists-first" };
|
||||
private static final String[] AB_OPTION_FIRST_ACTIVITY_RELEVANT_EVENTS = { StatisticsConstants.CREATE_TASK,
|
||||
StatisticsConstants.TASK_CREATED_TASKLIST,
|
||||
StatisticsConstants.USER_FIRST_TASK,
|
||||
StatisticsConstants.ACTFM_LIST_SHARED,
|
||||
StatisticsConstants.ACTFM_NEW_USER };//*/
|
||||
|
||||
public static final String AB_OPTION_WELCOME_LOGIN = "ab_welcome_login";
|
||||
private static final int[] AB_OPTION_WELCOME_LOGIN_PROBS = { 0, 1 };
|
||||
private static final String[] AB_OPTION_WELCOME_LOGIN_DESCRIPTIONS = { "ab-welcome-login-show", "ab-welcome-login-skip" };
|
||||
private static final String[] AB_OPTION_WELCOME_LOGIN_RELEVANT_EVENTS = { StatisticsConstants.CREATE_TASK,
|
||||
StatisticsConstants.TASK_CREATED_TASKLIST,
|
||||
StatisticsConstants.USER_FIRST_TASK,
|
||||
StatisticsConstants.ACTFM_LIST_SHARED,
|
||||
StatisticsConstants.ACTFM_NEW_USER };//*/
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package com.todoroo.astrid.service.abtesting;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.ContextManager;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.andlib.service.HttpRestClient;
|
||||
import com.todoroo.andlib.service.RestClient;
|
||||
import com.todoroo.astrid.utility.Constants;
|
||||
|
||||
@SuppressWarnings("nls")
|
||||
public class FeatureFlipper {
|
||||
|
||||
private static final String URL = "http://weloveastrid.com/settings.php";
|
||||
|
||||
private static final String KEY_SETTING_KEY = "key";
|
||||
private static final String KEY_SET_OPTION = "setOption";
|
||||
private static final String KEY_PROBABILITIES = "probabilities";
|
||||
|
||||
private final RestClient restClient = new HttpRestClient(4000);
|
||||
@Autowired private ABChooser abChooser;
|
||||
@Autowired private ABOptions abOptions;
|
||||
|
||||
public FeatureFlipper() {
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests a JSON array containing feature settings override data,
|
||||
* parses the result, and updates the AB settings for the corresponding features
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void updateFeatures() {
|
||||
JSONArray settingsBundle = requestOverrideSettings();
|
||||
if (settingsBundle == null || settingsBundle.length() == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < settingsBundle.length(); i++) {
|
||||
try {
|
||||
JSONObject settings = settingsBundle.getJSONObject(i);
|
||||
String key = settings.getString(KEY_SETTING_KEY);
|
||||
|
||||
if (settings.has(KEY_SET_OPTION)) {
|
||||
int option = settings.getInt(KEY_SET_OPTION);
|
||||
abChooser.setChoiceForOption(key, option);
|
||||
} else if (settings.has(KEY_PROBABILITIES)) {
|
||||
JSONArray newProbabilities = settings.getJSONArray(KEY_PROBABILITIES);
|
||||
int[] probs = new int[newProbabilities.length()];
|
||||
|
||||
for (int j = 0; j < newProbabilities.length(); j++) {
|
||||
probs[j] = newProbabilities.getInt(j);
|
||||
}
|
||||
|
||||
abOptions.setProbsForKey(key, probs);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private JSONArray requestOverrideSettings() {
|
||||
PackageManager pm = ContextManager.getContext().getPackageManager();
|
||||
try {
|
||||
PackageInfo pi = pm.getPackageInfo(Constants.PACKAGE, PackageManager.GET_META_DATA);
|
||||
int versionCode = pi.versionCode;
|
||||
String result = restClient.get(URL + "?version=" + versionCode + "&" +
|
||||
"language=" + Locale.getDefault().getISO3Language()); //$NON-NLS-1$
|
||||
if(TextUtils.isEmpty(result))
|
||||
return null;
|
||||
|
||||
return new JSONArray(result);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
} catch (NameNotFoundException e) {
|
||||
return null;
|
||||
} catch (JSONException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
package com.todoroo.astrid.welcome;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.Window;
|
||||
|
||||
import com.timsu.astrid.R;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.ContextManager;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.activity.FilterListActivity;
|
||||
import com.todoroo.astrid.activity.TaskListActivity;
|
||||
import com.todoroo.astrid.service.AstridDependencyInjector;
|
||||
import com.todoroo.astrid.service.StartupService;
|
||||
import com.todoroo.astrid.service.abtesting.ABChooser;
|
||||
import com.todoroo.astrid.service.abtesting.ABOptions;
|
||||
import com.todoroo.astrid.service.abtesting.FeatureFlipper;
|
||||
import com.todoroo.astrid.utility.AstridPreferences;
|
||||
|
||||
public class SplashScreenLauncher extends Activity {
|
||||
|
||||
private boolean shouldGoThroughWelcome = false;
|
||||
private int latestSetVersion = 0;
|
||||
|
||||
@Autowired ABChooser abChooser;
|
||||
|
||||
static {
|
||||
AstridDependencyInjector.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
super.onCreate(savedInstanceState);
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
setContentView(R.layout.splash_screen_launcher);
|
||||
latestSetVersion = AstridPreferences.getCurrentVersion();
|
||||
shouldGoThroughWelcome = ((latestSetVersion == 0) || !Preferences.getBoolean(WelcomeLogin.KEY_SHOWED_WELCOME_LOGIN, false));
|
||||
ContextManager.setContext(this);
|
||||
if (latestSetVersion == 0) {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
new FeatureFlipper().updateFeatures();
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new StartupService().onStartupApplication(SplashScreenLauncher.this);
|
||||
finishAndShowNext();
|
||||
}
|
||||
});
|
||||
}
|
||||
}.start();
|
||||
} else {
|
||||
new StartupService().onStartupApplication(this);
|
||||
finishAndShowNext();
|
||||
}
|
||||
}
|
||||
|
||||
private void finishAndShowNext() {
|
||||
if (shouldGoThroughWelcome) {
|
||||
int welcomeLoginChoice = abChooser.getChoiceForOption(ABOptions.AB_OPTION_WELCOME_LOGIN);
|
||||
welcomeLoginPath(welcomeLoginChoice);
|
||||
} else {
|
||||
int tasksOrListsChoice = abChooser.getChoiceForOption(ABOptions.AB_OPTION_FIRST_ACTIVITY);
|
||||
mainActivityPath(tasksOrListsChoice);
|
||||
}
|
||||
}
|
||||
|
||||
private void welcomeLoginPath(int welcomeLoginChoice) {
|
||||
Intent intent = new Intent();
|
||||
switch(welcomeLoginChoice) {
|
||||
case 0: // Show welcome login, then welcome screen
|
||||
intent.setClass(this, WelcomeLogin.class);
|
||||
break;
|
||||
case 1: // Go straight to welcome screen
|
||||
intent.setClass(this, WelcomeGraphic.class);
|
||||
intent.putExtra(WelcomeGraphic.KEY_SHOW_EULA, true);
|
||||
break;
|
||||
default:
|
||||
intent.setClass(this, WelcomeLogin.class);
|
||||
break;
|
||||
}
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void mainActivityPath(int tasksOrListsChoice) {
|
||||
Intent intent = new Intent();
|
||||
switch (tasksOrListsChoice) {
|
||||
case 0:
|
||||
intent.setClass(this, TaskListActivity.class); // Go to task list activity
|
||||
break;
|
||||
case 1:
|
||||
intent.setClass(this, FilterListActivity.class); // Go to filter list activity
|
||||
intent.putExtra(FilterListActivity.SHOW_BACK_BUTTON, false);
|
||||
break;
|
||||
default:
|
||||
intent.setClass(this, TaskListActivity.class);
|
||||
break;
|
||||
}
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package com.todoroo.astrid.welcome;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.Window;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.timsu.astrid.R;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.ContextManager;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
||||
import com.todoroo.astrid.activity.Eula;
|
||||
import com.todoroo.astrid.activity.FilterListActivity;
|
||||
import com.todoroo.astrid.activity.TaskListActivity;
|
||||
import com.todoroo.astrid.service.StartupService;
|
||||
import com.todoroo.astrid.service.StatisticsService;
|
||||
import com.todoroo.astrid.service.abtesting.ABChooser;
|
||||
import com.todoroo.astrid.service.abtesting.ABOptions;
|
||||
|
||||
public class WelcomeGraphic extends Activity {
|
||||
|
||||
public static final String KEY_SHOW_EULA = "show_eula"; //$NON-NLS-1$
|
||||
|
||||
@Autowired ABChooser abChooser;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
ContextManager.setContext(this);
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
new StartupService().onStartupApplication(this);
|
||||
setContentView(R.layout.welcome_screen);
|
||||
|
||||
if (getIntent().getBooleanExtra(KEY_SHOW_EULA, false))
|
||||
Eula.showEula(this);
|
||||
|
||||
final ImageView image = (ImageView)findViewById(R.id.welcome_image);
|
||||
image.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
image.setOnClickListener(null); // Prevent double click
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
AndroidUtilities.sleepDeep(1000L);
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finishAndStartNext();
|
||||
}
|
||||
});
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
StatisticsService.sessionPause();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
StatisticsService.sessionStart(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
StatisticsService.sessionStop(this);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
private void finishAndStartNext() {
|
||||
Intent nextActivity = getNextIntent();
|
||||
startActivity(nextActivity);
|
||||
finish();
|
||||
}
|
||||
|
||||
private Intent getNextIntent() {
|
||||
Intent intent = new Intent();
|
||||
int choice = abChooser.getChoiceForOption(ABOptions.AB_OPTION_FIRST_ACTIVITY);
|
||||
switch (choice) {
|
||||
case 0:
|
||||
intent.setClass(this, TaskListActivity.class);
|
||||
break;
|
||||
case 1:
|
||||
intent.setClass(this, FilterListActivity.class);
|
||||
intent.putExtra(FilterListActivity.SHOW_BACK_BUTTON, false);
|
||||
break;
|
||||
default:
|
||||
intent.setClass(this, TaskListActivity.class);
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package com.todoroo.astrid.welcome;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.Window;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.timsu.astrid.R;
|
||||
import com.todoroo.andlib.service.ContextManager;
|
||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.activity.Eula;
|
||||
import com.todoroo.astrid.activity.TaskListActivity;
|
||||
import com.todoroo.astrid.service.StartupService;
|
||||
|
||||
public class WelcomeScreen extends Activity {
|
||||
|
||||
public static final String KEY_SHOWED_WELCOME_SCREEN = "key_showed_welcome_screen"; //$NON-NLS-1$
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
ContextManager.setContext(this);
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
new StartupService().onStartupApplication(this);
|
||||
setContentView(R.layout.welcome_screen);
|
||||
|
||||
Eula.showEula(this);
|
||||
|
||||
final ImageView image = (ImageView)findViewById(R.id.welcome_image);
|
||||
image.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
image.setOnClickListener(null); // Prevent double click
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
AndroidUtilities.sleepDeep(1000L);
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finishAndStartNext();
|
||||
}
|
||||
});
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
});
|
||||
|
||||
if(Preferences.getBoolean(KEY_SHOWED_WELCOME_SCREEN, false)) {
|
||||
finishAndStartNext();
|
||||
}
|
||||
}
|
||||
|
||||
private void finishAndStartNext() {
|
||||
Intent taskListStartup = new Intent(this, TaskListActivity.class);
|
||||
startActivity(taskListStartup);
|
||||
finish();
|
||||
Preferences.setBoolean(KEY_SHOWED_WELCOME_SCREEN, true);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue