4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.tx/config:
--------------------------------------------------------------------------------
1 | [main]
2 | host = https://www.transifex.com
3 |
4 | [o:bodhi-timer:p:bodhi-timer-app:r:strings_xml]
5 | file_filter = app/src/main/res/values-/strings.xml
6 | source_file = app/src/main/res/values/strings.xml
7 | type = ANDROID
8 | minimum_perc = 0
9 | resource_name = strings.xml
10 |
11 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem "fastlane"
4 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.3)
5 | addressable (2.8.0)
6 | public_suffix (>= 2.0.2, < 5.0)
7 | atomos (0.1.3)
8 | aws-eventstream (1.1.0)
9 | aws-partitions (1.416.0)
10 | aws-sdk-core (3.111.1)
11 | aws-eventstream (~> 1, >= 1.0.2)
12 | aws-partitions (~> 1, >= 1.239.0)
13 | aws-sigv4 (~> 1.1)
14 | jmespath (~> 1.0)
15 | aws-sdk-kms (1.41.0)
16 | aws-sdk-core (~> 3, >= 3.109.0)
17 | aws-sigv4 (~> 1.1)
18 | aws-sdk-s3 (1.87.0)
19 | aws-sdk-core (~> 3, >= 3.109.0)
20 | aws-sdk-kms (~> 1)
21 | aws-sigv4 (~> 1.1)
22 | aws-sigv4 (1.2.2)
23 | aws-eventstream (~> 1, >= 1.0.2)
24 | babosa (1.0.4)
25 | claide (1.0.3)
26 | colored (1.2)
27 | colored2 (3.1.2)
28 | commander-fastlane (4.4.6)
29 | highline (~> 1.7.2)
30 | declarative (0.0.20)
31 | declarative-option (0.1.0)
32 | digest-crc (0.6.3)
33 | rake (>= 12.0.0, < 14.0.0)
34 | domain_name (0.5.20190701)
35 | unf (>= 0.0.5, < 1.0.0)
36 | dotenv (2.7.6)
37 | emoji_regex (3.2.1)
38 | excon (0.78.1)
39 | faraday (1.3.0)
40 | faraday-net_http (~> 1.0)
41 | multipart-post (>= 1.2, < 3)
42 | ruby2_keywords
43 | faraday-cookie_jar (0.0.7)
44 | faraday (>= 0.8.0)
45 | http-cookie (~> 1.0.0)
46 | faraday-net_http (1.0.1)
47 | faraday_middleware (1.0.0)
48 | faraday (~> 1.0)
49 | fastimage (2.2.1)
50 | fastlane (2.171.0)
51 | CFPropertyList (>= 2.3, < 4.0.0)
52 | addressable (>= 2.3, < 3.0.0)
53 | aws-sdk-s3 (~> 1.0)
54 | babosa (>= 1.0.3, < 2.0.0)
55 | bundler (>= 1.12.0, < 3.0.0)
56 | colored
57 | commander-fastlane (>= 4.4.6, < 5.0.0)
58 | dotenv (>= 2.1.1, < 3.0.0)
59 | emoji_regex (>= 0.1, < 4.0)
60 | excon (>= 0.71.0, < 1.0.0)
61 | faraday (~> 1.0)
62 | faraday-cookie_jar (~> 0.0.6)
63 | faraday_middleware (~> 1.0)
64 | fastimage (>= 2.1.0, < 3.0.0)
65 | gh_inspector (>= 1.1.2, < 2.0.0)
66 | google-api-client (>= 0.37.0, < 0.39.0)
67 | google-cloud-storage (>= 1.15.0, < 2.0.0)
68 | highline (>= 1.7.2, < 2.0.0)
69 | json (< 3.0.0)
70 | jwt (>= 2.1.0, < 3)
71 | mini_magick (>= 4.9.4, < 5.0.0)
72 | multipart-post (~> 2.0.0)
73 | plist (>= 3.1.0, < 4.0.0)
74 | rubyzip (>= 2.0.0, < 3.0.0)
75 | security (= 0.1.3)
76 | simctl (~> 1.6.3)
77 | slack-notifier (>= 2.0.0, < 3.0.0)
78 | terminal-notifier (>= 2.0.0, < 3.0.0)
79 | terminal-table (>= 1.4.5, < 2.0.0)
80 | tty-screen (>= 0.6.3, < 1.0.0)
81 | tty-spinner (>= 0.8.0, < 1.0.0)
82 | word_wrap (~> 1.0.0)
83 | xcodeproj (>= 1.13.0, < 2.0.0)
84 | xcpretty (~> 0.3.0)
85 | xcpretty-travis-formatter (>= 0.0.3)
86 | gh_inspector (1.1.3)
87 | google-api-client (0.38.0)
88 | addressable (~> 2.5, >= 2.5.1)
89 | googleauth (~> 0.9)
90 | httpclient (>= 2.8.1, < 3.0)
91 | mini_mime (~> 1.0)
92 | representable (~> 3.0)
93 | retriable (>= 2.0, < 4.0)
94 | signet (~> 0.12)
95 | google-apis-core (0.2.0)
96 | addressable (~> 2.5, >= 2.5.1)
97 | googleauth (~> 0.14)
98 | httpclient (>= 2.8.1, < 3.0)
99 | mini_mime (~> 1.0)
100 | representable (~> 3.0)
101 | retriable (>= 2.0, < 4.0)
102 | rexml
103 | signet (~> 0.14)
104 | google-apis-iamcredentials_v1 (0.1.0)
105 | google-apis-core (~> 0.1)
106 | google-apis-storage_v1 (0.1.0)
107 | google-apis-core (~> 0.1)
108 | google-cloud-core (1.5.0)
109 | google-cloud-env (~> 1.0)
110 | google-cloud-errors (~> 1.0)
111 | google-cloud-env (1.4.0)
112 | faraday (>= 0.17.3, < 2.0)
113 | google-cloud-errors (1.0.1)
114 | google-cloud-storage (1.30.0)
115 | addressable (~> 2.5)
116 | digest-crc (~> 0.4)
117 | google-apis-iamcredentials_v1 (~> 0.1)
118 | google-apis-storage_v1 (~> 0.1)
119 | google-cloud-core (~> 1.2)
120 | googleauth (~> 0.9)
121 | mini_mime (~> 1.0)
122 | googleauth (0.14.0)
123 | faraday (>= 0.17.3, < 2.0)
124 | jwt (>= 1.4, < 3.0)
125 | memoist (~> 0.16)
126 | multi_json (~> 1.11)
127 | os (>= 0.9, < 2.0)
128 | signet (~> 0.14)
129 | highline (1.7.10)
130 | http-cookie (1.0.3)
131 | domain_name (~> 0.5)
132 | httpclient (2.8.3)
133 | jmespath (1.6.1)
134 | json (2.5.1)
135 | jwt (2.2.2)
136 | memoist (0.16.2)
137 | mini_magick (4.11.0)
138 | mini_mime (1.0.2)
139 | multi_json (1.15.0)
140 | multipart-post (2.0.0)
141 | nanaimo (0.3.0)
142 | naturally (2.2.0)
143 | os (1.1.1)
144 | plist (3.6.0)
145 | public_suffix (4.0.6)
146 | rake (13.0.3)
147 | representable (3.0.4)
148 | declarative (< 0.1.0)
149 | declarative-option (< 0.2.0)
150 | uber (< 0.2.0)
151 | retriable (3.1.2)
152 | rexml (3.2.5)
153 | rouge (2.0.7)
154 | ruby2_keywords (0.0.2)
155 | rubyzip (2.3.0)
156 | security (0.1.3)
157 | signet (0.14.0)
158 | addressable (~> 2.3)
159 | faraday (>= 0.17.3, < 2.0)
160 | jwt (>= 1.5, < 3.0)
161 | multi_json (~> 1.10)
162 | simctl (1.6.8)
163 | CFPropertyList
164 | naturally
165 | slack-notifier (2.3.2)
166 | terminal-notifier (2.0.0)
167 | terminal-table (1.8.0)
168 | unicode-display_width (~> 1.1, >= 1.1.1)
169 | tty-cursor (0.7.1)
170 | tty-screen (0.8.1)
171 | tty-spinner (0.9.3)
172 | tty-cursor (~> 0.7)
173 | uber (0.1.0)
174 | unf (0.1.4)
175 | unf_ext
176 | unf_ext (0.0.7.7)
177 | unicode-display_width (1.7.0)
178 | word_wrap (1.0.0)
179 | xcodeproj (1.19.0)
180 | CFPropertyList (>= 2.3.3, < 4.0)
181 | atomos (~> 0.1.3)
182 | claide (>= 1.0.2, < 2.0)
183 | colored2 (~> 3.1)
184 | nanaimo (~> 0.3.0)
185 | xcpretty (0.3.0)
186 | rouge (~> 2.0.7)
187 | xcpretty-travis-formatter (1.0.1)
188 | xcpretty (~> 0.2, >= 0.0.7)
189 |
190 | PLATFORMS
191 | x86_64-linux
192 |
193 | DEPENDENCIES
194 | fastlane
195 |
196 | BUNDLED WITH
197 | 2.2.4
198 |
--------------------------------------------------------------------------------
/PRIVACY.md:
--------------------------------------------------------------------------------
1 |
2 | ## Privacy policy
3 |
4 | **Bodhi Timer does not collect, log or share your personal information.**
5 |
6 |
7 | The timer stores your settings on the device, they are never uploaded or shared anywhere.
8 |
9 | The Google Play Store collects data on crashes, you can opt-out of this in the Play Store settings. These crash reports are anonymized and contain no identifiable information.
10 |
11 | If you have any questions or concerns about this privacy notice, or our practices with regards to your personal information, please contact us at bodhitimer@riseup.net.
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # About
2 | Bodhi Timer is an elegant, minimalist countdown timer.
3 | It is designed mainly for use as a meditation timer but can easily be used for any similar purpose.
4 | This app is free and developed as [open-source](https://github.com/yuttadhammo/BodhiTimer) software.
5 | It collects no data and uses the minimal permissions necessary.
6 |
7 | **Want to help with translating the app? [It's easy](https://www.transifex.com/bodhi-timer/bodhi-timer-app/)**
8 |
9 | # Install
10 | [](https://f-droid.org/en/packages/org.yuttadhammo.BodhiTimer/)
11 |
12 | # Screenshots
13 |
14 | | Timer | Setting up timer | Customization | Settings |
15 | | :--: | :--: | :--: | :--: |
16 | |  |  |  |  |
17 |
18 | ## How to use
19 |
20 | Set the time via the clock icon in the top left. You may set presets by choosing a time then holding down on one of the three preset buttons.
21 |
22 | Pause / resume via the button in the bottom left, and stop the timer via the button in the bottom right. The top right button is the preference button.
23 |
24 | Animation may be toggled between an image and one of four circle animations, chosen from the preferences screen.
25 |
26 | It uses Android's built-in notification system to trigger the alarm, which means it works even when your device is asleep.
27 |
28 | ## Features
29 |
30 | - minimalist full screen UI, no clutter
31 | - uses scroll and fling gestures to set the time
32 | - set up to three presets on the time chooser
33 |
34 | - displays two animation types: fade in static image (defaults to Bodhi leaf) and animated Zen Enso (brush circle)
35 | - option to use custom image for fade in
36 |
37 | - option for timer auto-restarting
38 | - option to set multiple consecutive timers via the "adv" button
39 | - speech recognition via long-press on clock button (set multiple timers separated by the word "again")
40 |
41 | - includes different meditation timer sounds (Burmese bell, Tibetan bell, Tibetan singing bowls, Zen gong, and bird song)
42 | - option to use any ring tone as timer sound
43 | - option to use custom sound file as timer sound
44 | - other apps can start an alarm using a deep link like `bodhi://timer?times=60,120`
45 |
46 | - licensed under the [GPL 3+](https://www.gnu.org/licenses/gpl.html)
47 |
48 | ## Credits
49 |
50 | Some code is based on the free and open source [TeaTimer by Ralph Gootee](https://play.google.com/store/apps/details?id=goo.TeaTimer).
51 |
52 | The Enso image was drawn by Ryōnen Gensō (1646-1711).
53 | Next to the Enso she has written:
54 | "When you do understand yourself fully,
55 | there is not one thing."
56 |
57 | Singing Bowl Low sound
58 | [Recorded by juskiddink](https://freesound.org/people/juskiddink/sounds/122647/)
59 | Licensed under CC-BY-SA 3.0
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 |
4 | def keystoreProperties = new Properties()
5 | def keystorePropertiesFile = rootProject.file('key.properties')
6 | if (keystorePropertiesFile.exists()) {
7 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
8 | }
9 |
10 | android {
11 | compileSdkVersion 33
12 |
13 | defaultConfig {
14 | applicationId "org.yuttadhammo.BodhiTimer"
15 | minSdkVersion 23
16 | targetSdkVersion 33
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | }
19 |
20 | signingConfigs {
21 | release {
22 | keyAlias keystoreProperties['keyAlias']
23 | keyPassword keystoreProperties['keyPassword']
24 | storeFile file(keystoreProperties['storeFile'])
25 | storePassword keystoreProperties['storePassword']
26 | }
27 | }
28 |
29 | buildTypes {
30 | release {
31 | signingConfig signingConfigs.release
32 | minifyEnabled true
33 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
34 | }
35 | }
36 | compileOptions {
37 | sourceCompatibility JavaVersion.VERSION_1_8
38 | targetCompatibility JavaVersion.VERSION_1_8
39 | }
40 | namespace 'org.yuttadhammo.BodhiTimer'
41 |
42 | }
43 |
44 | dependencies {
45 | def lifecycle_version = '2.5.1'
46 |
47 | implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
48 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
49 | implementation "com.jakewharton.timber:timber:5.0.1"
50 | implementation 'androidx.preference:preference-ktx:1.2.0'
51 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
52 | implementation 'com.google.android.material:material:1.7.0'
53 | androidTestImplementation 'tools.fastlane:screengrab:2.1.1'
54 | implementation "androidx.core:core-ktx:1.9.0"
55 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
56 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
57 | androidTestImplementation 'androidx.test:runner:1.5.1'
58 | androidTestImplementation 'androidx.test:rules:1.5.0'
59 | androidTestImplementation 'tools.fastlane:screengrab:2.1.1'
60 | }
61 |
62 | repositories {
63 | mavenCentral()
64 | }
65 |
--------------------------------------------------------------------------------
/app/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/org/yuttadhammo/BodhiTimer/TimerActivityTest.kt:
--------------------------------------------------------------------------------
1 | package org.yuttadhammo.BodhiTimer
2 |
3 |
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import androidx.test.espresso.Espresso.onView
7 | import androidx.test.espresso.action.ViewActions.click
8 | import androidx.test.espresso.action.ViewActions.scrollTo
9 | import androidx.test.espresso.assertion.ViewAssertions.matches
10 | import androidx.test.espresso.matcher.ViewMatchers.*
11 | import androidx.test.filters.LargeTest
12 | import androidx.test.rule.ActivityTestRule
13 | import androidx.test.runner.AndroidJUnit4
14 | import org.hamcrest.Description
15 | import org.hamcrest.Matcher
16 | import org.hamcrest.Matchers.allOf
17 | import org.hamcrest.TypeSafeMatcher
18 | import org.junit.Rule
19 | import org.junit.Test
20 | import org.junit.runner.RunWith
21 | import tools.fastlane.screengrab.Screengrab
22 |
23 | @LargeTest
24 | @RunWith(AndroidJUnit4::class)
25 | class TimerActivityTest {
26 |
27 | @Rule
28 | @JvmField
29 | var mActivityTestRule = ActivityTestRule(TimerActivity::class.java)
30 |
31 | @Test
32 | fun timerActivityTest() {
33 | Screengrab.screenshot("main")
34 | val appCompatImageButton = onView(
35 | allOf(
36 | withId(R.id.setButton), withContentDescription("Set"),
37 | childAtPosition(
38 | allOf(
39 | withId(R.id.mainLayout),
40 | childAtPosition(
41 | withId(android.R.id.content),
42 | 0
43 | )
44 | ),
45 | 4
46 | ),
47 | isDisplayed()
48 | )
49 | )
50 | appCompatImageButton.perform(click())
51 |
52 | //val gallery = onView()
53 |
54 | val button = onView(
55 | allOf(
56 | withId(R.id.btnOk), withText("OK"),
57 | childAtPosition(
58 | allOf(
59 | withId(R.id.button_cont),
60 | childAtPosition(
61 | withId(R.id.container),
62 | 6
63 | )
64 | ),
65 | 2
66 | )
67 | )
68 | )
69 | button.perform(scrollTo(), click())
70 |
71 | val textView = onView(
72 | allOf(
73 | withId(R.id.text_top),
74 | withParent(
75 | allOf(
76 | withId(R.id.mainLayout),
77 | withParent(withId(android.R.id.content))
78 | )
79 | ),
80 | isDisplayed()
81 | )
82 | )
83 | textView.check(matches(withText("0")))
84 |
85 | }
86 |
87 | private fun childAtPosition(
88 | parentMatcher: Matcher, position: Int
89 | ): Matcher {
90 |
91 | return object : TypeSafeMatcher() {
92 | override fun describeTo(description: Description) {
93 | description.appendText("Child at position $position in parent ")
94 | parentMatcher.describeTo(description)
95 | }
96 |
97 | public override fun matchesSafely(view: View): Boolean {
98 | val parent = view.parent
99 | return parent is ViewGroup && parentMatcher.matches(parent)
100 | && view == parent.getChildAt(position)
101 | }
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/org/yuttadhammo/BodhiTimer/TimerActivityTest2.java:
--------------------------------------------------------------------------------
1 | package org.yuttadhammo.BodhiTimer;
2 |
3 |
4 | import static androidx.test.espresso.Espresso.onView;
5 | import static androidx.test.espresso.action.ViewActions.click;
6 | import static androidx.test.espresso.action.ViewActions.scrollTo;
7 | import static androidx.test.espresso.assertion.ViewAssertions.matches;
8 | import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
9 | import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
10 | import static androidx.test.espresso.matcher.ViewMatchers.withId;
11 | import static androidx.test.espresso.matcher.ViewMatchers.withParent;
12 | import static androidx.test.espresso.matcher.ViewMatchers.withText;
13 | import static org.hamcrest.Matchers.allOf;
14 |
15 | import android.view.View;
16 | import android.view.ViewGroup;
17 | import android.view.ViewParent;
18 |
19 | import androidx.test.espresso.ViewInteraction;
20 | import androidx.test.filters.LargeTest;
21 | import androidx.test.rule.ActivityTestRule;
22 | import androidx.test.runner.AndroidJUnit4;
23 |
24 | import org.hamcrest.Description;
25 | import org.hamcrest.Matcher;
26 | import org.hamcrest.TypeSafeMatcher;
27 | import org.junit.Rule;
28 | import org.junit.Test;
29 | import org.junit.runner.RunWith;
30 |
31 | import tools.fastlane.screengrab.Screengrab;
32 |
33 | @LargeTest
34 | @RunWith(AndroidJUnit4.class)
35 | public class TimerActivityTest2 {
36 |
37 | @Rule
38 | public ActivityTestRule mActivityTestRule = new ActivityTestRule<>(TimerActivity.class);
39 |
40 | private static Matcher childAtPosition(
41 | final Matcher parentMatcher, final int position) {
42 |
43 | return new TypeSafeMatcher() {
44 | @Override
45 | public void describeTo(Description description) {
46 | description.appendText("Child at position " + position + " in parent ");
47 | parentMatcher.describeTo(description);
48 | }
49 |
50 | @Override
51 | public boolean matchesSafely(View view) {
52 | ViewParent parent = view.getParent();
53 | return parent instanceof ViewGroup && parentMatcher.matches(parent)
54 | && view.equals(((ViewGroup) parent).getChildAt(position));
55 | }
56 | };
57 | }
58 |
59 | @Test
60 | public void timerActivityTest2() {
61 | Screengrab.screenshot("main");
62 | ViewInteraction appCompatImageButton = onView(
63 | allOf(withId(R.id.setButton), withContentDescription("Set"),
64 | childAtPosition(
65 | allOf(withId(R.id.mainLayout),
66 | childAtPosition(
67 | withId(android.R.id.content),
68 | 0)),
69 | 4),
70 | isDisplayed()));
71 | appCompatImageButton.perform(click());
72 |
73 | ViewInteraction button = onView(
74 | allOf(withId(R.id.btnOk), withText("OK"),
75 | childAtPosition(
76 | allOf(withId(R.id.button_cont),
77 | childAtPosition(
78 | withId(R.id.container),
79 | 6)),
80 | 2)));
81 | button.perform(scrollTo(), click());
82 |
83 | ViewInteraction textView = onView(
84 | allOf(withId(R.id.text_top),
85 | withParent(allOf(withId(R.id.mainLayout),
86 | withParent(withId(android.R.id.content)))),
87 | isDisplayed()));
88 | textView.check(matches(withText("0")));
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
20 |
21 |
27 |
32 |
33 |
34 |
35 |
36 |
37 |
40 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
57 |
58 |
59 |
64 |
65 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Animation/BodhiLeaf.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of Bodhi Timer.
3 |
4 | Bodhi Timer is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | Bodhi Timer is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with Bodhi Timer. If not, see .
16 | */
17 | package org.yuttadhammo.BodhiTimer.Animation
18 |
19 | import android.content.Context
20 | import android.graphics.Bitmap
21 | import android.graphics.BitmapFactory
22 | import android.graphics.Canvas
23 | import android.graphics.Color
24 | import android.graphics.Paint
25 | import android.graphics.PorterDuff
26 | import android.graphics.PorterDuffXfermode
27 | import android.graphics.Rect
28 | import android.net.Uri
29 | import android.os.ParcelFileDescriptor
30 | import org.yuttadhammo.BodhiTimer.Animation.TimerAnimation.TimerDrawing
31 | import org.yuttadhammo.BodhiTimer.R
32 | import org.yuttadhammo.BodhiTimer.Util.Settings
33 | import timber.log.Timber
34 | import java.io.IOException
35 |
36 |
37 | internal class BodhiLeaf(context: Context) : TimerDrawing {
38 | private var mBitmap: Bitmap? = null
39 | private val mWidth: Int
40 | private val mHeight: Int
41 | private val mProgressPaint: Paint = Paint()
42 | private var isCustom: Boolean = false
43 |
44 | init {
45 | mProgressPaint.color = Color.BLACK
46 | mProgressPaint.alpha = 255
47 | mProgressPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
48 |
49 | // Get custom bitmap
50 | mBitmap = if (!Settings.customBmp || Settings.bmpUri.isEmpty()) {
51 | isCustom = false
52 | BitmapFactory.decodeResource(context.resources, R.drawable.leaf)
53 | } else {
54 | isCustom = true
55 | val bmpUrl = Settings.bmpUri
56 | val selectedImage = Uri.parse(bmpUrl)
57 | val resolver = context.contentResolver
58 | val readOnlyMode = "r"
59 | var file: ParcelFileDescriptor? = null
60 | try {
61 | file = resolver.openFileDescriptor(selectedImage, readOnlyMode)
62 | BitmapFactory.decodeFileDescriptor(file?.fileDescriptor)
63 | } catch (e: IOException) {
64 | Timber.e(e)
65 | BitmapFactory.decodeResource(context.resources, R.drawable.leaf)
66 | } finally {
67 | file?.close()
68 | }
69 | }
70 | mHeight = mBitmap!!.height
71 | mWidth = mBitmap!!.width
72 | }
73 |
74 | /**
75 | * Updates the image to be in sync with the current time
76 | *
77 | * @param time in milliseconds
78 | * @param max the original time set in milliseconds
79 | */
80 | override fun updateImage(canvas: Canvas, time: Int, max: Int) {
81 | canvas.save()
82 | val w = canvas.clipBounds.width()
83 | val h = canvas.clipBounds.height()
84 | val rs = Rect(0, 0, mWidth, mHeight)
85 | val rd: Rect
86 | if (mHeight / mWidth > h / w) { // image skinnier than canvas
87 | val nWidth = (mWidth * (h.toFloat() / mHeight.toFloat())).toInt()
88 | val shift = (w - nWidth) / 2
89 | rd = Rect(shift, 0, nWidth + shift, h)
90 | } else { // image fatter than or equal to canvas
91 | val nHeight = (mHeight * (w.toFloat() / mWidth.toFloat())).toInt()
92 | var shift = (h - nHeight) / 2
93 | // Special tweak to visually center the leaf image:
94 | if (!isCustom) shift -= 90
95 | rd = Rect(0, shift, w, nHeight + shift)
96 | }
97 |
98 | val p = if (max != 0) (time / max.toFloat()) else 0F
99 | val alpha = (255 - 255 * p).toInt()
100 | val color = Paint()
101 | color.alpha = alpha
102 | canvas.drawBitmap(mBitmap!!, rs, rd, color)
103 | canvas.restore()
104 | }
105 |
106 | override fun configure(isEditMode: Boolean) {
107 | // Void
108 | }
109 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Animation/TimerAnimation.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of Bodhi Timer.
3 |
4 | Bodhi Timer is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | Bodhi Timer is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with Bodhi Timer. If not, see .
16 | */
17 | package org.yuttadhammo.BodhiTimer.Animation
18 |
19 | import android.content.Context
20 | import android.content.SharedPreferences
21 | import android.graphics.Canvas
22 | import android.util.AttributeSet
23 | import androidx.appcompat.widget.AppCompatImageView
24 | import timber.log.Timber
25 | import java.io.FileNotFoundException
26 | import java.util.Vector
27 |
28 | class TimerAnimation : AppCompatImageView {
29 | var mDrawings = Vector()
30 | var mIndex = 1
31 | var mLastTime = 0
32 | var mLastMax = 0
33 | var prefs: SharedPreferences? = null
34 | private val mContext: Context
35 |
36 | interface TimerDrawing {
37 | /**
38 | * Updates the image to be in sync with the current time
39 | *
40 | * @param time in milliseconds
41 | * @param max the original time set in milliseconds
42 | */
43 | fun updateImage(canvas: Canvas, time: Int, max: Int)
44 | fun configure(isEditMode: Boolean)
45 | }
46 |
47 | constructor(context: Context) : super(context) {
48 | mContext = context
49 | createDrawings(mContext)
50 | }
51 |
52 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
53 | mContext = context
54 | createDrawings(mContext)
55 | }
56 |
57 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
58 | context,
59 | attrs,
60 | defStyleAttr
61 | ) {
62 | mContext = context
63 | createDrawings(mContext)
64 | }
65 |
66 | private fun createDrawings(mContext: Context) {
67 | mDrawings = Vector()
68 | mDrawings.add(BodhiLeaf(mContext))
69 | mDrawings.add(CircleAnimation(mContext))
70 | }
71 |
72 | @set:Throws(FileNotFoundException::class)
73 | var index: Int
74 | get() = mIndex
75 | set(i) {
76 | Timber.d("Setting animation index to $i")
77 | mIndex = i.coerceAtLeast(0).coerceAtMost(mDrawings.size)
78 | invalidate()
79 | }
80 |
81 | fun updateImage(time: Int, max: Int) {
82 | mLastTime = time
83 | mLastMax = max
84 | invalidate()
85 | }
86 |
87 | public override fun onDraw(canvas: Canvas) {
88 | if (mIndex < 0 || mIndex >= mDrawings.size) mIndex = 0
89 | if (isInEditMode) {
90 | createDrawings(context)
91 | configure()
92 | }
93 | val drawing = mDrawings[mIndex]
94 | drawing.updateImage(canvas, mLastTime, mLastMax)
95 | }
96 |
97 | fun configure() {
98 | for (drawing in mDrawings) {
99 | drawing.configure(isInEditMode)
100 | }
101 | }
102 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/BodhiApp.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * BodhiApp.kt
3 | * Copyright (C) 2014-2022 BodhiTimer developers
4 | *
5 | * Distributed under terms of the GNU GPLv3 license.
6 | */
7 |
8 | package org.yuttadhammo.BodhiTimer
9 |
10 | import android.app.Application
11 | import android.content.BroadcastReceiver
12 | import android.content.Context
13 | import android.content.Intent
14 | import android.content.IntentFilter
15 | import android.os.Build
16 | import android.os.StrictMode
17 | import android.os.StrictMode.ThreadPolicy
18 | import android.os.StrictMode.VmPolicy
19 | import org.yuttadhammo.BodhiTimer.Const.BroadcastTypes
20 | import org.yuttadhammo.BodhiTimer.Models.AlarmTaskManager
21 | import timber.log.Timber
22 | import timber.log.Timber.DebugTree
23 |
24 | /**
25 | * The Main class of the Application
26 | */
27 |
28 | class BodhiApp : Application() {
29 |
30 | var alarmTaskManager: AlarmTaskManager? = null
31 | private var initiated: Boolean = false
32 |
33 | init {
34 | instance = this
35 | if (BuildConfig.DEBUG) {
36 | StrictMode.setThreadPolicy(ThreadPolicy.Builder().detectAll().penaltyLog().build())
37 | StrictMode.setVmPolicy(VmPolicy.Builder().detectAllExceptSocket().penaltyLog().build())
38 | }
39 | }
40 |
41 |
42 | override fun onCreate() {
43 | initiated = true
44 | super.onCreate()
45 |
46 | if (BuildConfig.DEBUG) {
47 | Timber.plant(DebugTree())
48 | }
49 |
50 | Timber.d("onCreate called")
51 |
52 | alarmTaskManager = AlarmTaskManager(this)
53 |
54 | val filter = IntentFilter()
55 | filter.addAction(BroadcastTypes.BROADCAST_END)
56 | registerReceiver(alarmEndReceiver, filter)
57 | }
58 |
59 |
60 | // Should move to Manager....
61 | // receiver to get restart
62 | private val alarmEndReceiver: BroadcastReceiver = object : BroadcastReceiver() {
63 | override fun onReceive(context: Context, intent: Intent) {
64 | Timber.v("Received app alarm callback in App scope")
65 | Timber.d("id " + intent.getIntExtra("id", -1))
66 | alarmTaskManager!!.onAlarmEnd(intent.getIntExtra("id", -1))
67 | }
68 | }
69 |
70 | companion object {
71 | var instance: BodhiApp? = null
72 |
73 | fun applicationContext(): Context {
74 | return instance!!.applicationContext
75 | }
76 | }
77 | }
78 |
79 | private fun VmPolicy.Builder.detectAllExceptSocket(): VmPolicy.Builder {
80 | detectLeakedSqlLiteObjects()
81 | detectActivityLeaks()
82 | detectLeakedClosableObjects()
83 | detectLeakedRegistrationObjects()
84 | detectFileUriExposure()
85 |
86 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
87 | detectContentUriWithoutPermission()
88 | }
89 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
90 | detectCredentialProtectedWhileLocked()
91 | }
92 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
93 | detectUnsafeIntentLaunch()
94 | detectIncorrectContextUse()
95 | }
96 | return this
97 | }
98 |
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Const/BroadcastTypes.kt:
--------------------------------------------------------------------------------
1 | package org.yuttadhammo.BodhiTimer.Const
2 |
3 | object BroadcastTypes {
4 | const val BROADCAST_UPDATE = "org.yuttadhammo.BodhiTimer.ACTION_CLOCK_UPDATE"
5 | const val BROADCAST_STOP = "org.yuttadhammo.BodhiTimer.ACTION_CLOCK_CANCEL"
6 | const val BROADCAST_PLAY = "org.yuttadhammo.BodhiTimer.ACTION_PLAY"
7 | const val BROADCAST_RESET = "org.yuttadhammo.BodhiTimer.RESTART"
8 | const val BROADCAST_END = "org.yuttadhammo.BodhiTimer.ALARMEND"
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Const/SessionTypes.kt:
--------------------------------------------------------------------------------
1 | package org.yuttadhammo.BodhiTimer.Const
2 |
3 | enum class SessionTypes {
4 | REAL, PREPARATION, INVALID
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Const/TimerState.kt:
--------------------------------------------------------------------------------
1 | package org.yuttadhammo.BodhiTimer.Const
2 |
3 | object TimerState {
4 | const val RUNNING = 0
5 | const val STOPPED = 1
6 | const val PAUSED = 2
7 |
8 | fun getText(number: Int): String {
9 | return when (number) {
10 | 0 -> "RUNNING"
11 | 1 -> "STOPPED"
12 | 2 -> "PAUSED"
13 | else -> "UNDEFINED"
14 | }
15 | }
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Models/AlarmTask.kt:
--------------------------------------------------------------------------------
1 | package org.yuttadhammo.BodhiTimer.Models
2 |
3 | import android.app.AlarmManager
4 | import android.app.AlarmManager.AlarmClockInfo
5 | import android.app.PendingIntent
6 | import android.content.Context
7 | import android.content.Intent
8 | import android.net.Uri
9 | import android.os.Build
10 | import android.provider.Settings
11 | import androidx.core.content.ContextCompat
12 | import org.yuttadhammo.BodhiTimer.Const.BroadcastTypes
13 | import org.yuttadhammo.BodhiTimer.Const.SessionTypes
14 | import org.yuttadhammo.BodhiTimer.Service.TimerReceiver
15 | import timber.log.Timber
16 |
17 |
18 | data class AlarmTask(val context: Context, val offset: Int, val duration: Int) {
19 |
20 | // The Android system alarm manager
21 | private var mAlarmMgr: AlarmManager =
22 | context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
23 | private var mPendingIntent: PendingIntent? = null
24 |
25 | var sessionType: SessionTypes = SessionTypes.INVALID
26 | var uri: String = ""
27 | var id: Int = 0
28 |
29 | fun run() {
30 | if (!ensureNecessaryPermission()) return
31 | val intent = Intent(context, TimerReceiver::class.java)
32 | intent.putExtra("offset", offset)
33 | intent.putExtra("duration", duration)
34 | intent.putExtra("uri", uri)
35 | intent.putExtra("id", id)
36 | intent.action = BroadcastTypes.BROADCAST_END
37 | val time = duration + offset
38 | Timber.i(
39 | "Running new alarm task $id, " +
40 | "uri: $uri, type: $sessionType " +
41 | "due in ${time / 1000}, duration $duration"
42 | )
43 |
44 | mPendingIntent = PendingIntent.getBroadcast(
45 | context,
46 | id,
47 | intent,
48 | PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
49 | )
50 |
51 | val alarmInfoIntent = Intent(context, TimerReceiver::class.java)
52 | val pendingAlarmInfo = PendingIntent.getBroadcast(context,
53 | id + 1000, alarmInfoIntent, PendingIntent.FLAG_IMMUTABLE)
54 | val info = AlarmClockInfo(System.currentTimeMillis() + time, pendingAlarmInfo)
55 | mAlarmMgr.setAlarmClock(info, mPendingIntent)
56 | }
57 |
58 | private fun ensureNecessaryPermission(): Boolean {
59 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
60 | val alarmManager = ContextCompat.getSystemService(context, AlarmManager::class.java)
61 | if (alarmManager?.canScheduleExactAlarms() == false) {
62 | Intent().also { intent ->
63 | intent.action = Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM
64 | intent.data = Uri.fromParts("package", context.packageName, null)
65 | intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or
66 | Intent.FLAG_ACTIVITY_CLEAR_TASK
67 | context.startActivity(intent)
68 | }
69 | return false
70 | }
71 | }
72 | return true
73 | }
74 |
75 | fun cancel() {
76 | if (mPendingIntent != null) mAlarmMgr.cancel(mPendingIntent)
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Models/TimerList.kt:
--------------------------------------------------------------------------------
1 | package org.yuttadhammo.BodhiTimer.Models
2 |
3 | import android.text.TextUtils
4 | import org.yuttadhammo.BodhiTimer.Const.SessionTypes
5 | import timber.log.Timber
6 |
7 | class TimerList {
8 | class Timer {
9 | val duration: Int
10 | val uri: String
11 | val sessionType: SessionTypes
12 |
13 | constructor(mDuration: Int, mUri: String) : super() {
14 | duration = mDuration
15 | uri = mUri
16 | sessionType = SessionTypes.REAL
17 | }
18 |
19 | constructor(mDuration: Int, mUri: String, mSessionType: SessionTypes) : super() {
20 | duration = mDuration
21 | uri = mUri
22 | sessionType = mSessionType
23 | }
24 | }
25 |
26 | val timers: ArrayList
27 |
28 | constructor(advTimeString: String) {
29 | timers = timeStringToList(advTimeString)
30 | }
31 |
32 | constructor() {
33 | timers = ArrayList()
34 | }
35 |
36 | val string: String
37 | get() = listToTimeString(timers)
38 |
39 | companion object {
40 | fun timeStringToList(advTimeString: String): ArrayList {
41 | val list = ArrayList()
42 | val advTime = advTimeString.split("^").toTypedArray()
43 | for (s in advTime) {
44 | // advTime[n] will be of format timeInMs#pathToSound
45 | val thisAdvTime = s.split("#").toTypedArray()
46 | var duration: Int
47 | try {
48 | duration = thisAdvTime[0].toInt()
49 | val timer = Timer(duration, thisAdvTime[1])
50 | list.add(timer)
51 | } catch (e: Exception) {
52 | Timber.e(e)
53 | }
54 | }
55 | return list
56 | }
57 |
58 | fun listToTimeString(list: ArrayList): String {
59 | val stringArray = ArrayList()
60 | for (timer in list) {
61 | stringArray.add(timer.duration.toString() + "#" + timer.uri + "#" + timer.sessionType)
62 | }
63 | return TextUtils.join("^", stringArray)
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Service/SoundService.kt:
--------------------------------------------------------------------------------
1 | package org.yuttadhammo.BodhiTimer.Service
2 |
3 | import android.app.Service
4 | import android.content.BroadcastReceiver
5 | import android.content.Context
6 | import android.content.Intent
7 | import android.content.IntentFilter
8 | import android.media.MediaPlayer
9 | import android.os.Binder
10 | import android.os.IBinder
11 | import org.yuttadhammo.BodhiTimer.Const.BroadcastTypes
12 | import org.yuttadhammo.BodhiTimer.Const.BroadcastTypes.BROADCAST_PLAY
13 | import org.yuttadhammo.BodhiTimer.Util.Notifications.getServiceNotification
14 | import org.yuttadhammo.BodhiTimer.Util.Settings
15 | import org.yuttadhammo.BodhiTimer.Util.Sounds
16 | import timber.log.Timber
17 | import java.lang.ref.WeakReference
18 |
19 | class SoundService : Service() {
20 |
21 | private var lastMediaPlayer: MediaPlayer? = null
22 | private var stop: Boolean = false
23 | private var lastStamp: Long = 0L
24 | private var active: Int = 0
25 |
26 | private lateinit var soundManager: Sounds
27 |
28 | // Create the instance on the service.
29 | private val binder = LocalBinder()
30 |
31 | override fun onCreate() {
32 | super.onCreate()
33 | startForeground(1312, getServiceNotification(this))
34 | Timber.v("here")
35 | }
36 |
37 | override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
38 | startForeground(1312, getServiceNotification(this))
39 |
40 | soundManager = Sounds(applicationContext)
41 |
42 | val action = intent.action
43 |
44 | if (BROADCAST_PLAY == action) {
45 | Timber.v("Received Play Start")
46 | playIntent(intent)
47 | }
48 |
49 | val filter = IntentFilter()
50 | filter.addAction(BroadcastTypes.BROADCAST_END)
51 | registerReceiver(alarmEndReceiver, filter)
52 |
53 | val filter2 = IntentFilter()
54 | filter2.addAction(BroadcastTypes.BROADCAST_STOP)
55 | registerReceiver(stopReceiver, filter2)
56 |
57 | return START_NOT_STICKY
58 | }
59 |
60 | fun playIntent(intent: Intent) {
61 | val volume = Settings.toneVolume
62 | val stamp = intent.getLongExtra("stamp", 0L)
63 | val uri = intent.getStringExtra("uri")
64 |
65 | if (uri != null && stamp != lastStamp) {
66 | lastStamp = stamp
67 | active++
68 |
69 | lastMediaPlayer = soundManager.play(uri, volume)
70 |
71 | lastMediaPlayer?.setOnCompletionListener { mp ->
72 | Timber.v("Resetting media player...")
73 | mp.reset()
74 | mp.release()
75 | active--
76 |
77 | if (stop && active < 1) {
78 | Timber.v("Stopping service")
79 | stopSelf()
80 | }
81 | }
82 |
83 | } else {
84 | Timber.v("Skipping play")
85 | }
86 | }
87 |
88 | override fun onBind(intent: Intent): IBinder {
89 | binder.onBind(this)
90 | return binder
91 | }
92 |
93 | override fun onDestroy() {
94 | super.onDestroy()
95 | unregisterReceiver(alarmEndReceiver)
96 | unregisterReceiver(stopReceiver)
97 | }
98 |
99 | private val alarmEndReceiver: BroadcastReceiver = object : BroadcastReceiver() {
100 | override fun onReceive(context: Context, intent: Intent) {
101 | Timber.v("Received Broadcast")
102 | playIntent(intent)
103 | }
104 | }
105 |
106 | private val stopReceiver: BroadcastReceiver = object : BroadcastReceiver() {
107 | override fun onReceive(context: Context, intent: Intent) {
108 | Timber.e("Received Stop Broadcast, active = $active")
109 |
110 | if (active == 0) {
111 | Timber.v("Stopping service")
112 | stopSelf()
113 | }
114 | stop = true
115 | }
116 | }
117 |
118 | class LocalBinder : Binder() {
119 | private var weakService: WeakReference? = null
120 |
121 | // Inject service instance to weak reference.
122 | fun onBind(service: SoundService) {
123 | weakService = WeakReference(service)
124 | }
125 |
126 | fun getService(): SoundService? {
127 | return weakService?.get()
128 | }
129 | }
130 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Service/TTSService.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of Bodhi Timer.
3 |
4 | Bodhi Timer is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | Bodhi Timer is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with Bodhi Timer. If not, see .
16 | */
17 | package org.yuttadhammo.BodhiTimer.Service
18 |
19 | import android.app.Service
20 | import android.content.Intent
21 | import android.os.IBinder
22 | import android.speech.tts.TextToSpeech
23 | import android.speech.tts.TextToSpeech.OnInitListener
24 | import timber.log.Timber
25 |
26 | class TTSService : Service(), OnInitListener {
27 | private var mTts: TextToSpeech? = null
28 | private var spokenText: String? = null
29 | override fun onCreate() {
30 | mTts = TextToSpeech(this, this)
31 | }
32 |
33 | override fun onInit(status: Int) {
34 | Timber.i("initializing TTSService")
35 | if (status == TextToSpeech.SUCCESS) {
36 | val hashAudio = HashMap()
37 | hashAudio[TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID] = "english"
38 | Timber.i("speaking: $spokenText")
39 | mTts!!.setOnUtteranceCompletedListener { s: String? ->
40 | Timber.d("utterance completed")
41 | stopSelf()
42 | }
43 | mTts!!.speak(spokenText, TextToSpeech.QUEUE_FLUSH, hashAudio)
44 | } else Timber.e("error initializing TTSService")
45 | }
46 |
47 | override fun onDestroy() {
48 | if (mTts != null) {
49 | mTts!!.stop()
50 | mTts!!.shutdown()
51 | }
52 | super.onDestroy()
53 | }
54 |
55 | override fun onBind(arg0: Intent): IBinder? {
56 | return null
57 | }
58 |
59 | override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
60 | spokenText = intent.getStringExtra("spoken_text")
61 | Timber.d(spokenText!!)
62 | return START_STICKY
63 | }
64 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Service/TimerReceiver.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of Bodhi Timer.
3 |
4 | Bodhi Timer is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | Bodhi Timer is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with Bodhi Timer. If not, see .
16 | */
17 | package org.yuttadhammo.BodhiTimer.Service
18 |
19 | import android.content.BroadcastReceiver
20 | import android.content.Context
21 | import android.content.Intent
22 | import androidx.preference.PreferenceManager
23 | import org.yuttadhammo.BodhiTimer.Const.BroadcastTypes.BROADCAST_END
24 | import org.yuttadhammo.BodhiTimer.Const.BroadcastTypes.BROADCAST_PLAY
25 | import org.yuttadhammo.BodhiTimer.Util.Notifications.show
26 | import org.yuttadhammo.BodhiTimer.Util.Sounds
27 | import timber.log.Timber
28 |
29 |
30 | // This class handles the alarm callback
31 | class TimerReceiver : BroadcastReceiver() {
32 | private var notificationUri: String? = null
33 | private var stamp: Long = 0
34 | private var volume: Int = 100
35 | lateinit var mContext: Context
36 |
37 | override fun onReceive(context: Context, mIntent: Intent) {
38 | Timber.v("Received system alarm callback ")
39 |
40 | stamp = System.currentTimeMillis()
41 | mContext = context
42 |
43 | // Send Broadcast to main activity
44 | // This will be only received if the app is not stopped (or destroyed)...
45 | val broadcast = Intent()
46 | broadcast.putExtra("duration", mIntent.getIntExtra("duration", 0))
47 | broadcast.putExtra("id", mIntent.getIntExtra("id", 0))
48 | broadcast.putExtra("uri", mIntent.getStringExtra("uri"))
49 | broadcast.putExtra("stamp", stamp)
50 | broadcast.action = BROADCAST_END
51 | mContext.sendBroadcast(broadcast)
52 |
53 | // Show notification
54 | notificationUri = mIntent.getStringExtra("uri")
55 | val duration = mIntent.getIntExtra("duration", 0)
56 | val prefs = PreferenceManager.getDefaultSharedPreferences(mContext)
57 | val alwaysShow = prefs.getBoolean("showAlwaysNotifications", false)
58 |
59 | if (alwaysShow || notificationUri == null) {
60 | show(mContext, duration)
61 | }
62 |
63 | volume = prefs.getInt("tone_volume", 0)
64 |
65 | if (notificationUri == null) return
66 |
67 | if (!prefs.getBoolean("useOldNotification", false)) {
68 | val playIntent = getServiceIntent(mContext)
69 |
70 | try {
71 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
72 | mContext.startForegroundService(playIntent)
73 | } else {
74 | mContext.startService(playIntent)
75 | }
76 | } catch (e: Exception) {
77 | Timber.e("Could not start service")
78 | Sounds(mContext).play(notificationUri!!, volume)
79 | }
80 | } else {
81 | Sounds(mContext).play(notificationUri!!, volume)
82 | }
83 | }
84 |
85 |
86 | private fun getServiceIntent(mContext: Context): Intent {
87 | val playIntent = Intent(mContext, SoundService::class.java)
88 | playIntent.action = BROADCAST_PLAY
89 | playIntent.putExtra("uri", notificationUri)
90 | playIntent.putExtra("volume", volume)
91 | playIntent.putExtra("stamp", stamp)
92 | return playIntent
93 | }
94 |
95 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/SettingsActivity.kt:
--------------------------------------------------------------------------------
1 | package org.yuttadhammo.BodhiTimer
2 |
3 | import android.content.Intent
4 | import android.content.SharedPreferences
5 | import android.media.RingtoneManager
6 | import android.net.Uri
7 | import android.os.Bundle
8 | import android.view.View
9 | import androidx.appcompat.app.AppCompatActivity
10 | import androidx.appcompat.widget.Toolbar
11 | import androidx.preference.PreferenceManager
12 | import org.yuttadhammo.BodhiTimer.Util.Settings
13 | import org.yuttadhammo.BodhiTimer.Util.Themes
14 | import timber.log.Timber
15 |
16 | class SettingsActivity : AppCompatActivity() {
17 | private var prefs: SharedPreferences? = null
18 |
19 | override fun onCreate(savedInstanceState: Bundle?) {
20 | super.onCreate(savedInstanceState)
21 | Themes.applyTheme(this)
22 | setContentView(R.layout.settings_activity)
23 | val toolbar = findViewById(R.id.toolbar)
24 | setSupportActionBar(toolbar)
25 | val actionBar = supportActionBar
26 | if (actionBar != null) {
27 | actionBar.setDisplayHomeAsUpEnabled(true)
28 | actionBar.title = getString(R.string.preferences)
29 | toolbar.setNavigationOnClickListener { v: View? -> onBackPressed() }
30 | }
31 | supportFragmentManager
32 | .beginTransaction()
33 | .replace(R.id.settings, SettingsFragment())
34 | .commit()
35 | prefs = PreferenceManager.getDefaultSharedPreferences(baseContext)
36 | }
37 |
38 |
39 | public override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
40 | super.onActivityResult(requestCode, resultCode, intent)
41 | if (resultCode == RESULT_OK) {
42 | var uri = intent!!.data
43 | val uriString = intent.dataString
44 | when (requestCode) {
45 | SELECT_RINGTONE -> {
46 | uri = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
47 | if (uri != null) {
48 | Timber.i("Got ringtone $uri")
49 | Settings.systemUri = uri.toString()
50 | }
51 | }
52 | SELECT_PRE_RINGTONE -> {
53 | uri = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
54 | if (uri != null) {
55 | Timber.i("Got ringtone $uri")
56 | Settings.preSystemUri = uri.toString()
57 | }
58 | }
59 | SELECT_FILE -> // Get the Uri of the selected file
60 | if (uriString != null) {
61 | getPersistablePermission(uri)
62 | Timber.i("File Path: " + uri.toString())
63 | Settings.fileUri = uri.toString()
64 | }
65 | SELECT_PRE_FILE -> // Get the Uri of the selected file
66 | if (uriString != null) {
67 | getPersistablePermission(uri)
68 | Timber.i("File Path: " + uri.toString())
69 | Settings.preFileUri = uri.toString()
70 | }
71 | SELECT_PHOTO -> if (uri != null) {
72 | getPersistablePermission(uri)
73 | Settings.bmpUri = uri.toString()
74 | }
75 | }
76 | }
77 | }
78 |
79 | private fun getPersistablePermission(uri: Uri?) {
80 | try {
81 | contentResolver.takePersistableUriPermission(
82 | uri!!,
83 | Intent.FLAG_GRANT_READ_URI_PERMISSION
84 | )
85 | } catch (e: Exception) {
86 | Timber.e(e.toString())
87 | }
88 | }
89 |
90 |
91 | companion object {
92 | private const val SELECT_RINGTONE = 0
93 | private const val SELECT_FILE = 1
94 | private const val SELECT_PRE_RINGTONE = 2
95 | private const val SELECT_PRE_FILE = 3
96 | private const val SELECT_PHOTO = 4
97 |
98 | }
99 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Util/Notifications.kt:
--------------------------------------------------------------------------------
1 | package org.yuttadhammo.BodhiTimer.Util
2 |
3 | import android.app.Notification
4 | import android.app.NotificationChannel
5 | import android.app.NotificationManager
6 | import android.app.PendingIntent
7 | import android.content.Context
8 | import android.content.Intent
9 | import android.content.SharedPreferences
10 | import android.os.Build
11 | import androidx.core.app.NotificationCompat
12 | import androidx.preference.PreferenceManager
13 | import org.yuttadhammo.BodhiTimer.R
14 | import org.yuttadhammo.BodhiTimer.TimerActivity
15 | import timber.log.Timber
16 |
17 |
18 | object Notifications {
19 |
20 | private const val ALARM_CHANNEL_ID = "ALARMS"
21 | private const val SERVICE_CHANNEL_ID = "SERVICE"
22 |
23 |
24 | fun show(context: Context, time: Int) {
25 | Timber.v("Showing notification... $time")
26 |
27 | // Get Notification Manager & Prefs
28 | val mNotificationManager =
29 | context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
30 | val prefs = PreferenceManager.getDefaultSharedPreferences(context)
31 |
32 |
33 | // Cancel any previous notifications
34 | mNotificationManager.cancelAll()
35 |
36 |
37 | // Construct strings
38 | val setTimeStr = Time.time2humanStr(context, time)
39 | val text = context.getText(R.string.Notification)
40 | val textLatest: CharSequence =
41 | String.format(context.getString(R.string.timer_for_x), setTimeStr)
42 |
43 |
44 | // Create the notification
45 | val mBuilder = NotificationCompat.Builder(context.applicationContext, ALARM_CHANNEL_ID)
46 | .setSmallIcon(R.drawable.notification)
47 | .setContentTitle(text)
48 | .setContentText(textLatest)
49 | .setPriority(NotificationCompat.PRIORITY_HIGH)
50 |
51 | // Handle light and vibrate in older devices
52 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
53 | legacyHandler(mBuilder, prefs)
54 | }
55 |
56 | mNotificationManager.notify(0, mBuilder.build())
57 | }
58 |
59 | fun getServiceNotification(context: Context): Notification {
60 |
61 | // Create pending intent to be triggered when user clicks on notification
62 | val contentIntent = PendingIntent.getActivity(
63 | context, 0,
64 | Intent(context, TimerActivity::class.java), PendingIntent.FLAG_IMMUTABLE
65 | )
66 |
67 | return NotificationCompat.Builder(context, SERVICE_CHANNEL_ID)
68 | .setContentTitle(context.getText(R.string.app_name))
69 | .setContentText(context.getText(R.string.service_text))
70 | .setSmallIcon(R.drawable.notification)
71 | .setContentIntent(contentIntent)
72 | .setTicker(context.getText(R.string.service_text))
73 | .build()
74 | }
75 |
76 | private fun legacyHandler(mBuilder: NotificationCompat.Builder, prefs: SharedPreferences) {
77 | val vibrate = prefs.getBoolean("Vibrate", true)
78 | val led = prefs.getBoolean("LED", false)
79 |
80 | // Vibrate
81 | if (vibrate) {
82 | mBuilder.setDefaults(Notification.DEFAULT_VIBRATE)
83 | }
84 |
85 | // Have a light
86 | if (led) {
87 | mBuilder.setLights(-0xff0100, 300, 1000)
88 | }
89 | }
90 |
91 | fun createNotificationChannel(context: Context) {
92 | // Get Notification Manager & Prefs
93 | val mNotificationManager =
94 | context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
95 | val prefs = PreferenceManager.getDefaultSharedPreferences(context)
96 |
97 | // Create the NotificationChannel, but only on API 26+ because
98 | // the NotificationChannel class is new and not in the support library
99 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
100 | var name: CharSequence = context.getString(R.string.alarm_channel_name)
101 | var description = context.getString(R.string.alarm_channel_description)
102 | var importance = NotificationManager.IMPORTANCE_HIGH
103 |
104 | val alarmChannel = NotificationChannel(ALARM_CHANNEL_ID, name, importance)
105 | alarmChannel.description = description
106 |
107 | // Customize
108 | val vibrate = prefs.getBoolean("Vibrate", true)
109 | val led = prefs.getBoolean("LED", false)
110 |
111 | // Vibrate
112 | if (vibrate) {
113 | val pattern = longArrayOf(0, 400, 200, 400)
114 | alarmChannel.vibrationPattern = pattern
115 | alarmChannel.enableVibration(true)
116 | }
117 |
118 | // Have a light
119 | if (led) {
120 | alarmChannel.lightColor = -0xff0100
121 | alarmChannel.enableLights(true)
122 | }
123 |
124 | // We are playing the sound ourselves,
125 | // because notification channels don't allow changing sounds.
126 | alarmChannel.setSound(null, null)
127 |
128 | // Register the channel with the system; you can't change the importance
129 | // or other notification behaviors after this
130 | mNotificationManager.createNotificationChannel(alarmChannel)
131 |
132 | name = context.getString(R.string.service_channel_name)
133 | description = context.getString(R.string.service_channel_description)
134 | importance = NotificationManager.IMPORTANCE_LOW
135 |
136 | val serviceChannel = NotificationChannel(SERVICE_CHANNEL_ID, name, importance)
137 | serviceChannel.description = description
138 | mNotificationManager.createNotificationChannel(serviceChannel)
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Util/Settings.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Settings.kt
3 | * Copyright (C) 2009-2022 Ultrasonic developers
4 | *
5 | * Distributed under terms of the GNU GPLv3 license.
6 | */
7 |
8 | package org.yuttadhammo.BodhiTimer.Util
9 |
10 | import android.content.Context
11 | import android.content.SharedPreferences
12 | import androidx.preference.PreferenceManager
13 | import org.yuttadhammo.BodhiTimer.BodhiApp
14 | import org.yuttadhammo.BodhiTimer.R
15 |
16 | /**
17 | * Contains convenience functions for reading and writing preferences
18 | */
19 | const val DEFAULT_DURATION = 120000
20 |
21 | object Settings {
22 |
23 | val toneVolume by IntSetting(
24 | "tone_volume",
25 | 90
26 | )
27 |
28 | val customBmp by BooleanSetting(
29 | "custom_bmp",
30 | false
31 | )
32 | var bmpUri by StringSetting(
33 | "bmp_url"
34 | )
35 | val doNotDisturb by BooleanSetting(
36 | "doNotDisturb",
37 | false
38 | )
39 | val hideTime by BooleanSetting(
40 | "hideTime",
41 | false
42 | )
43 | val switchTimeMode by BooleanSetting(
44 | "SwitchTimeMode",
45 | false
46 | )
47 | var preFileUri by StringSetting(
48 | "PreFileUri",
49 | ""
50 | )
51 | var preSystemUri by StringSetting(
52 | "PreSystemUri",
53 | ""
54 | )
55 | val preparationTime by IntSetting(
56 | "preparationTime",
57 | 0
58 | )
59 |
60 | val preSoundUri by StringSetting(
61 | "PreSoundUri", ""
62 | )
63 | val notificationUri by StringSetting(
64 | "NotificationUri", Sounds.DEFAULT_SOUND
65 | )
66 | var systemUri by StringSetting(
67 | "SystemUri", Sounds.DEFAULT_SOUND
68 | )
69 | var fileUri by StringSetting(
70 | "FileUri", Sounds.DEFAULT_SOUND
71 | )
72 | val speakTime by BooleanSetting(
73 | "SpeakTime", false
74 | )
75 | val wakeLock by BooleanSetting(
76 | "WakeLock",
77 | false
78 | )
79 |
80 | var lastSimpleTime by IntSetting(
81 | "LastSimpleTime",
82 | DEFAULT_DURATION
83 | )
84 |
85 | @JvmStatic
86 | val preferences: SharedPreferences
87 | get() = PreferenceManager.getDefaultSharedPreferences(appContext)
88 |
89 |
90 | @JvmStatic
91 | var theme by StringSetting(
92 | getKey(R.string.setting_key_theme),
93 | getKey(R.string.setting_key_theme_day_night)
94 | )
95 |
96 | val isDarkTheme: Boolean
97 | get() = (theme == getKey(R.string.setting_key_theme_dark) ||
98 | theme == getKey(R.string.setting_key_theme_black))
99 |
100 | var drawingIndex by IntSetting(
101 | "DrawingIndex",
102 | 1
103 | )
104 |
105 | var preset1 by StringSetting(
106 | "pre1"
107 | )
108 | var preset2 by StringSetting(
109 | "pre2"
110 | )
111 | var preset3 by StringSetting(
112 | "pre3"
113 | )
114 | var preset4 by StringSetting(
115 | "pre4"
116 | )
117 |
118 | var fullscreen by BooleanSetting(
119 | "FULLSCREEN",
120 | false
121 | )
122 |
123 | var advTimeString by StringSetting(
124 | "advTimeString",
125 | ""
126 | )
127 |
128 | var timeString by StringSetting(
129 | "timeString",
130 | ""
131 | )
132 |
133 | var lastWasSimple by BooleanSetting(
134 | "LastWasSimple",
135 | true
136 | )
137 |
138 | var autoRestart by BooleanSetting(
139 | "AutoRestart",
140 | false
141 | )
142 |
143 | fun hasKey(key: String): Boolean {
144 | return preferences.contains(key)
145 | }
146 |
147 | private fun getKey(key: Int): String {
148 | return appContext.getString(key)
149 | }
150 |
151 | fun getAllKeys(): List {
152 | val prefs = PreferenceManager.getDefaultSharedPreferences(BodhiApp.applicationContext())
153 | return prefs.all.keys.toList()
154 | }
155 |
156 | private val appContext: Context
157 | get() = BodhiApp.applicationContext()
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Util/SettingsDelegate.kt:
--------------------------------------------------------------------------------
1 | package org.yuttadhammo.BodhiTimer.Util
2 |
3 | import android.content.SharedPreferences
4 | import androidx.core.content.edit
5 | import androidx.preference.PreferenceManager
6 | import org.yuttadhammo.BodhiTimer.BodhiApp
7 | import kotlin.properties.ReadWriteProperty
8 | import kotlin.reflect.KProperty
9 |
10 | /**
11 | * Yet another implementation of Shared Preferences using Delegated Properties
12 | *
13 | * Check out https://medium.com/@FrostRocketInc/delegated-shared-preferences-in-kotlin-45b82d6e52d0
14 | * for a detailed walkthrough.
15 | *
16 | * @author Matthew Groves
17 | */
18 |
19 | abstract class SettingsDelegate : ReadWriteProperty {
20 | protected val sharedPreferences: SharedPreferences by lazy {
21 | PreferenceManager.getDefaultSharedPreferences(BodhiApp.applicationContext())
22 | }
23 | }
24 |
25 | class StringSetting(private val key: String, private val defaultValue: String = "") :
26 | SettingsDelegate() {
27 | override fun getValue(thisRef: Any, property: KProperty<*>) =
28 | sharedPreferences.getString(key, defaultValue)!!
29 |
30 | override fun setValue(thisRef: Any, property: KProperty<*>, value: String) =
31 | sharedPreferences.edit { putString(key, value) }
32 | }
33 |
34 | class IntSetting(private val key: String, private val defaultValue: Int = 0) :
35 | SettingsDelegate() {
36 | override fun getValue(thisRef: Any, property: KProperty<*>) =
37 | sharedPreferences.getInt(key, defaultValue)
38 |
39 | override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) =
40 | sharedPreferences.edit { putInt(key, value) }
41 | }
42 |
43 | class StringIntSetting(private val key: String, private val defaultValue: Int = 0) :
44 | SettingsDelegate() {
45 | override fun getValue(thisRef: Any, property: KProperty<*>) =
46 | sharedPreferences.getString(key, defaultValue.toString())!!.toInt()
47 |
48 | override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) =
49 | sharedPreferences.edit { putString(key, value.toString()) }
50 | }
51 |
52 | class LongSetting(private val key: String, private val defaultValue: Long = 0.toLong()) :
53 | SettingsDelegate() {
54 | override fun getValue(thisRef: Any, property: KProperty<*>) =
55 | sharedPreferences.getLong(key, defaultValue)
56 |
57 | override fun setValue(thisRef: Any, property: KProperty<*>, value: Long) =
58 | sharedPreferences.edit { putLong(key, value) }
59 | }
60 |
61 | class FloatSetting(
62 | private val key: String,
63 | private val defaultValue: Float = 0.toFloat()
64 | ) : SettingsDelegate() {
65 | override fun getValue(thisRef: Any, property: KProperty<*>) =
66 | sharedPreferences.getFloat(key, defaultValue)
67 |
68 | override fun setValue(thisRef: Any, property: KProperty<*>, value: Float) =
69 | sharedPreferences.edit { putFloat(key, value) }
70 | }
71 |
72 | class BooleanSetting(private val key: String, private val defaultValue: Boolean = false) :
73 | SettingsDelegate() {
74 | override fun getValue(thisRef: Any, property: KProperty<*>) =
75 | sharedPreferences.getBoolean(key, defaultValue)
76 |
77 | override fun setValue(thisRef: Any, property: KProperty<*>, value: Boolean) =
78 | sharedPreferences.edit { putBoolean(key, value) }
79 |
80 | constructor(stringId: Int, defaultValue: Boolean = false) : this(
81 | BodhiApp.applicationContext().getString(stringId), defaultValue
82 | )
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Util/Sounds.kt:
--------------------------------------------------------------------------------
1 | package org.yuttadhammo.BodhiTimer.Util
2 |
3 | import android.content.Context
4 | import android.media.MediaPlayer
5 | import android.net.Uri
6 | import android.os.PowerManager
7 | import androidx.preference.PreferenceManager
8 | import org.yuttadhammo.BodhiTimer.R
9 | import timber.log.Timber
10 | import kotlin.math.ln
11 |
12 |
13 | class Sounds(private val mContext: Context) {
14 |
15 | private val flags: Int = PowerManager.PARTIAL_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP
16 |
17 | private fun play(mUri: Uri, volume: Int): MediaPlayer? {
18 |
19 | try {
20 | val mediaPlayer = MediaPlayer()
21 |
22 | mediaPlayer.setDataSource(mContext, mUri)
23 | mediaPlayer.prepare()
24 |
25 | //Timber.v("Volume: " + volume)
26 | if (volume != 0) {
27 | val log1 = (ln((100 - volume).toDouble()) / ln(100.0)).toFloat()
28 | mediaPlayer.setVolume(1 - log1, 1 - log1)
29 | //Timber.v("Volume: " + (1 -log1))
30 | }
31 |
32 |
33 | mediaPlayer.isLooping = false
34 |
35 | mediaPlayer.setOnCompletionListener { mp ->
36 | Timber.v("Resetting media player...")
37 | mp.reset()
38 | mp.release()
39 | }
40 |
41 | mediaPlayer.setOnErrorListener { _, what, extra ->
42 | Timber.e("what:" + what + " extra:" + extra)
43 | true
44 | }
45 |
46 | mediaPlayer.setOnInfoListener { _, what, extra ->
47 | Timber.e("what:" + what + " extra:" + extra)
48 | true
49 | }
50 |
51 | mediaPlayer.setWakeMode(mContext, flags)
52 | mediaPlayer.start()
53 |
54 | Timber.v("Playing sound")
55 |
56 | return mediaPlayer
57 | } catch (e: Exception) {
58 | Timber.w("Problem playing sound, uri: $mUri")
59 | e.printStackTrace()
60 | //throw (e)
61 | }
62 |
63 | return null
64 | }
65 |
66 |
67 | fun play(mUri: String, volume: Int): MediaPlayer? {
68 | val uri = resolveUri(mUri, mContext)
69 |
70 | if (uri != "") {
71 | return play(Uri.parse(uri), volume)
72 | }
73 |
74 | return null
75 | }
76 |
77 | companion object {
78 | const val DEFAULT_SOUND = "android.resource://org.yuttadhammo.BodhiTimer/${R.raw.bowl1}"
79 |
80 | fun resolveUri(mUri: String, mContext: Context): String {
81 | val prefs = PreferenceManager.getDefaultSharedPreferences(mContext)
82 | var result = ""
83 |
84 | result = mUri
85 |
86 | if (result == "sys_def") {
87 | result = prefs.getString("NotificationUri", "").toString()
88 | }
89 |
90 | when (result) {
91 | "system" -> result = prefs.getString("SystemUri", "")!!
92 | "file" -> result = prefs.getString("FileUri", "")!!
93 | }
94 |
95 | return result
96 |
97 | }
98 | }
99 |
100 |
101 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Util/Themes.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Themes.kt
3 | * Copyright (C) 2014-2022 BodhiTimer developers
4 | *
5 | * Distributed under terms of the GNU GPLv3 license.
6 | */
7 |
8 | package org.yuttadhammo.BodhiTimer.Util
9 |
10 | import android.content.Context
11 | import org.yuttadhammo.BodhiTimer.R
12 |
13 | object Themes {
14 | fun applyTheme(context: Context?) {
15 | if (context == null) return
16 | val style = getStyleFromSettings(context)
17 | // First set the theme (light, dark, etc.)
18 | context.setTheme(style)
19 | // Then set an overlay controlling the status bar behaviour etc.
20 | context.setTheme(R.style.BodhiTheme_Base)
21 | }
22 |
23 | private fun getStyleFromSettings(context: Context): Int {
24 | return when (Settings.theme.lowercase()) {
25 | context.getString(R.string.setting_key_theme_dark) -> {
26 | R.style.BodhiTheme_Dark
27 | }
28 | context.getString(R.string.setting_key_theme_light) -> {
29 | R.style.BodhiTheme_Light
30 | }
31 | context.getString(R.string.setting_key_theme_black) -> {
32 | R.style.BodhiTheme_Black
33 | }
34 | else -> {
35 | R.style.BodhiTheme_DayNight
36 | }
37 | }
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Util/Time.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Time.kt
3 | * Copyright (C) 2014-2022 BodhiTimer developers
4 | *
5 | * Distributed under terms of the GNU GPLv3 license.
6 | */
7 |
8 | package org.yuttadhammo.BodhiTimer.Util
9 |
10 | import android.content.Context
11 | import android.text.TextUtils
12 | import androidx.appcompat.app.AppCompatActivity
13 | import org.yuttadhammo.BodhiTimer.R
14 | import timber.log.Timber
15 | import java.util.regex.Pattern
16 |
17 | object Time {
18 | const val TIME_SEPARATOR = " again "
19 |
20 | private fun msFromNumbers(hour: Int, minutes: Int, seconds: Int): Int {
21 | return hour * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000
22 | }
23 |
24 | @JvmStatic
25 | fun msFromArray(numbers: IntArray): Int {
26 | return msFromNumbers(numbers[0], numbers[1], numbers[2])
27 | }
28 |
29 | private fun padWithZeroes(number: Int): String {
30 | return if (number > 9) {
31 | number.toString()
32 | } else {
33 | "0$number"
34 | }
35 | }
36 |
37 | /**
38 | * Converts a millisecond time to a string time
39 | * Not meant to be pretty, but fast..
40 | *
41 | * @param ms the time in milliseconds
42 | * @return the formatted string
43 | */
44 | private fun ms2Str(ms: Int): String {
45 | val time = time2Array(ms)
46 | return if (time[0] == 0 && time[1] == 0) {
47 | time[2].toString()
48 | } else if (time[0] == 0) {
49 | time[1].toString() + ":" + padWithZeroes(
50 | time[2]
51 | )
52 | } else {
53 | time[0].toString() + ":" + padWithZeroes(
54 | time[1]
55 | ) + ":" + padWithZeroes(time[2])
56 | }
57 | }
58 |
59 | /**
60 | * Creates a time vector
61 | *
62 | * @param time the time in milliseconds
63 | * @return [hour, minutes, seconds, ms]
64 | */
65 | @JvmStatic
66 | fun time2Array(time: Int): IntArray {
67 | val ms = time % 1000
68 | var seconds = time / 1000 // 3550000 / 1000 = 3550
69 | var minutes = seconds / 60 // 59.16666
70 | var hours = minutes / 60 // 0.9
71 | if (hours > 60) hours = 60
72 | minutes %= 60
73 | seconds %= 60
74 | val timeVec = IntArray(4)
75 | timeVec[0] = hours
76 | timeVec[1] = minutes
77 | timeVec[2] = seconds
78 | timeVec[3] = ms
79 | return timeVec
80 | }
81 |
82 | @JvmStatic
83 | fun time2humanStr(context: Context, time: Int): String {
84 | val timeVec = time2Array(time)
85 | val hour = timeVec[0]
86 | val minutes = timeVec[1]
87 | val seconds = timeVec[2]
88 | val strList = ArrayList()
89 | val res = context.resources
90 |
91 | // string formatting
92 | if (hour != 0) {
93 | strList.add(res.getQuantityString(R.plurals.x_hours, hour, hour))
94 | }
95 | if (minutes != 0) {
96 | strList.add(res.getQuantityString(R.plurals.x_mins, minutes, minutes))
97 | }
98 | if (seconds != 0 || seconds >= 0 && minutes == 0 && hour == 0) {
99 | strList.add(res.getQuantityString(R.plurals.x_secs, seconds, seconds))
100 | }
101 | return TextUtils.join(", ", strList)
102 | }
103 |
104 | fun time2hms(time: Int): String {
105 | return ms2Str(time)
106 | }
107 |
108 | @JvmStatic
109 | fun str2complexTimeString(activity: AppCompatActivity, numberString: String): String {
110 | val out: String
111 | val stringArray = ArrayList()
112 | val strings = numberString.split(TIME_SEPARATOR).toTypedArray()
113 | for (string in strings) {
114 | val atime = str2timeString(activity, string)
115 | if (atime > 0) stringArray.add(atime.toString() + "#sys_def#" + activity.getString(R.string.sys_def))
116 | }
117 | out = TextUtils.join("^", stringArray)
118 | return out
119 | }
120 |
121 | @JvmStatic
122 | fun str2timeString(activity: AppCompatActivity, numberString: String): Int {
123 | val res = activity.resources
124 | val numbers = res.getStringArray(R.array.numbers)
125 | var newString = numberString
126 |
127 | for ((position, number) in numbers.withIndex()) {
128 | val num = 60 - position
129 | newString = newString.replace(number.toRegex(), num.toString())
130 | }
131 |
132 | val HOUR = Pattern.compile("([0-9]+) " + activity.getString(R.string.hour))
133 | val MINUTE = Pattern.compile("([0-9]+) " + activity.getString(R.string.minute))
134 | val SECOND = Pattern.compile("([0-9]+) " + activity.getString(R.string.second))
135 |
136 | var hours = 0
137 | var minutes = 0
138 | var seconds = 0
139 | var m = HOUR.matcher(newString)
140 | while (m.find()) {
141 | val match = m.group(1)
142 | hours += match?.toInt() ?: 0
143 | }
144 | m = MINUTE.matcher(newString)
145 | while (m.find()) {
146 | val match = m.group(1)
147 | minutes += match?.toInt() ?: 0
148 | }
149 | m = SECOND.matcher(newString)
150 | while (m.find()) {
151 | val match = m.group(1)
152 | seconds += match?.toInt() ?: 0
153 | }
154 | Timber.d("Got numbers: $hours hours, $minutes minutes, $seconds seconds")
155 | var total = hours * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000
156 | if (total > 60 * 60 * 60 * 1000 + 59 * 60 * 1000 + 59 * 1000) total =
157 | 60 * 60 * 60 * 1000 + 59 * 60 * 1000 + 59 * 1000
158 | return total
159 | }
160 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/yuttadhammo/BodhiTimer/Widget/BodhiAppWidgetProvider.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of Bodhi Timer.
3 |
4 | Bodhi Timer is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | Bodhi Timer is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with Bodhi Timer. If not, see .
16 | */
17 | package org.yuttadhammo.BodhiTimer.Widget
18 |
19 | import android.app.PendingIntent
20 | import android.appwidget.AppWidgetManager
21 | import android.appwidget.AppWidgetProvider
22 | import android.content.ComponentName
23 | import android.content.Context
24 | import android.content.Intent
25 | import android.content.IntentFilter
26 | import android.widget.RemoteViews
27 | import org.yuttadhammo.BodhiTimer.R
28 | import org.yuttadhammo.BodhiTimer.TimerActivity
29 | import timber.log.Timber
30 |
31 |
32 | class BodhiAppWidgetProvider : AppWidgetProvider() {
33 | private var isRegistered = false
34 |
35 | override fun onUpdate(
36 | context: Context,
37 | appWidgetManager: AppWidgetManager,
38 | appWidgetIds: IntArray
39 | ) {
40 | Timber.i("onUpdate")
41 | if (!isRegistered) {
42 | context.applicationContext.registerReceiver(this, IntentFilter(Intent.ACTION_SCREEN_ON))
43 | context.applicationContext.registerReceiver(
44 | this,
45 | IntentFilter(Intent.ACTION_SCREEN_OFF)
46 | )
47 | isRegistered = true
48 | }
49 | context.sendBroadcast(Intent(ACTION_CLOCK_UPDATE))
50 | }
51 |
52 | override fun onEnabled(context: Context) {
53 | super.onEnabled(context)
54 | Timber.i("onEnabled")
55 | if (!isRegistered) {
56 | context.applicationContext.registerReceiver(this, IntentFilter(Intent.ACTION_SCREEN_ON))
57 | context.applicationContext.registerReceiver(
58 | this,
59 | IntentFilter(Intent.ACTION_SCREEN_OFF)
60 | )
61 | isRegistered = true
62 | }
63 | context.sendBroadcast(Intent(ACTION_CLOCK_UPDATE))
64 | }
65 |
66 | override fun onDisabled(context: Context) {
67 | Timber.i("onDisabled")
68 | super.onDisabled(context)
69 | }
70 |
71 | override fun onDeleted(context: Context, appWidgetIds: IntArray) {
72 | Timber.d("onDeleted")
73 | }
74 |
75 | override fun onReceive(context: Context, i: Intent) {
76 | super.onReceive(context, i)
77 | val action = i.action
78 |
79 | //stopTicking = action.equals(BROADCAST_STOP) || action.equals(Intent.ACTION_SCREEN_OFF);
80 | doUpdate(context)
81 | }
82 |
83 | private fun doUpdate(context: Context) {
84 | Timber.i("updating")
85 | if (views == null) views = RemoteViews(context.packageName, R.layout.appwidget)
86 | val intent = Intent(context, TimerActivity::class.java)
87 | intent.putExtra("set", "true")
88 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
89 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
90 | val pendingIntent = PendingIntent.getActivity(
91 | context,
92 | 0,
93 | intent,
94 | PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
95 | )
96 | val resources = context.resources
97 | appWidgetManager = AppWidgetManager.getInstance(context)
98 | val appWidgets = ComponentName(
99 | context.packageName,
100 | "org.yuttadhammo.BodhiTimer.widget.BodhiAppWidgetProvider"
101 | )
102 | val widgetIds = appWidgetManager!!.getAppWidgetIds(appWidgets)
103 | val backgrounds = HashMap()
104 | if (widgetIds.isNotEmpty()) {
105 | for (widgetId in widgetIds) {
106 |
107 |
108 | // Get the layout for the App Widget and attach an on-click listener
109 | // to the button
110 | views!!.setOnClickPendingIntent(R.id.mainImage, pendingIntent)
111 | views!!.setImageViewResource(R.id.mainImage, R.drawable.leaf);
112 |
113 | appWidgetManager?.updateAppWidget(widgetId, views)
114 | }
115 | }
116 | }
117 |
118 | companion object {
119 | private var appWidgetManager: AppWidgetManager? = null
120 | const val ACTION_CLOCK_UPDATE = "org.yuttadhammo.BodhiTimer.ACTION_CLOCK_UPDATE"
121 | private var views: RemoteViews? = null
122 | }
123 | }
--------------------------------------------------------------------------------
/app/src/main/res/color/gallery_item_color.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/leaf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/drawable-hdpi/leaf.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/leaf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/drawable-mdpi/leaf.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
18 |
22 |
23 |
24 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/leaf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/drawable-xhdpi/leaf.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/leaf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/drawable-xxhdpi/leaf.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
6 |
8 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/notification.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/pause.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
17 |
21 |
25 |
29 |
33 |
37 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/play.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/preferences.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
17 |
21 |
25 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/set.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
17 |
21 |
25 |
29 |
33 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/stop.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/widget_background_black.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/widget_background_black_square.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/font/source_sans.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
10 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/font/sourcesanspro_light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/font/sourcesanspro_light.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/sourcesanspro_regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/font/sourcesanspro_regular.ttf
--------------------------------------------------------------------------------
/app/src/main/res/layout/about.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/adv_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
16 |
17 |
26 |
27 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/adv_number_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
14 |
15 |
20 |
21 |
24 |
25 |
33 |
34 |
42 |
43 |
51 |
52 |
58 |
59 |
64 |
65 |
66 |
69 |
70 |
77 |
78 |
84 |
85 |
86 |
90 |
91 |
98 |
99 |
105 |
106 |
107 |
108 |
109 |
115 |
116 |
124 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/appwidget.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
21 |
22 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/gallery_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
22 |
23 |
53 |
54 |
79 |
80 |
92 |
93 |
105 |
106 |
118 |
119 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/settings_activity.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
14 |
15 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/timepicker_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
19 |
20 |
25 |
26 |
32 |
33 |
41 |
42 |
48 |
49 |
57 |
58 |
64 |
65 |
73 |
74 |
81 |
82 |
91 |
92 |
98 |
99 |
107 |
108 |
109 |
114 |
115 |
121 |
122 |
130 |
131 |
137 |
138 |
146 |
147 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/bell.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/bell.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/bell1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/bell1.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/bell2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/bell2.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/bell3.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/bell3.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/bell4.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/bell4.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/bell_1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/bell_1.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/bell_2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/bell_2.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/birds.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/birds.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/bowl.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/bowl.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/bowl1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/bowl1.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/bowl2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/bowl2.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/bowl_low.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/bowl_low.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/gong.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/gong.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/gong1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/gong1.ogg
--------------------------------------------------------------------------------
/app/src/main/res/raw/gong2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/app/src/main/res/raw/gong2.ogg
--------------------------------------------------------------------------------
/app/src/main/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Bodhi Timer
4 | Zurücksetzen
5 |
6 | Timer abgelaufen!
7 |
8 | Einstellungen
9 |
10 |
11 |
12 |
13 |
14 | Einstellungen
15 | Töne
16 | Timer-Ton
17 | Ton der bei Ablauf des Timers gepielt wird.
18 |
19 | Teste Ton
20 | Spielt den aktuellen Timer-Ton ab.
21 |
22 | Ton stoppen
23 | Klicken um den aktuellen Ton zu stoppen.
24 |
25 | Start-Ton
26 | Optionaler Ton, der zu Beginn des Timers gespielt wird.
27 |
28 | Vorbereitungszeit
29 | Eine Verzögerung einstellen, bevor der Timer beginnt.
30 |
31 | Start-Ton testen
32 | Spielt den aktuellen Start-Ton ab.
33 |
34 | Lautstärke
35 | in Prozent der Medienlautstärke
36 |
37 |
38 | Benachrichtigung
39 |
40 | Timer automatisch neustarten
41 | Nach dem Ende eines Timers direkt einen neuen starten.
42 |
43 | Erscheinungsbild
44 |
45 | Einblend-Animation
46 | Kreis-Animation
47 | Stil der Animation
48 |
49 | Individuelles Bild benutzen
50 |
51 | Vollbild
52 | Verstecke die Statusanzeige während das Programm läuft.
53 |
54 | Zeit verstecken
55 | Nicht die verbleibende Zeit anzeigen.
56 |
57 | Sonstiges
58 |
59 | Wach bleiben
60 | Das Telefon nicht einschlafen lassen.
61 |
62 | Über Bodhi Timer
63 |
64 |
65 |
66 | Schließen
67 | Info
68 | Über Bodhi Timer
69 | Setzen
70 | Pause
71 |
72 | Std
73 | Min
74 | Sek
75 |
76 | Bitte installieren Sie eine Spracherkennungs-App!
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/app/src/main/res/values-eo/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values-fr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Bodhi Timer
4 | Effacer
5 |
6 | Préférences
7 |
8 | Avertissement sonore
9 | Effet sonore de la notification.
10 |
11 | Notification
12 |
13 | Visuel
14 |
15 | Divers
16 |
17 | Rester éveillé
18 | Empêcher le téléphone de se mettre en veille.
19 |
20 |
21 | Fermer
22 | À propos
23 | À propos de Bodhi Timer
24 | Veuillez installer une application de reconnaissance vocale !
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/values-pl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | sixty
6 | fifty-nine
7 | fifty-eight
8 | fifty-seven
9 | fifty-six
10 | fifty-five
11 | fifty-four
12 | fifty-three
13 | fifty-two
14 | fifty-one
15 | fifty
16 | forty-nine
17 | forty-eight
18 | forty-seven
19 | forty-six
20 | forty-five
21 | forty-four
22 | forty-three
23 | forty-two
24 | forty-one
25 | forty
26 | thirty-nine
27 | thirty-eight
28 | thirty-seven
29 | thirty-six
30 | thirty-five
31 | thirty-four
32 | thirty-three
33 | thirty-two
34 | thirty-one
35 | thirty
36 | twenty-nine
37 | twenty-eight
38 | twenty-seven
39 | twenty-six
40 | twenty-five
41 | twenty-four
42 | twenty-three
43 | twenty-two
44 | twenty-one
45 | twenty
46 | nineteen
47 | eighteen
48 | seventeen
49 | sixteen
50 | fifteen
51 | fourteen
52 | thirteen
53 | twelve
54 | eleven
55 | ten
56 | nine
57 | eight
58 | seven
59 | six
60 | five
61 | four
62 | three
63 | two
64 | one
65 |
66 |
67 | @string/setting.sound_name_no_sound
68 | @string/setting.sound_name_bell_3
69 | @string/setting.sound_name_bell_2
70 | @string/setting.sound_name_bell_1
71 | @string/setting.sound_name_bell1
72 | @string/setting.sound_name_bell2
73 | @string/setting.sound_name_bell3
74 | @string/setting.sound_name_bell4
75 | @string/setting.sound_name_gong
76 | @string/setting.sound_name_gong1
77 | @string/setting.sound_name_gong2
78 | @string/setting.sound_name_bowl
79 | @string/setting.sound_name_bowl1
80 | @string/setting.sound_name_bowl2
81 | @string/setting.sound_name_birds
82 | @string/setting.sound_name_system_tone
83 | @string/setting.sound_name_sound_file
84 |
85 |
86 |
87 |
88 |
93 | android.resource://org.yuttadhammo.BodhiTimer/raw/bell
94 | android.resource://org.yuttadhammo.BodhiTimer/raw/bell_2
95 | android.resource://org.yuttadhammo.BodhiTimer/raw/bell_1
96 | android.resource://org.yuttadhammo.BodhiTimer/raw/bell1
97 | android.resource://org.yuttadhammo.BodhiTimer/raw/bell2
98 | android.resource://org.yuttadhammo.BodhiTimer/raw/bell3
99 | android.resource://org.yuttadhammo.BodhiTimer/raw/bell4
100 | android.resource://org.yuttadhammo.BodhiTimer/raw/gong
101 | android.resource://org.yuttadhammo.BodhiTimer/raw/gong1
102 | android.resource://org.yuttadhammo.BodhiTimer/raw/gong2
103 | android.resource://org.yuttadhammo.BodhiTimer/raw/bowl
104 |
109 | android.resource://org.yuttadhammo.BodhiTimer/raw/bowl_low
110 | android.resource://org.yuttadhammo.BodhiTimer/raw/bowl2
111 | android.resource://org.yuttadhammo.BodhiTimer/raw/birds
112 | system
113 | file
114 | tts
115 |
116 |
117 |
118 |
119 | @string/settings.theme_day_night
120 | @string/settings.theme_light
121 | @string/settings.theme_dark
122 | @string/settings.theme_black
123 |
124 |
125 | @string/setting_key.theme_day_night
126 | @string/setting_key.theme_light
127 | @string/setting_key.theme_dark
128 | @string/setting_key.theme_black
129 |
130 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 0dp
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/setting_keys.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | theme
4 | light
5 | dark
6 | black
7 | dayNight
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
17 |
18 |
19 |
20 |
24 |
25 |
29 |
30 |
34 |
35 |
41 |
42 |
45 |
46 |
50 |
51 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_descriptor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/bodhi_appwidget_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
10 |
11 |
16 |
21 |
22 |
23 |
28 |
33 |
34 |
42 |
43 |
50 |
51 |
57 |
58 |
59 |
62 |
63 |
69 |
70 |
71 |
77 |
78 |
83 |
84 |
87 |
88 |
95 |
100 |
105 |
111 |
117 |
120 |
126 |
132 |
138 |
144 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | ext.kotlin_version = '1.7.22'
4 | repositories {
5 | google()
6 | mavenCentral()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.3.1'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | maven {
17 | url 'https://maven.google.com'
18 | }
19 | google()
20 | mavenCentral()
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/detekt-config.yml:
--------------------------------------------------------------------------------
1 | naming:
2 | PackageNaming:
3 | active: false
--------------------------------------------------------------------------------
/fastlane/Appfile:
--------------------------------------------------------------------------------
1 | json_key_file("api.json")
2 | package_name("org.yuttadhammo.BodhiTimer")
3 |
--------------------------------------------------------------------------------
/fastlane/Fastfile:
--------------------------------------------------------------------------------
1 | # This file contains the fastlane.tools configuration
2 | # You can find the documentation at https://docs.fastlane.tools
3 | #
4 | # For a list of all available actions, check out
5 | #
6 | # https://docs.fastlane.tools/actions
7 | #
8 | # For a list of all available plugins, check out
9 | #
10 | # https://docs.fastlane.tools/plugins/available-plugins
11 | #
12 |
13 | # Uncomment the line if you want fastlane to automatically update itself
14 | # update_fastlane
15 |
16 | default_platform(:android)
17 |
18 | platform :android do
19 | desc "Runs all the tests"
20 | lane :test do
21 | gradle(task: "test")
22 | end
23 |
24 | desc "Submit a new Beta Build to Crashlytics Beta"
25 | lane :beta do
26 | gradle(task: "clean assembleRelease")
27 | crashlytics
28 |
29 | # sh "your_script.sh"
30 | # You can also use other beta testing services here
31 | end
32 |
33 | desc "Deploy a new version to the Google Play"
34 | lane :deploy do
35 | gradle(task: "clean")
36 | gradle(
37 | task: "bundle",
38 | build_type: "Release",
39 | print_command: true
40 | )
41 | upload_to_play_store(track: 'beta')
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/fastlane/README.md:
--------------------------------------------------------------------------------
1 | fastlane documentation
2 | ----
3 |
4 | # Installation
5 |
6 | Make sure you have the latest version of the Xcode command line tools installed:
7 |
8 | ```sh
9 | xcode-select --install
10 | ```
11 |
12 | For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
13 |
14 | # Available Actions
15 |
16 | ## Android
17 |
18 | ### android test
19 |
20 | ```sh
21 | [bundle exec] fastlane android test
22 | ```
23 |
24 | Runs all the tests
25 |
26 | ### android beta
27 |
28 | ```sh
29 | [bundle exec] fastlane android beta
30 | ```
31 |
32 | Submit a new Beta Build to Crashlytics Beta
33 |
34 | ### android deploy
35 |
36 | ```sh
37 | [bundle exec] fastlane android deploy
38 | ```
39 |
40 | Deploy a new version to the Google Play
41 |
42 | ----
43 |
44 | This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
45 |
46 | More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
47 |
48 | The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
49 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/59.txt:
--------------------------------------------------------------------------------
1 | 4.1.3
2 | * added text-to-speech options for timer tone
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/60.txt:
--------------------------------------------------------------------------------
1 | 5.0.0
2 | * Modernize project, update dependencies
3 | * Add vector Enso image and new sounds
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/61.txt:
--------------------------------------------------------------------------------
1 | 5.1.0
2 | * Refactored preference screen
3 | * Native time pickers
4 | * Better defaults
5 | * Updated translations
6 | * Persist access permissions for custom images
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/62.txt:
--------------------------------------------------------------------------------
1 | 5.1.1
2 | * Refactored preference screen
3 | * Native time pickers
4 | * Better defaults
5 | * Updated translations
6 | * Min SDK 21
7 | * Fixes various smaller bugs
8 | * Persist access permissions for custom images
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/63.txt:
--------------------------------------------------------------------------------
1 | 5.1.2
2 | * Refactored preference screen
3 | * Native time pickers
4 | * Better defaults
5 | * Updated translations
6 | * Min SDK 21
7 | * Fixes various smaller bugs
8 | * Persist access permissions for custom images
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/65.txt:
--------------------------------------------------------------------------------
1 | 5.2.0
2 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
3 |
4 | Other recent changes
5 | * Refactored preference screen
6 | * Native time pickers
7 | * Better defaults
8 | * Updated translations
9 | * Monospaced timer font
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/66.txt:
--------------------------------------------------------------------------------
1 | 5.3.0
2 | * Fix bugs with the Advanced Number picker, introduced in the last release. Thanks everyone for your help :)
3 | * Modernize the way notifications are shown
4 |
5 | Other recent changes
6 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
7 | * Refactored preference screen
8 | * Native time pickers
9 | * Better defaults
10 | * Updated translations
11 | * Monospaced timer font
12 |
13 | Known issues:
14 | * Auto repeat doesn't currently work. Use the advanced time picker to add the desired number of repetitions.
15 | * There are issues with the pause button.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/67.txt:
--------------------------------------------------------------------------------
1 | 5.3.1
2 | * Fix bugs with the Advanced Number picker, introduced in the last release. Thanks everyone for your help :)
3 | * Fix a bug with missing Start Sound.
4 | * Modernize the way notifications are shown
5 |
6 | Other recent changes
7 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
8 | * Refactored preference screen
9 | * Native time pickers
10 | * Better defaults
11 | * Updated translations
12 | * Monospaced timer font
13 |
14 | Known issues:
15 | * Auto repeat doesn't currently work. Use the advanced time picker to add the desired number of repetitions.
16 | * There are issues with the pause button.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/69.txt:
--------------------------------------------------------------------------------
1 | 5.3.2
2 | * Fix bugs with the Advanced Number picker, introduced in the last release. Thanks everyone for your help :)
3 | * Fix a bug with missing Start Sound.
4 | * Modernize the way notifications are shown
5 |
6 | Other recent changes
7 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
8 | * Refactored preference screen
9 | * Native time pickers
10 | * Better defaults
11 | * Updated translations
12 | * Monospaced timer font
13 |
14 | Known issues:
15 | * Auto repeat doesn't currently work. Use the advanced time picker to add the desired number of repetitions.
16 | * There are issues with the pause button.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/70.txt:
--------------------------------------------------------------------------------
1 | 5.3.3
2 | * Fix some bugs introduced in the last major release. Thanks everyone for your help :)
3 |
4 | 5.3.0
5 | * Modernize the way notifications are shown
6 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
7 | * Refactored preference screen
8 | * Native time pickers
9 | * Better defaults
10 | * Updated translations
11 | * Monospaced timer font
12 |
13 | Known issues:
14 | * Auto repeat doesn't currently work. Use the advanced time picker to add the desired number of repetitions.
15 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/71.txt:
--------------------------------------------------------------------------------
1 | 5.3.4
2 | * Fixed custom sounds not playing
3 | * Fix some bugs introduced in the last major release. Thanks everyone for your help :)
4 |
5 | 5.3.0
6 | * Modernize the way notifications are shown
7 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
8 | * Refactored preference screen
9 | * Native time pickers
10 | * Better defaults
11 | * Updated translations
12 | * Monospaced timer font
13 |
14 | Known issues:
15 | * Auto repeat doesn't currently work. Use the advanced time picker to add the desired number of repetitions.
16 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/72.txt:
--------------------------------------------------------------------------------
1 | 5.3.5
2 | * Add back auto repeat function
3 | * Use green as the highlight color
4 | * Fixed custom sounds not playing
5 |
6 | 5.3.0
7 | * Fix some bugs introduced in the last major release. Thanks everyone for your help :)
8 | * Modernize the way notifications are shown
9 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
10 | * Refactored preference screen
11 | * Native time pickers
12 | * Better defaults
13 | * Updated translations
14 | * Monospaced timer font
15 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/73.txt:
--------------------------------------------------------------------------------
1 | 5.3.6
2 | * Load correct circle theme
3 | * Add auto repeat function
4 | * Use green as the highlight color
5 |
6 | 5.3.0
7 | * Fix some bugs introduced in the last major release. Thanks everyone for your help :)
8 | * Modernize the way notifications are shown
9 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
10 | * Refactored preference screen
11 | * Native time pickers
12 | * Better defaults
13 | * Updated translations
14 | * Monospaced timer font
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/74.txt:
--------------------------------------------------------------------------------
1 | 5.3.7
2 | * Load correct circle theme
3 | * Add auto repeat function
4 | * Use green as the highlight color
5 |
6 | 5.3.0
7 | * Fix some bugs introduced in the last major release. Thanks everyone for your help :)
8 | * Modernize the way notifications are shown
9 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
10 | * Refactored preference screen
11 | * Native time pickers
12 | * Better defaults
13 | * Updated translations
14 | * Monospaced timer font
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/75.txt:
--------------------------------------------------------------------------------
1 | 5.3.8
2 | * Restore previous time picker
3 | * Add auto repeat function
4 | * Use green as the highlight color
5 |
6 | 5.3.0
7 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
8 | * Fix some bugs introduced in the last major release. Thanks everyone for your help :)
9 | * Modernize the way notifications are shown
10 | * Refactored preference screen
11 | * Updated translations
12 | * Monospaced timer font
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/76.txt:
--------------------------------------------------------------------------------
1 | 5.3.9
2 | * Restore previous time picker
3 | * Add auto repeat function
4 | * Use green as the highlight color
5 |
6 | 5.3.0
7 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
8 | * Fix some bugs introduced in the last major release. Thanks everyone for your help :)
9 | * Modernize the way notifications are shown
10 | * Refactored preference screen
11 | * Updated translations
12 | * Monospaced timer font
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/77.txt:
--------------------------------------------------------------------------------
1 | 5.4.0
2 | * Add a Setting to enable "Do Not Disturb" Mode when meditating
3 | * Add Auto repeat function
4 | * Use green as the highlight color
5 | * Fix an issue when the device was rotated
6 |
7 | 5.3.0
8 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
9 | * Fix some bugs introduced in the last major release. Thanks everyone for your help :)
10 | * Modernize the way notifications are shown
11 | * Refactored preference screen
12 | * Updated translations
13 | * Monospaced timer font
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/78.txt:
--------------------------------------------------------------------------------
1 | 5.4.1
2 | * Use consistent font across the app
3 |
4 | 5.4.0
5 | * Add a Setting to enable "Do Not Disturb" Mode when meditating
6 | * Add Auto repeat function
7 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
8 | * Modernize the way notifications are shown
9 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/79.txt:
--------------------------------------------------------------------------------
1 | 5.4.2
2 | * Use consistent font across the app
3 | * Fix a crash with "Do Not Disturb" Mode
4 |
5 | 5.4.0
6 | * Add a Setting to enable "Do Not Disturb" Mode when meditating
7 | * Add Auto repeat function
8 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
9 | * Modernize the way notifications are shown
10 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/80.txt:
--------------------------------------------------------------------------------
1 | 5.5.0
2 | * Improve performance and battery usage
3 |
4 | 5.4.0
5 | * Use consistent font across the app
6 | * Add a Setting to enable "Do Not Disturb" Mode when meditating
7 | * Add Auto repeat function
8 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
9 | * Modernize the way notifications are shown
10 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/82.txt:
--------------------------------------------------------------------------------
1 | 5.5.2
2 | * There was a bug in 5.5.1. This release is the same as 5.5.0 for those who had installed the buggy version.
3 | * Improve performance and battery usage
4 |
5 | 5.4.0
6 | * Use consistent font across the app
7 | * Add a Setting to enable "Do Not Disturb" Mode when meditating
8 | * Add Auto repeat function
9 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
10 | * Modernize the way notifications are shown
11 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/86.txt:
--------------------------------------------------------------------------------
1 | 5.6.0
2 | * Fix a problem where Timer sounds were cut off. If you expirience problems with the new system, please activate the old method in the preferences and let me know: bodhitimer@riseup.net
3 | * Improve performance and battery usage
4 |
5 | 5.4.0
6 | * Use consistent font across the app
7 | * Add a Setting to enable "Do Not Disturb" Mode when meditating
8 | * Add Auto repeat function
9 | * Completely refactor Alarm Handling. It should be much more reliable, especially when the phone is in battery saver mode.
10 | * Modernize the way notifications are shown
11 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/87.txt:
--------------------------------------------------------------------------------
1 | 5.6.1
2 | * Fix a crash on Xiaomi devices
3 |
4 | 5.6.0
5 | * Fix a problem where Timer sounds were cut off. If you expirience problems with the new system, please activate the old method in the preferences and let me know: bodhitimer@riseup.net
6 | * Improve performance and battery usage
7 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/88.txt:
--------------------------------------------------------------------------------
1 | 5.6.2
2 | * Fix Auto Repeat
3 |
4 | 5.6.0
5 | * Fix a problem where Timer sounds were cut off. If you experience problems with the new system, please activate the old method in the preferences and let me know: bodhitimer@riseup.net
6 | * Improve performance and battery usage
7 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/90.txt:
--------------------------------------------------------------------------------
1 | 6.0.0
2 |
3 | * Update app to support latest Android versions.
4 | * Add support for other app to start an alarm using an URL like bodhi://timer?times=60,120
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/91.txt:
--------------------------------------------------------------------------------
1 | 6.1.0
2 |
3 | Update App for newer Android versions. Use Material design.
4 | This update removes rarely used features like the colored circle animation.
5 |
6 | 6.0.0
7 |
8 | * Update app to support latest Android versions.
9 | * Add support for other app to start an alarm using an URL like bodhi://timer?times=60,120
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/92.txt:
--------------------------------------------------------------------------------
1 | 6.1.1
2 |
3 | Fix a crash
4 |
5 | 6.1.0
6 |
7 | Update App for newer Android versions. Use Material design.
8 | This update removes rarely used features like the colored circle animation.
9 |
10 | 6.0.0
11 |
12 | * Update app to support latest Android versions.
13 | * Add support for other app to start an alarm using an URL like bodhi://timer?times=60,120
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/93.txt:
--------------------------------------------------------------------------------
1 | 6.1.2
2 |
3 | Fix two crashes
4 |
5 | 6.1.0
6 |
7 | Update App for newer Android versions. Use Material design.
8 | This update removes rarely used features like the colored circle animation.
9 |
10 | 6.0.0
11 |
12 | * Update app to support latest Android versions.
13 | * Add support for other app to start an alarm using an URL like bodhi://timer?times=60,120
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/94.txt:
--------------------------------------------------------------------------------
1 | 6.1.3
2 |
3 | Add black theme for AMOLED screens,
4 | fix preset names
5 |
6 | 6.1.0
7 |
8 | Update App for newer Android versions. Use Material design.
9 | This update removes rarely used features like the colored circle animation.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/95.txt:
--------------------------------------------------------------------------------
1 | 6.2.0
2 | Fix small bugs, add high-res graphics.
3 |
4 | 6.1.3
5 | Add black theme for AMOLED screens,
6 | fix preset names
7 |
8 | 6.1.0
9 | Update App for newer Android versions. Use Material design.
10 | This update removes rarely used features like the colored circle animation.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/96.txt:
--------------------------------------------------------------------------------
1 | 6.2.1
2 | Fix the widget to work again
3 |
4 | Previous changes:
5 | - Make graphics higher resolution
6 | - Add black theme for AMOLED screens
7 | - Update App for newer Android versions. Use Material design.
8 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/97.txt:
--------------------------------------------------------------------------------
1 | 6.3.0
2 | - Add some more sounds :)
3 |
4 | Previous changes:
5 | - Make graphics higher resolution
6 | - Fix widget
7 | - Add black theme for AMOLED screens
8 | - Update App for newer Android versions. Use Material design.
9 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/98.txt:
--------------------------------------------------------------------------------
1 | 6.3.0
2 | - Add some more sounds :)
3 |
4 | Previous changes:
5 | - Make graphics higher resolution
6 | - Fix widget
7 | - Add black theme for AMOLED screens
8 | - Update App for newer Android versions. Use Material design.
9 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/full_description-uni.txt:
--------------------------------------------------------------------------------
1 |
About
2 |
Bodhi Timer is an elegant, minimalist countdown timer. It is designed mainly for use as a meditation timer but can easily be used for any similar purpose. Bodhi Timer is free and open-source software. It collects no data and uses the minimal permissions necessary.
3 |
4 |
How to use
5 | 💠 Set the time via the clock icon in the top left. You may set presets by choosing a time then holding down on one of the three preset buttons.
6 | 💠 Pause / resume via the button in the bottom left, and stop the timer via the button in the bottom right. The top right button is the preference button.
7 | 💠 Animation may be toggled between an image and one of four circle animations, chosen from the preferences screen.
8 | 💠 It uses Android's built-in notification system to trigger the alarm, which means it works even when your device is asleep.
9 |
10 |
Features
11 | 🔶 minimalist full screen UI, no clutter
12 | 🔶 uses scroll and fling gestures to set the time
13 | 🔶 set up to three presets on the time chooser
14 | 🔶 option for timer auto-restarting
15 | 🔶 option to set multiple consecutive timers via the "adv" button
16 | 🔶 speech recognition via long-press on clock button (set multiple timers separated by the word "again")
17 | 🔶 displays two animation types: fade in static image (defaults to Bodhi leaf) and animated circle
18 | 🔶 option to use custom image for fade in
19 | 🔶 four themes for the animated circle: three colour themes and a draw in Zen Enso (brush circle)
20 | 🔶 includes different meditation timer sounds (Burmese bell, Tibetan bell, Tibetan singing bowls, Zen gong, and bird song)
21 | 🔶 option to use any ring tone as timer sound
22 | 🔶 option to use custom sound file as timer sound
23 | 🔶 vibration and LED blinking options
24 | 🔶 licensed under the GPL 3+
25 |
26 |
Credits
27 | 🔷 Some code is based on the free and open source TeaTimer by Ralph Gootee.
28 | 🔷 The Enso image was drawn by Ryōnen Gensō (1646-1711). Next to the Enso she has written: "When you do understand yourself fully, there is not one thing."
29 | 🔷 Singing Bowl Low sound Recorded by juskiddink Licensed under CC-BY-SA 3.0
30 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/full_description.txt:
--------------------------------------------------------------------------------
1 |
About
2 |
Bodhi Timer is an elegant, minimalist countdown timer.
3 | It is designed mainly for use as a meditation timer but can easily be used for any similar purpose.
4 | This app is developed as open-source software.
5 | It collects no data and uses the minimal permissions necessary.
6 |
7 |
How to use
8 | 🔷 Set the time via the clock icon in the top left. You may set presets by choosing a time then holding down on one of the three preset buttons.
9 | 🔷 Pause / resume via the button in the bottom left, and stop the timer via the button in the bottom right. The top right button is the preference button.
10 | 🔷 Animation may be toggled between an image and one of four circle animations, chosen from the preferences screen.
11 | 🔷 It uses Android's built-in notification system to trigger the alarm, which means it works even when your device is asleep.
12 |
13 |
Features
14 | • minimalist full screen UI, no clutter
15 | • displays two animation types: fade in static image (defaults to Bodhi leaf) and animated Zen Enso (brush circle)
16 | • option to use custom image for fade in
17 | • uses scroll and fling gestures to set the time
18 | • set up to three presets on the time chooser
19 | • option for timer auto-restarting
20 | • option to set multiple consecutive timers via the "adv" button
21 | • speech recognition via long-press on clock button (set multiple timers separated by the word "again")
22 | • includes different meditation timer sounds (Burmese bell, Tibetan bell, Tibetan singing bowls, Zen gong, and bird song)
23 | • option to use any ring tone as timer sound
24 | • option to use custom sound file as timer sound
25 | • licensed under the GPL 3+
26 |
27 |
Credits
28 | • Some code is based on the free and open source TeaTimer by Ralph Gootee.
29 | • The Enso image was drawn by Ryōnen Gensō (1646-1711). Next to the Enso she has written: "When you do understand yourself fully, there is not one thing."
30 | • Singing Bowl Low sound Recorded by juskiddink Licensed under CC-BY-SA 3.0
31 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/featureGraphic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/fastlane/metadata/android/en-US/images/featureGraphic.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/short_description.txt:
--------------------------------------------------------------------------------
1 | A countdown timer for meditation practice with elegant animations and sounds.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/title.txt:
--------------------------------------------------------------------------------
1 | Bodhi Timer
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/video.txt:
--------------------------------------------------------------------------------
1 | https://www.youtube.com/watch?v=t-uyRBiXa4o
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | android.enableJetifier=true
2 | android.useAndroidX=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Dec 30 12:11:33 CET 2020
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-7.4-bin.zip
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/image-sources/leaf-image-res.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/image-sources/leaf-image-res.png
--------------------------------------------------------------------------------
/image-sources/leaf-image-res.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/image-sources/leaf-image-res.xcf
--------------------------------------------------------------------------------
/image-sources/leaf-image.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/image-sources/leaf-image.xcf
--------------------------------------------------------------------------------
/image-sources/leaf.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/image-sources/leaf.xcf
--------------------------------------------------------------------------------
/image-sources/leaf2.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/image-sources/leaf2.xcf
--------------------------------------------------------------------------------
/image-sources/leaf3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/image-sources/leaf3.png
--------------------------------------------------------------------------------
/image-sources/leaf3.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuttadhammo/BodhiTimer/8c23e0441b4a7ef60b9a18e2895bf6519e027144/image-sources/leaf3.xcf
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------