├── .gitignore
├── AndroidManifest.xml
├── README
├── photoshop
├── TeaNotificationIcon.psd
├── tea.psd
└── tea2.psd
├── res
├── anim
│ ├── fade_in.xml
│ └── fade_out.xml
├── color
│ └── gallery_item_color.xml
├── drawable-hdpi
│ ├── icon.png
│ ├── timepicker_down_disabled.9.png
│ ├── timepicker_down_disabled_focused.9.png
│ ├── timepicker_down_normal.9.png
│ ├── timepicker_down_pressed.9.png
│ ├── timepicker_down_selected.9.png
│ ├── timepicker_input_disabled.9.png
│ ├── timepicker_input_normal.9.png
│ ├── timepicker_input_pressed.9.png
│ ├── timepicker_input_selected.9.png
│ ├── timepicker_up_disabled.9.png
│ ├── timepicker_up_disabled_focused.9.png
│ ├── timepicker_up_normal.9.png
│ ├── timepicker_up_pressed.9.png
│ └── timepicker_up_selected.9.png
├── drawable-mdpi
│ ├── timepicker_down_disabled.9.png
│ ├── timepicker_down_disabled_focused.9.png
│ ├── timepicker_down_normal.9.png
│ ├── timepicker_down_pressed.9.png
│ ├── timepicker_down_selected.9.png
│ ├── timepicker_input_disabled.9.png
│ ├── timepicker_input_normal.9.png
│ ├── timepicker_input_pressed.9.png
│ ├── timepicker_input_selected.9.png
│ ├── timepicker_up_disabled.9.png
│ ├── timepicker_up_disabled_focused.9.png
│ ├── timepicker_up_normal.9.png
│ ├── timepicker_up_pressed.9.png
│ └── timepicker_up_selected.9.png
├── drawable
│ ├── border.xml
│ ├── cancel.png
│ ├── cup.png
│ ├── icon.png
│ ├── notification.png
│ ├── pause.png
│ ├── play.png
│ ├── set.png
│ ├── teapot.png
│ ├── timepicker_down_btn.xml
│ ├── timepicker_input.xml
│ └── timepicker_up_btn.xml
├── layout-land
│ └── main.xml
├── layout
│ ├── about.xml
│ ├── gallery_item.xml
│ ├── main.xml
│ ├── n_number_picker_dialog.xml
│ ├── number_picker.xml
│ └── number_picker_dialog.xml
├── raw
│ ├── bell.ogg
│ └── big_ben.ogg
├── values-de
│ └── strings.xml
├── values-fr
│ └── strings.xml
├── values
│ ├── arrays.xml
│ ├── colors.xml
│ └── strings.xml
└── xml
│ └── preferences.xml
├── src
└── goo
│ └── TeaTimer
│ ├── Animation
│ ├── CircleAnimation.java
│ ├── Teapot.java
│ ├── TimerAnimation.java
│ ├── TrashCupAnimation.java
│ └── TresBarAnimation.java
│ ├── TimerActivity.java
│ ├── TimerPrefActivity.java
│ ├── TimerReceiver.java
│ ├── TimerUtils.java
│ └── widget
│ └── NNumberPickerDialog.java
└── use_cases.text
/.gitignore:
--------------------------------------------------------------------------------
1 | out/*
2 | bin/*
3 | gen/*
4 | .*
5 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 |
2 | TeaTimer BETA
3 | -------------------------------------------------
4 | Author: Ralph Gootee
5 |
6 | This software is free and open source, under the GPL v3 license, for more info please visit: http://www.gnu.org/copyleft/gpl.html
7 |
8 | Changelog
9 | -------------------------------------------------
10 |
11 | in version 1.3:
12 | - Pause Button
13 | - Fixed a bug with the number picker dialog
14 |
15 | in version 1.2:
16 | - Added seconds
17 |
18 | in version 1.1:
19 | - Added new timer animation
20 | - Removed package size
21 |
22 | Credits
23 | -------------------------------------------------
24 | Cool tea icon from:
25 | kidcomic.net
26 |
27 | Sound effect from:
28 | freesound.org user reinsamba
29 |
30 | Stole the number picker dialog from astrid for android,
31 | the best task management software I've ever used.
32 |
--------------------------------------------------------------------------------
/photoshop/TeaNotificationIcon.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/photoshop/TeaNotificationIcon.psd
--------------------------------------------------------------------------------
/photoshop/tea.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/photoshop/tea.psd
--------------------------------------------------------------------------------
/photoshop/tea2.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/photoshop/tea2.psd
--------------------------------------------------------------------------------
/res/anim/fade_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/anim/fade_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/color/gallery_item_color.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
7 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_down_disabled.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_down_disabled.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_down_disabled_focused.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_down_disabled_focused.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_down_normal.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_down_normal.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_down_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_down_pressed.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_down_selected.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_down_selected.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_input_disabled.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_input_disabled.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_input_normal.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_input_normal.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_input_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_input_pressed.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_input_selected.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_input_selected.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_up_disabled.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_up_disabled.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_up_disabled_focused.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_up_disabled_focused.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_up_normal.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_up_normal.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_up_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_up_pressed.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/timepicker_up_selected.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-hdpi/timepicker_up_selected.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_down_disabled.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_down_disabled.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_down_disabled_focused.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_down_disabled_focused.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_down_normal.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_down_normal.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_down_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_down_pressed.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_down_selected.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_down_selected.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_input_disabled.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_input_disabled.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_input_normal.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_input_normal.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_input_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_input_pressed.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_input_selected.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_input_selected.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_up_disabled.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_up_disabled.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_up_disabled_focused.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_up_disabled_focused.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_up_normal.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_up_normal.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_up_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_up_pressed.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/timepicker_up_selected.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable-mdpi/timepicker_up_selected.9.png
--------------------------------------------------------------------------------
/res/drawable/border.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/res/drawable/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable/cancel.png
--------------------------------------------------------------------------------
/res/drawable/cup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable/cup.png
--------------------------------------------------------------------------------
/res/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable/icon.png
--------------------------------------------------------------------------------
/res/drawable/notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable/notification.png
--------------------------------------------------------------------------------
/res/drawable/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable/pause.png
--------------------------------------------------------------------------------
/res/drawable/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable/play.png
--------------------------------------------------------------------------------
/res/drawable/set.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable/set.png
--------------------------------------------------------------------------------
/res/drawable/teapot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/drawable/teapot.png
--------------------------------------------------------------------------------
/res/drawable/timepicker_down_btn.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
21 |
23 |
25 |
27 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/res/drawable/timepicker_input.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
21 |
23 |
25 |
27 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/res/drawable/timepicker_up_btn.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
21 |
23 |
25 |
27 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/res/layout-land/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
20 |
21 |
28 |
29 |
35 |
36 |
44 |
45 |
53 |
54 |
63 |
64 |
65 |
66 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/res/layout/about.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
15 |
16 |
20 |
21 |
25 |
30 |
34 |
39 |
43 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/res/layout/gallery_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
18 |
19 |
26 |
27 |
33 |
34 |
42 |
43 |
51 |
52 |
60 |
61 |
62 |
63 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/res/layout/n_number_picker_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
25 |
32 |
39 |
44 |
51 |
57 |
65 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/res/layout/number_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
26 |
27 |
36 |
37 |
42 |
43 |
--------------------------------------------------------------------------------
/res/layout/number_picker_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
23 |
26 |
--------------------------------------------------------------------------------
/res/raw/bell.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/raw/bell.ogg
--------------------------------------------------------------------------------
/res/raw/big_ben.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralphleon/TeaTimer/63dc4a82bce3e4a2f6271241764c1749f72bcdeb/res/raw/big_ben.ogg
--------------------------------------------------------------------------------
/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Tea Timer
4 | Timer setzen
5 | Zurücksetzen
6 |
7 | Timer abgelaufen!
8 | Stunde:Min:Sek
9 |
10 | Das Telefon ist im lautlosen Modus! Vibration und Töne werden unterdrückt!
11 | Warnung
12 | Ok
13 |
14 | "Timer für "
15 |
16 | Einstellungen
17 |
18 |
19 | Benachrichtigung
20 | Benachrichtigungston
21 | Der Ton der Benachrichtigung.
22 |
23 | Vibration bei Benachrichtigung
24 | Soll der der Timer vibrieren?
25 |
26 | LED Benachrichtigung
27 | Lässt die LED während der Benachrichtigung blinken.
28 |
29 | Erscheinungsbild
30 |
31 | Farben-Theme
32 | Wähle das Theme für die Anzeige.
33 |
34 | Sonstige
35 |
36 | Nerfender Dialog
37 | Zeige einen nerfenden Dialog wenn lautlos.
38 |
39 | Bildschirm anlassen
40 | Das Telefon wird den Bildschrim nicht ausschalten.
41 |
42 |
43 | Schließen
44 | Info
45 | Tea Timer auf Github
46 | Über Tea Timer
47 | TeaTimer hilft dir deinen Tee richtig zu kochen.\n\nTea Timer ist freie Software (nicht im Sinne von Kostenlos) und du kannst sie auch für die Spaghetti nutzen.
48 | TeaTimer ist freie Software: Du kannst es unter den Bedingungen der GNU General Public License der Version 3 verbreiten, verändern etc.
49 | http://www.gnu.org/licenses/gpl.html
50 | Auf Github gibt es die aktuelle Version des Quellcodes.
51 | https://github.com/ralphleon/TeaTimer
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/res/values-fr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Tea Timer
4 | Démarrer
5 | Effacer
6 |
7 | C\'est l\'heure du thé!
8 | Heure:Min:Sec
9 |
10 | Le téléphone est en mode silencieux. Vibration et son seront supprimés.
11 | Attention
12 | Ok
13 |
14 | "Timer pour "
15 |
16 | Préférences
17 |
18 |
19 | Notification
20 | Avertissement sonore
21 | Effet sonore de la notification.
22 |
23 | Notification vibrante
24 | Faire vibrer le téléphone pour la notification.
25 |
26 | Notification LED
27 | Faire clignoter les LED pour la notification.
28 |
29 | Visuel
30 |
31 | Thème
32 | Sélectionner le thème visuel
33 |
34 | Divers
35 |
36 | Message
37 | Afficher un message lorsque le téléphone est en mode silencieux.
38 |
39 | Rester éveillé
40 | Empêcher le téléphone de se mettre en veille.
41 |
42 |
43 | Fermer
44 | À propos
45 | Tea Timer sur Github
46 | À propos de Tea Timer
47 | Tea timer vous aidera à avoir un thé prêt à l\'heure, ni trop tôt, ni trop tard.\n\nTea Timer est un logiciel libre, vous pouvez aussi l\'utiliser pour vos spaghettis.
48 | TeaTimer est un logiciel libre : vous pouvez le redistribuer ou le modifier selon les termes de la Licence Publique Générale GNU, telle que publiée par la Free Software Foundation, soit dans sa version3, ou dans n\'importe quelle version plus récente.
49 | http://www.gnu.org/licenses/gpl.html
50 | La dernière version du code source est disponible sur Github.
51 | https://github.com/ralphleon/TeaTimer
52 |
53 |
--------------------------------------------------------------------------------
/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - Original Green
5 | - Bright Yellow
6 | - Sky
7 |
8 |
9 |
10 | - "0"
11 | - "1"
12 | - "2"
13 |
14 |
15 |
--------------------------------------------------------------------------------
/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #c9ea91
4 | #101010
5 | #3C3C3C
6 | #5A5A5A
7 |
8 |
9 | #3C3C3C
10 | #5A5A5A
11 | #c9ea91
12 | #7A7A7A
13 | #5A5A5A
14 |
15 |
16 | #1E181E
17 | #303730
18 | #C3992E
19 | #E6C346
20 | #FFE978
21 |
22 |
23 | #3F3139
24 | #6A737D
25 | #9FC5C1
26 | #C1D4C8
27 | #DEDAB8
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Tea Timer
4 | Set Timer
5 | Clear
6 |
7 | Tea time!
8 | Hour:Min:Sec
9 |
10 | The phone is in silent mode! Notification vibration and sound will be supressed.
11 | Warning
12 | Ok
13 |
14 | "Timer for "
15 |
16 | Preferences
17 |
18 |
19 | Notification
20 | Notification Diddy
21 | The notification\'s sound effects.
22 |
23 | Notification Vibrate
24 | Should the timer notification Vibrate.
25 |
26 | LED Notification
27 | Blink the LED lights during the notification.
28 |
29 | Clear automatically
30 | Clear notification automatically after a few seconds.
31 |
32 | Visual
33 |
34 | Color Theme
35 | Select the theme for the visualization
36 |
37 | Misc
38 |
39 | Nag Dialog
40 | When the phone is set to silent show a nag dialog.
41 |
42 | Stay awake
43 | Don\'t allow the phone to go to sleep.
44 |
45 | Repeat rate
46 | Button repeats per second on long press in the number picker.
47 |
48 |
49 | Close
50 | About
51 | Tea Timer on Github
52 | About Tea Timer
53 | TeaTimer will help you make sure your tea is there on time, not too early, not too late.\n\nTea Timer is a free software (as in free speech, not as in free beer), you can also use it for your spaghetti.
54 | TeaTimer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.
55 | http://www.gnu.org/licenses/gpl.html
56 | Get the latest version of the source code on Github.
57 | https://github.com/ralphleon/TeaTimer
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/res/xml/preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
13 |
14 |
19 |
20 |
25 |
26 |
31 |
32 |
33 |
34 |
35 |
36 |
42 |
43 |
44 |
45 |
46 |
47 |
52 |
57 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/src/goo/TeaTimer/Animation/CircleAnimation.java:
--------------------------------------------------------------------------------
1 | package goo.TeaTimer.Animation;
2 | import goo.TeaTimer.R;
3 | import goo.TeaTimer.TimerUtils;
4 | import android.content.Context;
5 | import android.content.SharedPreferences;
6 | import android.content.res.Resources;
7 | import android.graphics.Canvas;
8 | import android.graphics.Color;
9 | import android.graphics.Paint;
10 | import android.graphics.PorterDuff;
11 | import android.graphics.PorterDuffXfermode;
12 | import android.graphics.RadialGradient;
13 | import android.graphics.RectF;
14 | import android.graphics.Shader;
15 | import android.preference.PreferenceManager;
16 |
17 | class CircleAnimation implements TimerAnimation.TimerDrawing
18 | {
19 | private static final float MAX_SIZE = 100;
20 |
21 | private final int START_ANGLE = 270;
22 |
23 | private float mRadius = 75,mInnerRadius=30,mSecondRadius=90,mMsRadius=mSecondRadius+5;
24 | private float scale;
25 |
26 | private Paint mCirclePaint,mInnerPaint,mArcPaint,mMsPaint,mTickerPaint;
27 |
28 | RadialGradient mInnerGradient;
29 |
30 | /** Paint for the seconds arc */
31 | private Paint mSecondPaint, mSecondBgPaint;
32 |
33 | int mInnerColor = 0;
34 |
35 | private boolean showMs = false;
36 | boolean mMsFlipper = false;
37 | private int [] mLastTime;
38 |
39 | /** Rects for the arcs */
40 | private RectF mArcRect,mSecondRect,mMsRect;
41 |
42 | private Context mContext;
43 |
44 | private int mWidth;
45 |
46 | private int mHeight;
47 |
48 | private float mSecondGap;
49 |
50 | private float mMsGap;
51 |
52 | public CircleAnimation(Context context)
53 | {
54 | mContext = context;
55 |
56 | // Create the rects
57 | mSecondRect = new RectF();
58 | mArcRect = new RectF();
59 | mMsRect = new RectF();
60 |
61 | configure();
62 | }
63 |
64 | public void configure()
65 | {
66 | Resources resources = mContext.getResources();
67 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
68 |
69 | // LOAD The correct theme
70 | int theme = Integer.parseInt(prefs.getString("Theme", "0"));
71 |
72 | int [] colors;
73 |
74 | switch(theme){
75 | case 0:
76 | colors = new int[] { resources.getColor(R.color.themeA1),
77 | resources.getColor(R.color.themeA2),
78 | resources.getColor(R.color.themeA3),
79 | resources.getColor(R.color.themeA4),
80 | resources.getColor(R.color.themeA5)
81 | };
82 | break;
83 |
84 | case 1:
85 | colors = new int[] { resources.getColor(R.color.themeB1),
86 | resources.getColor(R.color.themeB2),
87 | resources.getColor(R.color.themeB3),
88 | resources.getColor(R.color.themeB4),
89 | resources.getColor(R.color.themeB5)
90 | };
91 | break;
92 |
93 | case 2:
94 | default:
95 | colors = new int[] { resources.getColor(R.color.themeC1),
96 | resources.getColor(R.color.themeC2),
97 | resources.getColor(R.color.themeC3),
98 | resources.getColor(R.color.themeC4),
99 | resources.getColor(R.color.themeC5)
100 | };
101 | break;
102 |
103 | }
104 |
105 | mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
106 | mCirclePaint.setColor(colors[0]);
107 |
108 | mInnerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
109 | mInnerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
110 |
111 | // Paint for the seconds line
112 | mSecondPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
113 | mSecondPaint.setColor(colors[3]);
114 |
115 | mSecondBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
116 | mSecondBgPaint.setColor(colors[1]);
117 |
118 | // Paint for the miliseconds
119 | mMsPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
120 | mMsPaint.setColor(colors[4]);
121 |
122 | mInnerColor = colors[2];
123 |
124 | mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
125 | mArcPaint.setStyle(Paint.Style.FILL);
126 |
127 | mTickerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
128 | mTickerPaint.setColor(0xFFFFFFFF);
129 |
130 | scale = resources.getDisplayMetrics().density;
131 |
132 | if(mWidth != 0 && mHeight != 0) sizeChange(mWidth,mHeight);
133 | }
134 |
135 | public void sizeChange(int w, int h){
136 |
137 | mWidth = w;
138 | mHeight = h;
139 |
140 | mMsRadius = Math.min(Math.min(w/2.0f,h/2.0f),MAX_SIZE*scale);
141 | mMsGap = mMsRadius * .95f;
142 | mSecondRadius = mMsRadius * .97f;
143 | mSecondGap = mMsRadius *.85f;
144 | mRadius = mMsRadius * .85f;
145 | mInnerRadius=30*scale;
146 |
147 | int offset = 75;
148 |
149 | int r = Color.red(mInnerColor) - offset;
150 | int g = Color.green(mInnerColor) - offset;
151 | int b = Color.blue(mInnerColor) - offset;
152 |
153 | int start = Color.rgb(r, g, b);
154 |
155 | Shader shader = new RadialGradient(0, 0, mRadius,
156 | start,
157 | mInnerColor,
158 | Shader.TileMode.CLAMP);
159 | mArcPaint.setShader(shader);
160 | }
161 |
162 | /**
163 | * Updates the image to be in sync with the current time
164 | * @param time in milliseconds
165 | * @param max the original time set in milliseconds
166 | */
167 | public void updateImage(Canvas canvas, int time, int max) {
168 |
169 | canvas.save();
170 |
171 | float p = (max == 0) ? 0 : (time/(float)max);
172 | int [] timeVec = TimerUtils.time2Mhs(time);
173 | if(mLastTime == null) mLastTime = timeVec;
174 | if(mLastTime[2] != timeVec[2]) mMsFlipper = !mMsFlipper;
175 |
176 | float pSecond = (max == 0) ? 1 : (timeVec[2] + timeVec[3]/1000.0f )/60.0f;
177 | float thetaSecond = pSecond*360;
178 |
179 |
180 | if(mWidth != canvas.getClipBounds().width() || mHeight != canvas.getClipBounds().height())
181 | sizeChange(canvas.getClipBounds().width(),canvas.getClipBounds().height());
182 |
183 | canvas.translate(mWidth/2.0f, mHeight/2.0f);
184 |
185 | mSecondRect.set(-mSecondRadius, -mSecondRadius, mSecondRadius, mSecondRadius);
186 | mArcRect.set(-mRadius, -mRadius, mRadius, mRadius);
187 |
188 | // Ms Arc
189 | if(showMs){
190 | float pMs = (float)((timeVec[3]/1000.00));
191 | float thetaMs = pMs*360;
192 |
193 | mMsRect.set(-mMsRadius, -mMsRadius, mMsRadius, mMsRadius);
194 | canvas.drawCircle(0,0,mMsRadius, (mMsFlipper) ? mCirclePaint : mMsPaint );
195 | canvas.drawArc(mMsRect, START_ANGLE, thetaMs, true, (mMsFlipper) ? mMsPaint: mCirclePaint);
196 | }
197 | // We want to draw a very thin border
198 | else{
199 | canvas.drawCircle(0,0,mMsRadius, mMsPaint );
200 | }
201 |
202 | // Gap between the ms and seconds
203 | canvas.drawCircle(0,0,mMsGap,mInnerPaint);
204 |
205 | //Second arc
206 | canvas.drawCircle(0,0,mSecondRadius,mSecondBgPaint);
207 | canvas.drawArc(mSecondRect, START_ANGLE, thetaSecond, true, mSecondPaint);
208 |
209 | // Gap between the seconds and the inner radius
210 | canvas.drawCircle(0,0,mSecondGap,mInnerPaint);
211 |
212 | // Background fill
213 | canvas.drawCircle(0,0,mRadius,mCirclePaint);
214 |
215 | // Main arc
216 | canvas.drawArc(mArcRect,START_ANGLE,360*(1-p),true,mArcPaint);
217 |
218 | // Inner paint
219 | canvas.drawCircle(0,0,mInnerRadius,mInnerPaint);
220 |
221 | mLastTime = timeVec;
222 |
223 | canvas.restore();
224 | }
225 | }
--------------------------------------------------------------------------------
/src/goo/TeaTimer/Animation/Teapot.java:
--------------------------------------------------------------------------------
1 | package goo.TeaTimer.Animation;
2 |
3 | import goo.TeaTimer.R;
4 | import android.content.Context;
5 | import android.content.res.Resources;
6 | import android.graphics.Bitmap;
7 | import android.graphics.BitmapFactory;
8 | import android.graphics.Canvas;
9 | import android.graphics.Color;
10 | import android.graphics.Paint;
11 | import android.graphics.PorterDuffXfermode;
12 | import android.graphics.RectF;
13 | import android.graphics.PorterDuff.Mode;
14 |
15 | class Teapot implements TimerAnimation.TimerDrawing
16 | {
17 | private Bitmap mCupBitmap;
18 |
19 | private int mWidth = 0;
20 | private int mHeight = 0;
21 |
22 | private Paint mProgressPaint = null;
23 |
24 | private Bitmap mBitmap = null;
25 |
26 | public Teapot(Context context)
27 | {
28 | Resources resources = context.getResources();
29 |
30 | mProgressPaint = new Paint();
31 | mProgressPaint.setColor(Color.GRAY);
32 | mProgressPaint.setAlpha(135);
33 | mProgressPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
34 |
35 | mCupBitmap = BitmapFactory.decodeResource(resources, R.drawable.teapot);
36 | mHeight = mCupBitmap.getHeight();
37 | mWidth = mCupBitmap.getWidth();
38 |
39 | mBitmap = Bitmap.createBitmap(mWidth,mHeight,Bitmap.Config.ARGB_8888);
40 | }
41 |
42 | /**
43 | * Updates the image to be in sync with the current time
44 | * @param time in milliseconds
45 | * @param max the original time set in milliseconds
46 | */
47 | public void updateImage(Canvas canvas, int time, int max) {
48 |
49 | canvas.save();
50 | float w = canvas.getClipBounds().width();
51 | float h = canvas.getClipBounds().height();
52 |
53 | canvas.translate(w/2.0f - mWidth/2.0f,
54 | h/2.0f - mHeight/2.0f);
55 |
56 | canvas.drawBitmap(mCupBitmap, 0, 0,null);
57 |
58 | float p = (max != 0) ? (time/(float)max) : 0;
59 |
60 | if(p == 0) p = 1;
61 |
62 | RectF fill = new RectF(0,mHeight*(p),mWidth,mHeight);
63 | canvas.drawRect(fill,mProgressPaint);
64 |
65 | canvas.restore();
66 | }
67 |
68 | public void configure() {
69 | // TODO Auto-generated method stub
70 |
71 | }
72 | }
--------------------------------------------------------------------------------
/src/goo/TeaTimer/Animation/TimerAnimation.java:
--------------------------------------------------------------------------------
1 | package goo.TeaTimer.Animation;
2 |
3 | import goo.TeaTimer.R;
4 |
5 | import java.util.Vector;
6 |
7 | import android.content.Context;
8 | import android.content.SharedPreferences;
9 | import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
10 | import android.graphics.Bitmap;
11 | import android.graphics.Canvas;
12 | import android.preference.PreferenceManager;
13 | import android.util.AttributeSet;
14 | import android.view.View;
15 | import android.view.View.OnClickListener;
16 | import android.view.animation.AnimationUtils;
17 |
18 | public class TimerAnimation extends View implements OnClickListener, OnSharedPreferenceChangeListener
19 | {
20 | Vector mDrawings = null;
21 | int mIndex = 0;
22 | int mLastTime =0,mLastMax=0;
23 |
24 | Bitmap mBitmap = null;
25 |
26 | Context mContext;
27 |
28 | public interface TimerDrawing{
29 |
30 | /**
31 | * Updates the image to be in sync with the current time
32 | * @param time in milliseconds
33 | * @param max the original time set in milliseconds
34 | */
35 | public void updateImage( Canvas canvas, int time,int max);
36 |
37 | public void configure();
38 | }
39 |
40 | public TimerAnimation(Context context, AttributeSet attrs){
41 |
42 | super(context, attrs);
43 | mContext = context;
44 |
45 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
46 | prefs.registerOnSharedPreferenceChangeListener(this);
47 |
48 | mDrawings = new Vector();
49 | mDrawings.add(new CircleAnimation(context));
50 | //mDrawings.add(new TrashCupAnimation(context));
51 | mDrawings.add(new Teapot(context));
52 |
53 | setOnClickListener(this);
54 | }
55 |
56 | /**
57 | * TODO eventually we'll want to move this index into the preferences
58 | * @param i
59 | */
60 | public void setIndex(int i){
61 | if(i < 0 || i >= mDrawings.size()) i = 0;
62 | mIndex = i;
63 | invalidate();
64 | }
65 |
66 | public int getIndex(){ return mIndex;}
67 |
68 | public void updateImage(int time,int max){
69 | mLastTime = time;
70 | mLastMax = max;
71 |
72 | invalidate();
73 | }
74 |
75 | @Override
76 | public void onDraw(Canvas canvas){
77 | mDrawings.get(mIndex).updateImage(canvas, mLastTime, mLastMax);
78 | }
79 |
80 | public void onClick(View v){
81 |
82 | startAnimation(AnimationUtils.loadAnimation(mContext, R.anim.fade_out));
83 |
84 | mIndex++;
85 | mIndex %= mDrawings.size();
86 |
87 | startAnimation(AnimationUtils.loadAnimation(mContext, R.anim.fade_in));
88 |
89 | invalidate();
90 | }
91 |
92 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
93 |
94 | if(key.equals("Theme")){
95 | for(TimerDrawing drawing : mDrawings)
96 | {
97 | drawing.configure();
98 | }
99 | }
100 | invalidate();
101 | }
102 |
103 |
104 | }
--------------------------------------------------------------------------------
/src/goo/TeaTimer/Animation/TrashCupAnimation.java:
--------------------------------------------------------------------------------
1 | package goo.TeaTimer.Animation;
2 |
3 | import goo.TeaTimer.R;
4 | import android.content.res.Resources;
5 | import android.graphics.Bitmap;
6 | import android.graphics.BitmapFactory;
7 | import android.graphics.Canvas;
8 | import android.graphics.Paint;
9 | import android.graphics.RectF;
10 |
11 | /**
12 | * Sample timer animation.
13 | * The trash cup naming comes from the fact that the cup reminds my friends
14 | * of a "trash can"
15 | *
16 | * @author Ralph Gootee (rgootee@gmail.com)
17 | *
18 | */
19 | class TrashCupAnimation implements TimerAnimation.TimerDrawing
20 | {
21 | // buffer
22 | private final int TOP_BUFFER = 3;
23 | private final int BOTTOM_BUFFER = 5;
24 |
25 | private Bitmap mCupBitmap;
26 |
27 | private int mWidth;
28 |
29 | private int mHeight;
30 |
31 | private Paint mProgressPaint,mBgPaint;
32 |
33 | private RectF mTeaRect;
34 |
35 | public TrashCupAnimation(Resources resources)
36 | {
37 | // Load the bitmap
38 | mCupBitmap = BitmapFactory.decodeResource(resources, R.drawable.cup);
39 | mWidth = mCupBitmap.getWidth();
40 | mHeight = mCupBitmap.getHeight();
41 |
42 | mProgressPaint = new Paint();
43 | mProgressPaint.setColor(resources.getColor(R.color.tea_fill));
44 |
45 | mBgPaint = new Paint();
46 | mBgPaint.setColor(resources.getColor(R.color.dark_gray));
47 |
48 | mTeaRect = new RectF(0,0,0,0);
49 |
50 | }
51 |
52 | /**
53 | * Updates the image to be in sync with the current time
54 | * @param time in milliseconds
55 | * @param max the original time set in milliseconds
56 | */
57 | public void updateImage(Canvas canvas, int time, int max) {
58 |
59 | float p = (max == 0) ? 0 : (time/(float)max);
60 |
61 | canvas.save();
62 |
63 | float w = canvas.getClipBounds().width();
64 | float h = canvas.getClipBounds().height();
65 |
66 | canvas.translate(w/2.0f - mWidth/2.0f,
67 | h/2.0f - mHeight/2.0f);
68 |
69 | // Define the drawing rects
70 | mTeaRect.set(0,(mHeight)*p,mWidth,mHeight);
71 |
72 | // Unused part of the cup
73 | canvas.drawRect(mTeaRect,mBgPaint);
74 |
75 | // The filled part of the cup
76 | canvas.drawRect(mTeaRect,mProgressPaint);
77 | canvas.drawBitmap(mCupBitmap, 0, 0, null);
78 |
79 | canvas.restore();
80 | }
81 |
82 | public void configure() {
83 | // TODO Auto-generated method stub
84 |
85 | }
86 | }
--------------------------------------------------------------------------------
/src/goo/TeaTimer/Animation/TresBarAnimation.java:
--------------------------------------------------------------------------------
1 | /*package goo.TeaTimer.Animation;
2 |
3 | import goo.TeaTimer.R;
4 | import android.content.res.Resources;
5 | import android.graphics.Bitmap;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.Paint;
9 | import android.graphics.RectF;
10 | import goo.TeaTimer.TimerUtils;
11 |
12 | class TresBarAnimation implements TimerAnimation.TimerDrawing
13 | {
14 | // buffer
15 | private final int PADDING = 10;
16 | private final int BAR_HEIGHT = 5;
17 |
18 | private Bitmap mCupBitmap;
19 |
20 | private int mWidth = 250;
21 |
22 | private int mHeight = 200;
23 |
24 | private Paint mProgressPaint,mBgPaint;
25 |
26 | private RectF mHourRect,mMinuteRect,mSecondRect;
27 | private RectF mHourFillRect, mMinuteFillRect,mSecondFillRect;
28 | private Paint mTextPaint;
29 | private int mTextHeight = 16;
30 | private int mTopMargin = mTextHeight + PADDING;
31 |
32 | public TresBarAnimation(Resources resources)
33 | {
34 | mProgressPaint = new Paint();
35 | mProgressPaint.setColor(resources.getColor(R.color.tea_fill));
36 |
37 | mBgPaint = new Paint();
38 | mBgPaint.setColor(resources.getColor(R.color.dark_gray));
39 |
40 | mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
41 | mTextPaint.setColor(Color.WHITE);
42 |
43 | mHourRect = new RectF(PADDING,PADDING,mWidth-PADDING,PADDING+BAR_HEIGHT);
44 | mMinuteRect = new RectF(PADDING,mHourRect.bottom +mTopMargin,mWidth-PADDING,mHourRect.bottom +mTopMargin+BAR_HEIGHT);
45 | mSecondRect = new RectF(PADDING,mMinuteRect.bottom+mTopMargin,mWidth-PADDING,mMinuteRect.bottom+mTopMargin+BAR_HEIGHT);
46 |
47 | mHourFillRect = new RectF(mHourRect);
48 | mMinuteFillRect = new RectF(mMinuteRect);
49 | mSecondFillRect = new RectF(mSecondRect);
50 | }
51 |
52 | /**
53 | * Updates the image to be in sync with the current time
54 | * @param time in milliseconds
55 | * @param max the original time set in milliseconds
56 |
57 | public Bitmap updateImage(int time,int max)
58 | {
59 | Bitmap bitmap = Bitmap.createBitmap(mWidth,mHeight,Bitmap.Config.ARGB_8888);
60 | Canvas canvas = new Canvas(bitmap);
61 | bitmap.eraseColor(Color.TRANSPARENT);
62 |
63 | float p = (max == 0) ? 0 : (time/(float)max);
64 | int [] timeVec = TimerUtils.time2Mhs(time);
65 |
66 | float pSecond = (max == 0) ? 1 : (float)((timeVec[2]/60.0));
67 | float pMs = (float)((timeVec[3]/1000.00));
68 |
69 | // Hour rectangle
70 | mHourFillRect.right = mHourRect.left+ p*(mHourRect.right-mHourRect.left);
71 | canvas.drawRect(mHourRect,mBgPaint);
72 | canvas.drawRect(mHourFillRect,mProgressPaint);
73 | canvas.drawText(timeVec[0] + " hours", PADDING , mHourRect.top+mTextHeight+3, mTextPaint);
74 |
75 | // Minute rect
76 | canvas.drawRect(mMinuteRect,mBgPaint);
77 | canvas.drawText(timeVec[1] + " minutes", PADDING , mMinuteRect.top+mTextHeight+3, mTextPaint);
78 |
79 | // Second rect
80 | canvas.drawRect(mSecondRect,mBgPaint);
81 | canvas.drawText(timeVec[2] + " seconds", PADDING , mSecondRect.top+mTextHeight+3, mTextPaint);
82 |
83 | return bitmap;
84 | }
85 | }*/
--------------------------------------------------------------------------------
/src/goo/TeaTimer/TimerActivity.java:
--------------------------------------------------------------------------------
1 | /* @file TimerActivity.java
2 | *
3 | * TeaTimer is free software: you can redistribute it and/or modify
4 | * it under the terms of the GNU General Public License as published by
5 | * the Free Software Foundation, either version 3 of the License, or
6 | * any later version. More info: http://www.gnu.org/licenses/
7 | *
8 | * Copyright 2009 Ralph Gootee
9 | *
10 | */
11 |
12 | package goo.TeaTimer;
13 |
14 | import goo.TeaTimer.Animation.TimerAnimation;
15 | import goo.TeaTimer.widget.NNumberPickerDialog;
16 | import goo.TeaTimer.widget.NumberPicker;
17 | import goo.TeaTimer.widget.NNumberPickerDialog.OnNNumberPickedListener;
18 |
19 | import java.util.Date;
20 | import java.util.Timer;
21 | import java.util.TimerTask;
22 |
23 | import android.app.Activity;
24 | import android.app.AlarmManager;
25 | import android.app.AlertDialog;
26 | import android.app.Dialog;
27 | import android.app.PendingIntent;
28 | import android.app.AlertDialog.Builder;
29 | import android.content.Context;
30 | import android.content.DialogInterface;
31 | import android.content.Intent;
32 | import android.content.SharedPreferences;
33 | import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
34 | import android.graphics.Bitmap;
35 | import android.graphics.BitmapFactory;
36 | import android.media.AudioManager;
37 | // import android.net.Uri;
38 | import android.os.Bundle;
39 | import android.os.Handler;
40 | import android.os.Message;
41 | import android.os.PowerManager;
42 | import android.os.SystemClock;
43 | import android.os.PowerManager.WakeLock;
44 | import android.preference.PreferenceManager;
45 | import android.util.Log;
46 | import android.view.LayoutInflater;
47 | import android.view.Menu;
48 | import android.view.MenuItem;
49 | import android.view.View;
50 | import android.view.View.OnClickListener;
51 | import android.view.Window;
52 | import android.widget.Button;
53 | import android.widget.ImageButton;
54 | import android.widget.TextView;
55 | import android.widget.Toast;
56 |
57 | /**
58 | * The main activity which shows the timer and allows the user to set the time
59 | * @author Ralph Gootee (rgootee@gmail.com)
60 | */
61 | public class TimerActivity extends Activity implements OnClickListener,OnNNumberPickedListener,OnSharedPreferenceChangeListener
62 | {
63 | /** All possible timer states */
64 | private final static int RUNNING=0, STOPPED=1, PAUSED=2;
65 |
66 | /** Should the logs be shown */
67 | private final static boolean LOG = true;
68 |
69 | /** Menu item ids */
70 | private final static int PREF=0;
71 | private final static int ABOUT=1;
72 |
73 | /** Macros for our dialogs */
74 | private final static int NUM_PICKER_DIALOG = 0, ALERT_DIALOG = 1;
75 | /** debug string */
76 | private final String TAG = getClass().getSimpleName();
77 |
78 | /** Update rate of the internal timer */
79 | private final int TIMER_TIC = 100;
80 |
81 | /** The timer's current state */
82 | private int mCurrentState = -1;
83 |
84 | /** The maximum time */
85 | private int mLastTime = 0;
86 |
87 | /** The current timer time */
88 | private int mTime = 0;
89 |
90 | /** Internal increment class for the timer */
91 | private Timer mTimer = null;
92 |
93 | /** Handler for the message from the timer service */
94 | private Handler mHandler = new Handler() {
95 |
96 | @Override
97 | public void handleMessage(Message msg) {
98 |
99 | // The timer is finished
100 | if(msg.arg1 <= 0){
101 |
102 | if(mTimer != null){
103 | if(LOG) Log.v(TAG,"rcvd a <0 msg = " + msg.arg1);
104 |
105 | Context context = getApplicationContext();
106 | CharSequence text = getResources().getText(R.string.Notification);
107 | Toast.makeText(context, text,Toast.LENGTH_SHORT).show();
108 |
109 | timerStop();
110 |
111 |
112 | }
113 |
114 | // Update the time
115 | }else{
116 | mTime = msg.arg1;
117 |
118 | //enterState(RUNNING);
119 | onUpdateTime();
120 | }
121 | }
122 | };
123 |
124 | /** To save having to traverse the view tree */
125 | private ImageButton mPauseButton, mCancelButton;
126 | private Button mSetButton;
127 | private TimerAnimation mTimerAnimation;
128 | private TextView mTimerLabel;
129 |
130 | private Bitmap mPlayBitmap,mPauseBitmap;
131 |
132 | private AlarmManager mAlarmMgr;
133 |
134 | private PendingIntent mPendingIntent;
135 |
136 | private AudioManager mAudioMgr;
137 |
138 | private SharedPreferences mSettings;
139 |
140 | private WakeLock mWakeLock;
141 |
142 | /** Called when the activity is first created.
143 | * { @inheritDoc}
144 | */
145 | @Override
146 | public void onCreate(Bundle savedInstanceState)
147 | {
148 | super.onCreate(savedInstanceState);
149 |
150 | requestWindowFeature(Window.FEATURE_NO_TITLE);
151 |
152 | setContentView(R.layout.main);
153 |
154 | mCancelButton = (ImageButton)findViewById(R.id.cancelButton);
155 | mCancelButton.setOnClickListener(this);
156 |
157 | mSetButton = (Button)findViewById(R.id.setButton);
158 | mSetButton.setOnClickListener(this);
159 |
160 | mPauseButton = (ImageButton)findViewById(R.id.pauseButton);
161 | mPauseButton.setOnClickListener(this);
162 |
163 | mPauseBitmap = BitmapFactory.decodeResource(
164 | getResources(), R.drawable.pause);
165 |
166 | mPlayBitmap = BitmapFactory.decodeResource(
167 | getResources(), R.drawable.play);
168 |
169 | mTimerLabel = (TextView)findViewById(R.id.label);
170 |
171 | mTimerAnimation = (TimerAnimation)findViewById(R.id.imageView);
172 |
173 | enterState(STOPPED);
174 |
175 | // Store some useful values
176 | mSettings = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
177 | mAlarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
178 | mAudioMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
179 |
180 | mSettings.registerOnSharedPreferenceChangeListener(this);
181 | }
182 |
183 |
184 | /** { @inheritDoc} */
185 | @Override
186 | public boolean onCreateOptionsMenu(Menu menu)
187 | {
188 | MenuItem item = menu.add(0, PREF, 0, getResources().getString(R.string.prefs));
189 | MenuItem about = menu.add(1,1,0, getResources().getString(R.string.about));
190 |
191 | item.setIcon(android.R.drawable.ic_menu_preferences);
192 | about.setIcon(android.R.drawable.ic_menu_info_details);
193 |
194 | return super.onCreateOptionsMenu(menu);
195 | }
196 |
197 |
198 | /** { @inheritDoc} */
199 | @Override
200 | public boolean onOptionsItemSelected(MenuItem item)
201 | {
202 | switch(item.getItemId()){
203 |
204 | case PREF:
205 | startActivity(new Intent(this, TimerPrefActivity.class));
206 | break;
207 |
208 | case ABOUT:
209 | //new TimerAboutDialog(this).show();
210 | //break;
211 | LayoutInflater li = LayoutInflater.from(this);
212 | View view = li.inflate(R.layout.about, null);
213 |
214 | Builder p = new AlertDialog.Builder(this).setView(view);
215 | final AlertDialog alrt = p.create();
216 | alrt.setIcon(R.drawable.icon);
217 | alrt.setTitle(getString(R.string.about_title));
218 | alrt.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.close),
219 | new DialogInterface.OnClickListener() {
220 | public void onClick(DialogInterface dialog,
221 | int whichButton) {
222 | }
223 | });
224 | alrt.show();
225 | return true;
226 |
227 | default:
228 | return false;
229 | }
230 | return true;
231 | }
232 |
233 |
234 | /** { @inheritDoc} */
235 | @Override
236 | public void onPause()
237 | {
238 | super.onPause();
239 |
240 | // Save our settings
241 | SharedPreferences.Editor editor = mSettings.edit();
242 | editor.putInt("LastTime", mLastTime);
243 | editor.putInt("CurrentTime",mTime);
244 | editor.putInt("DrawingIndex",mTimerAnimation.getIndex());
245 | editor.putInt("State", mCurrentState);
246 |
247 | switch(mCurrentState){
248 |
249 | case RUNNING:
250 | {
251 | if(mTimer != null){
252 | mTimer.cancel();
253 | editor.putLong("TimeStamp", new Date().getTime() + mTime);
254 | }
255 | }break;
256 |
257 | case STOPPED:
258 | case PAUSED:
259 | {
260 | editor.putLong("TimeStamp", 1);
261 | }break;
262 | }
263 |
264 | editor.commit();
265 |
266 | releaseWakeLock();
267 | }
268 |
269 |
270 | /** {@inheritDoc} */
271 | @Override
272 | public void onResume()
273 | {
274 | super.onResume();
275 |
276 | // check the timestamp from the last update and start the timer.
277 | // assumes the data has already been loaded?
278 | mLastTime = mSettings.getInt("LastTime",0);
279 |
280 | mTimerAnimation.setIndex(mSettings.getInt("DrawingIndex",0));
281 | int state = mSettings.getInt("State",0);
282 |
283 | switch(state)
284 | {
285 | case RUNNING:
286 | long timeStamp = mSettings.getLong("TimeStamp", -1);
287 |
288 | Date now = new Date();
289 | Date then = new Date(timeStamp);
290 |
291 | // We still have a timer running!
292 | if(then.after(now)){
293 | int delta = (int)(then.getTime() - now.getTime());
294 | timerStart(delta,false);
295 | mCurrentState = RUNNING;
296 | // All finished
297 | }else{
298 | clearTime();
299 | enterState(STOPPED);
300 | }
301 | break;
302 |
303 | case STOPPED:
304 | enterState(STOPPED);
305 | break;
306 |
307 | case PAUSED:
308 | mTime = mSettings.getInt("CurrentTime",0);
309 | onUpdateTime();
310 | enterState(PAUSED);
311 | break;
312 | }
313 | }
314 |
315 |
316 | /**
317 | * Updates the time
318 | */
319 | public void onUpdateTime(){
320 |
321 | updateLabel(mTime);
322 | mTimerAnimation.updateImage(mTime,mLastTime);
323 | }
324 |
325 |
326 | /**
327 | * Updates the text label with the given time
328 | * @param time in milliseconds
329 | */
330 | public void updateLabel(int time){
331 | time += 999; // round seconds upwards
332 | String str = TimerUtils.time2str(time);
333 | int size = TimerUtils.textSize(str);
334 | mTimerLabel.setTextSize(size);
335 | mTimerLabel.setText(str);
336 | }
337 |
338 |
339 | /** {@inheritDoc} */
340 | @Override
341 | protected Dialog onCreateDialog(int id)
342 | {
343 | Dialog d = null;
344 |
345 | switch(id){
346 |
347 | case NUM_PICKER_DIALOG:
348 | d = new NNumberPickerDialog( this, this, getResources().getString(R.string.InputTitle));
349 | break;
350 |
351 | case ALERT_DIALOG:
352 | {
353 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
354 | builder.setMessage(getResources().getText(R.string.warning_text))
355 | .setCancelable(false)
356 | .setPositiveButton(getResources().getText(R.string.warning_button), null)
357 | .setTitle(getResources().getText(R.string.warning_title));
358 |
359 | d = builder.create();
360 |
361 | }break;
362 | }
363 |
364 | return d;
365 | }
366 |
367 |
368 | /**
369 | * Callback for the number picker dialog
370 | */
371 | public void onNumbersPicked(int[] number)
372 | {
373 | int hour = number[0];
374 | int min = number[1];
375 | int sec = number[2];
376 |
377 | mLastTime = hour*60*60*1000 + min*60*1000 + sec*1000;
378 |
379 | // Check to make sure the phone isn't set to silent
380 | boolean silent = (mAudioMgr.getRingerMode() == AudioManager.RINGER_MODE_SILENT);
381 | String noise = mSettings.getString("NotificationUri","");
382 | boolean vibrate = mSettings.getBoolean("Vibrate",true);
383 | boolean nag = mSettings.getBoolean("NagSilent",true);
384 |
385 | // If the conditions are _just_ right show a nag screen
386 | if(nag && silent && (noise != "" || vibrate) ){
387 | showDialog(ALERT_DIALOG);
388 | }
389 |
390 | timerStart(mLastTime,true);
391 | }
392 |
393 |
394 | /**
395 | * This only refers to the visual state of the application, used to manage
396 | * the view coming back into focus.
397 | *
398 | * @param state the visual state that is being entered
399 | */
400 | private void enterState(int state){
401 |
402 | if(mCurrentState != state){
403 |
404 | mCurrentState = state;
405 | if(LOG) Log.v(TAG,"Set current state = " + mCurrentState);
406 |
407 | switch(state)
408 | {
409 | case RUNNING:
410 | {
411 | mSetButton.setVisibility(View.GONE);
412 | mCancelButton.setVisibility(View.VISIBLE);
413 | mPauseButton.setVisibility(View.VISIBLE);
414 | mPauseButton.setImageBitmap(mPauseBitmap);
415 | }break;
416 |
417 | case STOPPED:
418 | {
419 | mPauseButton.setVisibility(View.GONE);
420 | mCancelButton.setVisibility(View.GONE);
421 | mSetButton.setVisibility(View.VISIBLE);
422 | clearTime();
423 |
424 | }break;
425 |
426 | case PAUSED:
427 | {
428 | mSetButton.setVisibility(View.GONE);
429 | mPauseButton.setVisibility(View.VISIBLE);
430 | mCancelButton.setVisibility(View.VISIBLE);
431 | mPauseButton.setImageBitmap(mPlayBitmap);
432 | }break;
433 | }
434 | }
435 | }
436 |
437 | /**
438 | * Cancels the alarm portion of the timer
439 | */
440 | private void stopAlarmTimer(){
441 | if(LOG) Log.v(TAG,"Stopping the alarm timer ...");
442 | mAlarmMgr.cancel(mPendingIntent);
443 | }
444 |
445 | /**
446 | * Stops the timer
447 | */
448 | private void timerStop()
449 | {
450 | if(LOG) Log.v(TAG,"Timer stopped");
451 |
452 | clearTime();
453 |
454 | // Stop our timer service
455 | enterState(STOPPED);
456 | mTimer.cancel();
457 |
458 | releaseWakeLock();
459 | }
460 |
461 | private void releaseWakeLock(){
462 | // Remove the wakelock
463 | if(mWakeLock != null && mWakeLock.isHeld()) {
464 | if(LOG) Log.v(TAG,"Releasing wake lock...");
465 | mWakeLock.release();
466 | mWakeLock = null;
467 | }
468 | }
469 | /**
470 | * Only aquires the wake lock _if_ it is set in the settings.
471 | */
472 | private void aquireWakeLock(){
473 | // We're going to start a wakelock
474 | if(mSettings.getBoolean("WakeLock", false)){
475 | if(LOG) Log.v(TAG,"Issuing a wakelock...");
476 |
477 | PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
478 | if(mWakeLock != null) Log.e(TAG,"There's already a wakelock... Shouldn't be there!");
479 |
480 | mWakeLock= pm.newWakeLock(
481 | PowerManager.SCREEN_DIM_WAKE_LOCK
482 | | PowerManager.ON_AFTER_RELEASE,
483 | TAG);
484 | mWakeLock.acquire();
485 | }
486 | }
487 |
488 | /**
489 | * Starts the timer at the given time
490 | * @param time with which to count down
491 | * @param service whether or not to start the service as well
492 | */
493 | private void timerStart(int time,boolean service)
494 | {
495 | if(LOG) Log.v(TAG,"Starting the timer...");
496 |
497 | // Star external service
498 | enterState(RUNNING);
499 |
500 | if(service){
501 | if(LOG) Log.v(TAG,"Starting the timer service ...");
502 | Intent intent = new Intent( getApplicationContext(), TimerReceiver.class);
503 | intent.putExtra("SetTime",mLastTime);
504 | mPendingIntent = PendingIntent.getBroadcast( getApplicationContext(), 0 , intent, PendingIntent.FLAG_CANCEL_CURRENT);
505 | mAlarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + time, mPendingIntent);
506 | }
507 |
508 | // Internal thread to properly update the GUI
509 | mTimer = new Timer();
510 | mTime = time;
511 | mTimer.scheduleAtFixedRate( new TimerTask(){
512 | public void run() {
513 | timerTic();
514 | }
515 | },
516 | 0,
517 | TIMER_TIC);
518 |
519 | aquireWakeLock();
520 | }
521 |
522 | /** Resume the time after being paused */
523 | private void resumeTimer()
524 | {
525 | if(LOG) Log.v(TAG,"Resuming the timer...");
526 |
527 | timerStart(mTime,true);
528 | enterState(RUNNING);
529 | }
530 |
531 | /** Pause the timer and stop the timer service */
532 | private void pauseTimer()
533 | {
534 | if(LOG) Log.v(TAG,"Pausing the timer...");
535 |
536 | mTimer.cancel();
537 | mTimer = null;
538 |
539 | stopAlarmTimer();
540 |
541 | enterState(PAUSED);
542 | }
543 |
544 | /** Called whenever the internal timer is updated */
545 | protected void timerTic()
546 | {
547 | mTime -= TIMER_TIC;
548 |
549 | if(mHandler != null){
550 | Message msg = new Message();
551 | msg.arg1 = mTime;
552 | mHandler.sendMessage(msg);
553 | }
554 | }
555 |
556 | /** Clears the time, sets the image and label to zero */
557 | private void clearTime()
558 | {
559 | mTime = 0;
560 | onUpdateTime();
561 | }
562 |
563 | /** {@inheritDoc} */
564 | public void onClick(View v)
565 | {
566 | switch(v.getId()){
567 |
568 | case R.id.setButton:
569 | {
570 | showDialog(NUM_PICKER_DIALOG);
571 | }break;
572 |
573 | case R.id.pauseButton:
574 | {
575 | switch(mCurrentState){
576 | case RUNNING:
577 | pauseTimer();
578 | break;
579 | case PAUSED:
580 | resumeTimer();
581 | break;
582 | }
583 | }break;
584 |
585 | case R.id.cancelButton:
586 | {
587 | // We need to be careful to not cancel timers
588 | // that are not running (e.g. if we're paused)
589 | switch(mCurrentState){
590 | case RUNNING:
591 | timerStop();
592 | stopAlarmTimer();
593 | break;
594 | case PAUSED:
595 | clearTime();
596 | enterState(STOPPED);
597 | break;
598 | }
599 | }break;
600 | }
601 | }
602 |
603 | /**
604 | * Mostly used for the wakelock currently -- should be used for the visual components eventually
605 | */
606 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
607 |
608 | // We need to check if the
609 | if(key == "WakeLock"){
610 | if(mSettings.getBoolean("WakeLock", false)) aquireWakeLock();
611 | else releaseWakeLock();
612 | }
613 | }
614 |
615 | }
616 |
--------------------------------------------------------------------------------
/src/goo/TeaTimer/TimerPrefActivity.java:
--------------------------------------------------------------------------------
1 | package goo.TeaTimer;
2 |
3 |
4 | import android.database.Cursor;
5 | import android.os.Bundle;
6 | import android.preference.ListPreference;
7 | import android.preference.PreferenceActivity;
8 | import android.provider.MediaStore;
9 | import android.util.Log;
10 |
11 | public class TimerPrefActivity extends PreferenceActivity
12 | {
13 | private static final String TAG = TimerPrefActivity.class.getSimpleName();
14 |
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 |
19 | // Load the preferences from an XML resource
20 | addPreferencesFromResource(R.xml.preferences);
21 |
22 | // Load the sounds
23 | ListPreference tone = (ListPreference)findPreference("NotificationUri");
24 |
25 | String [] cols = { MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE};
26 |
27 | // Lets check out the media provider
28 | Cursor cursor = managedQuery(MediaStore.Audio.Media.INTERNAL_CONTENT_URI,
29 | cols,
30 | "is_ringtone OR is_notification",
31 | null, "is_ringtone, title_key");
32 | int i=0;
33 |
34 | CharSequence[] items = null;
35 | CharSequence[] itemUris = null;
36 |
37 | if(cursor != null && cursor.getCount() > 0){
38 |
39 | items = new CharSequence[cursor.getCount()];
40 | itemUris = new CharSequence[cursor.getCount()];
41 |
42 | int colTitle = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE);;
43 | int colId = cursor.getColumnIndex(MediaStore.Audio.Media._ID);;
44 |
45 | while(!cursor.isLast()){
46 | cursor.moveToNext();
47 | items[i] = cursor.getString(colTitle);
48 | itemUris[i] = MediaStore.Audio.Media.INTERNAL_CONTENT_URI + "/" + cursor.getLong(colId);
49 | i++;
50 | }
51 |
52 | cursor.close();
53 | }
54 |
55 | CharSequence [] entries = {"No Sound","Big Ben","Bell"};
56 | CharSequence [] entryValues = {"","android.resource://goo.TeaTimer/" + R.raw.big_ben,"android.resource://goo.TeaTimer/" + R.raw.bell};
57 |
58 | //Default value
59 | if(tone.getValue() == null) tone.setValue((String)entryValues[1]);
60 |
61 | if( items != null && items.length > 0){
62 | tone.setEntries(concat(entries,items));
63 | tone.setEntryValues(concat(entryValues,itemUris));
64 | }else{
65 | tone.setEntries(entries);
66 | tone.setEntryValues(entryValues);
67 | }
68 |
69 | }
70 | static private CharSequence [] concat( CharSequence[] A, CharSequence[] B)
71 | {
72 | CharSequence[] C= new CharSequence[A.length+B.length];
73 | System.arraycopy(A, 0, C, 0, A.length);
74 | System.arraycopy(B, 0, C, A.length, B.length);
75 |
76 | return C;
77 | }
78 | }
--------------------------------------------------------------------------------
/src/goo/TeaTimer/TimerReceiver.java:
--------------------------------------------------------------------------------
1 | package goo.TeaTimer;
2 |
3 | import android.app.AlarmManager;
4 | import android.app.Notification;
5 | import android.app.NotificationManager;
6 | import android.app.PendingIntent;
7 | import android.content.BroadcastReceiver;
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.content.SharedPreferences;
11 | import android.media.MediaPlayer;
12 | import android.net.Uri;
13 | import android.os.SystemClock;
14 | import android.preference.PreferenceManager;
15 | import android.util.Log;
16 |
17 | public class TimerReceiver extends BroadcastReceiver
18 | {
19 | private final static String TAG = TimerReceiver.class.getSimpleName();
20 | private final static String CANCEL_NOTIFICATION = "CANCEL_NOTIFICATION";
21 |
22 | @Override
23 | public void onReceive(Context context, Intent pintent)
24 | {
25 | NotificationManager mNM = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
26 |
27 | // Cancel notification and return...
28 | if (CANCEL_NOTIFICATION.equals(pintent.getAction())) {
29 | Log.v(TAG,"Cancelling notification...");
30 | mNM.cancel(0);
31 | return;
32 | }
33 |
34 | // ...or display a new one
35 |
36 | Log.v(TAG,"Showing notification...");
37 |
38 | int setTime = pintent.getIntExtra("SetTime",0);
39 | String setTimeStr = TimerUtils.time2humanStr(setTime);
40 |
41 | // Load the settings
42 | SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
43 | boolean led = settings.getBoolean("LED",true);
44 | boolean vibrate = settings.getBoolean("Vibrate",true);
45 | String notificationUri = settings.getString("NotificationUri", "android.resource://goo.TeaTimer/" + R.raw.big_ben);
46 |
47 | CharSequence text = context.getText(R.string.Notification);
48 | CharSequence textLatest = context.getText(R.string.timer_for) + setTimeStr;
49 |
50 | Notification notification = new Notification(R.drawable.notification,
51 | text,
52 | System.currentTimeMillis());
53 |
54 | // Vibrate
55 | if(vibrate){
56 | notification.defaults = Notification.DEFAULT_VIBRATE;
57 | }
58 |
59 | // Have a light
60 | if(led){
61 | notification.ledARGB = 0xff00ff00;
62 | notification.ledOnMS = 300;
63 | notification.ledOffMS = 1000;
64 | notification.flags |= Notification.FLAG_SHOW_LIGHTS;
65 | }
66 |
67 | // Play a sound!
68 | if(notificationUri != ""){
69 | Uri uri = Uri.parse(notificationUri);
70 | notification.sound = uri;
71 | }
72 |
73 | notification.flags |= Notification.FLAG_AUTO_CANCEL;
74 |
75 | Intent intent = new Intent(context,TimerActivity.class);
76 | PendingIntent contentIntent = PendingIntent.getActivity(context, 0,intent, Intent.FLAG_ACTIVITY_NEW_TASK);
77 |
78 | notification.setLatestEventInfo(context, text,
79 | textLatest, contentIntent);
80 |
81 | // Create intent for cancelling the notification
82 | Context appContext = context.getApplicationContext();
83 | intent = new Intent(appContext, TimerReceiver.class);
84 | intent.setAction(CANCEL_NOTIFICATION);
85 |
86 | // Cancel the pending cancellation and create a new one
87 | PendingIntent pendingCancelIntent =
88 | PendingIntent.getBroadcast(appContext, 0, intent,
89 | PendingIntent.FLAG_CANCEL_CURRENT);
90 |
91 | if (settings.getBoolean("AutoClear", false)) {
92 | // Determine duration of notification sound
93 | int duration = 5000;
94 | if (notification.sound != null) {
95 | MediaPlayer player = new MediaPlayer();
96 | try {
97 | player.setDataSource(context, notification.sound);
98 | player.prepare();
99 | duration = Math.max(duration, player.getDuration() + 2000);
100 | }
101 | catch (java.io.IOException ex) {
102 | Log.e(TAG, "Cannot get sound duration: " + ex);
103 | duration = 30000; // on error, default to 30 seconds
104 | }
105 | finally {
106 | player.release();
107 | }
108 | }
109 | Log.v(TAG, "Notification duration: " + duration + " ms");
110 | // Schedule the cancellation
111 | AlarmManager alarmMgr = (AlarmManager)context
112 | .getSystemService(Context.ALARM_SERVICE);
113 | alarmMgr.set(AlarmManager.ELAPSED_REALTIME,
114 | SystemClock.elapsedRealtime() + duration,
115 | pendingCancelIntent);
116 | }
117 |
118 | // Show notification
119 | mNM.notify(0, notification);
120 | }
121 |
122 | }
--------------------------------------------------------------------------------
/src/goo/TeaTimer/TimerUtils.java:
--------------------------------------------------------------------------------
1 | package goo.TeaTimer;
2 |
3 | public class TimerUtils {
4 |
5 |
6 | /**
7 | * Returns the suggested text size for the string. A hack.
8 | * @param str the time string
9 | * @return the suggested text size to accommodate the string
10 | */
11 | public static int textSize(String str)
12 | {
13 | if(str.length() > 5){
14 | return 60;
15 | }else{
16 | return 80;
17 | }
18 | }
19 |
20 |
21 | /** Converts a millisecond time to a string time
22 | * @param time is the time in milliseconds
23 | * @return the formated string
24 | */
25 | public static String time2str(int ms)
26 | {
27 | int [] time = time2Mhs(ms);
28 |
29 | if(time[0] == 0){
30 | return String.format("%02d:%02d",time[1],time[2]);
31 | }else{
32 | return String.format("%02d:%02d:%02d",time[0],time[1],time[2]);
33 | }
34 | }
35 |
36 | /** Creates a time vector
37 | * @param ms the time in milliseconds
38 | * @return [hour,minutes,seconds,ms]
39 | */
40 | public static int [] time2Mhs(int time)
41 | {
42 | int ms = time % 1000;
43 | int seconds = (int) (time / 1000);
44 | int minutes = seconds / 60;
45 | int hour = minutes / 60;
46 |
47 | minutes = minutes % 60;
48 | seconds = seconds % 60;
49 |
50 | int [] timeVec = new int[4];
51 | timeVec[0] = hour;
52 | timeVec[1] = minutes;
53 | timeVec[2] = seconds;
54 | timeVec[3] = ms;
55 | return timeVec;
56 | }
57 |
58 | public static String time2humanStr(int time)
59 | {
60 | int [] timeVec = time2Mhs(time);
61 | int hour = timeVec[0], minutes=timeVec[1];
62 |
63 | String r = new String();
64 |
65 | // Ugly string formating
66 | if(hour != 0){
67 | r += hour + " hour";
68 | if(hour == 1) r+= "s";
69 | r+= " and ";
70 | }
71 |
72 | r += minutes + " min";
73 | if(minutes != 1) r+= "s";
74 |
75 | return r;
76 | }
77 | }
--------------------------------------------------------------------------------
/src/goo/TeaTimer/widget/NNumberPickerDialog.java:
--------------------------------------------------------------------------------
1 | package goo.TeaTimer.widget;
2 |
3 | /*
4 | * ASTRID: Android's Simple Task Recording Dashboard
5 | *
6 | * Copyright (c) 2009 Tim Su
7 | *
8 | * This program is free software; you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation; either version 2 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * This program is distributed in the hope that it will be useful, but
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 | * for more details.
17 | *
18 | * You should have received a copy of the GNU General Public License along
19 | * with this program; if not, write to the Free Software Foundation, Inc.,
20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 | */
22 |
23 | import goo.TeaTimer.R;
24 |
25 | import java.util.LinkedList;
26 | import java.util.List;
27 |
28 | import android.app.AlertDialog;
29 | import android.content.Context;
30 | import android.content.DialogInterface;
31 | import android.content.DialogInterface.OnClickListener;
32 | import android.graphics.Color;
33 | import android.view.Gravity;
34 | import android.view.LayoutInflater;
35 | import android.view.View;
36 | import android.widget.LinearLayout;
37 | import android.widget.TextView;
38 | import android.widget.FrameLayout.LayoutParams;
39 | import android.widget.Gallery;
40 | import android.widget.ArrayAdapter;
41 | import android.widget.AdapterView;
42 | import android.widget.AdapterView.OnItemSelectedListener;
43 |
44 | import android.view.GestureDetector;
45 | import android.view.GestureDetector.SimpleOnGestureListener;
46 | import android.view.MotionEvent;
47 |
48 | /** Dialog box with an arbitrary number of number pickers */
49 | public class NNumberPickerDialog extends AlertDialog implements OnClickListener {
50 |
51 | public interface OnNNumberPickedListener {
52 | void onNumbersPicked(int[] number);
53 | }
54 |
55 | private final List pickers = new LinkedList();
56 | private final OnNNumberPickedListener mCallback;
57 |
58 | private int hsel;
59 | private int msel;
60 | private int ssel;
61 |
62 | private GestureDetector gestureDetector;
63 |
64 |
65 | private Gallery hour;
66 | private Gallery min;
67 | private Gallery sec;
68 |
69 | /** Instantiate the dialog box.
70 | *
71 | * @param context
72 | * @param callBack callback function to get the numbers you requested
73 | * @param title title of the dialog box
74 | */
75 | public NNumberPickerDialog(Context context, OnNNumberPickedListener callBack,
76 | String title)
77 | {
78 | super(context);
79 |
80 | mCallback = callBack;
81 |
82 | setButton(context.getText(android.R.string.ok), this);
83 | setButton2(context.getText(android.R.string.cancel), (OnClickListener) null);
84 |
85 | LayoutInflater inflater = (LayoutInflater) context
86 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
87 | View view = inflater.inflate(R.layout.n_number_picker_dialog, null);
88 | setView(view);
89 |
90 | //setTitle(title);
91 | LayoutParams npLayout = new LayoutParams(LayoutParams.WRAP_CONTENT,
92 | LayoutParams.FILL_PARENT);
93 | npLayout.gravity = 1;
94 | LayoutParams sepLayout = new LayoutParams(LayoutParams.WRAP_CONTENT,
95 | LayoutParams.FILL_PARENT);
96 | String [] numbers = new String[61];
97 | for(int i = 0; i < 61; i++) {
98 | numbers[i] = Integer.toString(i);
99 | }
100 | hour = (Gallery) view.findViewById(R.id.gallery_hour);
101 | min = (Gallery) view.findViewById(R.id.gallery_min);
102 | sec = (Gallery) view.findViewById(R.id.gallery_sec);
103 |
104 | ArrayAdapter adapter1 = new ArrayAdapter(context, R.layout.gallery_item, numbers);
105 |
106 | hour.setAdapter(adapter1);
107 | min.setAdapter(adapter1);
108 | sec.setAdapter(adapter1);
109 |
110 | gestureDetector = new GestureDetector(new MyGestureDetector());
111 | hour.setOnTouchListener(new View.OnTouchListener() {
112 | public boolean onTouch(View v, MotionEvent ev) {
113 | return gestureDetector.onTouchEvent(ev);
114 | }
115 | });
116 | min.setOnTouchListener(new View.OnTouchListener() {
117 | public boolean onTouch(View v, MotionEvent ev) {
118 | return gestureDetector.onTouchEvent(ev);
119 | }
120 | });
121 | sec.setOnTouchListener(new View.OnTouchListener() {
122 | public boolean onTouch(View v, MotionEvent ev) {
123 | return gestureDetector.onTouchEvent(ev);
124 | }
125 | });
126 |
127 | }
128 |
129 | class MyGestureDetector extends SimpleOnGestureListener {
130 | @Override
131 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
132 |
133 | velocityX=velocityX/5;
134 |
135 | return super.onFling(e1, e2, velocityX, velocityY);
136 | }
137 |
138 |
139 | @Override
140 | public boolean onDown(MotionEvent e) {
141 | return super.onDown(e);
142 | //return true;
143 | }
144 |
145 | }
146 |
147 |
148 | public void onClick(DialogInterface dialog, int which) {
149 | if (mCallback != null) {
150 |
151 | hsel = hour.getSelectedItemPosition();
152 | msel = min.getSelectedItemPosition();
153 | ssel = sec.getSelectedItemPosition();
154 |
155 | int[] values = {hsel,msel,ssel};
156 | mCallback.onNumbersPicked(values);
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/use_cases.text:
--------------------------------------------------------------------------------
1 |
2 | User is within the activity:
3 | - User can start a timer
4 | - User can stop a timer
5 | - The timer shall go off when the time has ran out
6 | - User can leave the activity by pressing home (pause)
7 | - User can leave the activity by pressing back (kill)
8 |
9 | User has left the activity
10 | - The timer still executes in the background
11 | - User can re-enter the activity
12 |
13 | USer has reentered the activity
14 | - GUI has to be updated to most recent time
15 | - User is in the activity
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------