├── settings.gradle
├── app
├── src
│ └── main
│ │ ├── res
│ │ ├── raw
│ │ │ ├── beep.mp3
│ │ │ └── beepquiet.mp3
│ │ ├── drawable-hdpi
│ │ │ ├── ok.png
│ │ │ ├── clean.png
│ │ │ ├── nbarlogo.png
│ │ │ └── ic_launcher.png
│ │ ├── drawable-ldpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable-mdpi
│ │ │ └── ic_launcher.png
│ │ ├── menu
│ │ │ └── menu.xml
│ │ ├── layout
│ │ │ ├── main.xml
│ │ │ ├── row.xml
│ │ │ └── timer.xml
│ │ ├── values
│ │ │ ├── arrays.xml
│ │ │ └── strings.xml
│ │ └── xml
│ │ │ └── preferences.xml
│ │ ├── java
│ │ └── nl
│ │ │ └── ttys0
│ │ │ └── simplec25k
│ │ │ ├── ServiceReceiver.java
│ │ │ ├── MyPhoneStateListener.java
│ │ │ ├── MyPreferenceActivity.java
│ │ │ ├── WorkoutFileEditor.java
│ │ │ ├── MyAlarmService.java
│ │ │ ├── Simplec25kMainActivity.java
│ │ │ ├── TimerActivity.java
│ │ │ ├── CountdownChronometer.java
│ │ │ └── ProgramService.java
│ │ └── AndroidManifest.xml
└── build.gradle
├── gradle
└── wrapper
│ └── gradle-wrapper.properties
├── .idea
└── runConfigurations.xml
├── import-summary.txt
├── gradlew.bat
├── .gitignore
└── gradlew
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/beep.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Orbiter/Simple-C25K/HEAD/app/src/main/res/raw/beep.mp3
--------------------------------------------------------------------------------
/app/src/main/res/raw/beepquiet.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Orbiter/Simple-C25K/HEAD/app/src/main/res/raw/beepquiet.mp3
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ok.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Orbiter/Simple-C25K/HEAD/app/src/main/res/drawable-hdpi/ok.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/clean.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Orbiter/Simple-C25K/HEAD/app/src/main/res/drawable-hdpi/clean.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/nbarlogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Orbiter/Simple-C25K/HEAD/app/src/main/res/drawable-hdpi/nbarlogo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Orbiter/Simple-C25K/HEAD/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Orbiter/Simple-C25K/HEAD/app/src/main/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Orbiter/Simple-C25K/HEAD/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Mar 29 11:33:25 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | android {
3 | compileSdkVersion 23
4 | buildToolsVersion '25.0.2'
5 |
6 | defaultConfig {
7 | applicationId "nl.ttys0.simplec25k"
8 | minSdkVersion 14
9 | targetSdkVersion 15
10 | }
11 |
12 | buildTypes {
13 | release {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
16 | }
17 | }
18 | }
19 |
20 | dependencies {
21 | compile 'com.android.support:support-v4:26.0.0-alpha1'
22 | }
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/nl/ttys0/simplec25k/ServiceReceiver.java:
--------------------------------------------------------------------------------
1 | //http://www.tutorialforandroid.com/2009/01/get-phone-state-when-someone-is-calling_22.html
2 | package nl.ttys0.simplec25k;
3 |
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.telephony.PhoneStateListener;
8 | import android.telephony.TelephonyManager;
9 |
10 | public class ServiceReceiver extends BroadcastReceiver {
11 |
12 | @Override
13 | public void onReceive(Context context, Intent intent) {
14 | MyPhoneStateListener phoneListener = new MyPhoneStateListener();
15 | TelephonyManager telephony = (TelephonyManager) context
16 | .getSystemService(Context.TELEPHONY_SERVICE);
17 |
18 | telephony.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE);
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/row.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | - 1%
6 | - 5%
7 | - 10%
8 | - 20%
9 | - 40%
10 | - 60%
11 | - 80%
12 | - 100%
13 |
14 |
15 | - 1
16 | - 5
17 | - 10
18 | - 20
19 | - 40
20 | - 60
21 | - 80
22 | - 100
23 |
24 |
25 |
26 | - full
27 | - partial
28 | - dim
29 |
30 |
31 | - Full Wakelock
32 | - Partial Wakelock
33 | - Screen Dim Wakelock
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/nl/ttys0/simplec25k/MyPhoneStateListener.java:
--------------------------------------------------------------------------------
1 | //http://www.tutorialforandroid.com/2009/01/get-phone-state-when-someone-is-calling_22.html
2 |
3 | package nl.ttys0.simplec25k;
4 |
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.telephony.PhoneStateListener;
8 | import android.telephony.TelephonyManager;
9 |
10 | public class MyPhoneStateListener extends PhoneStateListener {
11 | protected static final String MY_ACTION = "MY_ACTION";
12 |
13 | public void onCallStateChanged(int state, String incomingNumber) {
14 | switch (state) {
15 | case TelephonyManager.CALL_STATE_IDLE:
16 | // Log.d("DEBUG", "IDLE");
17 | break;
18 | case TelephonyManager.CALL_STATE_OFFHOOK:
19 | // Log.d("DEBUG", "OFFHOOK");
20 | break;
21 | case TelephonyManager.CALL_STATE_RINGING:
22 | // Log.d("DEBUG", "RINGING");
23 |
24 | // setup intent for sending command
25 | Intent myIntent = new Intent();
26 | myIntent.setAction(MY_ACTION);
27 | myIntent.putExtra("DATA_TO_PS", "PAUSE");
28 |
29 | // send command
30 | Context context = TimerActivity.context;
31 | if (context != null) {
32 | context.sendBroadcast(myIntent);
33 | }
34 |
35 | // stop the countdown in the gui
36 | CountdownChronometer cc = TimerActivity.countdown;
37 | if (cc != null) {
38 | cc.stop();
39 | }
40 | break;
41 | }
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/import-summary.txt:
--------------------------------------------------------------------------------
1 | ECLIPSE ANDROID PROJECT IMPORT SUMMARY
2 | ======================================
3 |
4 | Ignored Files:
5 | --------------
6 | The following files were *not* copied into the new Gradle project; you
7 | should evaluate whether these are still needed in your project and if
8 | so manually move them:
9 |
10 | * .idea/
11 | * .idea/.name
12 | * .idea/Simple-C25K.iml
13 | * .idea/compiler.xml
14 | * .idea/copyright/
15 | * .idea/copyright/profiles_settings.xml
16 | * .idea/misc.xml
17 | * .idea/modules.xml
18 | * .idea/vcs.xml
19 | * .idea/workspace.xml
20 | * Android_Description
21 | * README
22 | * artwork/
23 | * artwork/SimpleC25K_icon.png
24 | * artwork/SimpleC25K_icon.xcf
25 | * artwork/SimpleC25K_icon_512x512.png
26 | * artwork/nBarSmallLogo.xcf
27 | * proguard.cfg
28 |
29 | Moved Files:
30 | ------------
31 | Android Gradle projects use a different directory structure than ADT
32 | Eclipse projects. Here's how the projects were restructured:
33 |
34 | * AndroidManifest.xml => app/src/main/AndroidManifest.xml
35 | * res/ => app/src/main/res/
36 | * src/ => app/src/main/java/
37 |
38 | Next Steps:
39 | -----------
40 | You can now build the project. The Gradle project needs network
41 | connectivity to download dependencies.
42 |
43 | Bugs:
44 | -----
45 | If for some reason your project does not build, and you determine that
46 | it is due to a bug or limitation of the Eclipse to Gradle importer,
47 | please file a bug at http://b.android.com with category
48 | Component-Tools.
49 |
50 | (This import summary is for your information only, and can be deleted
51 | after import once you are satisfied with the results.)
52 |
--------------------------------------------------------------------------------
/app/src/main/java/nl/ttys0/simplec25k/MyPreferenceActivity.java:
--------------------------------------------------------------------------------
1 | package nl.ttys0.simplec25k;
2 |
3 | import android.content.SharedPreferences;
4 | import android.media.MediaPlayer;
5 | import android.os.Bundle;
6 | import android.preference.Preference;
7 | import android.preference.Preference.OnPreferenceClickListener;
8 | import android.preference.PreferenceActivity;
9 | import android.preference.PreferenceManager;
10 | import android.view.Menu;
11 | import nl.ttys0.simplec25k.R;
12 |
13 | public class MyPreferenceActivity extends PreferenceActivity {
14 |
15 | @Override
16 | public void onCreate(Bundle savedInstanceState) {
17 | // Toast.makeText(this, "wham", Toast.LENGTH_LONG).show();
18 | super.onCreate(savedInstanceState);
19 | addPreferencesFromResource(R.xml.preferences);
20 |
21 | Preference customPref = (Preference) findPreference("test_volume");
22 | customPref
23 | .setOnPreferenceClickListener(new OnPreferenceClickListener() {
24 |
25 | public boolean onPreferenceClick(Preference preference) {
26 | playSound();
27 | return true;
28 | }
29 |
30 | });
31 | }
32 |
33 | @Override
34 | public boolean onCreateOptionsMenu(Menu menu) {
35 | menu.add(Menu.NONE, 0, 0, "Show current settings");
36 | return super.onCreateOptionsMenu(menu);
37 | }
38 |
39 | public void playSound() {
40 | SharedPreferences sharedPrefs = PreferenceManager
41 | .getDefaultSharedPreferences(this);
42 | float mediaSoundVolume = (float) (Integer.parseInt(sharedPrefs
43 | .getString("volume_percentage", "40")) / 100f);
44 | boolean mediaSoundBool = sharedPrefs.getBoolean("enable_sound", true);
45 | MediaPlayer mp = MediaPlayer.create(MyPreferenceActivity.this,
46 | R.raw.beep);
47 | if (mp != null && mediaSoundBool) {
48 | mp.setVolume(mediaSoundVolume, mediaSoundVolume);
49 | mp.start();
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/nl/ttys0/simplec25k/WorkoutFileEditor.java:
--------------------------------------------------------------------------------
1 | package nl.ttys0.simplec25k;
2 |
3 | import java.io.FileInputStream;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.io.InputStreamReader;
7 | import java.io.OutputStreamWriter;
8 |
9 | import android.content.Context;
10 | import android.widget.Toast;
11 |
12 | public class WorkoutFileEditor {
13 | private static Context context;
14 |
15 | public WorkoutFileEditor(Context context) {
16 |
17 | WorkoutFileEditor.context = context;
18 | }
19 |
20 | public String ReadSettings(String fileName) {
21 | FileInputStream fIn = null;
22 | InputStreamReader isr = null;
23 |
24 | char[] inputBuffer = new char[1000];
25 | String data = null;
26 |
27 | try {
28 | fIn = context.openFileInput(fileName);
29 | isr = new InputStreamReader(fIn);
30 | isr.read(inputBuffer);
31 | data = new String(inputBuffer);
32 | } catch (Exception e) {
33 | e.printStackTrace();
34 | Toast.makeText(context, "Settings not read", Toast.LENGTH_SHORT)
35 | .show();
36 | } finally {
37 | try {
38 | isr.close();
39 | fIn.close();
40 | } catch (IOException e) {
41 | e.printStackTrace();
42 | }
43 | }
44 | return data;
45 | }
46 |
47 | public void WriteSettings(String fileName, String data, int mode) {
48 |
49 | FileOutputStream fOut = null;
50 | OutputStreamWriter osw = null;
51 |
52 | try {
53 | fOut = context.openFileOutput(fileName, mode);
54 | osw = new OutputStreamWriter(fOut);
55 | osw.write(data);
56 | osw.flush();
57 | } catch (Exception e) {
58 |
59 | Toast.makeText(context, "Can't save changes. WTF?",
60 | Toast.LENGTH_SHORT).show();
61 | e.printStackTrace();
62 | } finally {
63 | try {
64 | osw.close();
65 | fOut.close();
66 | } catch (IOException e) {
67 | e.printStackTrace();
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Hello World, ListViewDynamicActivity!
5 | Simple C25K
6 | ok_icon
7 | Credits:\n\nVoice-over: Cynthia Ross\nDeveloper: Roel Blaauwgeers
8 |
9 | Brisk five-minute warmup walk. Then alternate 60 seconds of jogging and 90 seconds of walking for a total of 20 minutes.
10 | Brisk five-minute warmup walk. Then alternate 90 seconds of jogging and 2 minutes of walking for a total of 20 minutes.
11 | Brisk five-minute warmup walk. Then do two repetitions of the following:\n- Jog 90 seconds\n- Walk 90 seconds\n- Jog 3 minutes\n- Walk 3 minutes
12 | Brisk five-minute warmup walk. Then:\n- Jog 3 minutes\n- Walk 90 seconds\n- Jog 5 minutes\n- Walk 2½ minutes\n- Jog 3 minutes\n- Walk 90 seconds\n- Jog 5 minutes
13 | Brisk five-minute warmup walk, then:\n- Jog 5 minutes\n- Walk 3 minutes\n- Jog 5 minutes\n- Walk 3 minutes\n- Jog 5 minutes
14 | Brisk five-minute warmup walk, then:\n- Jog 8 minutes\n- Walk 5 minutes\n- Jog 8 minutes
15 | Brisk five-minute warmup walk, then jog 20 minutes with no walking.
16 | Brisk five-minute warmup walk, then:\n- Jog 5 minutes\n- Walk 3 minutes\n- Jog 8 minutes\n- Walk 3 minutes\n- Jog 5 minutes
17 | Brisk five-minute warmup walk, then:\n- Jog 10 minutes\n- Walk 3 minutes\n- Jog 10 minutes
18 | Brisk five-minute warmup walk, then jog 22 minutes with no walking.
19 | Brisk five-minute warmup walk, then jog 25 minutes.
20 | Brisk five-minute warmup walk, then jog 28 minutes.
21 | Brisk five-minute warmup walk, then jog 30 minutes.
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
32 |
33 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
15 |
16 |
26 |
32 |
38 |
39 |
40 |
41 |
44 |
51 |
52 |
53 |
56 |
57 |
58 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/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/nl/ttys0/simplec25k/MyAlarmService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * MyAlarmService.java
3 | *
4 | * Copyright 2012 Roel Blaauwgeers
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | *
19 | * info:
20 | * This Class is used for setting an alarm. When the alarm goes off a message
21 | * (MESSAGE) will be send to the notification bar.
22 | */
23 |
24 | package nl.ttys0.simplec25k;
25 |
26 | import android.app.Service;
27 | import android.content.Context;
28 | import android.content.Intent;
29 | import android.content.SharedPreferences;
30 | import android.media.MediaPlayer;
31 | import android.os.IBinder;
32 | import android.os.Vibrator;
33 | import android.preference.PreferenceManager;
34 |
35 | public class MyAlarmService extends Service {
36 |
37 | protected static final String MY_ACTION = "MY_ACTION";
38 | public static String message;
39 |
40 | private SharedPreferences sharedPrefs;
41 | private boolean mediaSoundBool;
42 | private boolean vibrateBool;
43 | private float mediaSoundVolume;
44 |
45 |
46 | @Override
47 | public void onCreate() {
48 | // Stop the service
49 | // to prevent random alerts after this service has run.
50 | this.stopSelf();
51 | }
52 |
53 | @Override
54 | public IBinder onBind(Intent intent) {
55 |
56 | return null;
57 |
58 | }
59 |
60 | @Override
61 | public void onDestroy() {
62 |
63 | super.onDestroy();
64 |
65 | }
66 |
67 | @Override
68 | public void onStart(Intent intent, int startId) {
69 |
70 | super.onStart(intent, startId);
71 |
72 | // retrieve workout info
73 | // message = intent.getStringExtra("MESSAGE");
74 |
75 | sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
76 |
77 |
78 | mediaSoundVolume = (float)(Integer.parseInt(sharedPrefs.getString("volume_percentage","40"))/100f);
79 | mediaSoundBool= sharedPrefs.getBoolean("enable_sound", true);
80 | MediaPlayer mp = MediaPlayer
81 | .create(MyAlarmService.this, R.raw.beep);
82 | if(mp!=null && mediaSoundBool){
83 | mp.setVolume(mediaSoundVolume, mediaSoundVolume);
84 | mp.start();
85 | }
86 |
87 | // let the user know we're done
88 | // setup vibrator
89 | vibrateBool = sharedPrefs.getBoolean("enable_vibrations", true);
90 | if(vibrateBool){
91 | Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
92 | long[] pat = { 0, 700, 400 };
93 | v.vibrate(pat, -1);
94 | }
95 |
96 | }
97 |
98 | @Override
99 | public boolean onUnbind(Intent intent) {
100 |
101 | return super.onUnbind(intent);
102 |
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/android,androidstudio
2 |
3 | ### Android ###
4 | # Built application files
5 | *.apk
6 | *.ap_
7 |
8 | # Files for the ART/Dalvik VM
9 | *.dex
10 |
11 | # Java class files
12 | *.class
13 |
14 | # Generated files
15 | bin/
16 | gen/
17 | out/
18 |
19 | # Gradle files
20 | .gradle/
21 | build/
22 |
23 | # Local configuration file (sdk path, etc)
24 | local.properties
25 |
26 | # Proguard folder generated by Eclipse
27 | proguard/
28 |
29 | # Log Files
30 | *.log
31 |
32 | # Android Studio Navigation editor temp files
33 | .navigation/
34 |
35 | # Android Studio captures folder
36 | captures/
37 |
38 | # Intellij
39 | *.iml
40 | .idea/workspace.xml
41 | .idea/tasks.xml
42 | .idea/gradle.xml
43 | .idea/dictionaries
44 | .idea/libraries
45 |
46 | # Keystore files
47 | *.jks
48 |
49 | # External native build folder generated in Android Studio 2.2 and later
50 | .externalNativeBuild
51 |
52 | # Google Services (e.g. APIs or Firebase)
53 | google-services.json
54 |
55 | # Freeline
56 | freeline.py
57 | freeline/
58 | freeline_project_description.json
59 |
60 | ### Android Patch ###
61 | gen-external-apklibs
62 |
63 | ### AndroidStudio ###
64 | # Covers files to be ignored for android development using Android Studio.
65 |
66 | # Built application files
67 |
68 | # Files for the ART/Dalvik VM
69 |
70 | # Java class files
71 |
72 | # Generated files
73 |
74 | # Gradle files
75 | .gradle
76 |
77 | # Signing files
78 | .signing/
79 |
80 | # Local configuration file (sdk path, etc)
81 |
82 | # Proguard folder generated by Eclipse
83 |
84 | # Log Files
85 |
86 | # Android Studio
87 | /*/build/
88 | /*/local.properties
89 | /*/out
90 | /*/*/build
91 | /*/*/production
92 | *.ipr
93 | *~
94 | *.swp
95 |
96 | # Android Patch
97 |
98 | # External native build folder generated in Android Studio 2.2 and later
99 |
100 | # NDK
101 | obj/
102 |
103 | # IntelliJ IDEA
104 | *.iws
105 | /out/
106 |
107 | # User-specific configurations
108 | .idea/libraries/
109 | .idea/.name
110 | .idea/compiler.xml
111 | .idea/copyright/profiles_settings.xml
112 | .idea/encodings.xml
113 | .idea/misc.xml
114 | .idea/modules.xml
115 | .idea/scopes/scope_settings.xml
116 | .idea/vcs.xml
117 | .idea/jsLibraryMappings.xml
118 | .idea/datasources.xml
119 | .idea/dataSources.ids
120 | .idea/sqlDataSources.xml
121 | .idea/dynamic.xml
122 | .idea/uiDesigner.xml
123 |
124 | # Keystore files
125 |
126 | # OS-specific files
127 | .DS_Store
128 | .DS_Store?
129 | ._*
130 | .Spotlight-V100
131 | .Trashes
132 | ehthumbs.db
133 | Thumbs.db
134 |
135 | # Legacy Eclipse project files
136 | .classpath
137 | .project
138 |
139 | # Mobile Tools for Java (J2ME)
140 | .mtj.tmp/
141 |
142 | # Package Files #
143 | *.jar
144 | *.war
145 | *.ear
146 |
147 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
148 | hs_err_pid*
149 |
150 | ## Plugin-specific files:
151 |
152 | # mpeltonen/sbt-idea plugin
153 | .idea_modules/
154 |
155 | # JIRA plugin
156 | atlassian-ide-plugin.xml
157 |
158 | # Mongo Explorer plugin
159 | .idea/mongoSettings.xml
160 |
161 | # Crashlytics plugin (for Android Studio and IntelliJ)
162 | com_crashlytics_export_strings.xml
163 | crashlytics.properties
164 | crashlytics-build.properties
165 | fabric.properties
166 |
167 | # End of https://www.gitignore.io/api/android,androidstudio
168 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/timer.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
24 |
25 |
33 |
34 |
35 |
36 |
44 |
45 |
53 |
54 |
62 |
63 |
72 |
73 |
82 |
83 |
91 |
92 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/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/nl/ttys0/simplec25k/Simplec25kMainActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Simplec25kMainActivity.java
3 | *
4 | * Copyright 2012 Roel Blaauwgeers
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | *
19 | */
20 |
21 | package nl.ttys0.simplec25k;
22 |
23 | import java.io.File;
24 | import java.util.StringTokenizer;
25 |
26 | import android.util.Log;
27 | import nl.ttys0.simplec25k.R;
28 |
29 | import android.os.Bundle;
30 | import android.app.ListActivity;
31 | import android.app.ProgressDialog;
32 | import android.content.BroadcastReceiver;
33 | import android.content.Context;
34 | import android.content.Intent;
35 | import android.view.LayoutInflater;
36 | import android.view.View;
37 | import android.view.ViewGroup;
38 | import android.widget.ArrayAdapter;
39 | import android.widget.ImageView;
40 | import android.widget.ListView;
41 | import android.widget.TextView;
42 |
43 | public class Simplec25kMainActivity extends ListActivity {
44 | TextView selection;
45 |
46 | // files
47 | static final String SCHEDULEFILE = "schedule";
48 | static final String SETTINGSFILE = "settings";
49 |
50 | // global arrays containing the schedule information
51 | static String[] programStringArr = new String[27]; // workout name
52 | static Boolean[] programDoneArr = new Boolean[27]; // workout completed?
53 | static String selectedProgram;
54 |
55 | private WorkoutFileEditor workoutFileEditor = new WorkoutFileEditor(this);
56 |
57 | /** Called when the activity is first created. */
58 | @Override
59 | public void onCreate(Bundle icicle) {
60 | super.onCreate(icicle);
61 | setContentView(R.layout.main);
62 | setListAdapter(new IconicAdapter());
63 |
64 | ProgressDialog dialog = ProgressDialog.show(
65 | Simplec25kMainActivity.this, "", "Loading. Please wait...",
66 | true);
67 |
68 | // test for schedule file, if it doesn't exists we'll create it
69 | File file = getBaseContext().getFileStreamPath(SCHEDULEFILE);
70 |
71 | if (!file.exists()) {
72 | Log.d("Simple", "Schedule file not found");
73 | workoutFileEditor
74 | .WriteSettings(SCHEDULEFILE,
75 | "w1d1;false,w1d2;false,w1d3;false,"
76 | + "w2d1;false,w2d2;false,w2d3;false,"
77 | + "w3d1;false,w3d2;false,w3d3;false,"
78 | + "w4d1;false,w4d2;false,w4d3;false,"
79 | + "w5d1;false,w5d2;false,w5d3;false,"
80 | + "w6d1;false,w6d2;false,w6d3;false,"
81 | + "w7d1;false,w7d2;false,w7d3;false,"
82 | + "w8d1;false,w8d2;false,w8d3;false,"
83 | + "w9d1;false,w9d2;false,w9d3;false,",
84 | MODE_PRIVATE);
85 | }
86 |
87 | // retrieve the schedule information
88 | String s[] = workoutFileEditor.ReadSettings(SCHEDULEFILE).split(",");
89 | for (int i = 0; i < 27; i++) {
90 | StringTokenizer tk = new StringTokenizer(s[i], ";");
91 | programStringArr[i] = tk.nextToken();
92 | programDoneArr[i] = Boolean.valueOf(tk.nextToken());
93 | }
94 |
95 | dialog.cancel();
96 |
97 | }
98 |
99 | @Override
100 | protected void onResume() {
101 | // retrieve the schedule information
102 | WorkoutFileEditor workoutFileEditor = new WorkoutFileEditor(this);
103 | String s[] = workoutFileEditor.ReadSettings(SCHEDULEFILE).split(",");
104 | for (int i = 0; i < 27; i++) {
105 | StringTokenizer tk = new StringTokenizer(s[i], ";");
106 | programStringArr[i] = tk.nextToken();
107 | programDoneArr[i] = Boolean.valueOf(tk.nextToken());
108 | }
109 | super.onResume();
110 | }
111 |
112 | // When a choice is made
113 | public void onListItemClick(ListView parent, View v, int position, long id) {
114 | Intent myIntent = new Intent(this, TimerActivity.class);
115 | myIntent.putExtra("selectedProgram", programStringArr[position]);
116 | startActivityForResult(myIntent, 0);
117 |
118 | }
119 |
120 | // class to fill list
121 | class IconicAdapter extends ArrayAdapter {
122 |
123 | IconicAdapter() {
124 | super(Simplec25kMainActivity.this, R.layout.row, programStringArr);
125 | }
126 |
127 | public View getView(int position, View convertView, ViewGroup parent) {
128 | LayoutInflater inflater = getLayoutInflater();
129 | View row = inflater.inflate(R.layout.row, parent, false);
130 | TextView label = (TextView) row.findViewById(R.id.label);
131 | label.setText(programStringArr[position].replace("w", "Week ")
132 | .replace("d", ", Day "));
133 |
134 | ImageView icon = (ImageView) row.findViewById(R.id.icon);
135 |
136 | if (Boolean
137 | .valueOf(Simplec25kMainActivity.programDoneArr[position])) {
138 | icon.setImageResource(R.drawable.ok);
139 | } else {
140 | icon.setImageResource(R.drawable.clean);
141 | }
142 |
143 | return (row);
144 | }
145 | }
146 |
147 | // class for receiving broadcasts. In this case it's being used to receive
148 | // commands from the TimerActivity
149 | @SuppressWarnings("unused")
150 | private class MyReceiver extends BroadcastReceiver {
151 |
152 | @Override
153 | public void onReceive(Context arg0, Intent arg1) {
154 |
155 | String orgData = arg1.getStringExtra("DATA_TO_MAIN");
156 |
157 | // here we can receive commands from timerActivity
158 |
159 | if (orgData != null) {
160 | if (orgData.equals("UPDATED")) {
161 | // retrieve the schedule information
162 | String s[] = workoutFileEditor.ReadSettings(SCHEDULEFILE)
163 | .split(",");
164 | for (int i = 0; i < 27; i++) {
165 | StringTokenizer tk = new StringTokenizer(s[i], ";");
166 | programStringArr[i] = tk.nextToken();
167 | programDoneArr[i] = Boolean.valueOf(tk.nextToken());
168 | }
169 | }
170 | }
171 | }
172 | }
173 | }
--------------------------------------------------------------------------------
/app/src/main/java/nl/ttys0/simplec25k/TimerActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * TimerActivity.java
3 | *
4 | * Copyright 2012 Roel Blaauwgeers
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | *
19 | * info:
20 | * Class to control the ProgramService from a GUI.
21 | */
22 |
23 | package nl.ttys0.simplec25k;
24 |
25 | import java.util.HashMap;
26 | import nl.ttys0.simplec25k.R;
27 | import android.app.Activity;
28 | import android.app.AlertDialog;
29 | import android.content.BroadcastReceiver;
30 | import android.content.Context;
31 | import android.content.DialogInterface;
32 | import android.content.Intent;
33 | import android.content.IntentFilter;
34 | import android.content.SharedPreferences;
35 | import android.content.res.Resources;
36 | import android.os.Bundle;
37 | import android.preference.PreferenceManager;
38 | import android.view.KeyEvent;
39 | import android.view.Menu;
40 | import android.view.MenuInflater;
41 | import android.view.MenuItem;
42 | import android.view.View;
43 | import android.view.View.OnClickListener;
44 | import android.widget.Button;
45 | import android.widget.TextView;
46 |
47 | public class TimerActivity extends Activity {
48 |
49 | protected static final String MY_ACTION = "MY_ACTION";
50 |
51 | public static CountdownChronometer countdown;
52 | public static CountdownChronometer totalCountdown;
53 | public static Context context;
54 |
55 | private TextView mainTitle;
56 | private TextView current;
57 | private TextView description;
58 | private MyReceiver myReceiver;
59 | private HashMap hashMap = new HashMap();
60 | private SharedPreferences prefs;
61 |
62 | private Button startButton;
63 | private Button pauseButton;
64 | private Button skipButton;
65 |
66 | private Boolean paused = false;
67 | private Boolean started = false;
68 |
69 | // selected from main
70 | public static String selectedProgram;
71 |
72 | @Override
73 | public void onCreate(Bundle savedInstanceState) {
74 | super.onCreate(savedInstanceState);
75 | this.setContentView(R.layout.timer);
76 |
77 | prefs = PreferenceManager.getDefaultSharedPreferences(this);
78 | // setup descriptions, there is probably an easier way that I don't know
79 | // of:)
80 | Resources res = getResources();
81 | hashMap.put("w1d1", res.getString(R.string.w1));
82 | hashMap.put("w1d2", res.getString(R.string.w1));
83 | hashMap.put("w1d3", res.getString(R.string.w1));
84 |
85 | hashMap.put("w2d1", res.getString(R.string.w2));
86 | hashMap.put("w2d2", res.getString(R.string.w2));
87 | hashMap.put("w2d3", res.getString(R.string.w2));
88 |
89 | hashMap.put("w3d1", res.getString(R.string.w3));
90 | hashMap.put("w3d2", res.getString(R.string.w3));
91 | hashMap.put("w3d3", res.getString(R.string.w3));
92 |
93 | hashMap.put("w4d1", res.getString(R.string.w4));
94 | hashMap.put("w4d2", res.getString(R.string.w4));
95 | hashMap.put("w4d3", res.getString(R.string.w4));
96 |
97 | hashMap.put("w5d1", res.getString(R.string.w5d1));
98 | hashMap.put("w5d2", res.getString(R.string.w5d2));
99 | hashMap.put("w5d3", res.getString(R.string.w5d3));
100 |
101 | hashMap.put("w6d1", res.getString(R.string.w6d1));
102 | hashMap.put("w6d2", res.getString(R.string.w6d2));
103 | hashMap.put("w6d3", res.getString(R.string.w6d3));
104 |
105 | hashMap.put("w7d1", res.getString(R.string.w7));
106 | hashMap.put("w7d2", res.getString(R.string.w7));
107 | hashMap.put("w7d3", res.getString(R.string.w7));
108 |
109 | hashMap.put("w8d1", res.getString(R.string.w8));
110 | hashMap.put("w8d2", res.getString(R.string.w8));
111 | hashMap.put("w8d3", res.getString(R.string.w8));
112 |
113 | hashMap.put("w9d1", res.getString(R.string.w9));
114 | hashMap.put("w9d2", res.getString(R.string.w9));
115 | hashMap.put("w9d3", res.getString(R.string.w9));
116 |
117 | TimerActivity.context = this;
118 |
119 | // Register BroadcastReceiver
120 | // to receive event from our service
121 | myReceiver = new MyReceiver();
122 | IntentFilter intentFilter = new IntentFilter();
123 | intentFilter.addAction(ProgramService.MY_ACTION);
124 | registerReceiver(myReceiver, intentFilter);
125 |
126 | // get the selected program. It shouldn't ever be null, but just in case, we start a new main
127 | // activity and kill the current timerActivity.
128 | selectedProgram = getIntent().getStringExtra("selectedProgram");
129 | if (selectedProgram == null) {
130 | // restart app
131 | Intent i = getBaseContext().getPackageManager()
132 | .getLaunchIntentForPackage(
133 | getBaseContext().getPackageName());
134 | i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
135 | startActivity(i);
136 | } else {
137 |
138 | countdown = (CountdownChronometer) findViewById(R.id.chronometer1);
139 | // countdown.setBase(System.currentTimeMillis() + 30000);
140 |
141 | mainTitle = (TextView) findViewById(R.id.name);
142 | mainTitle.setText(selectedProgram.replace("w", "Week ").replace(
143 | "d", ", Day "));
144 |
145 | startButton = (Button) findViewById(R.id.startButton);
146 | pauseButton = (Button) findViewById(R.id.pauseButton);
147 | skipButton = (Button) findViewById(R.id.skipButton);
148 |
149 | // Watch for button clicks.
150 | // button = (Button) findViewById(R.id.startButton);
151 | startButton.setOnClickListener(mStartListener);
152 |
153 | // button = (Button) findViewById(R.id.pauseButton);
154 | pauseButton.setOnClickListener(mPauseListener);
155 | pauseButton.setClickable(false);
156 |
157 | // button = (Button) findViewById(R.id.skipButton);
158 | skipButton.setOnClickListener(mSkipListener);
159 | skipButton.setClickable(false);
160 |
161 | // set text fields
162 | description = (TextView) findViewById(R.id.textView1);
163 | description.setText(hashMap.get(selectedProgram));
164 |
165 | current = (TextView) findViewById(R.id.textView2);
166 | current.setText("Press Start to begin.");
167 |
168 | countdown = (CountdownChronometer) findViewById(R.id.chronometer1);
169 | totalCountdown = (CountdownChronometer) findViewById(R.id.chronometer2);
170 |
171 | }
172 |
173 | }
174 |
175 | private void sendIntent(String intent) {
176 | Intent i = new Intent();
177 | i.setAction(MY_ACTION);
178 | i.putExtra("DATA_TO_PS", intent);
179 | sendBroadcast(i);
180 | }
181 |
182 | void stop() {
183 | if (prefs.getBoolean("show_alerts", true)) {
184 | new AlertDialog.Builder(this)
185 | .setMessage("Are you sure you want to exit?")
186 | .setCancelable(false)
187 | .setPositiveButton("Yes",
188 | new DialogInterface.OnClickListener() {
189 | public void onClick(DialogInterface dialog, int id) {
190 | sendIntent("STOP");
191 | finish();
192 | }
193 | })
194 | .setNegativeButton("No", new DialogInterface.OnClickListener() {
195 | public void onClick(DialogInterface dialog, int id) {
196 | dialog.cancel();
197 | }
198 | })
199 | .show();
200 | } else {
201 | sendIntent("STOP");
202 | finish();
203 | }
204 | }
205 |
206 | @Override
207 | protected void onDestroy() {
208 | unregisterReceiver(myReceiver);
209 | super.onDestroy();
210 | }
211 |
212 | // ===========================================================
213 | // Buttons
214 | // ===========================================================
215 | // Start button
216 | View.OnClickListener mStartListener = new OnClickListener() {
217 | public void onClick(View v) {
218 |
219 | if (!started) {
220 |
221 | // change into stopbutton
222 | started = true;
223 | startButton.setText("Stop");
224 |
225 | // enable skipbutton
226 | skipButton.setClickable(true);
227 |
228 | // enable pausebutton
229 | pauseButton.setClickable(true);
230 |
231 | // Start our service
232 | Intent svcIntent = new Intent(TimerActivity.this,
233 | nl.ttys0.simplec25k.ProgramService.class);
234 | svcIntent.putExtra("INIT_DATA", selectedProgram);
235 | startService(svcIntent);
236 |
237 | } else
238 | stop();
239 | }
240 | };
241 |
242 | // pause button
243 | View.OnClickListener mPauseListener = new OnClickListener() {
244 | public void onClick(View v) {
245 | // mChronometer.stop();
246 |
247 | if (!paused) {
248 |
249 | // change pausebutton into a resume button
250 | paused = true;
251 | pauseButton.setText("Resume");
252 |
253 | // disable skipbutton
254 | skipButton.setClickable(false);
255 |
256 | sendIntent("PAUSE");
257 |
258 | countdown.stop();
259 | totalCountdown.stop();
260 |
261 | } else {
262 |
263 | // change into pausebutton
264 | paused = false;
265 | pauseButton.setText("Pause");
266 |
267 | skipButton.setClickable(true);
268 |
269 | sendIntent("RESUME");
270 | }
271 |
272 | }
273 | };
274 |
275 | // skip button
276 | View.OnClickListener mSkipListener = new OnClickListener() {
277 | public void onClick(View v) {
278 | if (prefs.getBoolean("show_alerts", true)) {
279 | new AlertDialog.Builder(TimerActivity.this)
280 | .setMessage("Are you sure you want to skip?")
281 | .setCancelable(false)
282 | .setPositiveButton("Yes",
283 | new DialogInterface.OnClickListener() {
284 | public void onClick(DialogInterface dialog, int id) {
285 | sendIntent("SKIP");
286 | }
287 | })
288 | .setNegativeButton("No", new DialogInterface.OnClickListener() {
289 | public void onClick(DialogInterface dialog, int id) {
290 | dialog.cancel();
291 | }
292 | })
293 | .show();
294 | } else {
295 | sendIntent("SKIP");
296 | }
297 | }
298 | };
299 |
300 | // back button (hard button)
301 | @Override
302 | public boolean onKeyDown(int keyCode, KeyEvent event) {
303 | if ((keyCode == KeyEvent.KEYCODE_BACK)) {
304 | stop();
305 | }
306 | return super.onKeyDown(keyCode, event);
307 | }
308 |
309 | // menu button
310 | @Override
311 | public boolean onCreateOptionsMenu(Menu menu) {
312 | MenuInflater inflater = getMenuInflater();
313 | inflater.inflate(R.menu.menu, menu);
314 | return true;
315 | }
316 |
317 | @Override
318 | public boolean onOptionsItemSelected(MenuItem item) {
319 | Intent myIntent = new Intent(this, MyPreferenceActivity.class);
320 | startActivityForResult(myIntent, 0);
321 | return true;
322 | }
323 |
324 | // class for receiving broadcasts
325 | private class MyReceiver extends BroadcastReceiver {
326 |
327 | @Override
328 | public void onReceive(Context arg0, Intent arg1) {
329 |
330 | String orgData = arg1.getStringExtra("DATA_TO_TA");
331 |
332 | if (orgData != null) {
333 | if (orgData.equals("DONE")) {
334 | if (prefs.getBoolean("show_alerts", true)) {
335 | new AlertDialog.Builder(TimerActivity.this)
336 | .setMessage("Workout finished. Well done!")
337 | .setNeutralButton("Ok",
338 | new DialogInterface.OnClickListener() {
339 | public void onClick(DialogInterface arg0, int arg1) {
340 | finish();
341 | }
342 | })
343 | .show();
344 | } else {
345 | finish();
346 | }
347 | } else if (orgData.equals("STARTED")) {
348 | if (prefs.getBoolean("show_alerts", true)) {
349 | new AlertDialog.Builder(TimerActivity.this)
350 | .setMessage("Workout started. Warmup for five minutes.\nGet started!")
351 | .setNeutralButton("Ok",
352 | new DialogInterface.OnClickListener() {
353 | public void onClick(DialogInterface arg0, int arg1) {}
354 | })
355 | .show();
356 | }
357 | } else if (orgData.contains("SET_CURRENT")) {
358 | String[] s = orgData.split(";");
359 |
360 | current.setText(s[1]);
361 |
362 | countdown.setBase(System.currentTimeMillis()
363 | + Integer.parseInt(s[2]));
364 | totalCountdown.setBase(System.currentTimeMillis()
365 | + Integer.parseInt(s[3]));
366 | countdown.start();
367 | totalCountdown.start();
368 |
369 | }
370 |
371 | /*
372 | * unused atm else if
373 | * (orgData.contains("UPDATE_TOTAL_COUNTDOWN")){
374 | * totalCountdown.setBase(System.currentTimeMillis() +
375 | * arg1.getIntExtra("TIMESET", 0)*1000); totalCountdown.start();
376 | * }
377 | */
378 |
379 | }
380 |
381 | }
382 |
383 | }
384 |
385 | }
386 |
--------------------------------------------------------------------------------
/app/src/main/java/nl/ttys0/simplec25k/CountdownChronometer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 readyState Software Ltd
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 nl.ttys0.simplec25k;
18 |
19 | import java.util.Formatter;
20 | import java.util.IllegalFormatException;
21 | import java.util.Locale;
22 |
23 | import android.content.Context;
24 | import android.os.Handler;
25 | import android.os.Message;
26 | import android.os.SystemClock;
27 | //import android.text.format.DateUtils;
28 | import android.util.AttributeSet;
29 | import android.widget.Chronometer;
30 |
31 | public class CountdownChronometer extends Chronometer {
32 | private static final String TAG = "CountdownChronometer";
33 |
34 | private static final String FAST_FORMAT_DHHMMSS = "%1$02d:%2$02d:%3$02d:%4$02d";
35 | private static final String FAST_FORMAT_HMMSS = "%1$02d:%2$02d:%3$02d";
36 | private static final String FAST_FORMAT_MMSS = "%1$02d:%2$02d";
37 | private static final char TIME_PADDING = '0';
38 | private static final char TIME_SEPARATOR = ':';
39 |
40 | private long mBase;
41 | private boolean mVisible;
42 | private boolean mStarted;
43 | private boolean mRunning;
44 | private boolean mLogged;
45 | private String mFormat;
46 | private Formatter mFormatter;
47 | private Locale mFormatterLocale;
48 | private Object[] mFormatterArgs = new Object[1];
49 | private StringBuilder mFormatBuilder;
50 | private OnChronometerTickListener mOnChronometerTickListener;
51 | private OnChronometerTickListener mOnCountdownCompleteListener;
52 | private StringBuilder mRecycle = new StringBuilder(8);
53 |
54 | private String mChronoFormat;
55 |
56 | private static final int TICK_WHAT = 3;
57 |
58 | /**
59 | * Initialize this CountdownChronometer object.
60 | */
61 | public CountdownChronometer(Context context) {
62 | this(context, null, 0, 0);
63 | }
64 |
65 | /**
66 | * Initialize this CountdownChronometer object.
67 | *
68 | * @param base
69 | * Use the {@link SystemClock#elapsedRealtime} time base.
70 | */
71 | public CountdownChronometer(Context context, long base) {
72 | this(context, null, 0, base);
73 | }
74 |
75 | /**
76 | * Initialize with standard view layout information.
77 | *
78 | * @param base
79 | * Use the {@link SystemClock#elapsedRealtime} time base.
80 | */
81 | public CountdownChronometer(Context context, AttributeSet attrs) {
82 | this(context, attrs, 0, 0);
83 | }
84 |
85 | /**
86 | * Initialize with standard view layout information and style.
87 | *
88 | * @param base
89 | * Use the {@link SystemClock#elapsedRealtime} time base.
90 | */
91 | public CountdownChronometer(Context context, AttributeSet attrs,
92 | int defStyle, long base) {
93 | super(context, attrs, defStyle);
94 | init(base);
95 | }
96 |
97 | private void init(long base) {
98 | mBase = base;
99 | updateText(System.currentTimeMillis());
100 | }
101 |
102 | /**
103 | * Set the time that the count-down timer is in reference to.
104 | *
105 | * @param base
106 | * Use the {@link SystemClock#elapsedRealtime} time base.
107 | */
108 | @Override
109 | public void setBase(long base) {
110 | mBase = base;
111 | dispatchChronometerTick();
112 | updateText(System.currentTimeMillis());
113 | }
114 |
115 | /**
116 | * Return the base time.
117 | */
118 | @Override
119 | public long getBase() {
120 | return mBase;
121 | }
122 |
123 | /**
124 | * Sets the format string used for display. The CountdownChronometer will
125 | * display this string, with the first "%s" replaced by the current timer
126 | * value in "MM:SS", "H:MM:SS" or "D:HH:MM:SS" form.
127 | *
128 | * If the format string is null, or if you never call setFormat(), the
129 | * Chronometer will simply display the timer value in "MM:SS", "H:MM:SS" or
130 | * "D:HH:MM:SS" form.
131 | *
132 | * @param format
133 | * the format string.
134 | */
135 | @Override
136 | public void setFormat(String format) {
137 | mFormat = format;
138 | if (format != null && mFormatBuilder == null) {
139 | mFormatBuilder = new StringBuilder(format.length() * 2);
140 | }
141 | }
142 |
143 | /**
144 | * Returns the current format string as set through {@link #setFormat}.
145 | */
146 | @Override
147 | public String getFormat() {
148 | return mFormat;
149 | }
150 |
151 | /**
152 | * Sets a custom format string used for the timer value.
153 | *
154 | * Example:
155 | * "%1$02d days, %2$02d hours, %3$02d minutes and %4$02d seconds remaining"
156 | *
157 | * @param format
158 | * the format string.
159 | */
160 | public void setCustomChronoFormat(String chronoFormat) {
161 | this.mChronoFormat = chronoFormat;
162 | }
163 |
164 | /**
165 | * Returns the current format string as set through
166 | * {@link #setCustomChronoFormat}.
167 | */
168 | public String getCustomChronoFormat() {
169 | return mChronoFormat;
170 | }
171 |
172 | /**
173 | * Sets the listener to be called when the chronometer changes.
174 | *
175 | * @param listener
176 | * The listener.
177 | */
178 | @Override
179 | public void setOnChronometerTickListener(OnChronometerTickListener listener) {
180 | mOnChronometerTickListener = listener;
181 | }
182 |
183 | /**
184 | * @return The listener (may be null) that is listening for chronometer
185 | * change events.
186 | */
187 | @Override
188 | public OnChronometerTickListener getOnChronometerTickListener() {
189 | return mOnChronometerTickListener;
190 | }
191 |
192 | /**
193 | * Sets the listener to be called when the countdown is complete.
194 | *
195 | * @param listener
196 | * The listener.
197 | */
198 | public void setOnCompleteListener(OnChronometerTickListener listener) {
199 | mOnCountdownCompleteListener = listener;
200 | }
201 |
202 | /**
203 | * @return The listener (may be null) that is listening for countdown
204 | * complete event.
205 | */
206 | public OnChronometerTickListener getOnCompleteListener() {
207 | return mOnCountdownCompleteListener;
208 | }
209 |
210 | /**
211 | * Start counting down. This does not affect the base as set from
212 | * {@link #setBase}, just the view display.
213 | *
214 | * CountdownChronometer works by regularly scheduling messages to the
215 | * handler, even when the Widget is not visible. To make sure resource leaks
216 | * do not occur, the user should make sure that each start() call has a
217 | * reciprocal call to {@link #stop}.
218 | */
219 | @Override
220 | public void start() {
221 | mStarted = true;
222 | updateRunning();
223 | }
224 |
225 | /**
226 | * Stop counting down. This does not affect the base as set from
227 | * {@link #setBase}, just the view display.
228 | *
229 | * This stops the messages to the handler, effectively releasing resources
230 | * that would be held as the chronometer is running, via {@link #start}.
231 | */
232 | @Override
233 | public void stop() {
234 | mStarted = false;
235 | updateRunning();
236 | }
237 |
238 | /**
239 | * The same as calling {@link #start} or {@link #stop}.
240 | */
241 | public void setStarted(boolean started) {
242 | mStarted = started;
243 | updateRunning();
244 | }
245 |
246 | @Override
247 | protected void onDetachedFromWindow() {
248 | super.onDetachedFromWindow();
249 | mVisible = false;
250 | updateRunning();
251 | }
252 |
253 | @Override
254 | protected void onWindowVisibilityChanged(int visibility) {
255 | super.onWindowVisibilityChanged(visibility);
256 | mVisible = visibility == VISIBLE;
257 | updateRunning();
258 | }
259 |
260 | private synchronized boolean updateText(long now) {
261 | long seconds = mBase - now;
262 | seconds /= 1000;
263 | boolean stillRunning = true;
264 | if (seconds <= 0) {
265 | stillRunning = false;
266 | seconds = 0;
267 | }
268 | String text = formatRemainingTime(mRecycle, seconds);
269 |
270 | if (mFormat != null) {
271 | Locale loc = Locale.getDefault();
272 | if (mFormatter == null || !loc.equals(mFormatterLocale)) {
273 | mFormatterLocale = loc;
274 | mFormatter = new Formatter(mFormatBuilder, loc);
275 | }
276 | mFormatBuilder.setLength(0);
277 | mFormatterArgs[0] = text;
278 | try {
279 | mFormatter.format(mFormat, mFormatterArgs);
280 | text = mFormatBuilder.toString();
281 | } catch (IllegalFormatException ex) {
282 | if (!mLogged) {
283 | // Log.w(TAG, "Illegal format string: " + mFormat);
284 | mLogged = true;
285 | }
286 | }
287 | }
288 | setText(text);
289 | return stillRunning;
290 | }
291 |
292 | private void updateRunning() {
293 | boolean running = mVisible && mStarted;
294 | if (running != mRunning) {
295 | if (running) {
296 | if (updateText(System.currentTimeMillis())) {
297 | dispatchChronometerTick();
298 | mHandler.sendMessageDelayed(
299 | Message.obtain(mHandler, TICK_WHAT), 1000);
300 | } else {
301 | running = false;
302 | mHandler.removeMessages(TICK_WHAT);
303 | }
304 | } else {
305 | mHandler.removeMessages(TICK_WHAT);
306 | }
307 | mRunning = running;
308 | }
309 | }
310 |
311 | private Handler mHandler = new Handler() {
312 | public void handleMessage(Message m) {
313 | if (mRunning) {
314 | if (updateText(System.currentTimeMillis())) {
315 | dispatchChronometerTick();
316 | sendMessageDelayed(Message.obtain(this, TICK_WHAT), 1000);
317 | } else {
318 | dispatchCountdownCompleteEvent();
319 | stop();
320 | }
321 |
322 | }
323 | }
324 | };
325 |
326 | void dispatchChronometerTick() {
327 | if (mOnChronometerTickListener != null) {
328 | mOnChronometerTickListener.onChronometerTick(this);
329 | }
330 | }
331 |
332 | void dispatchCountdownCompleteEvent() {
333 | if (mOnCountdownCompleteListener != null) {
334 | mOnCountdownCompleteListener.onChronometerTick(this);
335 | }
336 | }
337 |
338 | /**
339 | * Formats remaining time in the form "MM:SS", "H:MM:SS" or "D:HH:MM:SS".
340 | *
341 | * @param recycle
342 | * {@link StringBuilder} to recycle, if possible
343 | * @param elapsedSeconds
344 | * the remaining time in seconds.
345 | */
346 | private String formatRemainingTime(StringBuilder recycle,
347 | long elapsedSeconds) {
348 |
349 | long days = 0;
350 | long hours = 0;
351 | long minutes = 0;
352 | long seconds = 0;
353 |
354 | if (elapsedSeconds >= 86400) {
355 | days = elapsedSeconds / 86400;
356 | elapsedSeconds -= days * 86400;
357 | }
358 | if (elapsedSeconds >= 3600) {
359 | hours = elapsedSeconds / 3600;
360 | elapsedSeconds -= hours * 3600;
361 | }
362 | if (elapsedSeconds >= 60) {
363 | minutes = elapsedSeconds / 60;
364 | elapsedSeconds -= minutes * 60;
365 | }
366 | seconds = elapsedSeconds;
367 |
368 | if (mChronoFormat != null) {
369 | return formatRemainingTime(recycle, mChronoFormat, days, hours,
370 | minutes, seconds);
371 | } else if (days > 0) {
372 | return formatRemainingTime(recycle, FAST_FORMAT_DHHMMSS, days,
373 | hours, minutes, seconds);
374 | } else if (hours > 0) {
375 | return formatRemainingTime(recycle, FAST_FORMAT_HMMSS, hours,
376 | minutes, seconds);
377 | } else {
378 | return formatRemainingTime(recycle, FAST_FORMAT_MMSS, minutes,
379 | seconds);
380 | }
381 | }
382 |
383 | private static String formatRemainingTime(StringBuilder recycle,
384 | String format, long days, long hours, long minutes, long seconds) {
385 | if (FAST_FORMAT_DHHMMSS.equals(format)) {
386 | StringBuilder sb = recycle;
387 | if (sb == null) {
388 | sb = new StringBuilder(8);
389 | } else {
390 | sb.setLength(0);
391 | }
392 | sb.append(days);
393 | sb.append(TIME_SEPARATOR);
394 | if (hours < 10) {
395 | sb.append(TIME_PADDING);
396 | } else {
397 | sb.append(toDigitChar(hours / 10));
398 | }
399 | sb.append(toDigitChar(hours % 10));
400 | sb.append(TIME_SEPARATOR);
401 | if (minutes < 10) {
402 | sb.append(TIME_PADDING);
403 | } else {
404 | sb.append(toDigitChar(minutes / 10));
405 | }
406 | sb.append(toDigitChar(minutes % 10));
407 | sb.append(TIME_SEPARATOR);
408 | if (seconds < 10) {
409 | sb.append(TIME_PADDING);
410 | } else {
411 | sb.append(toDigitChar(seconds / 10));
412 | }
413 | sb.append(toDigitChar(seconds % 10));
414 | return sb.toString();
415 | } else {
416 | return String.format(format, days, hours, minutes, seconds);
417 | }
418 | }
419 |
420 | private static String formatRemainingTime(StringBuilder recycle,
421 | String format, long hours, long minutes, long seconds) {
422 | if (FAST_FORMAT_HMMSS.equals(format)) {
423 | StringBuilder sb = recycle;
424 | if (sb == null) {
425 | sb = new StringBuilder(8);
426 | } else {
427 | sb.setLength(0);
428 | }
429 | sb.append(hours);
430 | sb.append(TIME_SEPARATOR);
431 | if (minutes < 10) {
432 | sb.append(TIME_PADDING);
433 | } else {
434 | sb.append(toDigitChar(minutes / 10));
435 | }
436 | sb.append(toDigitChar(minutes % 10));
437 | sb.append(TIME_SEPARATOR);
438 | if (seconds < 10) {
439 | sb.append(TIME_PADDING);
440 | } else {
441 | sb.append(toDigitChar(seconds / 10));
442 | }
443 | sb.append(toDigitChar(seconds % 10));
444 | return sb.toString();
445 | } else {
446 | return String.format(format, hours, minutes, seconds);
447 | }
448 | }
449 |
450 | private static String formatRemainingTime(StringBuilder recycle,
451 | String format, long minutes, long seconds) {
452 | if (FAST_FORMAT_MMSS.equals(format)) {
453 | StringBuilder sb = recycle;
454 | if (sb == null) {
455 | sb = new StringBuilder(8);
456 | } else {
457 | sb.setLength(0);
458 | }
459 | if (minutes < 10) {
460 | sb.append(TIME_PADDING);
461 | } else {
462 | sb.append(toDigitChar(minutes / 10));
463 | }
464 | sb.append(toDigitChar(minutes % 10));
465 | sb.append(TIME_SEPARATOR);
466 | if (seconds < 10) {
467 | sb.append(TIME_PADDING);
468 | } else {
469 | sb.append(toDigitChar(seconds / 10));
470 | }
471 | sb.append(toDigitChar(seconds % 10));
472 | return sb.toString();
473 | } else {
474 | return String.format(format, minutes, seconds);
475 | }
476 | }
477 |
478 | private static char toDigitChar(long digit) {
479 | return (char) (digit + '0');
480 | }
481 |
482 | }
--------------------------------------------------------------------------------
/app/src/main/java/nl/ttys0/simplec25k/ProgramService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProgramService.java
3 | *
4 | * Copyright 2012 Roel Blaauwgeers
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | *
19 | */
20 |
21 | package nl.ttys0.simplec25k;
22 |
23 | import android.app.AlarmManager;
24 | import android.app.Notification;
25 | import android.app.NotificationManager;
26 | import android.app.PendingIntent;
27 | import android.app.Service;
28 | import android.content.BroadcastReceiver;
29 | import android.content.Context;
30 | import android.content.Intent;
31 | import android.content.IntentFilter;
32 | import android.content.SharedPreferences;
33 | import android.media.MediaPlayer;
34 | import android.os.IBinder;
35 | import android.os.PowerManager;
36 | import android.os.PowerManager.WakeLock;
37 | import android.os.Vibrator;
38 | import android.preference.PreferenceManager;
39 | import android.support.v4.app.NotificationCompat;
40 | import android.util.Log;
41 |
42 | import java.util.Calendar;
43 |
44 | public class ProgramService extends Service {
45 | private static final String SCHEDULEFILE = "schedule";
46 | protected static final String MY_ACTION = "MY_ACTION";
47 |
48 | private String selectedProgram;
49 | private boolean running, paused, skipInterval;
50 |
51 | private Thread workoutThread;
52 | private WakeLock mWakeLock;
53 | private PendingIntent pi;
54 | private MyReceiver myReceiver;
55 | private AlarmManager alarmManager;
56 |
57 | private boolean mediaSoundBool;
58 | private boolean vibrateBool;
59 | private float mediaSoundVolume;
60 |
61 | private String lastTickerText;
62 |
63 | private int totalTimeLeft;
64 |
65 | @Override
66 | public IBinder onBind(Intent intent) {
67 | return null;
68 | }
69 |
70 | @Override
71 | public void onCreate() {
72 | alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
73 |
74 | // Register BroadcastReceiver
75 | // to receive event from our service
76 | myReceiver = new MyReceiver();
77 | IntentFilter intentFilter = new IntentFilter();
78 | intentFilter.addAction(MyAlarmService.MY_ACTION);
79 | registerReceiver(myReceiver, intentFilter);
80 |
81 | // Toast.makeText(this, "program Service Started", Toast.LENGTH_LONG)
82 | // .show();
83 | // Log.d(TAG, "onCreate");
84 |
85 | PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
86 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
87 | String wakelockType = prefs.getString("wakelock", "dim");
88 | Log.d("SimpleC25K", "Wakelock: " + wakelockType);
89 |
90 | if(wakelockType.equals("partial")){
91 | mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
92 | }
93 | else if(wakelockType.equals("full")){
94 | mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "TAG");
95 | }
96 | else{
97 | mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "TAG");
98 | }
99 | mWakeLock.acquire();
100 |
101 | }
102 |
103 | @Override
104 | public void onDestroy() {
105 | // Toast.makeText(this, "program Service destroyed", Toast.LENGTH_LONG)
106 | // .show();
107 | // Log.d(TAG, "onDestroy");
108 |
109 | mWakeLock.release();
110 |
111 | workoutThread.interrupt();// probably not even necessary
112 | unregisterReceiver(myReceiver);
113 | super.onDestroy();
114 | }
115 |
116 | @Override
117 | public void onStart(Intent intent, int startid) {
118 | // retrieve workout info
119 | selectedProgram = intent.getStringExtra("INIT_DATA");
120 |
121 | workoutThread = new Thread() {
122 | @Override
123 | public void run() {
124 |
125 | startWorkout();
126 | }
127 | };
128 | workoutThread.start();
129 |
130 | }
131 |
132 | // ============================================================
133 | // ROUTINES
134 | // ============================================================
135 | // awful bloody mess, in the future the information should be read from a
136 | // file
137 | private void startWorkout() {
138 |
139 | totalTimeLeft = 0;
140 |
141 | Intent myIntent = new Intent();
142 | myIntent.setAction(MY_ACTION);
143 | myIntent.putExtra("DATA_TO_TA", "STARTED");
144 | sendBroadcast(myIntent);
145 |
146 | running = true;
147 | paused = false;
148 | skipInterval = false;
149 |
150 | // w1d1, w1d2, w1d3 //total workout time: 25m
151 | if (selectedProgram.contains("w1")) {
152 | totalTimeLeft = 25 * 60 * 1000;
153 | // warmup 5m
154 | if (running)
155 | countdown(5 * 60, 500, "Warmup", "Warmup: Brisk walk for ");
156 |
157 | // 60+90=2.5minutes, so do this 8 times for a total of 20
158 | for (int i = 0; i < 8; i++) {
159 | // jogging 60s
160 | if (running)
161 | countdown(60, 500, "Jogging", "Jog for ");
162 | // walking 90s
163 | if (running)
164 | countdown(90, 500, "Walking", "Walk for ");
165 | }
166 | }
167 |
168 | // w2
169 | else if (selectedProgram.contains("w2")) {
170 | totalTimeLeft = 25 * 60 * 1000;
171 | // warmup 5m
172 | if (running)
173 | countdown(5 * 60, 500, "Warmup", "Warmup: Brisk walk for ");
174 |
175 | // 90+120=3.5minutes, so do this 5 times
176 | for (int i = 0; i < 5; i++) {
177 | // jogging 90s
178 | if (running)
179 | countdown(90, 500, "Jogging", "Jog for ");
180 | // walking 120s
181 | if (running)
182 | countdown(120, 500, "Walking", "Walk for ");
183 | }
184 | // more running 'cause 5*3.5=17.5
185 | if (running)
186 | countdown(90, 500, "Jogging", "Jog for ");
187 | // walk 60
188 | if (running)
189 | countdown(60, 500, "Walking", "Walk for ");
190 | }
191 |
192 | // w3
193 | else if (selectedProgram.contains("w3")) {
194 | totalTimeLeft = 1380 * 1000;
195 | // warmup 5m
196 | if (running)
197 | countdown(5 * 60, 500, "Warmup", "Warmup: Brisk walk for ");
198 |
199 | // 2 times
200 | for (int i = 0; i < 2; i++) {
201 | // jogging 90s
202 | if (running)
203 | countdown(90, 500, "Jogging", "Jog for ");
204 | // walking 90s
205 | if (running)
206 | countdown(90, 500, "Walking", "Walk for ");
207 | // jogging 3m
208 | if (running)
209 | countdown(180, 500, "Jogging", "Jog for ");
210 | // walking 3m
211 | if (running)
212 | countdown(180, 500, "Walking", "Walk for ");
213 | }
214 | }
215 |
216 | // w4
217 | else if (selectedProgram.contains("w4")) {
218 | totalTimeLeft = 1590 * 1000;
219 |
220 | // warmup 5m
221 | if (running)
222 | countdown(5 * 60, 500, "Warmup", "Warmup: Brisk walk for ");
223 |
224 | // jogging 3m
225 | if (running)
226 | countdown(180, 500, "Jogging", "Jog for ");
227 | // walking 90s
228 | if (running)
229 | countdown(90, 500, "Walking", "Walk for ");
230 | // jogging 5m
231 | if (running)
232 | countdown(300, 500, "Jogging", "Jog for ");
233 | // walking 2.5m
234 | if (running)
235 | countdown(150, 500, "Walking", "Walk for ");
236 | // jogging 3m
237 | if (running)
238 | countdown(180, 500, "Jogging", "Jog for ");
239 | // walking 90s
240 | if (running)
241 | countdown(90, 500, "Walking", "Walk for ");
242 | // jogging 5m
243 | if (running)
244 | countdown(300, 500, "Jogging", "Jog for ");
245 |
246 | }
247 |
248 | // w5
249 | else if (selectedProgram.contains("w5")) {
250 |
251 | if (selectedProgram.equals("w5d1")) {
252 | totalTimeLeft = 1560 * 1000;
253 | // warmup 5m
254 | if (running)
255 | countdown(5 * 60, 500, "Warmup", "Warmup: Brisk walk for ");
256 |
257 | // jogging 5m
258 | if (running)
259 | countdown(300, 500, "Jogging", "Jog for ");
260 | // walking 3m
261 | if (running)
262 | countdown(180, 500, "Walking", "Walk for ");
263 | // jogging 5m
264 | if (running)
265 | countdown(300, 500, "Jogging", "Jog for ");
266 | // walking 3m
267 | if (running)
268 | countdown(180, 500, "Walking", "Walk for ");
269 | // jogging 5m
270 | if (running)
271 | countdown(300, 500, "Jogging", "Jog for ");
272 |
273 | }
274 |
275 | if (selectedProgram.equals("w5d2")) {
276 | totalTimeLeft = 1560 * 1000;
277 | // warmup 5m
278 | if (running)
279 | countdown(5 * 60, 500, "Warmup", "Warmup: Brisk walk for ");
280 |
281 | // jogging 8m
282 | if (running)
283 | countdown(480, 500, "Jogging", "Jog for ");
284 | // walking 5m
285 | if (running)
286 | countdown(300, 500, "Walking", "Walk for ");
287 | // jogging 8m
288 | if (running)
289 | countdown(480, 500, "Jogging", "Jog for ");
290 | }
291 |
292 | if (selectedProgram.equals("w5d3")) {
293 | totalTimeLeft = (1500 * 1000);
294 | // warmup 5m
295 | if (running)
296 | countdown(5 * 60, 500, "Warmup", "Warmup: Brisk walk for ");
297 |
298 | // jogging 20m
299 | if (running)
300 | countdown(1200, 500, "Jogging", "Jog for ");
301 |
302 | }
303 | }
304 |
305 | // w6
306 | else if (selectedProgram.contains("w6")) {
307 |
308 | if (selectedProgram.equals("w6d1")) {
309 | totalTimeLeft = 1740 * 1000;
310 |
311 | // warmup 5m
312 | if (running)
313 | countdown(5 * 60, 500, "Warmup", "Warmup: Brisk walk for ");
314 |
315 | // jogging 5m
316 | if (running)
317 | countdown(300, 500, "Jogging", "Jog for ");
318 | // walking 3m
319 | if (running)
320 | countdown(180, 500, "Walking", "Walk for ");
321 | // jogging 8m
322 | if (running)
323 | countdown(480, 500, "Jogging", "Jog for ");
324 | // walking 3m
325 | if (running)
326 | countdown(180, 500, "Walking", "Walk for ");
327 | // jogging 5m
328 | if (running)
329 | countdown(300, 500, "Jogging", "Jog for ");
330 |
331 | } else if (selectedProgram.equals("w6d2")) {
332 | totalTimeLeft = 1680 * 1000;
333 | // warmup 5m
334 | if (running)
335 | countdown(5 * 60, 500, "Warmup", "Warmup: Brisk walk for ");
336 |
337 | // jogging 10m
338 | if (running)
339 | countdown(600, 500, "Jogging", "Jog for ");
340 | // walking 3m
341 | if (running)
342 | countdown(180, 500, "Walking", "Walk for ");
343 | // jogging 10m
344 | if (running)
345 | countdown(600, 500, "Jogging", "Jog for ");
346 |
347 | } else if (selectedProgram.equals("w6d3")) {
348 | totalTimeLeft = 1620 * 1000;
349 | // warmup 5m
350 | if (running)
351 | countdown(5 * 60, 500, "Warmup", "Warmup: Brisk walk for ");
352 |
353 | // jogging 22m
354 | if (running)
355 | countdown(1320, 500, "Jogging", "Jog for ");
356 |
357 | }
358 |
359 | }
360 |
361 | else if (selectedProgram.contains("w7")) {
362 | totalTimeLeft = 1800 * 1000;
363 | // warmup 5m
364 | if (running)
365 | countdown(5 * 60, 500, "Warmup", "Warmup: Brisk walk for ");
366 | // jogging 25m
367 | if (running)
368 | countdown(1500, 500, "Jogging", "Jog for ");
369 |
370 | }
371 |
372 | else if (selectedProgram.contains("w8")) {
373 | totalTimeLeft = 1980 * 1000;
374 | // warmup 5m
375 | if (running)
376 | countdown(5 * 60, 500, "Warmup", "Warmup: Brisk walk for ");
377 | // jogging 28m
378 | if (running)
379 | countdown(1680, 500, "Jogging", "Jog for ");
380 |
381 | }
382 |
383 | // w9d3
384 | else if (selectedProgram.contains("w9")) {
385 | totalTimeLeft = 2100 * 1000;
386 | // warmup 5m
387 | if (running)
388 | countdown(5 * 60, 500, "Warmup", "Warmup: Brisk walk for ");
389 | // jogging 30m
390 | if (running)
391 | countdown(1800, 500, "Jogging", "Jog for ");
392 | }
393 |
394 | workoutFinished();
395 | }
396 |
397 | // ============================================================
398 | // END OF ROUTINES
399 | // ============================================================
400 |
401 | private void workoutFinished() {
402 | WorkoutFileEditor wfe = new WorkoutFileEditor(this);
403 |
404 | // if running is still true, we can safely assume the workout is
405 | // completed. If running == false, the workout is interrupted by the
406 | // user and should not be marked 'done'
407 | if (running) {
408 |
409 | try {
410 | Thread.sleep(500);
411 | } catch (InterruptedException e) {
412 | e.printStackTrace();
413 | }
414 |
415 | //get preferences
416 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
417 |
418 | mediaSoundVolume = (float)(Integer.parseInt(prefs.getString("volume_percentage","40"))/100f);
419 | mediaSoundBool= prefs.getBoolean("enable_sound", true);
420 | MediaPlayer mp = MediaPlayer
421 | .create(ProgramService.this, R.raw.beep);
422 | if(mp!=null && mediaSoundBool){
423 | mp.setVolume(mediaSoundVolume, mediaSoundVolume);
424 | mp.start();
425 | }
426 |
427 | sendNotificationReal("Workout completed", "Workout completed.", true);
428 |
429 | // let the user know we're done
430 | // setup vibrator
431 | vibrateBool = prefs.getBoolean("enable_vibrations", true);
432 | if(vibrateBool){
433 | Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
434 | long[] pat = { 0, 700, 400 };
435 | v.vibrate(pat, -1);
436 | }
437 |
438 | // update file
439 | String s = wfe.ReadSettings(SCHEDULEFILE);
440 | s = s.replace(selectedProgram + ";false", selectedProgram + ";true");
441 |
442 | wfe.WriteSettings(SCHEDULEFILE, s, MODE_PRIVATE);
443 |
444 | // alert main to update its array
445 | Intent myIntent = new Intent();
446 | myIntent.setAction(MY_ACTION);
447 | myIntent.putExtra("DATA_TO_MAIN", "UPDATED");
448 | sendBroadcast(myIntent);
449 |
450 | // inform the timerActivity that the workout is done
451 | myIntent.setAction(MY_ACTION);
452 | myIntent.putExtra("DATA_TO_TA", "DONE");
453 | sendBroadcast(myIntent);
454 |
455 | }
456 | // stop this service
457 | this.stopSelf();
458 |
459 | }
460 |
461 | // counts down and sends message
462 | // 'time' in seconds, 'interval' in ms
463 | // make sure interval is never set on 0, this will
464 | // overload the statusbar
465 | // returns false if the user pressed stop
466 | private boolean countdown(int time, int interval, String workout,
467 | String message) {
468 | long syst = System.currentTimeMillis();
469 | long systFinished = syst + time * 1000;
470 | long msUntilFinished = +systFinished - syst;
471 | int secondsUntilFinished = (int) ((systFinished - syst) / 1000);
472 |
473 | int seconds = secondsUntilFinished % 60;
474 | int minutes = secondsUntilFinished / 60;
475 |
476 | // set alarm
477 | startTimer(time * 1000, 0, "");
478 |
479 | // Send data for the GUI
480 | Intent myIntent = new Intent();
481 | myIntent.setAction(MY_ACTION);
482 | myIntent.putExtra("DATA_TO_TA", "SET_CURRENT;" + workout + ";"
483 | + (time * 1000) + ";" + totalTimeLeft);
484 | sendBroadcast(myIntent);
485 |
486 | // dialog.cancel();
487 |
488 | // updateTotalCountdown(totalTimeLeft);
489 |
490 | // send initial notification
491 | sendNotification(workout, message + String.format("%02d", minutes)
492 | + ":" + String.format("%02d", seconds));
493 |
494 | while (systFinished - syst > 0) {
495 | if ((!paused) && (running)) {
496 | try {
497 |
498 | // get current systemtime
499 | syst = System.currentTimeMillis();
500 |
501 | // how many ms are left
502 | msUntilFinished = systFinished - syst;
503 |
504 | // how many seconds are left
505 | secondsUntilFinished = (int) (msUntilFinished / 1000);
506 |
507 | // parse to readable format (mm:ss)
508 | seconds = secondsUntilFinished % 60;
509 | minutes = secondsUntilFinished / 60;
510 |
511 | // if (secondsUntilFinished%5==0){//take it easy on the
512 | // notificationbar
513 |
514 | sendNotification(workout,
515 | message + String.format("%02d", minutes) + ":"
516 | + String.format("%02d", seconds));
517 | // }
518 |
519 | Thread.sleep(interval);
520 | } catch (InterruptedException e) {
521 | // TODO Auto-generated catch block
522 | e.printStackTrace();
523 | }
524 |
525 | } else if (paused) {
526 |
527 | // cancel scheduled alarm
528 | stopTimer(pi);
529 |
530 | sendNotification("Paused", message + "-Paused-");
531 |
532 | // myIntent.putExtra("DATA_TO_TA", "SET_CURRENT;" + workout +
533 | // ";" + (msUntilFinished));
534 |
535 | // wait for resume
536 | while (paused) {
537 | try {
538 | // Get out of here if the user requests a STOP
539 | if (!running) {
540 | break;
541 | }
542 |
543 | Thread.sleep(500);
544 | } catch (InterruptedException e) {
545 | // TODO Auto-generated catch block
546 | e.printStackTrace();
547 | }
548 | }
549 |
550 | // reset gui elements
551 | // Send data for the GUI
552 | myIntent.setAction(MY_ACTION);
553 | myIntent.putExtra("DATA_TO_TA", "SET_CURRENT;" + workout + ";"
554 | + (msUntilFinished) + ";"
555 | + (totalTimeLeft - (time * 1000 - msUntilFinished)));
556 | sendBroadcast(myIntent);
557 |
558 | // set alarm again with the last known resulting time
559 | startTimer((int) msUntilFinished, 0, "");
560 |
561 | // update systFinished
562 | systFinished = System.currentTimeMillis() + msUntilFinished;
563 |
564 | }
565 |
566 | if ((skipInterval) && (running) && (!paused)) {
567 | // Disable skipping
568 | skipInterval = false;
569 |
570 | // cancel scheduled alarm
571 | stopTimer(pi);
572 |
573 | totalTimeLeft -= time * 1000;
574 | // return to startworkout for next interval
575 | return false;
576 |
577 | }
578 |
579 | if (!running) {
580 | // cancel scheduled alarm
581 | stopTimer(pi);
582 |
583 | totalTimeLeft -= time * 1000;
584 |
585 | // make sure the thread is stopped before killing this service
586 | // systFinished = 0;
587 | return false;
588 |
589 | }
590 |
591 | }
592 | totalTimeLeft -= time * 1000;
593 | return true;
594 | }
595 |
596 | // start a timer with time in mili seconds
597 | public void startTimer(int time, int id, String message) {
598 |
599 | Intent mi = new Intent(ProgramService.this, MyAlarmService.class);
600 | mi.putExtra("MESSAGE", message);
601 |
602 | Calendar calendar = Calendar.getInstance();
603 | calendar.setTimeInMillis(System.currentTimeMillis());
604 | calendar.add(Calendar.MILLISECOND, time);
605 |
606 | pi = PendingIntent.getService(ProgramService.this, id, mi, 0);
607 |
608 | alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
609 | pi);
610 |
611 | // Toast.makeText(ProgramService.this, "Start Alarm", Toast.LENGTH_LONG)
612 | // .show();
613 |
614 | }
615 |
616 | public void stopTimer(PendingIntent timerpi) {
617 | pi.cancel();
618 |
619 | }
620 |
621 | // ===========================================================
622 | // Helper functions
623 | // ===========================================================
624 | // Get a place in the notification bar and send a string
625 | // For sending 'real' notifications (those which can be 'cleaned')
626 | // from the bar, I'd suggest using 'sendNotificationReal()'
627 | public void sendNotification(String tickerText, String contentText) {
628 | //int icon = nl.ttys0.simplec25k.R.drawable.nbarlogo;//runner
629 | int icon = nl.ttys0.simplec25k.R.drawable.ic_launcher;
630 |
631 | CharSequence contentTitle = "Simple C25K";
632 |
633 | // define the actions to perform when user touch the notification
634 | Intent launchApp = new Intent(this, TimerActivity.class);
635 | // launchApp.putExtra("com.xxxxxxx.xxxxxxxxx.bean.Item",
636 | // "anyObjectYouWant");
637 |
638 | launchApp.setAction("VIEW_DETAILS_PROPERTY");
639 | PendingIntent launchNotification = PendingIntent.getActivity(
640 | getApplicationContext(), 0, launchApp, 0);
641 |
642 | NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
643 | builder.setSmallIcon(icon);
644 | builder.setTicker(tickerText);
645 | builder.setWhen(System.currentTimeMillis());
646 | builder.setContentTitle(contentTitle);
647 | builder.setContentText(contentText);
648 | builder.setContentIntent(launchNotification);
649 | final Notification notification = builder.build();
650 |
651 | boolean vibrate = false;
652 | if (! tickerText.equals(lastTickerText)) {
653 | vibrate = true;
654 | lastTickerText = tickerText;
655 | }
656 | sendNotificationReal(tickerText, contentText, vibrate);
657 |
658 | startForeground(1337, notification);
659 | }
660 |
661 | // This method can be used to send a 'real' notification. This notification
662 | // can be removed from the bar by the user by clicking them
663 | // Additionally these notifications are shown on connected Wear-devices.
664 | // There's a hardcoded id in here, so the can only be only one notification
665 | // at the time.
666 | public void sendNotificationReal(String tickerText, String contentText, boolean vibrate) {
667 |
668 | // Get a reference to the NotificationManager:
669 | String ns = Context.NOTIFICATION_SERVICE;
670 | NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
671 |
672 | // Instantiate the Notification:
673 | int icon = nl.ttys0.simplec25k.R.drawable.ic_launcher;
674 | long when = System.currentTimeMillis();
675 |
676 |
677 | // Define the notification's message and PendingIntent:
678 | Context context = getApplicationContext();
679 | CharSequence contentTitle = "Simple C25K";
680 | Intent notificationIntent = new Intent(this, TimerActivity.class);
681 | PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
682 | notificationIntent, 0);
683 |
684 | NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
685 | builder.setSmallIcon(icon);
686 | builder.setTicker(tickerText);
687 | builder.setWhen(System.currentTimeMillis());
688 | builder.setContentTitle(contentTitle);
689 | builder.setContentText(contentText);
690 | builder.setContentIntent(contentIntent);
691 | if (vibrate) {
692 | builder.setVibrate(new long[]{0, 1000});
693 | Log.e("VibrationMotor", "sendNotificationReal: VIBRATING");
694 | }
695 | final Notification notification = builder.build();
696 |
697 | // Pass the Notification to the NotificationManager:
698 | final int HELLO_ID = 1;
699 | mNotificationManager.notify(HELLO_ID, notification);
700 |
701 | if (vibrate) {
702 | try {
703 | // pause so that vibration is not interrupted by next notification
704 | Thread.sleep(1000);
705 | } catch (InterruptedException e) {
706 | e.printStackTrace();
707 | }
708 | }
709 |
710 | }
711 |
712 | // class for receiving broadcasts. In this case it's being used to receive
713 | // commands from the TimerActivity
714 | private class MyReceiver extends BroadcastReceiver {
715 |
716 | @Override
717 | public void onReceive(Context arg0, Intent arg1) {
718 |
719 | String orgData = arg1.getStringExtra("DATA_TO_PS");
720 |
721 | if (orgData != null) {
722 | if (orgData.equals("PAUSE"))
723 | paused = true;
724 | if (orgData.equals("RESUME"))
725 | paused = false;
726 | if (orgData.equals("STOP"))
727 | running = false;
728 | if (orgData.equals("SKIP"))
729 | skipInterval = true;
730 | }
731 |
732 | }
733 |
734 | }
735 |
736 | }
737 |
--------------------------------------------------------------------------------