7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2015 Google Inc. All Rights Reserved.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | #Fri Jan 09 12:15:18 PST 2015
18 | distributionBase=GRADLE_USER_HOME
19 | distributionPath=wrapper/dists
20 | zipStoreBase=GRADLE_USER_HOME
21 | zipStorePath=wrapper/dists
22 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
23 |
--------------------------------------------------------------------------------
/.idea/copyright/Apache_2_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 | 782016241500
22 |
23 |
24 | CgkI3L6znuEWEAIQAQ
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_in_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_out_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
25 |
--------------------------------------------------------------------------------
/app/src/main/assets/missions/ex_06_long_timer_mission.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
22 |
25 | 100.0
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
21 | 64dp
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
25 |
--------------------------------------------------------------------------------
/app/src/main/assets/missions/ex_01_timer_moment.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
22 |
25 | 0.5
26 | Played a mission!
27 | Played a timer moment
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/utils/MissionParseException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame.utils;
18 |
19 | /**
20 | * An exception for problems during the parsing of a mission.
21 | */
22 | public class MissionParseException extends Exception {
23 | private static final String MOMENT_TYPE_EXCEPTION = "Mission could not be parsed.";
24 |
25 | public MissionParseException() {
26 | super(MOMENT_TYPE_EXCEPTION);
27 | }
28 |
29 | public MissionParseException(String s) {
30 | super(s);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/assets/missions/ex_02_spoken_text_moment.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
22 |
25 |
26 | Hello! This is a spoken text moment that uses text to speech.
27 |
28 | Heard a spoken text moment
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/assets/missions/ex_05_broken_timer_moment.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 |
23 |
26 | Played a mission!
27 | Played a timer moment
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/placeholder_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
22 |
23 |
29 |
--------------------------------------------------------------------------------
/app/src/main/assets/missions/ex_04_sfx_moment.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
22 |
25 | android.resource://com.google.fpl.gim.examplegame/raw/brains
26 | BRAINS
27 | BRAINS BRAINS BRAINS
28 | BRAAAAAAAAAAAAAAAINS
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/assets/legacy_missions/texttospeechmission.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
22 |
25 |
26 | Hello!
27 |
28 |
31 | Agent, your mission is simple. Good luck. Godspeed!
32 |
33 |
--------------------------------------------------------------------------------
/docs/src/programmers_guide/gameplay.md:
--------------------------------------------------------------------------------
1 | Gameplay {#games_in_motion_guide_gameplay}
2 | ===============
3 |
4 | The player selects a mission to play and exercises (walk or run) while receiving
5 | audio cues on gameplay.
6 |
7 | The player can customize their experience based on their fitness level and
8 | goals. In order to fulfill the player's exercise goals, they can select an
9 | interval speed they want to challenge. If the player wants to listen to music
10 | while running, they can switch over to their favourite music app and start
11 | playing the music before starting a mission.
12 |
13 | While on a mission, the player listens to mission details, learning about the
14 | story and what the game goals are. When it comes time to interact with the game,
15 | the player will make choices by tapping or swiping on their [Android Wear][]
16 | device or the notifications menu of their phone or tablet.
17 |
18 | If the player has been consistently running at the interval speed,
19 | they will be rewarded with extra choices.
20 |
21 | Upon completion of the mission, the app presents the statistics of the exercise
22 | and uploads the data to [Google Fit][].
23 |
24 | \s\s
25 |
26 | [Android Wear]: https://developer.android.com/wear/index.html
27 | [Google Fit]: https://developers.google.com/fit/
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | apply plugin: 'com.android.application'
18 |
19 | android {
20 | compileSdkVersion 21
21 | buildToolsVersion "19.1"
22 |
23 | defaultConfig {
24 | applicationId "com.google.fpl.gim.examplegame"
25 | minSdkVersion 16
26 | targetSdkVersion 21
27 | versionCode 1
28 | versionName "1.0"
29 | }
30 | buildTypes {
31 | }
32 | }
33 |
34 | repositories {
35 | mavenCentral()
36 | flatDir {
37 | dirs 'libs'
38 | }
39 | }
40 |
41 | dependencies {
42 | compile group: 'junit', name: 'junit', version: '3.8'
43 | compile 'com.android.support:appcompat-v7:21.+'
44 | compile 'com.google.android.gms:play-services:6.1.+'
45 | }
--------------------------------------------------------------------------------
/app/src/main/assets/legacy_missions/timermission.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
22 |
25 |
26 | 0.1
27 |
28 |
31 |
32 | 0.1
33 |
34 |
37 | 0.1
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/assets/legacy_missions/sfx mission.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
22 |
25 |
26 | You will now hear a snap sound.
27 |
28 |
31 | android.resource://com.google.fpl.gim.examplegame/raw/snap
32 |
33 |
34 |
37 | That was a snap! You're welcome.
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/Outcome.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame;
18 |
19 | /**
20 | * Encapsulates the game data changes that should occur as a result of a certain Choice
21 | */
22 | public class Outcome {
23 | private boolean mDepleteWeaponCharge;
24 | private boolean mIncrementNumEnemiesDefeated;
25 |
26 | public Outcome(boolean depleteWeaponCharge, boolean incrementNumEnemiesDefeated) {
27 | this.mDepleteWeaponCharge = depleteWeaponCharge;
28 | this.mIncrementNumEnemiesDefeated = incrementNumEnemiesDefeated;
29 | }
30 |
31 | public boolean weaponChargeDepleted() {
32 | return this.mDepleteWeaponCharge;
33 | }
34 |
35 | public boolean numEnemiesDefeatedIncremented() {
36 | return this.mIncrementNumEnemiesDefeated;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/CONTRIBUTING:
--------------------------------------------------------------------------------
1 | Contributing {#contributing}
2 | ============
3 |
4 | Want to contribute? Great! First, read this page (including the small print at
5 | the end).
6 |
7 | # Before you contribute
8 | Before we can use your code, you must sign the
9 | [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1)
10 | (CLA), which you can do online. The CLA is necessary mainly because you own the
11 | copyright to your changes, even after your contribution becomes part of our
12 | codebase, so we need your permission to use and distribute your code. We also
13 | need to be sure of various other things—for instance that you'll tell us if you
14 | know that your code infringes on other people's patents. You don't have to sign
15 | the CLA until after you've submitted your code for review and a member has
16 | approved it, but you must do it before we can put your code into our codebase.
17 | Before you start working on a larger contribution, you should get in touch with
18 | us first through the issue tracker with your idea so that we can help out and
19 | possibly guide you. Coordinating up front makes it much easier to avoid
20 | frustration later on.
21 |
22 | # Code reviews
23 | All submissions, including submissions by project members, require review. We
24 | use Github pull requests for this purpose.
25 |
26 | # The small print
27 | Contributions made by corporations are covered by a different agreement than
28 | the one above, the Software Grant and Corporate Contributor License Agreement.
29 |
--------------------------------------------------------------------------------
/docs/src/contributing.md:
--------------------------------------------------------------------------------
1 | Contributing {#contributing}
2 | ============
3 |
4 | Want to contribute? Great! First, read this page (including the small print at
5 | the end).
6 |
7 | # Before you contribute
8 | Before we can use your code, you must sign the
9 | [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1)
10 | (CLA), which you can do online. The CLA is necessary mainly because you own the
11 | copyright to your changes, even after your contribution becomes part of our
12 | codebase, so we need your permission to use and distribute your code. We also
13 | need to be sure of various other things—for instance that you'll tell us if you
14 | know that your code infringes on other people's patents. You don't have to sign
15 | the CLA until after you've submitted your code for review and a member has
16 | approved it, but you must do it before we can put your code into our codebase.
17 | Before you start working on a larger contribution, you should get in touch with
18 | us first through the issue tracker with your idea so that we can help out and
19 | possibly guide you. Coordinating up front makes it much easier to avoid
20 | frustration later on.
21 |
22 | # Code reviews
23 | All submissions, including submissions by project members, require review. We
24 | use Github pull requests for this purpose.
25 |
26 | # The small print
27 | Contributions made by corporations are covered by a different agreement than
28 | the one above, the Software Grant and Corporate Contributor License Agreement.
29 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2015 Google Inc. All Rights Reserved.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | # Project-wide Gradle settings.
18 |
19 | # IDE (e.g. Android Studio) users:
20 | # Settings specified in this file will override any Gradle settings
21 | # configured through the IDE.
22 |
23 | # For more details on how to configure your build environment visit
24 | # http://www.gradle.org/docs/current/userguide/build_environment.html
25 |
26 | # Specifies the JVM arguments used for the daemon process.
27 | # The setting is particularly useful for tweaking memory settings.
28 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
29 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
30 |
31 | # When configured, Gradle will run in incubating parallel mode.
32 | # This option should only be used with decoupled projects. More details, visit
33 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
34 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/app/src/main/res/drawable/weapon_charge_progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
21 |
22 |
23 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/docs/generate_docs.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # Copyright 2015 Google Inc. All Rights Reserved.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | """Generate html documentation from markdown and doxygen comments."""
17 |
18 | import os
19 | import sys
20 |
21 | THIS_DIR = os.path.realpath(os.path.dirname(__file__))
22 | PROJECT_DIR = os.path.realpath(os.path.join(THIS_DIR, os.pardir))
23 | sys.path.extend(
24 | [os.path.realpath(os.path.join(PROJECT_DIR, os.pardir, os.pardir, '../libs', 'fplutil')),
25 | os.path.realpath(os.path.join(PROJECT_DIR, 'dependencies', 'fplutil'))])
26 | import docs # pylint: disable=C6204
27 |
28 |
29 | def main():
30 | """Generate html documentation from markdown and doxygen comments.
31 |
32 | Returns:
33 | 0 if successful, 1 otherwise.
34 | """
35 | sys.argv.extend(('--linklint-dir', THIS_DIR,
36 | '--source-dir', os.path.join(THIS_DIR, 'src'),
37 | '--project-dir', PROJECT_DIR))
38 | return docs.generate_docs.main()
39 |
40 | if __name__ == '__main__':
41 | sys.exit(main())
42 |
--------------------------------------------------------------------------------
/app/src/main/assets/legacy_missions/spoken_plus_timer_mission.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
22 |
25 |
26 |
27 | This is the first moment in this mission. The next moment should be a timer moment
28 | lasting 6 seconds.
29 |
30 |
31 |
34 |
35 | 0.1
36 |
37 |
40 |
41 | This is the final moment, and the previous moment lasted 6 seconds.
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
22 |
23 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu_music_selection.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
23 |
24 |
29 |
30 |
35 |
36 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/TimerMomentData.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame;
18 |
19 | import java.util.ArrayList;
20 |
21 | /**
22 | * Encapsulates the data that is needed to define a unique TimerMoment.
23 | */
24 | public class TimerMomentData extends MomentData {
25 |
26 | // The length of the timer in minutes
27 | private float mLengthMinutes;
28 |
29 | /**
30 | * Constructor to explicitly set all fields for a ChoiceMomentData.
31 | * @param momentId Identifier for the ChoiceMoment.
32 | * @param nextMomentId The moment following this one. Null if is the last moment in a mission.
33 | * @param fictionalProgress Fictional progress for this moment.
34 | * @param lengthMinutes Length of this moment.
35 | */
36 | public TimerMomentData(String momentId, String nextMomentId,
37 | ArrayList fictionalProgress, float lengthMinutes) {
38 | super(momentId, nextMomentId, fictionalProgress);
39 | mLengthMinutes = lengthMinutes;
40 | }
41 |
42 | public float getLengthMinutes() {
43 | return mLengthMinutes;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/SpokenTextMomentData.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame;
18 |
19 | import java.util.ArrayList;
20 |
21 | /**
22 | * Encapsulates the data that is needed to define a unique FictionAudioMoment.
23 | */
24 | public class SpokenTextMomentData extends MomentData {
25 | // The text to be read aloud using text-to-speech
26 | private final String mTextToSpeak;
27 |
28 | /**
29 | * Constructor to explicitly set all fields for a ChoiceMomentData.
30 | * @param momentId Identifier for the ChoiceMoment.
31 | * @param nextMomentId The moment following this one. Null if is the last moment in a mission.
32 | * @param fictionalProgress Fictional progress for this moment.
33 | * @param textToSpeak Words associated with this moment.
34 | */
35 | public SpokenTextMomentData(String momentId, String nextMomentId,
36 | ArrayList fictionalProgress, String textToSpeak) {
37 | super(momentId, nextMomentId, fictionalProgress);
38 | mTextToSpeak = textToSpeak;
39 | }
40 |
41 | public String getTextToSpeak() {
42 | return mTextToSpeak;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/SfxMomentData.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame;
18 |
19 | import android.net.Uri;
20 |
21 | import java.util.ArrayList;
22 |
23 | /**
24 | * Encapsulates the data that is needed to define a unique NotificationAudioMoment.
25 | */
26 | public class SfxMomentData extends MomentData {
27 |
28 | // The name of the sound file to play
29 | private final Uri mUriAsset;
30 |
31 | /**
32 | * Constructor to explicitly set all fields for a ChoiceMomentData.
33 | * @param momentId Identifier for the ChoiceMoment.
34 | * @param nextMomentId The moment following this one. Null if is the last moment in a mission.
35 | * @param fictionalProgress Fictional progress for this moment.
36 | * @param uriAsset Uri of the sound to play with this moment.
37 | */
38 | public SfxMomentData(String momentId, String nextMomentId,
39 | ArrayList fictionalProgress, Uri uriAsset) {
40 | super(momentId, nextMomentId, fictionalProgress);
41 | mUriAsset = uriAsset;
42 | }
43 |
44 | public Uri getUriAsset() {
45 | return mUriAsset;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | 16dp
20 | 16dp
21 | 10dp
22 | 10dp
23 | 20dp
24 | 20dp
25 | 40dp
26 | 50dp
27 | 100dp
28 | 175dp
29 | 120dp
30 | 10dp
31 | 10dp
32 | 70dp
33 | 40sp
34 | 25sp
35 | 15sp
36 | 20sp
37 | 350dp
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/google/FitDataTypeSetting.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame.google;
18 |
19 | import com.google.android.gms.fitness.data.DataType;
20 |
21 | /**
22 | * This keeps track of the sensor data types and their registration settings.
23 | */
24 | public class FitDataTypeSetting {
25 | private boolean mRequired; // Is this data type required for the game to start?
26 | private DataType mDataType;
27 | private long mSamplingRateSeconds;
28 | private int mAccuracyMode;
29 |
30 | public FitDataTypeSetting(
31 | boolean required, DataType dataType, long samplingRateSeconds, int accuracyMode) {
32 | mRequired = required;
33 | mDataType = dataType;
34 | mSamplingRateSeconds = samplingRateSeconds;
35 | mAccuracyMode = accuracyMode;
36 | }
37 |
38 | public boolean isRequired() {
39 | return mRequired;
40 | }
41 |
42 | public DataType getDataType() {
43 | return mDataType;
44 | }
45 |
46 | public long getSamplingRateSeconds() {
47 | return mSamplingRateSeconds;
48 | }
49 |
50 | public int getAccuracyMode() {
51 | return mAccuracyMode;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/MomentData.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame;
18 |
19 | import java.util.ArrayList;
20 |
21 | /**
22 | * Encapsulates the data that is needed to define a unique Moment.
23 | */
24 | public abstract class MomentData {
25 |
26 | // The id of this moment. Must be unique across the moments in the mission.
27 | private final String mMomentId;
28 | // The moment to go to next.
29 | private final String mNextMomentId;
30 |
31 | private ArrayList mFictionalProgress;
32 |
33 | /**
34 | * Constructor to explicitly set all fields for a ChoiceMomentData.
35 | * @param momentId Identifier for the ChoiceMoment.
36 | * @param nextMomentId The moment following this one. Null if is the last moment in a mission.
37 | * @param fictionalProgress Fictional progress for this moment.
38 | */
39 | public MomentData(String momentId, String nextMomentId, ArrayList fictionalProgress) {
40 | mMomentId = momentId;
41 | mNextMomentId = nextMomentId;
42 | mFictionalProgress = fictionalProgress;
43 | }
44 |
45 | public String getMomentId() {
46 | return mMomentId;
47 | }
48 |
49 | public String getNextMomentId() {
50 | return mNextMomentId;
51 | }
52 |
53 | public ArrayList getFictionalProgress() {
54 | return mFictionalProgress;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/docs/src/programmers_guide/overview.md:
--------------------------------------------------------------------------------
1 | Overview {#games_in_motion_guide_overview}
2 | ========
3 |
4 | ## Downloading
5 |
6 | [Games in Motion][] can be downloaded from [GitHub](http://github.com/googlesamples/android-play-games-in-motion)
7 | or the [releases page](http://github.com/googlesamples/android-play-games-in-motion/releases).
8 |
9 | ~~~{.sh}
10 | git clone --recursive https://github.com/googlesamples/android-play-games-in-motion.git
11 | ~~~
12 |
13 | ## Subsystems
14 |
15 | Games in Motion code is divided into the following subsystems
16 | - [Core][] -- holds game subsystems, state, and state machine, and UI.
17 | - [Mission System][] -- a simple, game-agnostic system for organizing game
18 | events.
19 | - [Google Api][] -- an interface with different Google services APIs.
20 | - [Audio][] -- various systems integrated to produce audio and control focus.
21 |
22 | ## Source Layout
23 |
24 | The following bullets describe the directory structure of the game.
25 |
26 |
27 | | Path | Description |
28 | |-------------------------------|----------------------------------------------|
29 | | base directory | Android Studio project files. |
30 | | `app/main` | Main directory for all code and assets. |
31 | | `app/main/assets` | Game mission data in .xml format. |
32 | | `app/main/java` | Where the main code lives. |
33 | | `app/main/res` | Android-style assets. |
34 | | `app/androidTest` | JUnit tests for the mission authoring |
35 | | | component. |
36 | | `docs` | Documentation source and html files. |
37 |
38 |
39 | \s\s
40 |
41 | [Audio]: @ref games_in_motion_guide_audio
42 | [Core]: @ref games_in_motion_guide_core
43 | [Games in Motion]: @ref games_in_motion_index
44 | [Google Api]: @ref games_in_motion_guide_google_api
45 | [Mission System]: @ref games_in_motion_guide_mission
46 |
--------------------------------------------------------------------------------
/docs/src/programmers_guide/google_api.md:
--------------------------------------------------------------------------------
1 | Google API {#games_in_motion_guide_google_api}
2 | ==========
3 |
4 | Google APIs provide feature access that is important to Games in Motion. These
5 | APIs are accessed using the [GoogleApiClient][] which handles all connections to
6 | Google-provided services.
7 |
8 | The `GoogleApiClientWrapper` class handles all connections to
9 | [GoogleApiClient][]. Find it in
10 | `app/src/main/java/com/google/fpl/gim/examplegame/google`.
11 |
12 | At start up, Games in Motion attempts to connect to various Google API services.
13 | The connection flow is delegated to the appropriate service by default.
14 |
15 | # Google Fit
16 |
17 | [Google Fit][] is a core part of Games in Motion. It keeps track of step count,
18 | tracks speed, and keeps data in the cloud so the player can retrieve it via
19 | other apps that connect to [Google Fit][].
20 |
21 | When the player is ready to start a mission, Games in Motion connects to
22 | [Google Fit][] to register callbacks for step count and speed. When all the
23 | sensors are connected properly (detected via callbacks), Games in Motion start
24 | the mission.
25 |
26 | Note that for certain Android devices, there might be a noticeable delay between
27 | successfully registering a sensor and actually getting sensor data. If the
28 | device has hardware sensors: the sensors might take some time to start up. Games
29 | in Motion does not account for the delay.
30 |
31 | For tracking speed, Games in Motion provide a fallback mechanism to calculating
32 | speed in case sensor data is unreliable.
33 |
34 | # Google Play Games Service
35 |
36 | In the `GoogleApiClientWrapper` class, Games in Motion connects to
37 | [Google Play Games Services][] in the same manner as it connects to
38 | [Google Fit][]. Games in Motion provides an example of unlocking achievements.
39 | Look for the `unlockAchievement` method in `MainService.java`.
40 |
41 | \s\s
42 |
43 | [GoogleApiClient]: https://developer.android.com/reference/com/google/android/gms/common/api/GoogleApiClient.html
44 | [Google Fit]: https://developers.google.com/fit/
45 | [Google Play Games Services]: https://developer.android.com/google/play-services/games.html
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/gui/MusicSelectionFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame.gui;
18 |
19 | import android.app.Fragment;
20 | import android.os.Bundle;
21 | import android.view.LayoutInflater;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.widget.Button;
25 |
26 | import com.google.fpl.gim.examplegame.MainActivity;
27 | import com.google.fpl.gim.examplegame.R;
28 |
29 | /**
30 | * A fragment notifying the user to go select music to listen to during their run.
31 | */
32 | public class MusicSelectionFragment extends Fragment {
33 | private static final String TAG = MusicSelectionFragment.class.getSimpleName();
34 |
35 | private Button mReadyButton;
36 |
37 | @Override
38 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
39 | Bundle savedInstanceState) {
40 | View rootView = inflater.inflate(R.layout.menu_music_selection, container, false);
41 | mReadyButton = (Button) rootView.findViewById(R.id.start_mission_button);
42 | mReadyButton.setEnabled(true);
43 | mReadyButton.setText(R.string.start_mission_button);
44 | return rootView;
45 | }
46 |
47 | @Override public void onResume() {
48 | super.onResume();
49 | ((MainActivity)getActivity()).setActionBarTitle(R.string.select_music_title);
50 | }
51 |
52 | public void disableReadyButton() {
53 | mReadyButton.setEnabled(false);
54 | mReadyButton.setText(R.string.start_mission_pressed);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/assets/missions/ex_03_choice_moment.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
22 |
25 | 0.5
26 | What will you do?
27 |
28 | Made a choice
29 |
31 | Fire!!!
32 |
35 |
36 |
37 |
39 | Use a sling shot.
40 |
43 | Used a slingshot
44 |
45 |
46 |
48 | Drop a smoke bomb.
49 |
52 | Used a smoke bomb
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/end_screen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
22 |
23 |
32 |
33 |
37 |
38 |
47 |
48 |
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/assets/legacy_missions/choice_mission.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
22 |
25 | 0.5
26 | What will you do?
27 |
28 |
30 | Fire!!!
31 |
32 |
35 |
36 |
37 |
39 | Use a sling shot.
40 |
41 |
44 |
45 |
46 |
48 | Drop a smoke bomb.
49 |
50 |
53 |
54 |
55 |
56 |
59 | 0.1
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
44 |
45 |
46 |
48 |
49 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/SfxMoment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame;
18 |
19 | import android.media.MediaPlayer;
20 |
21 | import java.io.IOException;
22 | import java.util.ArrayList;
23 |
24 | /**
25 | * Describes a Moment in which the user listens to a pre-recorded sound effect as part of the
26 | * gameplay.
27 | */
28 | public class SfxMoment extends Moment implements MediaPlayer.OnCompletionListener {
29 |
30 | private SfxMomentData mData;
31 |
32 | public SfxMoment(Mission mission, SfxMomentData data) {
33 | super(mission);
34 | this.mData = data;
35 | }
36 |
37 | @Override
38 | public void start(long nowNanos) {
39 | super.start(nowNanos);
40 | getMission().getService().queueSound(mData.getUriAsset(), this);
41 | }
42 |
43 | @Override
44 | public void end() {
45 | }
46 |
47 | @Override
48 | public String getNextMomentId() {
49 | return mData.getNextMomentId();
50 | }
51 |
52 | /**
53 | * We need to know when our specific sfx is done playing. Then we fallback to the default
54 | * onCompletionListener to finish cleaning up.
55 | */
56 | @Override
57 | public void onCompletion(MediaPlayer mp) {
58 | setIsDone(true);
59 | getMission().getService().onCompletion(mp);
60 | }
61 |
62 | @Override
63 | public void restart(long nowNanos) {
64 | getMission().getService().dequeueSound(mData.getUriAsset());
65 | // Try to start again.
66 | start(nowNanos);
67 | }
68 |
69 | public SfxMomentData getMomentData() {
70 | return this.mData;
71 | }
72 |
73 | @Override
74 | public ArrayList getFictionalProgress() {
75 | return mData.getFictionalProgress();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/docs/src/programmers_guide/building.md:
--------------------------------------------------------------------------------
1 | Building {#games_in_motion_guide_building}
2 | ========
3 |
4 | Developers can build the game from source for [Android][], using
5 | [Android Studio][].
6 |
7 | # Version Requirements
8 |
9 | Following are the minimum required versions for the tools and libraries you
10 | need for building [Games in Motion][] for [Android][]:
11 |
12 | - [Android Studio][]: Version 1.0.
13 | - [Android SDK][]: Android 4.1 (API Level 16)
14 | - [Android Support Library][]: v7.21
15 | - [Google Play Games Services][]: 6.1 or above
16 | - Install [fplutil prerequisites][] if you would like to build documentation.
17 |
18 | # Before Building
19 |
20 | - Install [Android Studio][].
21 | - Install all required libraries via the Android SDK Manager from within
22 | [Android Studio][].
23 |
24 | ## Set up Google Play Games Services
25 |
26 | To use the [Google Play Games Services][] features in the game, follow the steps
27 | below to set up [Google Play Games Services][] IDs:
28 |
29 | - Create an App ID with new package name in the
30 | [Google Play Developer Console][].
31 | - Replace `app_id` in `app/build.gradle` with the newly created one.
32 | - Update the package name in `app/src/AndroidManifest.xml` and Java source files.
33 | - For example, rename `com.google.fpl.gim.examplegame` to
34 | `com.mystudio.coolgame`.
35 | - Add [Google Play Games Services][] to the App in the
36 | [Google Play Developer Console][].
37 | - For testing prior to publishing an APK, in the Game Services sections of the
38 | [Google Play Developer Console][], add your email account as a tester.
39 |
40 | # Building, installing, testing and running the game.
41 |
42 | You can use the standard build and run instructions for [Android Studio][].
43 |
44 | To build and run the JUnit tests, change the configuration from `app` to
45 | `All Tests`.
46 |
47 |
50 |
51 | \s\s
52 |
53 | [Android]: http://www.android.com
54 | [Android SDK]: http://developer.android.com/sdk/index.html
55 | [Android Studio]: http://developer.android.com/tools/studio/index.html
56 | [Android Support Library]: https://developer.android.com/tools/support-library/features.html
57 | [fplutil prerequisites]: http://google.github.io/fplutil/fplutil_prerequisites.html
58 | [Games in Motion]: @ref games_in_motion_guide_overview
59 | [Google Play Developer Console]: http://play.google.com/apps/publish/
60 | [Google Play Games Services]: https://developer.android.com/google/play-services/games.html
61 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | Games in Motion {#games_in_motion_readme}
2 | ===============
3 |
4 | Games in Motion Version 1.0.0
5 |
6 | [Games in Motion][] is a simple game that motivates you to run.
7 |
8 | ## Motivation
9 |
10 | [Games in Motion][] is a demonstration for several Android-specific
11 | technologies, including the [Google Fit][] API, the [Android Wear][] API, and a
12 | simple data-driven design mechanic. It is also compatible with
13 | [Material design][]. This sample shows how all these pieces can be put together
14 | in a fun context, while also giving players a good motivation to exercise.
15 |
16 | [Games in Motion][] is developed entirely using [Android Studio][].
17 |
18 | ## Downloading
19 |
20 | [Games in Motion][] can be downloaded from:
21 | * [GitHub][] (source)
22 | * [GitHub Releases Page](http://github.com/googlesamples/android-play-games-in-motion/releases) (source)
23 |
24 | ~~~{.sh}
25 | git clone --recursive https://github.com/googlesamples/android-play-games-in-motion.git
26 | ~~~
27 |
28 | ## Documentation
29 |
30 | The documentation is include with the GitHub codebase. It is in the `docs`
31 | directory.
32 |
33 | Required libraries:
34 | * Python 2.7
35 | * [fplutil][] library
36 |
37 | [fplutil][] is referenced as a submodule from the [Games in Motion][]
38 | repository, so the download command referenced above will automatically download
39 | it as well.
40 |
41 | After all required libraries are downloaded, run:
42 |
43 | ~~~{.sh}
44 | ./docs/generate_docs.py
45 | ~~~
46 |
47 | The generated documentation will be in `docs/html`.
48 |
49 | To contribute the this project see [CONTRIBUTING][]. The license file is at
50 | [LICENSE][].
51 |
52 | \s\s
53 |
54 | [Android Studio]: http://developer.android.com/tools/studio/index.html
55 | [Android Wear]: https://developer.android.com/wear/index.html
56 | [Build and Run Games in Motion]: http://github.com/googlesamples/android-play-games-in-motiongames_in_motion_guide_building.html
57 | [fplutil]: http://google.github.io/fplutil/
58 | [Games in Motion]: http://github.com/googlesamples/android-play-games-in-motion
59 | [Google Fit]: https://developers.google.com/fit/
60 | [Material design]: http://www.google.com/design/spec/material-design/introduction.html
61 | [Programmer's Guide]: http://github.com/googlesamples/android-play-games-in-motion/games_in_motion_guide_overview.html
62 | [CONTRIBUTING]: http://github.com/googlesamples/android-play-games-in-motion/blob/master/CONTRIBUTING
63 | [LICENSE]: http://github.com/googlesamples/android-play-games-in-motion/blob/master/LICENSE
64 | [GitHub]: http://github.com/googlesamples/android-play-games-in-motion
65 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu_run_specifications.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
23 |
24 |
29 |
30 |
35 |
36 |
44 |
45 |
54 |
55 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
22 |
23 |
27 |
28 |
32 |
33 |
38 |
39 |
45 |
46 |
47 |
48 |
62 |
63 |
--------------------------------------------------------------------------------
/docs/src/programmers_guide/mission.md:
--------------------------------------------------------------------------------
1 | Mission System {#games_in_motion_guide_mission}
2 | ==============
3 |
4 | The data-driven mission system is controlled by the `Mission` class. Only one
5 | `Mission`, consisting of a list `Moments`, may be active at any point during
6 | gameplay. Only one `Moment` is active at a time while the player plays.
7 |
8 | Each of the data-driven classes (`Mission` and various `Moment` classes) has
9 | an equivalent Data class that contains the actual data parsed from the input XML
10 | files. Find them in `app/src/main/java/com/google/fpl/gim/examplegame`.
11 |
12 | # Mission
13 |
14 | `Mission` keeps track of the game progression, as well as all the data generated
15 | by the user during the game. Its `update()` method is being called by
16 | `MainService` per game frame, and it will trigger the relevant `Moment`s as time
17 | progresses.
18 |
19 | On mission start, `Mission` will register listeners with [Google Fit][]. This
20 | allows us to obtain speed and step data, which is critical to the game.
21 |
22 | # Moment
23 |
24 | A `Moment` is a discrete event within a `Mission`. `Moment`s know when they
25 | finish, the next `Moment` to start, are sequential, and are never reused.
26 |
27 | We have implemented a few types of `Moment`s for a basic mission flow.
28 |
29 | * Choice moments.
30 |
31 | These give the player a couple choices to choose from. The choices are
32 | shown to players as [Notifications][].
33 |
34 | * Sfx moments.
35 |
36 | These queue a simple sound effect to be played.
37 |
38 | * Spoken Text moments.
39 |
40 | These use [Android Text to Speech][] to generate spoken speech to be played
41 | when the app gets [Audio Focus][].
42 |
43 | * Timer moments.
44 |
45 | These are simple timers to space out the other `Moment`s.
46 |
47 | As you can see, each of these `Moment`s are small encapsulated game events. They
48 | are highly specialized, but can be authored to allow for variety and flexibility
49 | in each game mission.
50 |
51 | # Parser
52 |
53 | In order to parse our custom data formats for `Mission`s and `Moment`s, we have
54 | written a `MissionParser`. Find it in
55 | `app/src/main/java/com/google/fpl/gim/examplegame/utils`.
56 |
57 | This is a runtime parser that will parse XML data when the player selected a
58 | mission. It is also the only component that has unit testing. Find the test in
59 | `app/src/androidTest/`.
60 |
61 | \s\s
62 |
63 | [Android Text to Speech]: http://developer.android.com/reference/android/speech/tts/TextToSpeech.html
64 | [Audio Focus]: http://developer.android.com/training/managing-audio/audio-focus.html
65 | [Google Fit]: https://developers.google.com/fit/
66 | [Notifications]: http://developer.android.com/guide/topics/ui/notifiers/notifications.html
67 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/gui/EndSummaryFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame.gui;
18 |
19 | import android.app.Fragment;
20 | import android.os.Bundle;
21 | import android.view.LayoutInflater;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.widget.ArrayAdapter;
25 | import android.widget.ListView;
26 |
27 | import com.google.fpl.gim.examplegame.MainActivity;
28 | import com.google.fpl.gim.examplegame.R;
29 |
30 | import java.util.ArrayList;
31 |
32 | /**
33 | * Displays post-run summary.
34 | */
35 | public class EndSummaryFragment extends Fragment {
36 | private ListView mFictionalProgressList;
37 | private ListView mFitnessStatisticsList;
38 | private boolean mGoogleFitConnected = false;
39 |
40 | @Override
41 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
42 | Bundle savedInstanceState) {
43 | View rootView = inflater.inflate(R.layout.end_screen, container, false);
44 | mFictionalProgressList = (ListView) rootView.findViewById(R.id.fictionalProgressList);
45 | mFitnessStatisticsList = (ListView) rootView.findViewById(R.id.fitnessStatisticsList);
46 | return rootView;
47 | }
48 |
49 | @Override public void onResume() {
50 | super.onResume();
51 | if (mGoogleFitConnected) {
52 | ((MainActivity) getActivity()).setActionBarTitle(R.string.run_finish_text);
53 | } else {
54 | ((MainActivity) getActivity()).setActionBarTitle(R.string.fit_disconnected_text);
55 | }
56 | }
57 |
58 | public void displayStats(ArrayList fictionalProgress,
59 | ArrayList fitnessStatistics) {
60 | mFictionalProgressList.setAdapter(new ArrayAdapter<>(getActivity(),
61 | R.layout.menu_list_item, R.id.list_item_text, fictionalProgress));
62 | mFitnessStatisticsList.setAdapter(new ArrayAdapter<>(getActivity(),
63 | R.layout.menu_list_item, R.id.list_item_text, fitnessStatistics));
64 | }
65 |
66 | public void onFitStatusUpdated(boolean connected) {
67 | mGoogleFitConnected = connected;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/Choice.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame;
18 |
19 | import java.util.ArrayList;
20 |
21 | /**
22 | * Describes an option that a user has when presented with a decision to make.
23 | * Choices are only meaningful when there are 2+ of them.
24 | */
25 | public class Choice {
26 | // Unique identifier for this Choice.
27 | private String mChoiceId;
28 | // The description of this option.
29 | private String mDescription;
30 | // The moment to go to next.
31 | private String mNextMomentId;
32 | // The set of changes to make if the player chooses this option.
33 | private Outcome mOutcome;
34 | // Whether or not this Choice requires a charged weapon.
35 | private boolean mRequiresChargedWeapon;
36 | // Fictional Progress associated with this choice.
37 | private ArrayList mFictionalProgress;
38 | // The name of the resource icon to display for this Choice's action.
39 | private String mDrawableResourceName;
40 |
41 | public Choice(String choiceId, String text, String nextMomentId, Outcome outcome,
42 | boolean requiresChargedWeapon, ArrayList fictionalProgress,
43 | String drawableResourceName) {
44 | mChoiceId = choiceId;
45 | mDescription = text;
46 | mNextMomentId = nextMomentId;
47 | mOutcome = outcome;
48 | mRequiresChargedWeapon = requiresChargedWeapon;
49 | mFictionalProgress = fictionalProgress;
50 | mDrawableResourceName = drawableResourceName;
51 | }
52 |
53 | public String getChoiceId() {
54 | return mChoiceId;
55 | }
56 |
57 | public String getDescription() {
58 | return mDescription;
59 | }
60 |
61 | public String getNextMomentId() {
62 | return mNextMomentId;
63 | }
64 |
65 | public Outcome getOutcome() {
66 | return mOutcome;
67 | }
68 |
69 | public ArrayList getFictionalProgress() {
70 | return mFictionalProgress;
71 | }
72 |
73 | public boolean requiresChargedWeapon() {
74 | return mRequiresChargedWeapon;
75 | }
76 |
77 | public String getDrawableResourceName() {
78 | return mDrawableResourceName;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/TimerMoment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame;
18 |
19 | import com.google.fpl.gim.examplegame.utils.Utils;
20 |
21 | import java.util.ArrayList;
22 |
23 | /**
24 | * Describes a Moment in which there is a gap in gameplay for a given amount of time.
25 | */
26 | public class TimerMoment extends Moment {
27 | private static final String TAG = TimerMoment.class.getSimpleName();
28 | private TimerMomentData mData;
29 |
30 | private long mStartTimeNanos;
31 |
32 | public TimerMoment(Mission mission, TimerMomentData data) {
33 | super(mission);
34 | this.mData = data;
35 | }
36 |
37 | @Override
38 | public void update(long nowNanos) {
39 | setIsDone(hasMomentTimeElapsed(nowNanos));
40 | }
41 |
42 | @Override
43 | public void start(long nowNanos) {
44 | super.start(nowNanos);
45 | Utils.logDebug(TAG, "TimerMoment \"" + mData.getMomentId() + "\" started.");
46 | setStartTimeNanos(nowNanos);
47 | }
48 |
49 | @Override
50 | public void end() {
51 | Utils.logDebug(TAG, "TimerMoment \"" + mData.getMomentId() + "\" ended.");
52 | }
53 |
54 | /**
55 | * Determines if the TimerMoment has elapsed.
56 | * @param nowNanos The current time.
57 | * @return True if the time since the TimerMoment started is greater than or equal to the length
58 | * of the timer moment.
59 | */
60 | public boolean hasMomentTimeElapsed(long nowNanos) {
61 | return (nowNanos - mStartTimeNanos) >= Utils.minutesToNanos(mData.getLengthMinutes());
62 | }
63 |
64 | public void setStartTimeNanos(long startTimeNanos) {
65 | this.mStartTimeNanos = startTimeNanos;
66 | }
67 |
68 | @Override
69 | public String getNextMomentId() {
70 | return mData.getNextMomentId();
71 | }
72 |
73 | @Override
74 | public void restart(long nowNanos) {
75 | // No additional tear down or resetting necessary.
76 | start(nowNanos);
77 | }
78 |
79 | public TimerMomentData getMomentData() {
80 | return this.mData;
81 | }
82 |
83 | @Override
84 | public ArrayList getFictionalProgress() {
85 | return mData.getFictionalProgress();
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/docs/src/programmers_guide/assets.md:
--------------------------------------------------------------------------------
1 | Modifying Assets {#games_in_motion_guide_assets}
2 | ================
3 |
4 | ### Overview
5 |
6 | The game utilizes a data-driven component for gameplay. Games in Motion uses a
7 | XML-based data component to describe missions.
8 |
9 | To add a Mission, add a new XML file in the `app/src/main/assets/missions`
10 | folder. There are existing XML files in both the `app/src/main/assets/missions`
11 | and the `app/src/main/assets/legacy_missions` folders, which can be used as a
12 | starting point.
13 |
14 | The game loads gameplay assets from the `app/src/main/assets` directory, and
15 | other assets (art, strings, layouts) from the `app/src/main/res` directory, as
16 | is conventional in [Android Studio][].
17 |
18 | ### Authoring
19 |
20 | As outlined at the [Mission][] system page, missions are comprised of moments.
21 |
22 | Each moment signifies a different kind of event, and there is a `type` and an
23 | `id` associated with each of them. Each kind of moment can define specific XML
24 | tags. Optionally, each moment can have multiple `fictional_progress` tags, which
25 | are information displayed to the user at the end of the mission.
26 |
27 | Each mission has a `name` and a `start_id`. `start_id` signifies the first
28 | moment of the mission. A mission progresses through its moments linearly, the
29 | order of which is determined by the `next_moment_id` tag. The first moment will
30 | always have an `id` identical to the mission's `start_id` property, whereas the
31 | last moment will not have a `next_moment_id` tag.
32 |
33 | For example, here is a simple mission with one moment:
34 |
35 |
38 |
41 | 0.5
42 | Played a mission!
43 | Played a timer moment
44 |
45 |
46 |
47 | Here is a mission with two moments:
48 |
49 |
52 |
55 |
56 | android.resource://com.google.fpl.gim.examplegame/raw/bar
57 | Bar!
58 |
59 |
62 | 0.5
63 |
64 |
65 |
66 | ### Testing
67 |
68 | There are simple unit tests written for the mission parser. The tests are in the
69 | `app/src/androidTest` directory.
70 |
71 | The tests can be run by changing the run configuration to `All Tests`.
72 |
73 |
76 |
77 | \s\s
78 |
79 | [Android Studio]: http://developer.android.com/tools/studio/index.html
80 | [Mission]: @ref games_in_motion_guide_mission
81 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu_mission_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
23 |
24 |
29 |
30 |
42 |
43 |
51 |
52 |
60 |
61 |
62 |
68 |
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/gui/StartMenuFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame.gui;
18 |
19 | import android.app.Fragment;
20 | import android.os.Bundle;
21 | import android.view.LayoutInflater;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.widget.Button;
25 |
26 | import com.google.fpl.gim.examplegame.MainActivity;
27 | import com.google.fpl.gim.examplegame.R;
28 | import com.google.fpl.gim.examplegame.utils.Utils;
29 |
30 | /**
31 | * A UI fragment that represents the game's start screen. No data is stored in this fragment.
32 | */
33 | public class StartMenuFragment extends Fragment {
34 | private static final String TAG = StartMenuFragment.class.getSimpleName();
35 |
36 | private Button mStartButton;
37 | private boolean isGoogleFitConnected;
38 |
39 | @Override
40 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
41 | Bundle savedInstanceState) {
42 | View rootView = inflater.inflate(R.layout.menu_start, container, false);
43 | mStartButton = (Button) rootView.findViewById(R.id.start_button);
44 | updateStartButton();
45 | return rootView;
46 | }
47 |
48 | @Override public void onResume() {
49 | super.onResume();
50 | ((MainActivity)getActivity()).setActionBarTitle(R.string.app_name);
51 | }
52 |
53 | public void onStartButtonPressed() {
54 | Utils.logDebug(TAG, "onStartButtonPressed");
55 |
56 | // Display MissionSelectionFragment
57 | MissionSelectionFragment missionSelectionFragment =
58 | ((MainActivity) getActivity()).getGameViews().getListOfMissionsFragment();
59 | getActivity().getFragmentManager().beginTransaction()
60 | .setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left)
61 | .remove(this)
62 | .add(R.id.container, missionSelectionFragment, GameViews.LIST_OF_MISSIONS_TAG)
63 | .addToBackStack(null)
64 | .commit();
65 | }
66 |
67 | public void onFitStatusUpdated(boolean connected) {
68 | isGoogleFitConnected = connected;
69 | updateStartButton();
70 | }
71 |
72 | private void updateStartButton() {
73 | if (isGoogleFitConnected) {
74 | mStartButton.setEnabled(true);
75 | mStartButton.setText(R.string.start_button_ready);
76 | } else {
77 | mStartButton.setEnabled(false);
78 | mStartButton.setText(R.string.start_button_not_connected);
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/step_display.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
24 |
25 |
26 |
35 |
36 |
45 |
46 |
55 |
56 |
63 |
64 |
71 |
72 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/src/index.md:
--------------------------------------------------------------------------------
1 | Games in Motion
2 | ===============
3 |
4 | Games in Motion {#games_in_motion_index}
5 | ===============
6 |
7 | [Games in Motion][] is a game that motivates people to run, walk and get fit. It
8 | is a sample game with a goal to demonstrate different key game technologies.
9 |
10 | It is developed entirely using [Android Studio][].
11 |
12 | ## Motivation
13 |
14 | [Games in Motion][] demonstrates several Android-specific technologies,
15 | including the [Google Fit][] API, the [Android Wear][] API, and a simple
16 | data-driven design mechanism. [Games in Motion][] is also compatible with
17 | [Material design][] UX system.
18 |
19 | This sample shows how all these pieces can be put together in a fun context,
20 | while also giving players a good motivation to exercise.
21 |
22 | In addition, [fplutil][] is used to build the documentation of this project.
23 |
24 | ## Functionality
25 |
26 | [Games in Motion][] is an open-source sample on [Android][] that demonstrates
27 |
28 | * A fun game mechanic involving [Android Wear][],
29 | * Game development in [Android Studio][],
30 | * How to run a game entirely on a background service thread,
31 | * [Google Fit][] API integration,
32 | * [Android Wear][] API integration,
33 | * [Google Play Games Services][] API integration,
34 | * [Android Text to Speech][] API integration,
35 | * [Android Audio Focus][] API integration,
36 | * and JUnit test integration.
37 |
38 |
39 | ## Supported Platforms
40 |
41 | [Games in Motion][] has been tested on the following platforms:
42 |
43 | * [Android][] phones and tablets
44 |
45 | We use [Android Studio][] and gradle to build. The game is written in Java with
46 | data authored in XML.
47 |
48 | ## Download
49 |
50 | [Games in Motion][] can be downloaded from:
51 | * [GitHub][] (source)
52 | * [GitHub Releases Page](http://github.com/googlesamples/android-play-games-in-motion/releases)
53 | (source)
54 |
55 | **Important**: Games in Motion uses submodules to reference other components it
56 | depends upon, so download the source from [GitHub][] using:
57 |
58 | ~~~{.sh}
59 | git clone --recursive https://github.com/googlesamples/android-play-games-in-motion.git
60 | ~~~
61 |
62 | ## Feedback and Reporting Bugs
63 |
64 | * File issues on the [Games in Motion Issue Tracker][].
65 | * Post your questions to [stackoverflow.com][] with a mention of
66 | **gamesinmotion**.
67 |
68 | \s\s
69 |
70 | [Android]: http://developer.android.com
71 | [Android Audio Focus]: http://developer.android.com/training/managing-audio/audio-focus.html
72 | [Android Studio]: http://developer.android.com/tools/studio/index.html
73 | [Android Text to Speech]: http://developer.android.com/reference/android/speech/tts/TextToSpeech.html
74 | [Android Wear]: https://developer.android.com/wear/index.html
75 | [fplutil]: http://google.github.io/fplutil/
76 | [Games in Motion]: http://github.com/googlesamples/android-play-games-in-motion
77 | [Games in Motion Issue Tracker]: http://github.com/googlesamples/android-play-games-in-motion/issues
78 | [Google Fit]: https://developers.google.com/fit/
79 | [Google Play Games Services]: https://developer.android.com/google/play-services/games.html
80 | [GitHub]: http://github.com/googlesamples/android-play-games-in-motion
81 | [Material design]: http://www.google.com/design/spec/material-design/introduction.html
82 | [stackoverflow.com]: http://www.stackoverflow.com
83 |
84 |
--------------------------------------------------------------------------------
/docs/src/programmers_guide/audio.md:
--------------------------------------------------------------------------------
1 | Audio {#games_in_motion_guide_audio}
2 | =====
3 |
4 | Audio is essential to [Games in Motion][]. It signals game progression to the
5 | player, provides feedback for player actions, and allows the game to be played
6 | without many physical interactions with a device.
7 |
8 | We also want to let players choose their own background music.
9 | [Games in Motion][] is designed to encourage people to move more, and often
10 | background music helps set a pace for the activity.
11 |
12 | In [Games in Motion][], audio is mostly handled by the [MainService][].
13 |
14 | # Media Player {#games_in_motion_guide_mediaplayer}
15 |
16 | We use [Android Media Player][] to control the playback of our raw audio files.
17 | It is owned by [MainService][].
18 |
19 | In this sample, we do not allow any sound effects to be played on top of each
20 | other because all of them provide important user feedback. Therefore, we have
21 | implemented an audio queue to make sure that only one sound effect is played at
22 | a time.
23 |
24 | Currently, only `SfxMoment` and `Mission` are queueing audio to be played by the
25 | [Android Media Player][]. Any audio will only be played if we can properly
26 | obtain [Audio Focus][].
27 |
28 | # Text to Speech {#games_in_motion_guide_tts}
29 |
30 | For certain game story elements, it is better to be able to author and change
31 | them without having to re-record after every change. We use
32 | [Android Text to Speech][] to generate audio from these texts.
33 |
34 | The main Text to Speech module is owned by the [MainService][].
35 | `SpokenTextMoment` obtains a reference to the module in order to convert its
36 | text to audio and play it. Again, the audio will not be played unless we have
37 | properly obtained [Audio focus][].
38 |
39 | Unlike raw audio files, we cannot queue Text to Speech requests as easily.
40 | Instead, we restart any `SpokenTextMoment` until we are successful in calling
41 | the Text to Speech module.
42 |
43 | # Audio Focus {#games_in_motion_guide_audiofocus}
44 |
45 | A piece of audio, regardless of which module it is from, can only be played if
46 | we satisfy these following conditions:
47 |
48 | * No audio from the [Media Player][] or the [Text to Speech][] modules is
49 | currently playing.
50 | * We can obtain audio focus properly from the Android system.
51 |
52 | The first condition is important so we are not overlapping any important audio
53 | cues. The second is important because we want to make sure that the Android
54 | system isn't performing any important audio tasks.
55 |
56 | Because players can choose any background music from their favourite app, we
57 | need to be able to lower the volume of any audio from other apps before we can
58 | play our own. We return the volume level of those audio to previous levels once
59 | we are done. We manage this by properly implementing Android Audio Focus. Please
60 | review the [Managing Audio Focus][] page if you are not familiar with it.
61 |
62 | \s\s
63 |
64 | [Android Media Player]: http://developer.android.com/reference/android/media/MediaPlayer.html
65 | [Android Text to Speech]: http://developer.android.com/reference/android/speech/tts/TextToSpeech.html
66 | [Audio Focus]: @ref games_in_motion_guide_audiofocus
67 | [Games in Motion]: @ref games_in_motion_index
68 | [MainService]: @ref games_in_motion_guide_mainservice
69 | [Managing Audio Focus]: http://developer.android.com/training/managing-audio/audio-focus.html
70 | [Media Player]: @ref games_in_motion_guide_mediaplayer
71 | [Text to Speech]: @ref games_in_motion_guide_tts
72 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/Moment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame;
18 |
19 | import com.google.fpl.gim.examplegame.utils.Utils;
20 |
21 | import java.util.ArrayList;
22 |
23 | /**
24 | * A Moment is a discrete event within a Mission. Moments know when they are finished ('isDone()'),
25 | * and they are never reused.
26 | */
27 | public abstract class Moment {
28 | // The mission to which this moment belongs.
29 | private Mission mMission;
30 |
31 | public boolean mIsDone;
32 | private boolean mShouldRestart;
33 | private long mTimeWhenRestartRequestedNanos;
34 | private long mRestartDelayLengthNanos;
35 |
36 | /**
37 | * @param mission The Mission to which this moment belongs. Cannot be changed after
38 | * instantiation - each Moment is irrevocably associated with a Mission.
39 | */
40 | protected Moment(Mission mission) {
41 | this.mMission = mission;
42 | }
43 |
44 | public Mission getMission() {
45 | return mMission;
46 | }
47 |
48 | /**
49 | * Called on the current moment to determine if it is appropriate to move on.
50 | * @return true if this moment no longer should be active as the current moment.
51 | */
52 | public boolean isDone() {
53 | return mIsDone;
54 | }
55 |
56 | public void setIsDone(boolean isDone) {
57 | this.mIsDone = isDone;
58 | }
59 |
60 | /**
61 | * Update the moment information for the current time. Default behavior checks if the Moment
62 | * should restart, and does.
63 | * @param nowNanos The current time, represented in nanoseconds.
64 | */
65 | public void update(long nowNanos) {
66 | if (mShouldRestart
67 | && nowNanos > mTimeWhenRestartRequestedNanos + mRestartDelayLengthNanos) {
68 | restart(nowNanos);
69 | }
70 | }
71 |
72 | /**
73 | * Read the id of the next Moment associated with this Moment.
74 | * @return String identifying the next Moment associated with this Moment.
75 | */
76 |
77 | public abstract String getNextMomentId();
78 |
79 | /**
80 | * Make this moment active. Runs when the moment begins.
81 | * @param nowNanos The current time, represented in nanoseconds.
82 | */
83 | public void start(long nowNanos) {
84 | this.mIsDone = false;
85 | this.mShouldRestart = false;
86 | }
87 |
88 | /**
89 | * Ends the current moment.
90 | */
91 | public abstract void end();
92 |
93 | /**
94 | * Restarts a moment.
95 | */
96 | public abstract void restart(long nowNanos);
97 |
98 | public void restartWithDelay(long nowNanos, float secondsDelayRestart) {
99 | mShouldRestart = true;
100 | mTimeWhenRestartRequestedNanos = nowNanos;
101 | mRestartDelayLengthNanos = Utils.secondsToNanos(secondsDelayRestart);
102 | }
103 |
104 | public abstract ArrayList getFictionalProgress();
105 | }
106 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/utils/Utils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame.utils;
18 |
19 | import android.util.Log;
20 |
21 | import com.google.fpl.gim.examplegame.BuildConfig;
22 |
23 | import java.io.File;
24 | import java.util.ArrayList;
25 |
26 | /**
27 | * A few utility functions and classes used by the prototype.
28 | */
29 | public class Utils {
30 |
31 | public static final float SECONDS_TO_NANOS_SCALE = (float) Math.pow(10, 9);
32 | public static final float NANOS_TO_SECONDS_SCALE = (float) Math.pow(10, -9);
33 | public static final int MINUTES_TO_SECONDS_SCALE = 60;
34 | public static final float SECONDS_TO_MINUTES_SCALE = 1.0f / 60;
35 | public static final float MILES_TO_FEET_SCALE = 5280f;
36 | public static final float SECONDS_PER_METER_TO_MINUTES_PER_MILE_SCALE = 26.8224f;
37 |
38 | // user control for displaying log messages
39 | private static final boolean DEBUG_LOG = true;
40 |
41 | public static float nanosToSeconds(long nanos) {
42 | return nanos / SECONDS_TO_NANOS_SCALE;
43 | }
44 |
45 | public static long secondsToNanos(float seconds) {
46 | return (long) (seconds * SECONDS_TO_NANOS_SCALE);
47 | }
48 |
49 | public static long minutesToNanos(float min) {
50 | return (long) (min * MINUTES_TO_SECONDS_SCALE * SECONDS_TO_NANOS_SCALE);
51 | }
52 |
53 | public static float nanosToMinutes(long nanos) {
54 | return (nanos / SECONDS_TO_NANOS_SCALE / MINUTES_TO_SECONDS_SCALE);
55 | }
56 |
57 | public static float feetToMiles(float feet) {
58 | return feet / MILES_TO_FEET_SCALE;
59 | }
60 |
61 | public static float secondsToMinutes(float seconds) {
62 | return seconds / MINUTES_TO_SECONDS_SCALE;
63 | }
64 |
65 | public static float metersPerSecondToMinutesPerMile(float metersPerSecond) {
66 | if (metersPerSecond == 0.0f) {
67 | return 0.0f;
68 | }
69 | float secondsPerMeter = 1 / metersPerSecond; // seconds per meter
70 | return secondsPerMeter * SECONDS_PER_METER_TO_MINUTES_PER_MILE_SCALE;
71 | }
72 |
73 | /**
74 | * Prints debugging messages to the console.
75 | *
76 | * Disabled for non-debug builds.
77 | *
78 | * @param message - The message to print to the console.
79 | */
80 | public static void logDebug(String tag, String message) {
81 | if (BuildConfig.DEBUG || DEBUG_LOG) {
82 | Log.d(tag, message);
83 | }
84 | }
85 |
86 | /**
87 | * Builds a file path.
88 | * @param rootDirectory Highest directory or file name, if not nested.
89 | * @param subDirectories Sub-Directories, or the file name.
90 | * @return A string of the file path.
91 | */
92 | public static String makeFilePath(String rootDirectory, ArrayList subDirectories) {
93 | File file = new File(rootDirectory);
94 | for (String subDirectory : subDirectories) {
95 | file = new File(file, subDirectory);
96 | }
97 | return file.toString();
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/ChoiceMomentData.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame;
18 |
19 | import java.util.ArrayList;
20 | import java.util.HashMap;
21 |
22 | /**
23 | * Encapsulates the data that is needed to define a unique ChoiceMoment.
24 | */
25 | public class ChoiceMomentData extends MomentData {
26 |
27 | // The text describing the choice the player must make.
28 | private final String mDescription;
29 | // All of the choices that the user can have.
30 | private final HashMap mChoices;
31 | // Identifies the default Choice to execute in the case of a timeout.
32 | private final String mDefaultChoiceId;
33 | // Time in minutes until the default Choice is executed.
34 | private final float mTimeoutLengthMinutes;
35 |
36 | /**
37 | * Constructor to explicitly set all fields for a ChoiceMomentData.
38 | * @param momentId Identifier for the ChoiceMoment.
39 | * @param fictionalProgress Fictional progress for this moment.
40 | * @param description Text description for the decision to be made.
41 | * @param choices HashMap of Choices available to choose.
42 | * @param defaultChoiceId Default Choice in case of timeout.
43 | * @param timeoutLengthMinutes Length of time until timeout.
44 | */
45 | public ChoiceMomentData(String momentId, ArrayList fictionalProgress,
46 | String description, HashMap choices, String defaultChoiceId,
47 | float timeoutLengthMinutes) {
48 | super(momentId, null, fictionalProgress);
49 | mDescription = description;
50 | mChoices = choices;
51 | mDefaultChoiceId = defaultChoiceId;
52 | mTimeoutLengthMinutes = timeoutLengthMinutes;
53 | }
54 |
55 | /**
56 | * Constructor for information from XML.
57 | * @param momentId Identifier for the ChoiceMoment.
58 | * @param fictionalProgress Fictional progress for this moment.
59 | * @param description Text description for the decision to be made.
60 | * @param defaultChoiceId Default Choice in case of timeout.
61 | * @param timeoutLengthMinutes Length of time until timeout.
62 | */
63 | public ChoiceMomentData(String momentId, ArrayList fictionalProgress,
64 | String description, String defaultChoiceId, float timeoutLengthMinutes) {
65 | this(momentId, fictionalProgress, description, new HashMap(),
66 | defaultChoiceId, timeoutLengthMinutes);
67 | }
68 |
69 | public String getText() {
70 | return mDescription;
71 | }
72 |
73 | public Choice[] getChoices() {
74 | return mChoices.values().toArray(new Choice[mChoices.size()]);
75 | }
76 |
77 | public Choice getChoiceById(String choiceId) {
78 | return mChoices.get(choiceId);
79 | }
80 |
81 | public int getNumChoices() {
82 | return mChoices.size();
83 | }
84 |
85 | public void addChoice(Choice choice) {
86 | mChoices.put(choice.getChoiceId(), choice);
87 | }
88 |
89 | public String getDefaultChoiceId() {
90 | return mDefaultChoiceId;
91 | }
92 |
93 | public float getTimeoutLengthMinutes() {
94 | return mTimeoutLengthMinutes;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/SpokenTextMoment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame;
18 |
19 | import android.speech.tts.TextToSpeech;
20 | import android.speech.tts.UtteranceProgressListener;
21 |
22 | import com.google.fpl.gim.examplegame.utils.Utils;
23 |
24 | import java.util.ArrayList;
25 | import java.util.HashMap;
26 |
27 | /**
28 | * Describes a Moment in which the user listens to a piece of fiction as part of the gameplay.
29 | * The fiction will be read aloud using Android's text-to-speech capabilities.
30 | */
31 | public class SpokenTextMoment extends Moment {
32 |
33 | private static final String TAG = SpokenTextMoment.class.getSimpleName();
34 |
35 | private SpokenTextMomentData mData;
36 |
37 | private static final float RETRY_WAIT_TIME_SECONDS = 2.5f;
38 | // Buffer time before speaking with TextToSpeech.
39 | private static final long SILENCE_LENGTH_MILLIS = 500;
40 |
41 | // Determines behavior to execute during TextToSpeech speaking.
42 | private UtteranceProgressListener mUtteranceProgressListener = new UtteranceProgressListener() {
43 | @Override
44 | public void onStart(String utteranceId) {
45 | }
46 |
47 | @Override
48 | /**
49 | * Determines behavior when TextToSpeech has completed speaking, or stopped.
50 | */
51 | public void onDone(String utteranceId) {
52 | setIsDone(true);
53 | getMission().getService().endPlayback();
54 | }
55 |
56 | @Override
57 | public void onError(String utteranceId) {
58 | }
59 | };
60 |
61 | public SpokenTextMoment(Mission mission, SpokenTextMomentData data) {
62 | super(mission);
63 | this.mData = data;
64 | }
65 |
66 | @Override
67 | public void start(long nowNanos) {
68 | super.start(nowNanos);
69 | Utils.logDebug(TAG, "SpokenTextMoment \"" + mData.getMomentId() + "\" started.");
70 |
71 | if (getMission().getService().obtainAudioFocus()) {
72 | speak();
73 | } else {
74 | // Try again at a future time.
75 | restartWithDelay(nowNanos, RETRY_WAIT_TIME_SECONDS);
76 | }
77 | }
78 |
79 | @Override
80 | public void end() {
81 | Utils.logDebug(TAG, "SpokenTextMoment \"" + mData.getMomentId() + "\" ended.");
82 | getMission().getService().getTextToSpeech().setOnUtteranceProgressListener(null);
83 | getMission().getService().getTextToSpeech().stop();
84 | }
85 |
86 | public String getNextMomentId() {
87 | return mData.getNextMomentId();
88 | }
89 |
90 | /**
91 | * Use TextToSpeech to say the words associated with this Moment.
92 | */
93 | private void speak() {
94 | TextToSpeech textToSpeech = getMission().getService().getTextToSpeech();
95 | textToSpeech.setOnUtteranceProgressListener(mUtteranceProgressListener);
96 | textToSpeech.playSilence(SILENCE_LENGTH_MILLIS, TextToSpeech.QUEUE_ADD, null);
97 | HashMap map = new HashMap<>();
98 | map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, mData.getMomentId());
99 | textToSpeech.speak(mData.getTextToSpeak(), TextToSpeech.QUEUE_ADD, map);
100 | }
101 |
102 | @Override
103 | public void restart(long nowNanos) {
104 | // Stop anything in progress.
105 | end();
106 | start(nowNanos);
107 | }
108 |
109 | public SpokenTextMomentData getMomentData() {
110 | return this.mData;
111 | }
112 |
113 | @Override
114 | public ArrayList getFictionalProgress() {
115 | return mData.getFictionalProgress();
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/gui/FitnessDataDisplayFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame.gui;
18 |
19 | import android.app.Fragment;
20 | import android.os.Bundle;
21 | import android.view.LayoutInflater;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.widget.ProgressBar;
25 | import android.widget.TextView;
26 |
27 | import com.google.fpl.gim.examplegame.MainActivity;
28 | import com.google.fpl.gim.examplegame.Mission;
29 | import com.google.fpl.gim.examplegame.R;
30 |
31 | /**
32 | * Display fitness data during a run.
33 | */
34 | public class FitnessDataDisplayFragment extends Fragment {
35 |
36 | private TextView mNumSteps;
37 | private TextView mMinutesPerMile;
38 | private TextView mTimeExercised;
39 | private TextView mWeaponChargedPercentage;
40 | private ProgressBar mProgressBar;
41 | private String mMissionName = "Mission";
42 |
43 | @Override
44 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
45 | Bundle savedInstanceState) {
46 | View rootView = inflater.inflate(R.layout.step_display, container, false);
47 | mNumSteps = (TextView) rootView.findViewById(R.id.num_steps_taken);
48 | mMinutesPerMile = (TextView) rootView.findViewById(R.id.minutes_per_mile);
49 | mTimeExercised = (TextView) rootView.findViewById(R.id.time_exercised);
50 | mWeaponChargedPercentage = (TextView) rootView.findViewById(R.id.percent_weapon_charged);
51 |
52 | mProgressBar = (ProgressBar) rootView.findViewById(R.id.circular_progress_bar);
53 | mProgressBar.setMax(100);
54 | mProgressBar.setProgress(0);
55 |
56 | return rootView;
57 | }
58 |
59 | @Override public void onResume() {
60 | super.onResume();
61 | ((MainActivity)getActivity()).setActionBarTitle(mMissionName);
62 | }
63 |
64 | public void setNumSteps(int numSteps) {
65 | mNumSteps.setText(String.format("%d steps", numSteps));
66 | }
67 |
68 | public void setMinutesPerMile(float minutesPerMile, float challengePace) {
69 | if (minutesPerMile >= Mission.getMaximumMinutesPerMile()) {
70 | mMinutesPerMile.setText(getActivity().getString(R.string.not_moving_speed));
71 |
72 | } else {
73 | String minutesPerMileText = String.format("%.2f\nmins/mile", minutesPerMile);
74 | mMinutesPerMile.setText(minutesPerMileText);
75 | if (minutesPerMile <= challengePace) {
76 | mMinutesPerMile.setTextColor(getResources().getColor(R.color.green));
77 | } else {
78 | mMinutesPerMile.setTextColor(getResources().getColor(R.color.red));
79 | }
80 | }
81 | }
82 |
83 | public void setTimeExercised(int numMinutes, int numSeconds) {
84 | mTimeExercised.setText(String.format("%d:%02d", numMinutes, numSeconds));
85 | }
86 |
87 | public void setWeaponChargedPercentage(int weaponChargedPercentage) {
88 | mWeaponChargedPercentage.setText(String.format("%d%%", weaponChargedPercentage));
89 | mProgressBar.setProgress(weaponChargedPercentage);
90 | }
91 |
92 | public void setFitnessStats(Mission mission) {
93 | setNumSteps(mission.getNumSteps());
94 | setMinutesPerMile(mission.getMinutesPerMile(), mission.getChallengePace());
95 | setTimeExercised(mission.getNumMinutesExercised(), mission.getNumSecondsExercised());
96 | setWeaponChargedPercentage(mission.getWeaponChargedPercentage());
97 | }
98 |
99 | public void setMissionName(String missionName) {
100 | mMissionName = missionName;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/MissionData.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame;
18 |
19 | import java.util.HashMap;
20 |
21 | /**
22 | * Encapsulates all of the game data that is necessary to represent a mission.
23 | */
24 | public class MissionData {
25 |
26 | private String mMissionName; // User-facing name of mission.
27 | // ID must be unique to each mission
28 | private String mMissionId;
29 | private float mLengthOfMissionMinutes;
30 | private float mLengthOfIntervalMinutes;
31 | private float mChallengePaceMinutesPerMile;
32 | private HashMap mAllMoments;
33 | private String mFirstMomentId;
34 | private String mCurrentMomentId;
35 |
36 | /**
37 | * Most general constructor. Used when wanting to construct most pieces of MissionData
38 | * explicitly.
39 | * @param missionId String identifying the Mission.
40 | * @param lengthOfGameMinutes Total length of game.
41 | * @param lengthOfIntervalMinutes Length of a running interval.
42 | * @param allMoments HashMap containing all Moments that define this Mission.
43 | * @param firstMomentId The first Moment to be executed during the Mission.
44 | * @param currentMomentId The current Moment of the Mission.
45 | */
46 | public MissionData(String missionName, String missionId, float lengthOfGameMinutes,
47 | float lengthOfIntervalMinutes, float challengePaceMinutesPerMile,
48 | HashMap allMoments, String firstMomentId, String currentMomentId) {
49 | this.mMissionName = missionName;
50 | this.mMissionId = missionId;
51 | this.mLengthOfMissionMinutes = lengthOfGameMinutes;
52 | this.mLengthOfIntervalMinutes = lengthOfIntervalMinutes;
53 | this.mChallengePaceMinutesPerMile = challengePaceMinutesPerMile;
54 | this.mAllMoments = allMoments;
55 | this.mFirstMomentId = firstMomentId;
56 | this.mCurrentMomentId = currentMomentId;
57 | }
58 |
59 | /**
60 | * Constructor used when populating MissionData with information from menu screens.
61 | * @param missionId String identifying the Mission.
62 | * @param lengthOfGameMinutes Total length of game.
63 | * @param lengthOfIntervalMinutes Length of a running interval.
64 | */
65 | public MissionData(String missionName, String missionId, float lengthOfGameMinutes,
66 | float lengthOfIntervalMinutes, float challengePaceMinutesPerMile) {
67 | this(missionName, missionId, lengthOfGameMinutes, lengthOfIntervalMinutes,
68 | challengePaceMinutesPerMile, new HashMap(), null, null);
69 | }
70 |
71 | public String getMissionName() {
72 | return this.mMissionName;
73 | }
74 |
75 | public String getMissionId() {
76 | return this.mMissionId;
77 | }
78 |
79 | public float getLengthOfMissionMinutes() {
80 | return this.mLengthOfMissionMinutes;
81 | }
82 |
83 | public float getLengthOfIntervalMinutes() {
84 | return this.mLengthOfIntervalMinutes;
85 | }
86 |
87 | public float getChallengePaceMinutesPerMile() {
88 | return this.mChallengePaceMinutesPerMile;
89 | }
90 |
91 | public Moment getMomentFromId(String momentId) {
92 | return mAllMoments.get(momentId);
93 | }
94 |
95 | public String getCurrentMomentId() {
96 | return this.mCurrentMomentId;
97 | }
98 |
99 | public String getFirstMomentId() {
100 | return this.mFirstMomentId;
101 | }
102 |
103 | public void setCurrentMomentId(String currentMomentId) {
104 | this.mCurrentMomentId = currentMomentId;
105 | }
106 |
107 | public void addMoment(String momentId, Moment moment) {
108 | this.mAllMoments.put(momentId, moment);
109 | }
110 |
111 | public void setFirstMomentId(String firstMomentId) {
112 | this.mFirstMomentId = firstMomentId;
113 | }
114 |
115 | public Moment getCurrentMoment() {
116 | return mAllMoments.get(mCurrentMomentId);
117 | }
118 |
119 | public int getNumMoments() {
120 | return mAllMoments.size();
121 | }
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
22 |
23 |
35 |
36 |
48 |
49 |
63 |
64 |
72 |
73 |
81 |
82 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/docs/src/programmers_guide/core.md:
--------------------------------------------------------------------------------
1 | Games in Motion Core {#games_in_motion_guide_core}
2 | ====================
3 |
4 | There are two main components of the game, `MainActivity` and `MainService`.
5 | Find them in `app/src/main/java/com/google/fpl/gim/examplegame`.
6 |
7 | # MainActivity
8 |
9 | All Android applications have a main [Activity][] class which the application
10 | will show on launch. Please review the [Activity Lifecycle][] if you are not
11 | familiar with it.
12 |
13 | For Games in Motion, `MainActivity` manages all the UI elements that are shown
14 | on screen. It holds a pointer to `MainService` so it can query game state
15 | changes in order to update the UI dynamically.
16 |
17 | # MainService {#games_in_motion_guide_mainservice}
18 |
19 | `MainService` is included in `AndroidManifest.xml`. It will automatically be
20 | started when the app is launched. Please review the [Service Basics][] if you
21 | are not familiar with Services. `MainService` is run in the background to allow
22 | the game to continue running when the player turns off the phone screen, or when
23 | `MainActivity` is paused for any reason.
24 |
25 | `MainService::Run` contains the main game loop. Every call to that function is
26 | one frame of the game update.
27 |
28 | `MainService` holds references to all the key systems that are needed during
29 | gameplay. This includes the [Mission system][], [Android Audio Manager][],
30 | [Android Text to Speech][], and the [Google Api Wrapper][]. `MainService` is
31 | also responsible for starting up these systems and channeling queries to them.
32 |
33 | # UI
34 |
35 | Most of Games in Motion's UI is handled using [Fragments][].
36 | `MainActivity` holds all the fragments and handles all incoming requests to
37 | advance the UI, or delegate them to the fragments. The UI fragments reside in
38 | `app/src/main/java/com/google/fpl/gim/examplegame/gui`.
39 |
40 | Games in Motion also uses [Notifications][] extensively. They allow the user to
41 | interact with the game, either through the notifications menu, or an Android
42 | Wear device. Notifications are typically issued through `MainService` as they
43 | can occur while `MainActivity` is not in display.
44 |
45 | # State Machine
46 |
47 | There is a simple state machine that resides in `MainService`. The `mState`
48 | variable dictates the mission flow. There are two functions that help control
49 | the states: `canEnterState` checks if we can enter a state from the current
50 | state, and `setAndInitNextState` sets the state machine to the next state while
51 | doing all the initializations needed for the transition. The states and their
52 | transitions are described below.
53 |
54 | * `UNINITIALIZED` -- No mission is loaded. The player is still going through
55 | the UI flow and hasn't started a game mission yet.
56 |
57 | You can enter this state from any other states. It happens when the player
58 | hit the back button from `END_SCREEN`, when the connection to Google Apis
59 | has been disconnected, or when some other sort of error has occurred.
60 |
61 | * `MISSION_LOADED` -- The game mission has been loaded and we are waiting for
62 | a few key systems to finish starting up before we start the mission.
63 |
64 | When the player starts a mission, it will trigger a load from the `assets`
65 | folder. It will also trigger subscriptions to the appropriate [Google Fit][]
66 | data streams.
67 |
68 | When the game is ready, the game transitions to `MISSION_RUNNING`.
69 |
70 | * `MISSION_RUNNING` -- The misson is started when we transition to this state.
71 | While in this state, the mission is also running, even if the device screen
72 | is not on.
73 |
74 | This state can only be transitioned from `MISSION_LOADED`.
75 |
76 | * `END_SCREEN` -- When a mission is over (all its moments have been played),
77 | or when a player aborts a mission by hitting the back button, the game will
78 | transition to this state.
79 |
80 | The game will display the statistics from the mission while in this state.
81 |
82 | This state can only be transitioned from `MISSION_RUNNING`.
83 |
84 | \s\s
85 |
86 | [Activity]: http://developer.android.com/reference/android/app/Activity.html
87 | [Activity Lifecycle]: http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle
88 | [Android Audio Manager]: http://developer.android.com/reference/android/media/AudioManager.html
89 | [Android Text to Speech]: http://developer.android.com/reference/android/speech/tts/TextToSpeech.html
90 | [Fragments]: http://developer.android.com/reference/android/app/Activity.html#Fragments
91 | [Google Api Wrapper]: @ref games_in_motion_guide_google_api
92 | [Google Fit]: https://developers.google.com/fit/
93 | [Mission system]: @ref games_in_motion_guide_mission
94 | [Notifications]: http://developer.android.com/guide/topics/ui/notifiers/notifications.html
95 | [Service Basics]: http://developer.android.com/guide/components/services.html
96 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | Games in Motion
20 | Settings
21 | Example action clicked
22 | Enemy
23 | An enemy appeared! What will you do?
24 | Action
25 | Enemy!
26 | An enemy appeared, what will you do?
27 | Disconnected.
28 |
29 | Google client has been disconnected. Please make sure you have WiFi or cellular data
30 | turned on.
31 |
32 | Games in Motion
33 |
34 | You are in the midst of a zombie apocalypse. Your task is to evade as many zombies as
35 | possible throughout the course of your run.\n\nPlease put on your headphones so you can
36 | receive important instructions. A paired Android wear device is also recommended.
37 |
38 |
39 | Created by Kate Aplin and Veronica Wharton, Summer 2014 EP Interns
40 |
41 |
42 | Start!
43 |
44 |
45 | Connecting to Google Fit…
46 |
47 | Select Mission
48 |
49 | Select your mission
50 |
51 |
52 | How long will your mission be?
53 |
54 |
55 | How long will your intervals be?
56 |
57 |
58 | Slide to choose your challenge speed.
59 |
60 |
61 | Choose carefully! Your weapon will only charge when you are running at challenge pace.
62 | \n\nYour mission will last 0 to 60 minutes, with an interval length of 1.5 minutes.
63 |
64 | Choose Speed
65 | Confirm Interval Speed
66 |
67 | %.2f minutes/mile
68 |
69 | Select Music
70 |
71 | Select music to play during your run.
72 |
73 |
74 | Put on your headphones to receive play instructions when the mission starts.\n\nIf you would
75 | like to listen to music as you run, switch to the music app of your choice and play any
76 | track you would like. Return here and select "I am ready!" when you are ready to begin the
77 | mission.
78 |
79 | I am ready!
80 | Registering sensors...
81 | You are currently in a game. Sit tight.
82 | You finished a run!
83 | Google Fit disconnected
84 | Your progress:
85 | Your fitness:
86 | Restart
87 | ---\nmin/mile
88 | 0%
89 | 0 steps
90 | 0:00
91 | Challenge pace (min/mile):
92 | Number of steps taken: %d;
93 | Number of intervals completed: %d
94 |
95 | % charged.
96 |
97 | Keep running at interval speed to charge your weapon!
98 |
99 |
100 | minutes per mile.
101 | You are not moving.
102 |
103 |
104 |
--------------------------------------------------------------------------------
/app/src/main/assets/legacy_missions/mission.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
21 |
24 |
27 |
30 |
31 |
34 |
37 | 0.25
38 | Created a mission.
39 | Made a timer moment
40 |
41 |
42 |
45 |
50 | 0.5
51 | Example ChoiceMoment Description
52 |
53 |
55 |
59 |
61 |
65 |
68 | Example Choice Description 1
69 |
70 |
74 |
77 | Fired a weapon.
78 |
79 |
80 |
82 | Example Choice Description 2
83 |
84 |
87 |
88 |
89 |
91 | Example Choice Description 2
92 |
93 |
96 |
97 |
98 |
99 |
100 |
103 |
106 |
107 | path/to/something
108 |
109 |
110 |
113 |
116 |
117 | Hello!
118 |
119 |
120 |
--------------------------------------------------------------------------------
/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 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/app/src/main/java/com/google/fpl/gim/examplegame/google/FitResultCallback.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.fpl.gim.examplegame.google;
18 |
19 | import android.util.Log;
20 |
21 | import com.google.android.gms.common.api.Result;
22 | import com.google.android.gms.common.api.Status;
23 | import com.google.android.gms.fitness.FitnessStatusCodes;
24 | import com.google.android.gms.fitness.data.DataType;
25 | import com.google.fpl.gim.examplegame.utils.Utils;
26 |
27 | /**
28 | * Implements ResultCallback while allowing to record more state so we can change game flow
29 | * accordingly.
30 | */
31 | public class FitResultCallback
32 | implements com.google.android.gms.common.api.ResultCallback {
33 | private static final String TAG = "GoogleFitResultCallback";
34 |
35 | // Enum type for keeping track of which API generated this callback.
36 | public enum RegisterType {
37 | SENSORS,
38 | RECORDING,
39 | SESSION,
40 | }
41 |
42 | GoogleApiClientWrapper mGoogleApiClient;
43 | RegisterType mRegisterType;
44 | DataType mDataType;
45 | boolean mSubscribe; // True if subscribe, false if unsubscribe.
46 |
47 | /**
48 | * Default constructor.
49 | * @param googleApiClient The GoogleApiClientWrapper to pass data back to.
50 | * @param registerType The type of call that resulted in this callback.
51 | * @param dataType The data type we are trying to process.
52 | * @param subscribe If true, this is a subscribe or a register call.
53 | */
54 | public FitResultCallback(
55 | GoogleApiClientWrapper googleApiClient, RegisterType registerType, DataType dataType,
56 | boolean subscribe) {
57 | mGoogleApiClient = googleApiClient;
58 | mRegisterType = registerType;
59 | mDataType = dataType;
60 | mSubscribe = subscribe;
61 | }
62 |
63 | @Override
64 | public void onResult(R result) {
65 | switch (mRegisterType) {
66 | case SENSORS:
67 | onSensorResult(result.getStatus());
68 | break;
69 | case RECORDING:
70 | onRecordingResult(result.getStatus());
71 | break;
72 | case SESSION:
73 | onSessionResult(result.getStatus());
74 | break;
75 | default:
76 | Log.e(TAG, "Unknown enum type.");
77 | }
78 | }
79 |
80 | private void onSensorResult(Status status) {
81 | if (status.isSuccess()) {
82 | // There is a lapse between this callback to actually getting data from the listener,
83 | // depending on the data type. It is a known issue, by design. You can account for that
84 | // delay with display text or other mechanisms.
85 | mGoogleApiClient.sensorRegistered(mDataType);
86 | Utils.logDebug(TAG, "Successfully registered sensor for " + mDataType.toString());
87 | } else {
88 | Utils.logDebug(TAG, "There was a problem registering ." + mDataType + "\n" +
89 | status.getStatusMessage());
90 | }
91 |
92 | }
93 |
94 | private void onRecordingResult(Status status) {
95 | if (mSubscribe) {
96 | onRecordingSubscription(status);
97 | } else {
98 | onRecordingUnsubscription(status);
99 | }
100 | }
101 |
102 | private void onRecordingSubscription(Status status) {
103 | if (status.isSuccess()) {
104 | if (status.getStatusCode()
105 | == FitnessStatusCodes.SUCCESS_ALREADY_SUBSCRIBED) {
106 | Utils.logDebug(TAG, "Existing subscription for activity detected.");
107 | } else {
108 | Utils.logDebug(TAG, "Started recording data for " + mDataType);
109 | }
110 | } else {
111 | // For a better user experience, you can add visual error text indicating the session
112 | // will not be recorded to the cloud.
113 | Utils.logDebug(TAG, "Unable to start recording data for " + mDataType);
114 | }
115 | }
116 |
117 | private void onRecordingUnsubscription(Status status) {
118 | if (status.isSuccess()) {
119 | Utils.logDebug(TAG, "Stopped recording data for " + mDataType);
120 | } else {
121 | Utils.logDebug(TAG, "Unable to stop recording data for " + mDataType);
122 | }
123 | }
124 |
125 | private void onSessionResult(Status status) {
126 | if (mSubscribe) {
127 | onSessionSubscription(status);
128 | } else {
129 | onSessionUnsubscription(status);
130 | }
131 | }
132 |
133 | private void onSessionSubscription(Status status) {
134 | if (status.isSuccess()) {
135 | Utils.logDebug(TAG, "Started data session.");
136 | } else {
137 | // For a better user experience, you can add visual error text indicating the session
138 | // will not be identified in the cloud.
139 | Utils.logDebug(TAG, "Unable to start data session.");
140 | }
141 | }
142 |
143 | private void onSessionUnsubscription(Status status) {
144 | if (status.isSuccess()) {
145 | Utils.logDebug(TAG, "Ended data session.");
146 | } else {
147 | Utils.logDebug(TAG, "Unable to end data session.");
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------