• App Shortcuts provide a means of making a choice, and making a choice poses a new scenario
• Dialogs provide a means of viewing a list of achievements, gained by exploring various paths in a story
Unique colors, vibration patterns and sounds exist for different types of scenarios, and are used to theme the Notifications. Emoji provide a contextual icon for scenarios and choices.
Stories are defined in XML. A basic structure of Story, Choice, Scenario, Achievement and Finish tags allows ShortStories to parse and play almost any kind of text-based, multiple choice game.
View the source code and contribute your own stories on Github.
Notification sound audio produced by Calvin Davey.]]>
25 | Get the ShortStories app
26 | Interactive stories told through App Shortcuts and Notifications.\n\nGet the ShortStories app:\nhttps://play.google.com/store/apps/details?id=com.nickrout.shortstories
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nickrout/shortstories/model/type/ScenarioType.java:
--------------------------------------------------------------------------------
1 | package com.nickrout.shortstories.model.type;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 | import android.support.annotation.ColorInt;
6 | import android.support.annotation.ColorRes;
7 | import android.support.annotation.RawRes;
8 | import android.support.v4.content.ContextCompat;
9 |
10 | import com.nickrout.shortstories.R;
11 |
12 | public enum ScenarioType {
13 |
14 | UNKNOWN(R.color.color_primary, R.raw.unknown, new long[] {0, 100}),
15 | ACHIEVEMENT(R.color.red_600, R.raw.achievement, new long[] {0, 100, 100, 100, 100, 200}),
16 | CHALLENGE(R.color.pink_600, R.raw.challenge, new long[] {0, 100, 100, 100, 100, 100, 100, 100}),
17 | CONFLICT(R.color.purple_600, R.raw.conflict, new long[] {0, 200, 100, 200, 100, 200}),
18 | DIRECTION(R.color.deep_purple_600, R.raw.direction, new long[] {0, 200, 300, 200}),
19 | ENDING(R.color.black, R.raw.ending, new long[] {0, 100, 100, 300}),
20 | GAME(R.color.indigo_600, R.raw.game, new long[] {0, 200, 100, 100, 100, 200, 100, 100}),
21 | HAPPY(R.color.blue_600, R.raw.happy, new long[] {0, 100, 100, 200, 100, 300}),
22 | ITEM(R.color.light_blue_600, R.raw.item, new long[] {0, 200, 100, 100, 100, 100}),
23 | MEAL(R.color.cyan_600, R.raw.meal, new long[] {0, 100, 100, 200}),
24 | MEETING(R.color.teal_600, R.raw.meeting, new long[] {0, 100, 100, 100}),
25 | NEUTRAL(R.color.green_600, R.raw.neutral, new long[] {0, 100, 100, 100, 100, 100}),
26 | NUMERIC(R.color.light_green_600, R.raw.numeric, new long[] {0, 200, 100, 100}),
27 | OFFER(R.color.lime_600, R.raw.offer, new long[] {0, 100, 200, 300}),
28 | PREPARATION(R.color.yellow_600, R.raw.preparation, new long[] {0, 200, 100, 200}),
29 | PROBLEM(R.color.amber_600, R.raw.problem, new long[] {0, 200, 200, 100, 200, 200}),
30 | PUZZLE(R.color.orange_600, R.raw.puzzle, new long[] {0, 300, 200, 100, 100, 100}),
31 | REQUIREMENT(R.color.deep_orange_600, R.raw.requirement, new long[] {0, 200, 100, 100, 100, 100, 100, 100}),
32 | TRANSACTION(R.color.brown_600, R.raw.transaction, new long[] {0, 200, 100, 300, 100, 100}),
33 | UNHAPPY(R.color.grey_600, R.raw.unhappy, new long[] {0, 300, 100, 200, 100, 100}),
34 | YES_NO(R.color.blue_grey_600, R.raw.yes_no, new long[] {0, 200, 100, 100, 100, 200});
35 |
36 | private @ColorRes int mColorResId;
37 | private @RawRes int mSoundResId;
38 | public long[] vibratePattern;
39 |
40 | ScenarioType(@ColorRes int colorResId, @RawRes int soundResId, long[] vibratePattern) {
41 | mColorResId = colorResId;
42 | mSoundResId = soundResId;
43 | this.vibratePattern = vibratePattern;
44 | }
45 |
46 | public @ColorInt int getColor(Context context) {
47 | return ContextCompat.getColor(context, mColorResId);
48 | }
49 |
50 | public Uri getSound(Context context) {
51 | return Uri.parse("android.resource://" + context.getPackageName() + "/" + mSoundResId);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nickrout/shortstories/prefs/Achievements.java:
--------------------------------------------------------------------------------
1 | package com.nickrout.shortstories.prefs;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.util.Log;
6 |
7 | import com.nickrout.shortstories.model.Achievement;
8 |
9 | import java.util.ArrayList;
10 | import java.util.Collections;
11 | import java.util.Comparator;
12 | import java.util.List;
13 | import java.util.Map;
14 |
15 | public class Achievements {
16 |
17 | private static final String TAG = "Achievements";
18 |
19 | private Context mContext;
20 | private String mSharedPreferencesName;
21 | private SharedPreferences mSharedPreferences;
22 | private Comparator mAchievementComparator = new Comparator() {
23 | @Override
24 | public int compare(Achievement a1, Achievement a2) {
25 | return a1.name.compareTo(a2.name);
26 | }
27 | };
28 |
29 | public Achievements(Context context, String sharedPreferencesName) {
30 | mContext = context;
31 | mSharedPreferencesName = sharedPreferencesName;
32 | }
33 |
34 | private SharedPreferences sharedPreferences() {
35 | if (mSharedPreferences == null) {
36 | mSharedPreferences = mContext.getSharedPreferences(
37 | mSharedPreferencesName, Context.MODE_PRIVATE);
38 | }
39 | return mSharedPreferences;
40 | }
41 |
42 | public void setAll(Map achievements) {
43 | if (achievements == null || achievements.isEmpty()) {
44 | Log.e(TAG, "Attempted to set all achievements with empty map");
45 | return;
46 | }
47 | for (Map.Entry achievementEntry : achievements.entrySet()) {
48 | if (contains(achievementEntry.getKey())) {
49 | continue;
50 | }
51 | set(achievementEntry.getKey(), achievementEntry.getValue() == null ? false : achievementEntry.getValue());
52 | }
53 | }
54 |
55 | private boolean contains(String achievementName) {
56 | return sharedPreferences().contains(achievementName);
57 | }
58 |
59 | public void achieve(String achievementName) {
60 | set(achievementName, true);
61 | }
62 |
63 | private void set(String achievementName, boolean value) {
64 | sharedPreferences().edit().putBoolean(achievementName, value).apply();
65 | }
66 |
67 | public List getAchievements() {
68 | List achievements = new ArrayList<>();
69 | Map achievementsMap = getAll();
70 | if (achievementsMap == null || achievementsMap.isEmpty()) {
71 | return achievements;
72 | }
73 | for (Map.Entry achievementEntry : achievementsMap.entrySet()) {
74 | Achievement achievement = new Achievement(achievementEntry.getKey(), achievementEntry.getValue());
75 | achievements.add(achievement);
76 | }
77 | Collections.sort(achievements, mAchievementComparator);
78 | return achievements;
79 | }
80 |
81 | @SuppressWarnings("unchecked")
82 | private Map getAll() {
83 | try {
84 | return (Map) sharedPreferences().getAll();
85 | } catch (ClassCastException e) {
86 | Log.e(TAG, e.toString());
87 | return null;
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nickrout/shortstories/ui/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.nickrout.shortstories.ui;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.support.annotation.NonNull;
5 | import android.support.design.widget.BottomNavigationView;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.os.Bundle;
8 | import android.view.Menu;
9 | import android.view.MenuInflater;
10 | import android.view.MenuItem;
11 |
12 | import com.nickrout.shortstories.R;
13 | import com.nickrout.shortstories.databinding.ActivityMainBinding;
14 | import com.nickrout.shortstories.model.Story;
15 | import com.nickrout.shortstories.prefs.Achievements;
16 | import com.nickrout.shortstories.prefs.Progress;
17 | import com.nickrout.shortstories.util.IntentUtil;
18 |
19 | public class MainActivity extends AppCompatActivity implements StoryListener {
20 |
21 | private static final String TAG = "MainActivity";
22 |
23 | @Override
24 | protected void onCreate(Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | final ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
27 | if (savedInstanceState == null) {
28 | getSupportFragmentManager().beginTransaction().replace(R.id.container, StoriesFragment.newInstance()).commit();
29 | }
30 | binding.navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
31 | @Override
32 | public boolean onNavigationItemSelected(@NonNull MenuItem item) {
33 | switch (item.getItemId()) {
34 | case R.id.navigation_stories:
35 | if (binding.navigation.getSelectedItemId() == R.id.navigation_stories) {
36 | return true;
37 | }
38 | getSupportFragmentManager()
39 | .beginTransaction()
40 | .setCustomAnimations(R.anim.scale_up_fade_in, R.anim.scale_down_fade_out)
41 | .replace(R.id.container, StoriesFragment.newInstance()).commit();
42 | return true;
43 | case R.id.navigation_settings:
44 | if (binding.navigation.getSelectedItemId() == R.id.navigation_settings) {
45 | return true;
46 | }
47 | getSupportFragmentManager()
48 | .beginTransaction()
49 | .setCustomAnimations(R.anim.scale_up_fade_in, R.anim.scale_down_fade_out)
50 | .replace(R.id.container, SettingsFragment.newInstance()).commit();
51 | return true;
52 | }
53 | return false;
54 | }
55 | });
56 | }
57 |
58 | @Override
59 | public boolean onCreateOptionsMenu(Menu menu) {
60 | MenuInflater inflater = getMenuInflater();
61 | inflater.inflate(R.menu.menu_main, menu);
62 | return true;
63 | }
64 |
65 | @Override
66 | public boolean onOptionsItemSelected(MenuItem item) {
67 | switch (item.getItemId()) {
68 | case R.id.about:
69 | startActivity(IntentUtil.about(this));
70 | overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right);
71 | return true;
72 | case R.id.share:
73 | startActivity(IntentUtil.share(this));
74 | return true;
75 | }
76 | return super.onOptionsItemSelected(item);
77 | }
78 |
79 | @Override
80 | public void startStory(@NonNull Story story) {
81 | new Achievements(this, story.file).setAll(story.achievements);
82 | new Progress(this).setInProgress(story.file);
83 | finishAndRemoveTask();
84 | startActivity(IntentUtil.choice(MainActivity.this, story.choice));
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nickrout/shortstories/ui/StoriesFragment.java:
--------------------------------------------------------------------------------
1 | package com.nickrout.shortstories.ui;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.AsyncTask;
5 | import android.os.Bundle;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.v4.app.Fragment;
9 | import android.support.v7.widget.LinearLayoutManager;
10 | import android.util.Log;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 |
15 | import com.nickrout.shortstories.R;
16 | import com.nickrout.shortstories.databinding.FragmentStoriesBinding;
17 | import com.nickrout.shortstories.model.Story;
18 | import com.nickrout.shortstories.prefs.Progress;
19 | import com.nickrout.shortstories.ui.recyclerview.StoryAdapter;
20 | import com.nickrout.shortstories.ui.recyclerview.VerticalSpaceItemDecoration;
21 |
22 | import org.simpleframework.xml.Serializer;
23 | import org.simpleframework.xml.core.Persister;
24 |
25 | import java.util.ArrayList;
26 | import java.util.Arrays;
27 | import java.util.List;
28 |
29 | public class StoriesFragment extends Fragment implements StoryListener {
30 |
31 | private static final String TAG = "StoriesFragment";
32 |
33 | private FragmentStoriesBinding mBinding;
34 | private static final List STORY_FILES = new ArrayList<>(Arrays.asList("an_androids_tale.xml"));
35 |
36 | public StoriesFragment() {
37 | }
38 |
39 | public static StoriesFragment newInstance() {
40 | return new StoriesFragment();
41 | }
42 |
43 | @Nullable
44 | @Override
45 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
46 | mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_stories, container, false);
47 | mBinding.recycler.setLayoutManager(new LinearLayoutManager(getActivity()));
48 | mBinding.recycler.addItemDecoration(new VerticalSpaceItemDecoration(
49 | getResources().getDimensionPixelSize(R.dimen.padding_vertical), false));
50 | return mBinding.getRoot();
51 | }
52 |
53 | @Override
54 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
55 | super.onViewCreated(view, savedInstanceState);
56 | loadStories(STORY_FILES);
57 | }
58 |
59 | private void loadStories(@NonNull final List storyFiles) {
60 | new AsyncTask>() {
61 | @Override
62 | protected List doInBackground(Void... params) {
63 | Serializer serializer = new Persister();
64 | Progress progress = new Progress(getActivity());
65 | boolean foundInProgressStory = false;
66 | List stories = new ArrayList<>();
67 | for (String storyFile : storyFiles) {
68 | try {
69 | Story story = serializer.read(Story.class, getActivity().getAssets().open("stories/" + storyFile));
70 | story.file = storyFile;
71 | if (!foundInProgressStory) {
72 | boolean inProgress = progress.isInProgress(storyFile);
73 | story.inProgress = inProgress;
74 | foundInProgressStory = inProgress;
75 | } else {
76 | story.inProgress = false;
77 | }
78 | stories.add(story);
79 | } catch (Exception e) {
80 | Log.d(TAG, e.toString());
81 | }
82 | }
83 | return stories;
84 | }
85 | @Override
86 | protected void onPostExecute(List stories) {
87 | assignStoriesToView(stories);
88 | }
89 | }.execute();
90 | }
91 |
92 | private void assignStoriesToView(@NonNull List stories) {
93 | mBinding.recycler.setAdapter(new StoryAdapter(stories, this));
94 | }
95 |
96 | @Override
97 | public void startStory(@NonNull Story story) {
98 | if (!(getActivity() instanceof StoryListener)) {
99 | return;
100 | }
101 | ((StoryListener) getActivity()).startStory(story);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nickrout/shortstories/util/BitmapUtil.java:
--------------------------------------------------------------------------------
1 | package com.nickrout.shortstories.util;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.graphics.drawable.BitmapDrawable;
8 | import android.graphics.drawable.Drawable;
9 | import android.graphics.drawable.LayerDrawable;
10 | import android.graphics.drawable.ShapeDrawable;
11 | import android.graphics.drawable.shapes.OvalShape;
12 | import android.support.annotation.ColorInt;
13 | import android.support.annotation.DimenRes;
14 | import android.support.annotation.DrawableRes;
15 | import android.support.v4.content.ContextCompat;
16 | import android.support.v4.graphics.drawable.DrawableCompat;
17 | import android.text.TextUtils;
18 |
19 | import com.nickrout.shortstories.R;
20 |
21 | public class BitmapUtil {
22 |
23 | public static Bitmap getShortcutIcon(
24 | Context context, @ColorInt int backgroundColor,
25 | @DrawableRes int iconResId, @ColorInt int iconColor) {
26 | return getCircleIcon(context,
27 | backgroundColor,
28 | context.getResources().getDimensionPixelSize(R.dimen.inset_shortcut_icon_background),
29 | iconResId,
30 | iconColor,
31 | context.getResources().getDimensionPixelSize(R.dimen.inset_shortcut_icon));
32 | }
33 |
34 | private static Bitmap getCircleIcon(
35 | Context context, @ColorInt int backgroundColor, int backgroundInset,
36 | @DrawableRes int iconResId, @ColorInt int iconColor, int iconInset) {
37 | Drawable[] layers = new Drawable[2];
38 | ShapeDrawable background = new ShapeDrawable(new OvalShape());
39 | background.getPaint().setColor(backgroundColor);
40 | Drawable icon = ContextCompat.getDrawable(context, iconResId);
41 | Drawable tintedIcon = DrawableCompat.wrap(icon.mutate());
42 | DrawableCompat.setTint(tintedIcon, iconColor);
43 | layers[0] = background;
44 | layers[1] = tintedIcon;
45 | LayerDrawable layerDrawable = new LayerDrawable(layers);
46 | layerDrawable.setLayerInset(1, iconInset, iconInset, iconInset, iconInset);
47 | layerDrawable.setLayerInset(0, backgroundInset, backgroundInset, backgroundInset, backgroundInset);
48 | return drawableToBitmap(layerDrawable);
49 | }
50 |
51 | private static Bitmap drawableToBitmap(Drawable drawable) {
52 | if (drawable instanceof BitmapDrawable) {
53 | return ((BitmapDrawable) drawable).getBitmap();
54 | }
55 | int width = drawable.getIntrinsicWidth();
56 | width = width > 0 ? width : 1;
57 | int height = drawable.getIntrinsicHeight();
58 | height = height > 0 ? height : 1;
59 | Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
60 | Canvas canvas = new Canvas(bitmap);
61 | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
62 | drawable.draw(canvas);
63 | return bitmap;
64 | }
65 |
66 | public static Bitmap getShortcutEmoji(Context context, String emoji) {
67 | return getEmoji(context, emoji, R.dimen.size_emoji_shortcut, R.dimen.inset_emoji_shortcut);
68 | }
69 |
70 | public static Bitmap getNotificationEmoji(Context context, String emoji) {
71 | return getEmoji(context, emoji, R.dimen.size_emoji_notification, R.dimen.inset_emoji_notification);
72 | }
73 |
74 | private static Bitmap getEmoji(
75 | Context context, String emoji, @DimenRes int sizeResId, @DimenRes int insetResId) {
76 | if (context == null || TextUtils.isEmpty(emoji)) {
77 | return null;
78 | }
79 | int size = context.getResources().getDimensionPixelSize(sizeResId);
80 | int inset = context.getResources().getDimensionPixelSize(insetResId);
81 | Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
82 | paint.setStyle(Paint.Style.FILL);
83 | paint.setTextAlign(Paint.Align.LEFT);
84 | paint.setTextSize(size);
85 | float baseline = -paint.ascent();
86 | int width = (int) (paint.measureText(emoji) + 0.5f);
87 | int height = (int) (baseline + paint.descent() + 0.5f);
88 | Bitmap bitmap = Bitmap.createBitmap(width + inset * 2, height + inset * 2, Bitmap.Config.ARGB_8888);
89 | Canvas canvas = new Canvas(bitmap);
90 | canvas.drawText(emoji, inset, baseline + inset, paint);
91 | return bitmap;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_story.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
11 |
14 |
15 |
16 |
19 |
20 |
24 |
25 |
31 |
32 |
41 |
42 |
51 |
52 |
57 |
58 |
67 |
68 |
73 |
74 |
82 |
83 |
84 |
85 |
89 |
90 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nickrout/shortstories/util/IntentUtil.java:
--------------------------------------------------------------------------------
1 | package com.nickrout.shortstories.util;
2 |
3 | import android.app.PendingIntent;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.support.v4.content.IntentCompat;
7 | import android.util.Log;
8 |
9 | import com.nickrout.shortstories.R;
10 | import com.nickrout.shortstories.ui.AboutActivity;
11 | import com.nickrout.shortstories.ui.AddShowScenarioShortcutActivity;
12 | import com.nickrout.shortstories.model.Choice;
13 | import com.nickrout.shortstories.ui.QuitStoryActivity;
14 | import com.nickrout.shortstories.ui.ScenarioDialogActivity;
15 | import com.nickrout.shortstories.ui.ChoiceActivity;
16 | import com.nickrout.shortstories.ui.AchievementsDialogActivity;
17 |
18 | import org.simpleframework.xml.Serializer;
19 | import org.simpleframework.xml.core.Persister;
20 |
21 | import java.io.ByteArrayOutputStream;
22 |
23 | public class IntentUtil {
24 |
25 | private static final String TAG = "IntentUtil";
26 |
27 | public static final String EXTRA_CHOICE_XML = "choice_xml";
28 | public static final String EXTRA_SCENARIO = "scenario";
29 | public static final String EXTRA_HAS_ACHIEVEMENTS = "has_achievements";
30 | public static final String EXTRA_IS_FINISH = "is_finish";
31 |
32 | public static Intent choice(Context context, Choice choice) {
33 | Intent choiceIntent = new Intent(context, ChoiceActivity.class);
34 | choiceIntent = IntentCompat.makeRestartActivityTask(choiceIntent.getComponent());
35 | choiceIntent.setAction(Intent.ACTION_VIEW);
36 | choiceIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
37 | Serializer serializer = new Persister();
38 | ByteArrayOutputStream choiceOutputStream = new ByteArrayOutputStream();
39 | try {
40 | serializer.write(choice, choiceOutputStream);
41 | } catch (Exception e) {
42 | Log.d(TAG, e.toString());
43 | }
44 | String choiceXml = choiceOutputStream.toString();
45 | choiceIntent.putExtra(EXTRA_CHOICE_XML, choiceXml);
46 | return choiceIntent;
47 | }
48 |
49 | public static Intent addShowScenarioShortcut(Context context, Choice choice) {
50 | Intent addShowScenarioShortcutIntent = new Intent(context, AddShowScenarioShortcutActivity.class);
51 | addShowScenarioShortcutIntent = IntentCompat.makeRestartActivityTask(addShowScenarioShortcutIntent.getComponent());
52 | addShowScenarioShortcutIntent.setAction(Intent.ACTION_VIEW);
53 | addShowScenarioShortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
54 | Serializer serializer = new Persister();
55 | ByteArrayOutputStream choiceOutputStream = new ByteArrayOutputStream();
56 | try {
57 | serializer.write(choice, choiceOutputStream);
58 | } catch (Exception e) {
59 | Log.d(TAG, e.toString());
60 | }
61 | String choiceXml = choiceOutputStream.toString();
62 | addShowScenarioShortcutIntent.putExtra(EXTRA_CHOICE_XML, choiceXml);
63 | return addShowScenarioShortcutIntent;
64 | }
65 |
66 | public static Intent scenarioDialog(Context context, String scenario, boolean hasAchievements, boolean isFinish) {
67 | Intent scenarioDialogIntent = new Intent(context, ScenarioDialogActivity.class);
68 | scenarioDialogIntent = IntentCompat.makeRestartActivityTask(scenarioDialogIntent.getComponent());
69 | scenarioDialogIntent.putExtra(EXTRA_SCENARIO, scenario);
70 | scenarioDialogIntent.putExtra(EXTRA_HAS_ACHIEVEMENTS, hasAchievements);
71 | scenarioDialogIntent.putExtra(EXTRA_IS_FINISH, isFinish);
72 | return scenarioDialogIntent;
73 | }
74 |
75 | public static Intent achievementsDialog(Context context) {
76 | Intent achievementsDialogIntent = new Intent(context, AchievementsDialogActivity.class);
77 | achievementsDialogIntent = IntentCompat.makeRestartActivityTask(achievementsDialogIntent.getComponent());
78 | return achievementsDialogIntent;
79 | }
80 |
81 | public static Intent quitStory(Context context) {
82 | Intent quitStoryIntent = new Intent(context, QuitStoryActivity.class);
83 | quitStoryIntent = IntentCompat.makeRestartActivityTask(quitStoryIntent.getComponent());
84 | quitStoryIntent.setAction(Intent.ACTION_VIEW);
85 | quitStoryIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
86 | return quitStoryIntent;
87 | }
88 |
89 | public static Intent about(Context context) {
90 | return new Intent(context, AboutActivity.class);
91 | }
92 |
93 | public static Intent share(Context context) {
94 | Intent intent = new Intent();
95 | intent.setAction(Intent.ACTION_SEND);
96 | intent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.subject_share));
97 | intent.putExtra(Intent.EXTRA_TEXT, context.getString(R.string.text_share));
98 | intent.setType("text/plain");
99 | return Intent.createChooser(intent, context.getString(R.string.title_send_to));
100 | }
101 |
102 | public static PendingIntent makePendingIntent(Context context, Intent intent) {
103 | return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nickrout/shortstories/ui/ChoiceActivity.java:
--------------------------------------------------------------------------------
1 | package com.nickrout.shortstories.ui;
2 |
3 | import android.app.PendingIntent;
4 | import android.content.Intent;
5 | import android.content.pm.ShortcutInfo;
6 | import android.content.pm.ShortcutManager;
7 | import android.graphics.drawable.Icon;
8 | import android.os.Handler;
9 | import android.support.v4.app.NotificationCompat;
10 | import android.support.v4.app.NotificationManagerCompat;
11 | import android.util.Log;
12 |
13 | import com.nickrout.shortstories.R;
14 | import com.nickrout.shortstories.model.Achieve;
15 | import com.nickrout.shortstories.model.Choice;
16 | import com.nickrout.shortstories.prefs.Achievements;
17 | import com.nickrout.shortstories.prefs.Progress;
18 | import com.nickrout.shortstories.prefs.Settings;
19 | import com.nickrout.shortstories.util.IdUtil;
20 | import com.nickrout.shortstories.util.IntentUtil;
21 | import com.nickrout.shortstories.util.UiUtil;
22 |
23 | import org.simpleframework.xml.Serializer;
24 | import org.simpleframework.xml.core.Persister;
25 |
26 | import java.util.ArrayList;
27 | import java.util.List;
28 |
29 | public class ChoiceActivity extends NoDisplayActivity {
30 |
31 | private static final String TAG = "ChoiceActivity";
32 | private static final long DELAY_EXPAND_NOTIFICATION_PANEL = 1000;
33 | private static final int TIME_NOTIFICATION_LIGHTS = 1000;
34 |
35 | private Choice mChoice;
36 | private int mNotificationPriority = NotificationCompat.PRIORITY_DEFAULT;
37 | private boolean mGoHomeNewScenario = true;
38 | private boolean mExpandNotificationsNewScenario = true;
39 |
40 | @Override
41 | protected void performPreFinishOperations() {
42 | Serializer serializer = new Persister();
43 | String choiceXml = getIntent().getExtras().getString(IntentUtil.EXTRA_CHOICE_XML);
44 | try {
45 | mChoice = serializer.read(Choice.class, choiceXml);
46 | } catch (Exception e) {
47 | Log.d(TAG, e.toString());
48 | return;
49 | }
50 | Settings settings = new Settings(this);
51 | mNotificationPriority = settings.notificationPriority();
52 | mGoHomeNewScenario = settings.goHomeNewScenario();
53 | mExpandNotificationsNewScenario = settings.expandNotificationsNewScenario();
54 | setAchievements();
55 | maybeAdjustProgress();
56 | showScenarioNotification();
57 | disableExistingShortcuts();
58 | addChoiceShortcuts();
59 | goHomeToHideShortcuts();
60 | expandNotificationsPanelDelayed();
61 | }
62 |
63 | private void setAchievements() {
64 | if (mChoice.achievements == null || mChoice.achievements.isEmpty()) {
65 | return;
66 | }
67 | String storyFile = new Progress(this).getStoryFile();
68 | Achievements achievements = new Achievements(this, storyFile);
69 | for (Achieve achieve : mChoice.achievements) {
70 | achievements.achieve(achieve.achievementName);
71 | }
72 | }
73 |
74 | private void maybeAdjustProgress() {
75 | if (mChoice.isFinish()) {
76 | new Progress(this).setNotInProgress();
77 | }
78 | }
79 |
80 | private void showScenarioNotification() {
81 | Intent scenarioDialogIntent = IntentUtil.scenarioDialog(
82 | this, mChoice.getScenario(), mChoice.hasAchievements(), mChoice.isFinish());
83 | PendingIntent pendingScenarioDialogIntent = IntentUtil.makePendingIntent(this, scenarioDialogIntent);
84 | Intent achievementsDialogIntent = IntentUtil.achievementsDialog(this);
85 | PendingIntent pendingAchievementsDialogIntent = IntentUtil.makePendingIntent(this, achievementsDialogIntent);
86 | NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
87 | builder.setStyle(new NotificationCompat.BigTextStyle().bigText(mChoice.getScenario()))
88 | .setContentTitle(
89 | mChoice.hasAchievements() ? getString(R.string.title_achievement) :
90 | mChoice.isFinish() ? getString(R.string.title_end) :
91 | getString(R.string.title_scenario))
92 | .setPriority(mNotificationPriority)
93 | .setSmallIcon(R.drawable.ic_shortcuts_black_24dp)
94 | .setLargeIcon(mChoice.getScenarioEmoji(this))
95 | .setColor(mChoice.getScenarioType().getColor(this))
96 | .setLights(mChoice.getScenarioType().getColor(this), TIME_NOTIFICATION_LIGHTS, TIME_NOTIFICATION_LIGHTS)
97 | .setSound(mChoice.getScenarioType().getSound(this))
98 | .setVibrate(mChoice.getScenarioType().vibratePattern)
99 | .setContentIntent(pendingScenarioDialogIntent)
100 | .addAction(new NotificationCompat.Action(
101 | 0, getString(R.string.notification_action_achievements), pendingAchievementsDialogIntent));
102 | if (!mChoice.isFinish()) {
103 | Intent addShowScenarioShortcutIntent = IntentUtil.addShowScenarioShortcut(this, mChoice);
104 | PendingIntent pendingAddShowScenarioShortcutDialogIntent = IntentUtil.makePendingIntent(this, addShowScenarioShortcutIntent);
105 | builder.setDeleteIntent(pendingAddShowScenarioShortcutDialogIntent);
106 | Intent quitStoryIntent = IntentUtil.quitStory(this);
107 | PendingIntent pendingQuitStoryIntent = IntentUtil.makePendingIntent(this, quitStoryIntent);
108 | builder.addAction(new NotificationCompat.Action(
109 | 0, getString(R.string.notification_action_quit), pendingQuitStoryIntent));
110 | }
111 | NotificationManagerCompat.from(this).notify(IdUtil.TAG_NOTIFICATION, IdUtil.ID_NOTIFICATION, builder.build());
112 | }
113 |
114 | private void goHomeToHideShortcuts() {
115 | if (!mGoHomeNewScenario) {
116 | return;
117 | }
118 | Intent homeIntent = new Intent(Intent.ACTION_MAIN);
119 | homeIntent.addCategory(Intent.CATEGORY_HOME);
120 | startActivity(homeIntent);
121 | }
122 |
123 | private void disableExistingShortcuts() {
124 | ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
125 | List shortcutIds = new ArrayList<>();
126 | for (ShortcutInfo shortcutInfo: shortcutManager.getDynamicShortcuts()) {
127 | shortcutIds.add(shortcutInfo.getId());
128 | }
129 | shortcutManager.disableShortcuts(shortcutIds);
130 | }
131 |
132 | private void addChoiceShortcuts() {
133 | ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
134 | if (mChoice.isFinish()) {
135 | shortcutManager.removeAllDynamicShortcuts();
136 | return;
137 | }
138 | if (mChoice.choices == null || mChoice.choices.size() == 0) {
139 | return;
140 | }
141 | List choiceShortcuts = new ArrayList<>();
142 | int rank = 1;
143 | for (Choice choice : mChoice.choices) {
144 | ShortcutInfo choiceShortcut = new ShortcutInfo.Builder(this, IdUtil.getRandomUniqueShortcutId())
145 | .setShortLabel(choice.action)
146 | .setLongLabel(choice.action)
147 | .setDisabledMessage(getString(R.string.shortcut_disabled_message))
148 | .setIcon(Icon.createWithBitmap(choice.getActionEmoji(this)))
149 | .setIntent(IntentUtil.choice(this, choice))
150 | .setRank(rank)
151 | .build();
152 | choiceShortcuts.add(choiceShortcut);
153 | rank++;
154 | }
155 | shortcutManager.setDynamicShortcuts(choiceShortcuts);
156 | }
157 |
158 | private void expandNotificationsPanelDelayed() {
159 | if (!mExpandNotificationsNewScenario) {
160 | return;
161 | }
162 | new Handler().postDelayed(new Runnable() {
163 | @Override
164 | public void run() {
165 | UiUtil.expandNotificationsPanel(ChoiceActivity.this);
166 | }
167 | }, DELAY_EXPAND_NOTIFICATION_PANEL);
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2017 Nicholas Rout
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @color/blue_grey_500
5 | @color/blue_grey_700
6 | @color/blue_grey_500
7 | @color/grey_200
8 |
9 |
13 |
14 |
15 | #FFEBEE
16 | #FFCDD2
17 | #EF9A9A
18 | #E57373
19 | #EF5350
20 | #F44336
21 | #E53935
22 | #D32F2F
23 | #C62828
24 | #B71C1C
25 | #FF8A80
26 | #FF5252
27 | #FF1744
28 | #D50000
29 |
30 |
31 | #FCE4EC
32 | #F8BBD0
33 | #F48FB1
34 | #F06292
35 | #EC407A
36 | #E91E63
37 | #D81B60
38 | #C2185B
39 | #AD1457
40 | #880E4F
41 | #FF80AB
42 | #FF4081
43 | #F50057
44 | #C51162
45 |
46 |
47 | #F3E5F5
48 | #E1BEE7
49 | #CE93D8
50 | #BA68C8
51 | #AB47BC
52 | #9C27B0
53 | #8E24AA
54 | #7B1FA2
55 | #6A1B9A
56 | #4A148C
57 | #EA80FC
58 | #E040FB
59 | #D500F9
60 | #AA00FF
61 |
62 |
63 | #EDE7F6
64 | #D1C4E9
65 | #B39DDB
66 | #9575CD
67 | #7E57C2
68 | #673AB7
69 | #5E35B1
70 | #512DA8
71 | #4527A0
72 | #311B92
73 | #B388FF
74 | #7C4DFF
75 | #651FFF
76 | #6200EA
77 |
78 |
79 | #E8EAF6
80 | #C5CAE9
81 | #9FA8DA
82 | #7986CB
83 | #5C6BC0
84 | #3F51B5
85 | #3949AB
86 | #303F9F
87 | #283593
88 | #1A237E
89 | #8C9EFF
90 | #536DFE
91 | #3D5AFE
92 | #304FFE
93 |
94 |
95 | #E3F2FD
96 | #BBDEFB
97 | #90CAF9
98 | #64B5F6
99 | #42A5F5
100 | #2196F3
101 | #1E88E5
102 | #1976D2
103 | #1565C0
104 | #0D47A1
105 | #82B1FF
106 | #448AFF
107 | #2979FF
108 | #2962FF
109 |
110 |
111 | #E1F5FE
112 | #B3E5FC
113 | #81D4fA
114 | #4fC3F7
115 | #29B6FC
116 | #03A9F4
117 | #039BE5
118 | #0288D1
119 | #0277BD
120 | #01579B
121 | #80D8FF
122 | #40C4FF
123 | #00B0FF
124 | #0091EA
125 |
126 |
127 | #E0F7FA
128 | #B2EBF2
129 | #80DEEA
130 | #4DD0E1
131 | #26C6DA
132 | #00BCD4
133 | #00ACC1
134 | #0097A7
135 | #00838F
136 | #006064
137 | #84FFFF
138 | #18FFFF
139 | #00E5FF
140 | #00B8D4
141 |
142 |
143 | #E0F2F1
144 | #B2DFDB
145 | #80CBC4
146 | #4DB6AC
147 | #26A69A
148 | #009688
149 | #00897B
150 | #00796B
151 | #00695C
152 | #004D40
153 | #A7FFEB
154 | #64FFDA
155 | #1DE9B6
156 | #00BFA5
157 |
158 |
159 | #E8F5E9
160 | #C8E6C9
161 | #A5D6A7
162 | #81C784
163 | #66BB6A
164 | #4CAF50
165 | #43A047
166 | #388E3C
167 | #2E7D32
168 | #1B5E20
169 | #B9F6CA
170 | #69F0AE
171 | #00E676
172 | #00C853
173 |
174 |
175 | #F1F8E9
176 | #DCEDC8
177 | #C5E1A5
178 | #AED581
179 | #9CCC65
180 | #8BC34A
181 | #7CB342
182 | #689F38
183 | #558B2F
184 | #33691E
185 | #CCFF90
186 | #B2FF59
187 | #76FF03
188 | #64DD17
189 |
190 |
191 | #F9FBE7
192 | #F0F4C3
193 | #E6EE9C
194 | #DCE775
195 | #D4E157
196 | #CDDC39
197 | #C0CA33
198 | #A4B42B
199 | #9E9D24
200 | #827717
201 | #F4FF81
202 | #EEFF41
203 | #C6FF00
204 | #AEEA00
205 |
206 |
207 | #FFFDE7
208 | #FFF9C4
209 | #FFF590
210 | #FFF176
211 | #FFEE58
212 | #FFEB3B
213 | #FDD835
214 | #FBC02D
215 | #F9A825
216 | #F57F17
217 | #FFFF82
218 | #FFFF00
219 | #FFEA00
220 | #FFD600
221 |
222 |
223 | #FFF8E1
224 | #FFECB3
225 | #FFE082
226 | #FFD54F
227 | #FFCA28
228 | #FFC107
229 | #FFB300
230 | #FFA000
231 | #FF8F00
232 | #FF6F00
233 | #FFE57F
234 | #FFD740
235 | #FFC400
236 | #FFAB00
237 |
238 |
239 | #FFF3E0
240 | #FFE0B2
241 | #FFCC80
242 | #FFB74D
243 | #FFA726
244 | #FF9800
245 | #FB8C00
246 | #F57C00
247 | #EF6C00
248 | #E65100
249 | #FFD180
250 | #FFAB40
251 | #FF9100
252 | #FF6D00
253 |
254 |
255 | #FBE9A7
256 | #FFCCBC
257 | #FFAB91
258 | #FF8A65
259 | #FF7043
260 | #FF5722
261 | #F4511E
262 | #E64A19
263 | #D84315
264 | #BF360C
265 | #FF9E80
266 | #FF6E40
267 | #FF3D00
268 | #DD2600
269 |
270 |
271 | #EFEBE9
272 | #D7CCC8
273 | #BCAAA4
274 | #A1887F
275 | #8D6E63
276 | #795548
277 | #6D4C41
278 | #5D4037
279 | #4E342E
280 | #3E2723
281 |
282 |
283 | #FAFAFA
284 | #F5F5F5
285 | #EEEEEE
286 | #E0E0E0
287 | #BDBDBD
288 | #9E9E9E
289 | #757575
290 | #616161
291 | #424242
292 | #212121
293 |
294 |
295 | #ECEFF1
296 | #CFD8DC
297 | #B0BBC5
298 | #90A4AE
299 | #78909C
300 | #607D8B
301 | #546E7A
302 | #455A64
303 | #37474F
304 | #263238
305 |
306 | #000000
307 | #FFFFFF
308 |
309 |
310 |
--------------------------------------------------------------------------------
/app/src/main/assets/stories/an_androids_tale.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
58 |
59 |
60 | While immersed in a Daydream, a distant chime catches your attention.
63 | The sound rings again, this time louder and more urgent.
64 | You lift up your headset to see a colorful bicycle whizzing towards you!
65 | What will you do?
66 |
67 |
70 | The bicycle narrowly misses you and hurtles in to the distance.
73 | It's a bright, busy day in Plex with hundreds of Androids bustling about the common area.
74 | You decide to put your Daydream away and head inside to the cafeteria.
75 | What would you like to eat?
76 |
77 |
80 | You pick up a luke-warm spud and take a bite.
83 | Potatoes are of one of the two staple foods in Plex.
84 | While you chew on the tasteless, starchy substance, an elderly Android approaches you.
85 | "Are you enjoying that?" he asks.
86 |
87 |
90 | The elderly Android looks disappointed, and bids you farewell.
93 | You continue eating your potatoes, wondering if you have done anything wrong.
94 |
95 |
96 |
97 |
100 | The elderly Android lights up.
103 | "There was a time when sweet confectioneries and delicious treats were in abundance!" he says.
104 | "Come with me" he whispers, as he hurries out of the cafeteria with surprising speed.
105 | You follow him across the common area, through various alleyways and arrive at a small trapdoor.
106 | You crouch, step through and work your way down a small spiral of stairs into a dimly-lit room.
107 | You find the elderly Android standing in the centre of the room, pointing at six markings etched on the floor.
108 | They include a cylindrical shape, four thin bars and a small, badly-drawn oval.
109 | "These drawings depict half of the sweet treats enjoyed by Androids many clock cycles ago" he says.
110 | "Would you like to taste them for yourself?"
111 |
112 |
115 | You give a hurried goodbye to the elderly Android, head back up the stairs and exit through the trapdoor.
118 | A mixture of confusion and intrigue runs through your CPU as you slowly walk away.
119 |
120 |
121 |
122 |
125 | The elderly Android grins.
128 | "I can offer one of my possessions to assist you on your sugary journey" he says.
129 | You wonder where you will be journeying to and how you will get there, but the excitement of it all overwhelms you.
130 | He holds out his hand, ushering you to choose one of three items.
131 | Which one will you take?
132 |
133 |
136 | You stow the spoon, wondering what it could be used for.
139 | Suddenly, you feel a magnetic pull as the floor seems to sink beneath your feet.
140 | You catch a glimpse of the elderly Android waving goodbye as a blinding light engulfs you.
141 | Before you can say 'Nexus' you land on a grassy patch of land, your image sensors adjusting back to normal light.
142 | "Where am I?" you wonder.
143 | The general topography of the area feels familiar yet different, almost as if you have gone back in time.
144 | You snap out of it and remember that your goal is to locate the sweet treats the elderly Android spoke of.
145 | To your left lies a path that leads into a thick maze of trees.
146 | To your right you notice a young Android dressed in unfamiliar clothes.
147 | What will you do?
148 |
149 |
152 | You venture down the path, the foliage growing denser with every step.
155 | At one point, you notice the floor crunch beneath your feet.
156 | You find this a bit odd as the ground appears to be soft, due to the layers of leaves and mud.
157 | At one point, the crunching sound is very audible.
158 | You peer down and notice that specks of dark brown and white nestled between the grass.
159 | The specks appears to lead in to a small opening in the trees.
160 | What will you do?
161 |
162 |
165 | As you reach the end of the path, a humming sound grows gradually louder.
168 | Something buzzes past your ear.
169 | You have a strong dislike for bugs.
170 | "I'm lost" you mutter, noticing a dead end in front of you.
171 | Your disappointment turns to fear as you look up in horror to see hundreds of winged creatures flying towards you!
172 | You notice a small opening in the trees, a soft golden glow emanating from it.
173 | Act fast!
174 |
175 |
178 | You burst through the opening just in time.
181 | Thankfully, your winged opponents do not follow you inside.
182 | While catching your breath, you notice strange hexagonal openings in the walls.
183 | The yellow glow of the room calms your nerves and you decide to inspect them.
184 | Each hexagon appears to hold a thick, golden liquid.
185 | Without thinking, you dip your finger in to one of them and taste it.
186 | It's floral, sticky and sweet.
187 | You have found the Honeycomb treat!
188 |
189 | As you finish savouring on it, you feel the same magnetic pull that brought you to this place.
190 | With a bright flash of light, you are pulled away and thrown back in to Plex.
191 | Upon arrival, you seem to have only cached a vague memory of your adventure.
192 | "Did I imagine that?" you wonder.
193 | Unsure of it all, you decide to relax for now and postpone thinking about it until lunch time.
194 | You put on your Daydream headset...
195 |
196 |
197 |
198 |
199 |
200 |
203 | You approach the small opening in the trees.
206 | You lean down, placing your hand next to the opening in order to peer in.
207 | The plants give way and your lose your balance, falling into a small, hollow clearing.
208 | Geometric shapes arranged in a circular pattern are marked on the muddy ground.
209 | Dazed and a little confused, you look up to see a jar placed on a rock, like a shrine, in the middle of the area.
210 | You cautiously approach it.
211 | What will you do next?
212 |
213 |
216 | A delicious smell escapes the jar and instantly fills the enclosure.
219 | You notice disc-like items in the jar, each dark brown in color with a milky white centre.
220 | You extract one and feel compelled to take a bite.
221 | It's chocolaty, crunchy and sweet.
222 | You have found the mysterious 'O' treat!
223 |
224 | As you finish savouring on it, you feel the same magnetic pull that brought you to this place.
225 | With a bright flash of light, you are pulled away and thrown back in to Plex.
226 | Upon arrival, you seem to have only cached a vague memory of your adventure.
227 | "Was that real?" you wonder.
228 | Unsure of it all, you decide to relax for now and postpone thinking about it until lunch time.
229 | You put on your Daydream headset...
230 |
231 |
232 |
233 |
234 |
235 |
236 |
239 | You cautiously approach the young Android.
242 | She turns around, startled but seemingly happy to see you.
243 | "Hello! Who are you?" she says.
244 | "I'm Andy" you reply.
245 | You notice she is holding a small white container, holding a smooth substance that points up from the top.
246 | "I've brought this along to enjoy, but I have nothing to eat it with" she declares.
247 | "I'd offer you some if that weren't the case".
248 | What will you do?
249 |
250 |
253 | You present the spoon and the young Android smiles.
256 | "Here we go, have the first taste" she says, holding up the container.
257 | You take a scoop of the glossy substance place it in your mouth.
258 | It's cold, creamy and sweet.
259 | You have found the Froyo treat!
260 |
261 | As you finish savouring on it, you feel the same magnetic pull that brought you to this place.
262 | With a bright flash of light, you are pulled away and thrown back in to Plex.
263 | Upon arrival, you seem to have only cached a vague memory of your adventure.
264 | "Did that really happen?" you wonder.
265 | Unsure of it all, you decide to relax for now and postpone thinking about it until lunch time.
266 | You put on your Daydream headset...
267 |
268 |
269 |
270 |
271 |
272 |
273 |
276 | You put away the charging cable, wondering what it could be used for.
279 | Suddenly, you feel a magnetic pull as the floor seems to sink beneath your feet.
280 | You catch a glimpse of the elderly Android waving goodbye as a blinding light engulfs you.
281 | Before you can say 'Pixel' you land in what appears to be a small village.
282 | "What is this place?" you wonder.
283 | The general topography of the area feels familiar yet different, almost as if you have gone back in time.
284 | You snap out of it and remember that your goal is to locate the sweet treats the elderly Android spoke of.
285 | To your surprise you notice an angry-looking Android, wielding his fists, running towards you!
286 | What will you do?
287 |
288 |
291 | The Android slows down as he approaches you, his expression turning from angry to sheepish and his fists lowering.
294 | "Ummm, my job is to defend this village" he stutters.
295 | "I thought you would be afraid."
296 | Although you were startled, you admit to him that he is not very scary.
297 | You strike up a conversation and learn that the village has become quite hostile as a result of the main energy unit having failed.
298 | The inhabitants are without power and it has made them frustrated.
299 | He shows you the energy unit, a large Lithium-ion battery.
300 | You notice that the main cable is bent and frayed towards the base.
301 | Can you fix it?
302 |
303 |
306 | The charging cable is perfect for patching the energy unit.
309 | The battery hums and lights tinkle as the energy flows through the village.
310 | You hear cries of delight all around you and you receive an overwhelming hug from the Android.
311 | "Please, take this, it is the least I can offer for restoring our energy" he says.
312 | He hands you a light-colored item with a cuboid shape, a bit sticky and surprisingly light.
313 | There appear to be items embedded in it
314 | You decide to take a bite.
315 | It's light, chewy, and sweet with an occasional nutty crunch.
316 | You have found the Nougat treat!
317 |
318 | As you finish savouring on it, you feel the same magnetic pull that brought you to this place.
319 | With a bright flash of light, you are pulled away and thrown back in to Plex.
320 | Upon arrival, you seem to have only cached a vague memory of your adventure.
321 | "What was that?" you wonder.
322 | Unsure of it all, you decide to relax for now and postpone thinking about it until lunch time.
323 | You put on your Daydream headset...
324 |
325 |
326 |
327 |
328 |
329 |
332 | You run towards the Android but, to your surprise, he turns around in fear and runs in the opposite direction.
335 | Perplexed, you stand next to a small village hut and decide to inspect your surroundings.
336 | You don't get very far until an elderly female Android emerges from the hut and confronts you.
337 | She ushers you to come inside.
338 | A rich aroma fills your smell sensors.
339 | She points towards a table on which fifty or so small, Android-shaped objects are laid down.
340 | They are flat and a golden-brown color.
341 | She points at them.
342 | "Would you like to buy one?" she asks.
343 |
344 |
347 | She holds up a NFC pad and you tap your watch on it, transferring some credits.
350 | She hands you one of the objects.
351 | You find the Android shape strange, but the aroma is enticing and you bite in to it.
352 | It's crunchy, fruity, and sweet and melts in your mouth.
353 | You have found the Gingerbread treat!
354 |
355 | As you finish savouring on it, you feel the same magnetic pull that brought you to this place.
356 | With a bright flash of light, you are pulled away and thrown back in to Plex.
357 | Upon arrival, you seem to have only cached a vague memory of your adventure.
358 | "What on Plex..." you wonder.
359 | Unsure of it all, you decide to relax for now and postpone thinking about it until lunch time.
360 | You put on your Daydream headset...
361 |
362 |
363 |
364 |
365 |
366 |
367 |
370 | You tie the rope around your waist, wondering what it could be used for.
373 | Suddenly, you feel a magnetic pull as the floor seems to sink beneath your feet.
374 | You catch a glimpse of the elderly Android waving goodbye as a blinding light engulfs you.
375 | Before you can say 'Galaxy' you land with a splash in a rapid river.
376 | "Help!" you yell with all of your speakers' might, but there is no one in sight.
377 | You calm down and remember that your goal is to locate the sweet treats the elderly Android spoke of.
378 | Realising you need to act fast, you notice a patch of reeds between yourself and the river bank.
379 | What will you do?
380 |
381 |
384 | You untie the rope from your waist and sling it towards the reeds.
387 | Luckily, they latch on and hold tight.
388 | You pull yourself towards the river bank, exhausted and drenched in water.
389 | You take a look back at the reeds and notice they are uniform in shape and texture.
390 | "I must be imagining things" you mutter, but you have a nagging suspicion.
391 | What will you do?
392 |
393 |
396 | You were right!
399 | You quickly notice that it is certainly not a reed.
400 | The outside is brown and glossy, with writing - in a language you don't recognise - engraved in the surface.
401 | The inside is appears dry and almost hollow.
402 | Your exhaustion has lead to hunger and you decide to take a bite.
403 | It's crispy, chocolaty, and sweet.
404 | You have found the KitKat treat!
405 |
406 | As you finish savouring on it, you feel the same magnetic pull that brought you to this place.
407 | With a bright flash of light, you are pulled away and thrown back in to Plex.
408 | Upon arrival, you seem to have only cached a vague memory of your adventure.
409 | "I must be going crazy" you think.
410 | Unsure of it all, you decide to relax for now and postpone thinking about it until lunch time.
411 | You put on your Daydream headset...
412 |
413 |
414 |
415 |
416 |
417 |
420 | You have all but given up hope, when you feel yourself being lurched to the side.
423 | A Android has grabbed hold of you and is swimming you to the river bank.
424 | When you reach the side, he laughs and pats you on the back.
425 | "That was a close one!" he exclaims.
426 | "Thank you for saving me" you say, breathing a sigh of relief.
427 | "Is there anything I can do to repay you?"
428 | He scratches his head and appears to quickly make up his mind.
429 | He extracts a pack of cards, with markings on them you've never seen, and holds them out towards you.
430 | "I've been practising my magic but I don't have anyone to test it on, so pick one!" he exclaims.
431 | "Wanna play?"
432 |
433 |
436 | You draw a card, being careful not to show him.
439 | He puts it back in to the deck, shuffles, performs some magic and extracts it.
440 | "Is this the one?" he asks.
441 | After inspecting it and seeing that it is your card, you nod, much to his enjoyment.
442 | "You look worn out from your river adventure" he says.
443 | "Here, take this" he says, offering you some kind of a puffy item wrapped in colorful paper.
444 | Figuring it is some kind of food, you take a bite.
445 | It's light, fluffy, and sweet with a sugary layer on top.
446 | You have found the Cupcake treat!
447 |
448 | As you finish savouring on it, you feel the same magnetic pull that brought you to this place.
449 | With a bright flash of light, you are pulled away and thrown back in to Plex.
450 | Upon arrival, you seem to have only cached a vague memory of your adventure.
451 | "I must be going crazy" you think.
452 | Unsure of it all, you decide to relax for now and postpone thinking about it until lunch time.
453 | You put on your Daydream headset...
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
466 | You take a spoonful.
469 | Porridge is one of the two staple foods in Plex.
470 | While you chew on the tasteless, gooey substance, an elderly Android approaches you.
471 | "Are you enjoying that?" he asks.
472 |
473 |
476 | The elderly Android looks disappointed, and bids you farewell.
479 | You continue eating your porridge, wondering if you have done anything wrong.
480 |
481 |
482 |
483 |
486 | The elderly Android lights up.
489 | "There was a time when sweet confectioneries and delicious treats were in abundance!" he says.
490 | "Come with me" he whispers, as he hurries out of the cafeteria with surprising speed.
491 | You follow him across the common area, through various alleyways and arrive at a small, run-down building.
492 | You step through the open door.
493 | The room is empty.
494 | You find the elderly Android standing next to a dusty wall, pointing at six markings on its surface.
495 | They include some kind of ring, a hexagonal structure and what appears to be a small figure.
496 | "These drawings depict half of the sweet treats enjoyed by Androids many clock cycles ago" he says.
497 | "Do you want to experience their sugary goodness?"
498 |
499 |
502 | You give a hurried goodbye to the elderly Android and exit the small building.
505 | A mixture of confusion and intrigue runs through your CPU as you slowly walk away.
506 |
507 |
508 |
509 |
512 | The elderly Android grins.
515 | "You may need assistance on your tasty journey." he says.
516 | "I am willing to part with one of my possessions."
517 | You wonder where you will be journeying to and how you will get there, but the excitement of it all overwhelms you.
518 | He holds out his hand, ushering you to choose one of three items.
519 | Which one will you take?
520 |
521 |
524 | You stash the LED bulb, wondering what it could be used for.
527 | Suddenly, you feel a magnetic pull as the walls seems close in on you.
528 | You catch a glimpse of the elderly Android waving goodbye as a blinding light engulfs you.
529 | Before you can say 'Optimus' you land in a rocky cave with very little light.
530 | "What is this?" you wonder.
531 | You come to your senses and remember that your goal is to locate the sweet treats the elderly Android spoke of.
532 | You see a small, metal device on the floor and pick it up.
533 | You notice it has two small openings on the top.
534 | What will you do?
535 |
536 |
539 | To your delight, the device fills the cave with bright light.
542 | You look around you and notice that strangely-shaped stalactites hang from the ceiling.
543 | They're long and thin, with spheres of all different colors on their ends.
544 | They are peculiar but intriguing.
545 | What will you do?
546 |
547 |
550 | The object is strangely symmetrical, almost as if it had been hand-crafted.
553 | It's not rocky, like your surroundings, but glossy and semi-transparent.
554 | Something compels you to taste it.
555 | It's fruity and sweet.
556 | You have found the Lollipop treat!
557 |
558 | As you finish savouring on it, you feel the same magnetic pull that brought you to this place.
559 | With a bright flash of light, you are pulled away and thrown back in to Plex.
560 | Upon arrival, you seem to have only cached a vague memory of your adventure.
561 | "Interesting..." you think.
562 | Unsure of it all, you decide to relax for now and postpone thinking about it until lunch time.
563 | You put on your Daydream headset...
564 |
565 |
566 |
567 |
568 |
569 |
572 | The cave is dark and you are barely getting anywhere.
575 | On top of that, you are quite hungry.
576 | With no light, you do not have many options.
577 | What will you do?
578 |
579 |
582 | You place your hand on the floor in the hope of finding something useful.
585 | Alas, you find only cave pebbles.
586 | You hold them in your hand, struggling to come up with any more ideas.
587 | Strangely, the pebbles seem to be softening in your hands.
588 | You lift up your hand and a sweet aroma catches your senses.
589 | "What are these?" you wonder.
590 | Without thinking, you pop one in your mouth.
591 | It's fruity, chewy, and sweet with a sour zing.
592 | You have found the Jelly Bean treat!
593 |
594 | As you finish savouring on it, you feel the same magnetic pull that brought you to this place.
595 | With a bright flash of light, you are pulled away and thrown back in to Plex.
596 | Upon arrival, you seem to have only cached a vague memory of your adventure.
597 | "Strange..." you think.
598 | Unsure of it all, you decide to relax for now and postpone thinking about it until lunch time.
599 | You put on your Daydream headset...
600 |
601 |
602 |
603 |
604 |
605 |
606 |
609 | You put on the boots, wondering if they will come in handy.
612 | Suddenly, you feel a magnetic pull as the walls seems close in on you.
613 | You catch a glimpse of the elderly Android waving goodbye as a blinding light engulfs you.
614 | Before you can say 'Xperia' you land on a snowy slope, a blizzard howling around you.
615 | You let out an audible "Brrr" as your thermometer begins to drop.
616 | You try to ignore the temperature and remember that your goal is to locate the sweet treats the elderly Android spoke of.
617 | Thankfully, your boots prevent you from slipping on the ice and allow you to move around.
618 | The slope looks like a challenging climb, but it's not impossible.
619 | What will you do?
620 |
621 |
624 | The boots come in handy as you traverse up the slope.
627 | It seems like an eternity but you manage to reach a plateau where you notice a large cube of ice and a pick axe lying next to it.
628 | Inside the cube, you spot a vague outline of an item, brown and white in color.
629 | "What is that?" you wonder.
630 | What will you do?
631 |
632 |
635 | You pick up the axe and begin to chip away at the surface.
638 | It takes a long time but you are compelled to get to the item at the centre.
639 | You reach it and it falls from the cube, on to the snow.
640 | You pick it up and notice a frozen white substance nestled between two brown, pitted layers.
641 | You're exhausted and, somehow, the item seems edible.
642 | You sink your teeth in to it, despite how cold it is.
643 | It's cold, creamy, and sweet with a chewy outer layer.
644 | You have found the Ice Cream Sandwich treat!
645 |
646 | As you finish savouring on it, you feel the same magnetic pull that brought you to this place.
647 | With a bright flash of light, you are pulled away and thrown back in to Plex.
648 | Upon arrival, you seem to have only cached a vague memory of your adventure.
649 | "That's odd" you mumble.
650 | Unsure of it all, you decide to relax for now and postpone thinking about it until lunch time.
651 | You put on your Daydream headset...
652 |
653 |
654 |
655 |
656 |
657 |
660 | You decide to clear the immediate snow around you to build a bunker to shelter you.
663 | You dig and dig, almost endlessly, but you eventually form some kind of a shelter.
664 | On your last scoop of snow, your hand hits a wooden surface.
665 | Your energy rejuvenated by intrigue, you keep digging and unearth a small box.
666 | What will you do?
667 |
668 |
671 | Inside the box you find two cylindrical objects, one a light pink and the other white.
674 | They remind you of clouds and they feel soft to the touch.
675 | All this digging in the cold has made you hungry.
676 | Without thinking, you pop one of the objects in to your mouth.
677 | It's soft, fluffy, and sweet.
678 | You have found the Marshmallow treat!
679 |
680 | As you finish savouring on it, you feel the same magnetic pull that brought you to this place.
681 | With a bright flash of light, you are pulled away and thrown back in to Plex.
682 | Upon arrival, you seem to have only cached a vague memory of your adventure.
683 | "I wonder..." you mumble.
684 | Unsure of it all, you decide to relax for now and postpone thinking about it until lunch time.
685 | You put on your Daydream headset...
686 |
687 |
688 |
689 |
690 |
691 |
692 |
695 | You tuck away the button, unsure of its significance.
698 | Suddenly, you feel a magnetic pull as the walls seems close in on you.
699 | You catch a glimpse of the elderly Android waving goodbye as a blinding light engulfs you.
700 | Before you can say 'OnePlus' you land in the centre of a field.
701 | You can barely see past the tops of the beige-colored plants.
702 | You remember that your goal is to locate the sweet treats the elderly Android spoke of, but you are rather fatigued.
703 | What will you do?
704 |
705 |
708 | You doze off almost instantly and wake up some time after, feeling revitalised.
711 | You notice you are not alone in the field.
712 | A few other Androids dressed in denim are walking around, gathering the dried plants surrounding you.
713 | You wave at one of them and he makes his way towards you.
714 | "Howdy, partner" he exclaims.
715 | After a fair bit of conversation, you determine he comes from a family of 'bakers' (a word you have never heard of).
716 | "How about a sample of our goods?" he asks.
717 |
718 |
721 | He reaches in to his backpack and pulls out a cardboard carton.
724 | He opens it up and you take a look inside.
725 | You see 6 ring-shaped items, each with a glossy shell covering their tops.
726 | "Well, go ahead" he says.
727 | You don't hesitate to grab a pink-colored one and take a mouthful.
728 | It's warm, doughy, and sweet.
729 | You have found the Donut treat!
730 |
731 | As you finish savouring on it, you feel the same magnetic pull that brought you to this place.
732 | With a bright flash of light, you are pulled away and thrown back in to Plex.
733 | Upon arrival, you seem to have only cached a vague memory of your adventure.
734 | "Huh, weird" you think.
735 | Unsure of it all, you decide to relax for now and postpone thinking about it until lunch time.
736 | You put on your Daydream headset...
737 |
738 |
739 |
740 |
741 |
742 |
745 | The water provides you with some much-needed energy.
748 | You notice a thin wisp of smoke in the distance and decide to walk towards it.
749 | You find that the source of the smoke is a small, stone house with a sign on it that reads "The Bakers".
750 | Intrigued by this strange word, you enter the house.
751 | An Android is sitting at a table, engrossed in a task involving a large, white funnel from which she is piping a strange mixture.
752 | "Hi there" she says as she looks up.
753 | "These are fresh out of the oven, would you like to buy one?"
754 |
755 |
758 | She hands you an item that appears to be holding the same substance she was piping.
761 | There is a thin, brown shell on the top of it, which cracks as you take it in yiur hand.
762 | It looks enticing, so you bite in to it.
763 | It's creamy, chocolaty, and sweet with a doughy consistency.
764 | You have found the Eclair treat!
765 |
766 | As you finish savouring on it, you feel the same magnetic pull that brought you to this place.
767 | With a bright flash of light, you are pulled away and thrown back in to Plex.
768 | Upon arrival, you seem to have only cached a vague memory of your adventure.
769 | "That can't be real" you think.
770 | Unsure of it all, you decide to relax for now and postpone thinking about it until lunch time.
771 | You put on your Daydream headset...
772 |
773 |
774 |
775 |
776 |
777 |
778 |
779 |
780 |
781 |
782 |
785 | The bicycle begins to slow down rapidly as the rider hastily applies the brakes.
788 | It stops just before you and the owner is not impressed.
789 | The Android huffs and rides away.
790 | "That was close" you mutter out loud, wondering how you could have handled the situation better.
791 | You decide to get back to Daydreaming in order to stop thinking about it.
792 |
793 |
794 |
795 |
796 |
797 |
--------------------------------------------------------------------------------