├── app ├── .gitignore ├── src │ └── main │ │ ├── play_store_icon.png │ │ ├── res │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_add_shortcut.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_add_shortcut.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_add_shortcut.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_add_shortcut.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_add_shortcut.png │ │ │ └── ic_launcher_round.png │ │ ├── drawable-hdpi │ │ │ └── ic_notification.png │ │ ├── drawable-mdpi │ │ │ └── ic_notification.png │ │ ├── drawable-nodpi │ │ │ └── widget_preview.png │ │ ├── drawable-xhdpi │ │ │ └── ic_notification.png │ │ ├── drawable-xxhdpi │ │ │ └── ic_notification.png │ │ ├── drawable-xxxhdpi │ │ │ └── ic_notification.png │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ ├── ic_launcher_round.xml │ │ │ └── ic_add_shortcut.xml │ │ ├── values-v27 │ │ │ ├── strings.xml │ │ │ ├── themes.xml │ │ │ └── values.xml │ │ ├── drawable │ │ │ ├── ic_add.xml │ │ │ ├── ic_redo.xml │ │ │ ├── ic_undo.xml │ │ │ ├── ic_save.xml │ │ │ ├── ic_launcher_background.xml │ │ │ ├── ic_shortcut_background.xml │ │ │ ├── ic_add_shortcut_foreground.xml │ │ │ └── ic_launcher_foreground.xml │ │ ├── menu │ │ │ ├── timezone_action_bar.xml │ │ │ ├── settings_action_bar.xml │ │ │ └── progress_bar_action_bar.xml │ │ ├── layout │ │ │ ├── timezone_searchview.xml │ │ │ ├── toolbar.xml │ │ │ ├── timezone_item.xml │ │ │ ├── activity_preferences.xml │ │ │ ├── activity_timezone.xml │ │ │ ├── activity_progress_bars.xml │ │ │ ├── single_progress_bar_widget.xml │ │ │ ├── about_dialog.xml │ │ │ ├── single_progress_bar_row.xml │ │ │ ├── progress_bar_widget.xml │ │ │ └── activity_countdown_text.xml │ │ ├── xml │ │ │ ├── backup_descriptor.xml │ │ │ ├── data_extraction_rules.xml │ │ │ ├── widget_info.xml │ │ │ └── preferences.xml │ │ ├── values │ │ │ ├── bools.xml │ │ │ ├── dimens.xml │ │ │ ├── colors.xml │ │ │ ├── themes.xml │ │ │ ├── styles.xml │ │ │ └── values.xml │ │ ├── values-night │ │ │ ├── bools.xml │ │ │ └── colors.xml │ │ ├── values-v26 │ │ │ └── colors.xml │ │ ├── values-night-v26 │ │ │ └── colors.xml │ │ ├── values-v24 │ │ │ └── colors.xml │ │ ├── values-night-v24 │ │ │ └── colors.xml │ │ ├── values-v21 │ │ │ ├── strings.xml │ │ │ ├── themes.xml │ │ │ └── values.xml │ │ ├── drawable-anydpi-v26 │ │ │ └── ic_notification.xml │ │ ├── values-v23 │ │ │ └── themes.xml │ │ ├── values-v29 │ │ │ └── themes.xml │ │ ├── xml-v25 │ │ │ └── shortcuts.xml │ │ └── layout-land │ │ │ └── about_dialog.xml │ │ ├── java │ │ ├── util │ │ │ ├── Resetting_application.kt │ │ │ ├── About_dialog.kt │ │ │ ├── Dynamic_theme_activity.kt │ │ │ └── Preferences.kt │ │ ├── settings │ │ │ ├── Checkbox_dialog_frag.kt │ │ │ ├── Timepicker_frag.kt │ │ │ ├── Precision_dialog_frag.kt │ │ │ ├── Datepicker_frag.kt │ │ │ ├── Countdown_text.kt │ │ │ ├── listeners.kt │ │ │ └── TimeZone_disp.kt │ │ ├── db │ │ │ └── DB.kt │ │ └── list │ │ │ └── Touch_helper_callback.kt │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── metadata └── en-US │ ├── title.txt │ ├── short_description.txt │ ├── changelogs │ ├── 190020002 │ └── 190020102 │ ├── images │ ├── icon.png │ ├── featureGraphic.png │ └── phoneScreenshots │ │ ├── scrn_phone_land_1.png │ │ ├── scrn_phone_port_1.png │ │ └── scrn_phone_port_2.png │ └── full_description.txt ├── .idea ├── copyright │ └── profiles_settings.xml ├── dictionaries │ ├── mvchandler.xml │ └── matt.xml ├── vcs.xml ├── compiler.xml ├── encodings.xml ├── kotlinc.xml ├── deploymentTargetDropDown.xml ├── gradle.xml ├── modules.xml ├── jarRepositories.xml ├── inspectionProfiles │ └── Project_Default.xml └── misc.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── CREDITS ├── .gitignore ├── gradle.properties ├── PRIVACY.md ├── LICENSE ├── README.md ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /metadata/en-US/title.txt: -------------------------------------------------------------------------------- 1 | ProgressBars 2 | -------------------------------------------------------------------------------- /metadata/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | A simple timer / countdown app 2 | -------------------------------------------------------------------------------- /metadata/en-US/changelogs/190020102: -------------------------------------------------------------------------------- 1 | update for latest versions of android 2 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /metadata/en-US/changelogs/190020002: -------------------------------------------------------------------------------- 1 | * Allow deleting notification channels 2 | * Prevent orphaned notification channels 3 | -------------------------------------------------------------------------------- /metadata/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/metadata/en-US/images/icon.png -------------------------------------------------------------------------------- /app/src/main/play_store_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/play_store_icon.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.idea/dictionaries/mvchandler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /metadata/en-US/images/featureGraphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/metadata/en-US/images/featureGraphic.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_add_shortcut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-hdpi/ic_add_shortcut.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_add_shortcut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-mdpi/ic_add_shortcut.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/drawable-hdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/drawable-mdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/widget_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/drawable-nodpi/widget_preview.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_add_shortcut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-xhdpi/ic_add_shortcut.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_add_shortcut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-xxhdpi/ic_add_shortcut.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/drawable-xhdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/drawable-xxhdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/drawable-xxxhdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_add_shortcut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_add_shortcut.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /metadata/en-US/images/phoneScreenshots/scrn_phone_land_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/metadata/en-US/images/phoneScreenshots/scrn_phone_land_1.png -------------------------------------------------------------------------------- /metadata/en-US/images/phoneScreenshots/scrn_phone_port_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/metadata/en-US/images/phoneScreenshots/scrn_phone_port_1.png -------------------------------------------------------------------------------- /metadata/en-US/images/phoneScreenshots/scrn_phone_port_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattvchandler/ProgressBars/HEAD/metadata/en-US/images/phoneScreenshots/scrn_phone_port_2.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | Icons © Matthew Chandler under CC-BY 4.0 (http://creativecommons.org/licenses/by/4.0) 2 | progress_bar_launcher_icon.png 3 | progress_bar_launcher_round.png 4 | progress_bar_notification.png 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.apk 2 | *.iml 3 | .gradle 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | app/release 12 | .idea/caches 13 | .idea/codeStyles 14 | .idea/assetWizardSettings.xml 15 | .idea/deploymentTargetDropDown.xml 16 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_add_shortcut.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /metadata/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | Countdown/up to/from a specified time 2 | * Percentage complete for a time interval 3 | * Notifications on timer completion 4 | * Swipe to delete timers 5 | * Drag to reorder timers 6 | * Show time remaining / elapsed in any combination of, Years, Months, Weeks, 7 | Days, Hours, Minutes, or Seconds 8 | -------------------------------------------------------------------------------- /app/src/main/res/values-v27/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Automatically set from wallpaper 5 | White 6 | Black 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/menu/timezone_action_bar.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/timezone_searchview.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_redo.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_undo.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_save.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/dictionaries/matt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | barfill 5 | bootup 6 | ctor 7 | datepicker 8 | disp 9 | dropdowns 10 | edittext 11 | github 12 | mattvchandler 13 | noninfringement 14 | opensource 15 | progressbars 16 | rowid 17 | rowids 18 | sqlite 19 | timepicker 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | android.defaults.buildfeatures.buildconfig=true 13 | android.enableJetifier=true 14 | android.nonFinalResIds=false 15 | android.nonTransitiveRClass=true 16 | android.useAndroidX=true 17 | org.gradle.jvmargs=-Xmx1536m 18 | 19 | # When configured, Gradle will run in incubating parallel mode. 20 | # This option should only be used with decoupled projects. More details, visit 21 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 22 | # org.gradle.parallel=true 23 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /PRIVACY.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | ProgressBars does not: 4 | 5 | * record, collect, store any personal information 6 | * transmit or receive any information 7 | 8 | ### Note on location permission 9 | 10 | ProgressBars will ask for permission to access your device's locations if 11 | 12 | * You are running a version of Android before Marshmallow 13 | * OR you choose 'Auto (location / time based)' for the Day / Night theme setting 14 | 15 | The location is used to determine local sunset sunrise times and switch to the 16 | night theme after sunset. 17 | 18 | The location is only used within the built-in Android theming library code. See 19 | the [Android documentation](https://developer.android.com/reference/android/support/v7/app/AppCompatDelegate#mode_night_auto) 20 | for details. 21 | 22 | ProgressBars does not access, use, store, or transmit the location 23 | 24 | Should you choose to deny the location permission, normal functionality will 25 | continue, and the theming library falls back to switching on preset times. 26 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/matt/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2023 Matthew Chandler 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_descriptor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/values/bools.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | true 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/bools.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | false 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/values-v26/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | #B3FFFFFF 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/values-night-v26/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | #B3000000 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | #FF1B5E20 26 | #FF003300 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/values-v24/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | #C04CAF50 26 | #F0388E3C 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/values-night-v24/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | #C01B5E20 26 | #F0003300 27 | 28 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 8dp 26 | 16dp 27 | 8dp 28 | 2dp 29 | 8dp 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | #FF4CAF50 26 | #FF388E3C 27 | #FF009688 28 | #B3000000 29 | 30 | #8444 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 21 | 22 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_shortcut_background.xml: -------------------------------------------------------------------------------- 1 | 21 | 22 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/values-v21/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | 24 | Follow System 25 | Day 26 | Night 27 | Auto (location / time based) 28 | Auto Battery (Night mode when saving battery) 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/values-v21/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi-v26/ic_notification.xml: -------------------------------------------------------------------------------- 1 | 21 | 22 | 25 | 28 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/values-v21/values.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | @string/pref_theme_value_system 27 | @string/pref_theme_value_day 28 | @string/pref_theme_value_night 29 | @string/pref_theme_value_auto 30 | @string/pref_theme_value_auto_batt 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/menu/settings_action_bar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 28 | 33 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/values-v23/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/values-v27/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/values-v29/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/values-v27/values.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | auto 26 | 27 | 28 | @string/pref_widget_text_color_auto 29 | @string/pref_widget_text_color_white 30 | @string/pref_widget_text_color_black 31 | 32 | @string/pref_widget_text_color_auto 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add_shortcut_foreground.xml: -------------------------------------------------------------------------------- 1 | 21 | 22 | 25 | 28 | 31 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/xml-v25/shortcuts.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 32 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-kapt' 4 | 5 | android { 6 | compileSdk 34 7 | dataBinding.enabled = true 8 | defaultConfig { 9 | applicationId "org.mattvchandler.progressbars" 10 | minSdkVersion 19 11 | targetSdkVersion 34 12 | versionName "2.1.2" 13 | // version code is 0<2-digit MAJOR><2-digit MINOR><2-digit PATCH> 14 | versionCode 190020102 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | vectorDrawables.useSupportLibrary = true 17 | } 18 | compileOptions { 19 | sourceCompatibility = 1.8 20 | targetCompatibility = 1.8 21 | } 22 | kotlinOptions { 23 | jvmTarget = "1.8" 24 | } 25 | buildTypes { 26 | release { 27 | minifyEnabled true 28 | shrinkResources true 29 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | productFlavors { 33 | } 34 | namespace 'org.mattvchandler.progressbars' 35 | buildToolsVersion = '34.0.0' 36 | } 37 | 38 | dependencies { 39 | implementation fileTree(include: ['*.jar'], dir: 'libs') 40 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 41 | implementation 'androidx.appcompat:appcompat:1.6.1' 42 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 43 | implementation 'androidx.core:core-ktx:1.12.0-rc01' 44 | implementation 'androidx.core:core-ktx:1.12.0-rc01' 45 | implementation 'androidx.preference:preference-ktx:1.2.1' 46 | implementation 'androidx.recyclerview:recyclerview:1.3.1' 47 | implementation 'com.google.android.material:material:1.11.0-alpha02' 48 | } 49 | repositories { 50 | mavenCentral() 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/res/xml/widget_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProgressBars 2 | 3 | [Get it on F-Droid](https://f-droid.org/packages/org.mattvchandler.progressbars/) 6 | [Get it on Google Play](https://play.google.com/store/apps/details?id=org.mattvchandler.progressbars) 9 | 10 | ProgressBars is a simple timer / countdown app for Android. 11 | 12 | ![screenshot](/metadata/en-US/images/phoneScreenshots/scrn_phone_land_1.png?raw=true) 13 | 14 | ### Basic Features 15 | * Countdown/up to/from a specified time 16 | * Percentage complete for a time interval 17 | * Notifications on timer completion 18 | * Swipe to delete timers 19 | * Drag to reorder timers 20 | * Show time remaining / elapsed in any combination of: 21 | * Years 22 | * Months 23 | * Weeks 24 | * Days 25 | * Hours 26 | * Minutes 27 | * Seconds 28 | 29 | ## Note for recent versions of Android 30 | Android has had several changes in how notifications are handled, especially 31 | restricting the ability to send a notification at an exact time, and what apps 32 | are able to run in the background in the name of reducing battery usage. For 33 | ProgressBars, this impacts the ability to send notifications for timer 34 | completion reliably. I recommend giving ProgressBars Unrestricted battery usage 35 | if you are going to rely on notifications from this app. ProgressBars consumes 36 | very little battery with typical usage, even with optimizations turned off. 37 | 38 | ### Android 12/12L 39 | By default, apps can't set exact alarms on Android 12/12L. Timer notifications 40 | may go off seconds to minutes after the timer itself ends. If this will be a 41 | problem for you, you will need to grant the special "Alarms & reminders" 42 | permission to ProgressBars 43 | 44 | * This limitation did not exist in android versions prior to 12 45 | * Android 13, and later have a different permission mechanism to send exact alarms 46 | -------------------------------------------------------------------------------- /app/src/main/java/util/Resetting_application.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Matthew Chandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.mattvchandler.progressbars.util 23 | 24 | import android.app.Application 25 | 26 | import org.mattvchandler.progressbars.db.DB 27 | import org.mattvchandler.progressbars.db.Data 28 | import org.mattvchandler.progressbars.db.Progress_bars_table 29 | 30 | class Resetting_application: Application() 31 | { 32 | override fun onCreate() // runs on App startup 33 | { 34 | super.onCreate() 35 | 36 | val db = DB(this).writableDatabase 37 | val cursor = db.rawQuery(Progress_bars_table.SELECT_ALL_ROWS_NO_WIDGET, null) 38 | if(cursor.count == 0) 39 | { 40 | val data = Data(this) 41 | data.order_ind = 0 42 | data.insert(db) 43 | } 44 | 45 | cursor.close() 46 | db.close() 47 | 48 | // register notification handler 49 | Notification_handler.setup_notification_channel(this) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/res/menu/progress_bar_action_bar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 28 | 33 | 38 | 43 | 47 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/res/layout/timezone_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 42 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 29 | 30 | 34 | 41 | 48 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/java/util/About_dialog.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Matthew Chandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | package org.mattvchandler.progressbars.util 22 | 23 | import android.app.Dialog 24 | import android.os.Bundle 25 | import android.text.method.LinkMovementMethod 26 | import android.view.LayoutInflater 27 | import androidx.appcompat.app.AlertDialog 28 | import androidx.databinding.DataBindingUtil 29 | import androidx.fragment.app.DialogFragment 30 | import org.mattvchandler.progressbars.BuildConfig 31 | import org.mattvchandler.progressbars.R 32 | import org.mattvchandler.progressbars.databinding.AboutDialogBinding 33 | 34 | class About_dialog: DialogFragment() 35 | { 36 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog 37 | { 38 | val builder = AlertDialog.Builder(requireActivity()) 39 | val binding = DataBindingUtil.inflate(LayoutInflater.from(activity), R.layout.about_dialog, null, false) 40 | 41 | builder.setView(binding.root) 42 | binding.version.text = requireActivity().resources.getString(R.string.app_version, BuildConfig.VERSION_NAME) 43 | 44 | // allow clicking links in license and website text 45 | binding.license.movementMethod = LinkMovementMethod.getInstance() 46 | binding.website.movementMethod = LinkMovementMethod.getInstance() 47 | 48 | return builder.create() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 21 | 22 | 23 | 28 | 33 | 41 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_timezone.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 29 | 30 | 34 | 44 | 51 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 21 | 22 | 27 | 30 | 33 | 34 | 38 | 40 | 41 | 49 | 50 | 51 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_progress_bars.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 29 | 34 | 45 | 52 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/java/settings/Checkbox_dialog_frag.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Matthew Chandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.mattvchandler.progressbars.settings 23 | 24 | import android.app.Dialog 25 | import android.os.Bundle 26 | import androidx.appcompat.app.AlertDialog 27 | import androidx.fragment.app.DialogFragment 28 | import java.security.InvalidParameterException 29 | 30 | // Dialog box w/ checkboxes 31 | class Checkbox_dialog_frag: DialogFragment() 32 | { 33 | private var selection: BooleanArray? = null 34 | 35 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog 36 | { 37 | super.onCreateDialog(savedInstanceState) 38 | 39 | selection = if(savedInstanceState == null) 40 | requireArguments().getBooleanArray(SELECTION_ARG) 41 | else 42 | savedInstanceState.getBooleanArray(SELECTION_ARG) 43 | 44 | if(selection == null) 45 | throw InvalidParameterException("No selection specified") 46 | 47 | val builder = AlertDialog.Builder(requireActivity()) 48 | builder.setTitle(requireArguments().getInt(TITLE_ARG)) 49 | .setMultiChoiceItems(requireArguments().getStringArray(ENTRIES_ARG), selection ) { _, which, isChecked -> selection!![which] = isChecked } 50 | .setPositiveButton(android.R.string.ok) { _, _ -> (activity as Settings).on_checkbox_dialog_ok(tag!!, selection!!.toTypedArray()) } 51 | .setNegativeButton(android.R.string.cancel, null) 52 | 53 | return builder.create() 54 | } 55 | 56 | override fun onSaveInstanceState(out: Bundle) 57 | { 58 | out.putBooleanArray(SELECTION_ARG, selection) 59 | } 60 | 61 | companion object 62 | { 63 | const val TITLE_ARG = "TITLE_ARG" 64 | const val ENTRIES_ARG = "ENTRIES_ARG" 65 | const val SELECTION_ARG = "SELECTION_ARG" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /app/src/main/java/settings/Timepicker_frag.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Matthew Chandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.mattvchandler.progressbars.settings 23 | 24 | import android.app.Dialog 25 | import android.app.TimePickerDialog 26 | import android.os.Bundle 27 | import android.widget.Toast 28 | import androidx.fragment.app.DialogFragment 29 | import org.mattvchandler.progressbars.R 30 | import org.mattvchandler.progressbars.settings.Settings.Companion.get_time_format 31 | import java.security.InvalidParameterException 32 | import java.text.ParsePosition 33 | import java.util.* 34 | 35 | // time-picker dialog 36 | class Timepicker_frag: DialogFragment() 37 | { 38 | 39 | override fun onCreateDialog(saved_instance_state: Bundle?): Dialog 40 | { 41 | // parse from current widget text 42 | val hour: Int 43 | val minute: Int 44 | 45 | val cal = Calendar.getInstance() 46 | 47 | val time = requireArguments().getString(TIME) ?: throw InvalidParameterException("No time argument given") 48 | 49 | val df = get_time_format() 50 | val date_obj = df.parse(time, ParsePosition(0)) 51 | if(date_obj == null) 52 | { 53 | // couldn't parse 54 | Toast.makeText(activity, resources.getString(R.string.invalid_time, time, df.toLocalizedPattern()), Toast.LENGTH_LONG).show() 55 | 56 | // set to stored date 57 | cal.timeInMillis = requireArguments().getLong(STORE_TIME, 0) * 1000 58 | } 59 | else 60 | { 61 | cal.time = date_obj 62 | } 63 | 64 | hour = cal.get(Calendar.HOUR_OF_DAY) 65 | minute = cal.get(Calendar.MINUTE) 66 | 67 | return TimePickerDialog(activity, activity as Settings?, hour, minute, android.text.format.DateFormat.is24HourFormat(activity)) 68 | } 69 | 70 | companion object 71 | { 72 | const val STORE_TIME = "STORE_TIME" 73 | const val TIME = "TIME" 74 | const val AM_PM = "AM_PM" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/res/layout/single_progress_bar_widget.xml: -------------------------------------------------------------------------------- 1 | 21 | 22 | 33 | 34 | 43 | 44 | 53 | 59 | 64 | 65 | 66 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/java/db/DB.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Matthew Chandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.mattvchandler.progressbars.db 23 | 24 | import android.content.Context 25 | import android.database.Cursor 26 | import android.database.sqlite.SQLiteDatabase 27 | import android.database.sqlite.SQLiteOpenHelper 28 | 29 | // helper functions for getting data from a cursor 30 | fun Cursor.get_nullable_string(column_name: String): String? 31 | { 32 | val column_index = this.getColumnIndexOrThrow(column_name) 33 | return if(this.isNull(column_index)) null else this.getString(column_index) 34 | } 35 | fun Cursor.get_nullable_long(column_name: String): Long? 36 | { 37 | val column_index = this.getColumnIndexOrThrow(column_name) 38 | return if(this.isNull(column_index)) null else this.getLong(column_index) 39 | } 40 | fun Cursor.get_nullable_int(column_name: String): Int? 41 | { 42 | val column_index = this.getColumnIndexOrThrow(column_name) 43 | return if(this.isNull(column_index)) null else this.getInt(column_index) 44 | } 45 | fun Cursor.get_nullable_bool(column_name: String): Boolean? 46 | { 47 | val column_index = this.getColumnIndexOrThrow(column_name) 48 | return if(this.isNull(column_index)) null else this.getInt(column_index) != 0 49 | } 50 | 51 | // DB container 52 | class DB(private val context: Context): SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) 53 | { 54 | companion object 55 | { 56 | private const val DB_VERSION = 5 57 | private const val DB_NAME = "progress_bar_db" 58 | } 59 | // build the tables / whatever else when new 60 | override fun onCreate(sqLiteDatabase: SQLiteDatabase) 61 | { 62 | sqLiteDatabase.execSQL(Progress_bars_table.CREATE_TABLE) 63 | } 64 | 65 | // if DB schema changes, put logic to migrate data here 66 | override fun onUpgrade(db: SQLiteDatabase, old_version: Int, new_version: Int) 67 | { 68 | check(new_version == DB_VERSION) { "DB version mismatch" } 69 | 70 | if(old_version < 4) 71 | db.execSQL("DROP TABLE IF EXISTS undo") 72 | 73 | Progress_bars_table.upgrade(context, db, old_version) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 40 | 41 | 42 | 43 | 44 | 45 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 32 | 33 | 40 | 41 | 44 | 45 | 46 | 53 | 54 | 61 | 62 | 67 | 68 | 75 | 76 | -------------------------------------------------------------------------------- /app/src/main/java/settings/Precision_dialog_frag.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Matthew Chandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.mattvchandler.progressbars.settings 23 | 24 | import android.app.Dialog 25 | import android.os.Bundle 26 | import android.view.Gravity 27 | import android.view.ViewGroup 28 | import android.widget.FrameLayout 29 | import android.widget.NumberPicker 30 | import androidx.appcompat.app.AlertDialog 31 | import androidx.fragment.app.DialogFragment 32 | import org.mattvchandler.progressbars.R 33 | 34 | // a number_picker fragment for precision setting 35 | class Precision_dialog_frag: DialogFragment() 36 | { 37 | private lateinit var np: NumberPicker 38 | 39 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog 40 | { 41 | super.onCreateDialog(savedInstanceState) 42 | 43 | val builder = AlertDialog.Builder(requireActivity()) 44 | 45 | // unpack and set the starting value 46 | val precision: Int = savedInstanceState?.getInt(PRECISION_ARG) ?: requireArguments().getInt(PRECISION_ARG) 47 | 48 | // build a number picker with range 0-10 49 | np = NumberPicker(builder.context) 50 | np.minValue = 0 51 | np.maxValue = 10 52 | np.value = precision 53 | 54 | val margin_size = builder.context.resources.getDimensionPixelSize(R.dimen.margin_size) 55 | val layout_params = FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) 56 | layout_params.setMargins(margin_size, margin_size, margin_size, margin_size) 57 | layout_params.gravity = Gravity.CENTER 58 | np.layoutParams = layout_params 59 | 60 | val layout = FrameLayout(builder.context) 61 | layout.addView(np) 62 | 63 | // create a dialog containing the number picker, w/ OK and CANCEL buttons 64 | builder.setTitle(R.string.precision) 65 | .setView(layout) 66 | .setPositiveButton(android.R.string.ok) { _, _ -> 67 | // ON OK, set the value one last time, and call the listener 68 | (activity as Settings).on_precision_set(np.value) 69 | } 70 | .setNegativeButton(android.R.string.cancel, null) 71 | // return the finished dialog 72 | return builder.create() 73 | } 74 | 75 | override fun onSaveInstanceState(out: Bundle) 76 | { 77 | out.putInt(PRECISION_ARG, np.value) 78 | } 79 | 80 | companion object 81 | { 82 | const val PRECISION_ARG = "precision" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 23 | 24 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | 43 | 44 | 45 | 46 | 47 | 48 | 53 | 58 | 59 | 63 | 64 | 66 | 67 | 68 | 69 | 70 | 71 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /app/src/main/java/settings/Datepicker_frag.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Matthew Chandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.mattvchandler.progressbars.settings 23 | 24 | import android.app.DatePickerDialog 25 | import android.app.Dialog 26 | import android.os.Build 27 | import android.os.Bundle 28 | import android.widget.Toast 29 | import androidx.fragment.app.DialogFragment 30 | import androidx.preference.PreferenceManager 31 | import org.mattvchandler.progressbars.R 32 | import org.mattvchandler.progressbars.settings.Settings.Companion.get_date_format 33 | import java.security.InvalidParameterException 34 | import java.text.ParsePosition 35 | import java.util.* 36 | 37 | // date picker dialog 38 | class Datepicker_frag: DialogFragment() 39 | { 40 | override fun onCreateDialog(saved_instance_state: Bundle?): Dialog 41 | { 42 | // parse from current widget text 43 | val year: Int 44 | val month: Int 45 | val day: Int 46 | 47 | val cal = Calendar.getInstance() 48 | 49 | val preferenceManager = PreferenceManager.getDefaultSharedPreferences(requireActivity()) 50 | val date_format = preferenceManager.getString(resources.getString(R.string.pref_date_format_key), resources.getString(R.string.pref_date_format_default)) 51 | 52 | val date = requireArguments().getString(DATE) ?: throw InvalidParameterException("No date argument given") 53 | 54 | val df = get_date_format(requireActivity()) 55 | 56 | val date_obj = df.parse(date, ParsePosition(0)) 57 | if(date_obj == null) 58 | { 59 | // couldn't parse 60 | Toast.makeText(activity, resources.getString(R.string.invalid_date, date, if(date_format != "locale") date_format else df.toLocalizedPattern()), Toast.LENGTH_LONG).show() 61 | 62 | // set to stored date 63 | cal.timeInMillis = requireArguments().getLong(STORE_DATE, 0) * 1000 64 | } 65 | else 66 | { 67 | cal.time = date_obj 68 | } 69 | 70 | year = cal.get(Calendar.YEAR) 71 | month = cal.get(Calendar.MONTH) 72 | day = cal.get(Calendar.DAY_OF_MONTH) 73 | 74 | 75 | val date_picker = DatePickerDialog(requireActivity(), activity as Settings?, year, month, day) 76 | 77 | if(Build.VERSION.SDK_INT >= 21) 78 | { 79 | val first_day_of_wk = preferenceManager.getString(resources.getString(R.string.pref_first_day_of_wk_key), resources.getString(R.string.pref_first_day_of_wk_default))!!.toInt() 80 | if(first_day_of_wk != 0) 81 | date_picker.datePicker.firstDayOfWeek = first_day_of_wk 82 | } 83 | return date_picker 84 | } 85 | 86 | companion object 87 | { 88 | const val STORE_DATE = "STORE_DATE" 89 | const val DATE = "DATE" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/settings/Countdown_text.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Matthew Chandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.mattvchandler.progressbars.settings 23 | 24 | import android.content.Intent 25 | import android.os.Bundle 26 | import android.view.MenuItem 27 | import androidx.appcompat.widget.Toolbar 28 | import androidx.core.app.NavUtils 29 | import androidx.databinding.DataBindingUtil 30 | import org.mattvchandler.progressbars.R 31 | import org.mattvchandler.progressbars.databinding.ActivityCountdownTextBinding 32 | import org.mattvchandler.progressbars.db.Data 33 | import org.mattvchandler.progressbars.util.Dynamic_theme_activity 34 | import java.security.InvalidParameterException 35 | 36 | // displays EditText widgets for setting countdown messages 37 | class Countdown_text: Dynamic_theme_activity() 38 | { 39 | private var data: Data? = null 40 | private lateinit var binding: ActivityCountdownTextBinding 41 | 42 | override fun onCreate(savedInstanceState: Bundle?) 43 | { 44 | super.onCreate(savedInstanceState) 45 | 46 | binding = DataBindingUtil.setContentView(this, R.layout.activity_countdown_text) 47 | setSupportActionBar(binding.toolbar as Toolbar) 48 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 49 | 50 | consume_insets(this, binding.mainList, binding.appbarLayout) 51 | 52 | data = if(savedInstanceState == null) 53 | intent.getSerializableExtra(EXTRA_DATA) as Data 54 | else 55 | savedInstanceState.getSerializable(EXTRA_DATA) as Data 56 | 57 | if(data == null) 58 | throw InvalidParameterException("No data argument passed") 59 | 60 | binding.data = data 61 | } 62 | 63 | private fun save() 64 | { 65 | data!!.pre_text = binding.preText.text!!.toString() 66 | data!!.start_text = binding.startText.text!!.toString() 67 | data!!.countdown_text = binding.countdownText.text!!.toString() 68 | data!!.complete_text = binding.completeText.text!!.toString() 69 | data!!.post_text = binding.postText.text!!.toString() 70 | data!!.single_pre_text = binding.singlePreText.text!!.toString() 71 | data!!.single_complete_text = binding.singleCompleteText.text!!.toString() 72 | data!!.single_post_text = binding.singlePostText.text!!.toString() 73 | } 74 | 75 | override fun onSaveInstanceState(out: Bundle) 76 | { 77 | super.onSaveInstanceState(out) 78 | // save all data to be restored 79 | save() 80 | out.putSerializable(EXTRA_DATA, data) 81 | } 82 | 83 | private fun go_back() 84 | { 85 | save() 86 | val intent = Intent() 87 | intent.putExtra(EXTRA_DATA, data) 88 | setResult(RESULT_OK, intent) 89 | } 90 | 91 | @Deprecated("Deprecated in Java") 92 | override fun onBackPressed() 93 | { 94 | go_back() 95 | super.onBackPressed() 96 | } 97 | 98 | override fun onOptionsItemSelected(item: MenuItem) = when(item.itemId) 99 | { 100 | // make home button go back 101 | android.R.id.home -> 102 | { 103 | go_back() 104 | NavUtils.navigateUpFromSameTask(this) 105 | true 106 | } 107 | else -> super.onOptionsItemSelected(item) 108 | } 109 | 110 | companion object 111 | { 112 | const val EXTRA_DATA = "org.mattvchandler.progressbars.EXTRA_DATA" 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /app/src/main/java/list/Touch_helper_callback.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Matthew Chandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.mattvchandler.progressbars.list 23 | 24 | import android.graphics.Canvas 25 | import android.os.Build 26 | import android.util.TypedValue 27 | import androidx.core.view.ViewCompat 28 | import androidx.recyclerview.widget.ItemTouchHelper 29 | import androidx.recyclerview.widget.RecyclerView 30 | import org.mattvchandler.progressbars.R 31 | import kotlin.math.min 32 | 33 | // handle drag gestures for reorder and dismiss in RecyclerView 34 | class Touch_helper_callback(private val adapter: Adapter): ItemTouchHelper.Callback() 35 | { 36 | // long press and drag to reorder list 37 | override fun onMove(recyclerView: RecyclerView, source: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean 38 | { 39 | adapter.move_item(source.adapterPosition, target.adapterPosition) 40 | return true 41 | } 42 | 43 | // swipe to delete row 44 | override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) 45 | { 46 | adapter.on_item_dismiss(viewHolder.adapterPosition) 47 | } 48 | 49 | override fun isLongPressDragEnabled(): Boolean 50 | { 51 | return true 52 | } 53 | 54 | override fun isItemViewSwipeEnabled(): Boolean 55 | { 56 | return true 57 | } 58 | 59 | override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int 60 | { 61 | // reorder up and down, swipe left and right 62 | return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.START or ItemTouchHelper.END) 63 | } 64 | 65 | override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) 66 | { 67 | super.onSelectedChanged(viewHolder, actionState) 68 | // notify when a row is selected 69 | if(actionState != ItemTouchHelper.ACTION_STATE_IDLE) 70 | adapter.on_selected(viewHolder!!.adapterPosition) 71 | } 72 | 73 | override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) 74 | { 75 | super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) 76 | if(isCurrentlyActive) 77 | { 78 | if(Build.VERSION.SDK_INT >= 21) 79 | { 80 | // raise the selected row up (it will do a little bit of this by default, but we can make it more noticeable 81 | ViewCompat.setElevation(viewHolder.itemView, recyclerView.context.resources.getDimension(R.dimen.selected_elevation)) 82 | } 83 | else 84 | { 85 | // get the background color 86 | val tv = TypedValue() 87 | recyclerView.context.theme.resolveAttribute(android.R.attr.colorBackground, tv, true) 88 | // make it darker and-semi transparent 89 | viewHolder.itemView.setBackgroundColor(min(tv.data - 0x40202020, 0)) 90 | } 91 | } 92 | } 93 | 94 | override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) 95 | { 96 | super.clearView(recyclerView, viewHolder) 97 | // notify when a row is deselected 98 | adapter.on_cleared(viewHolder.adapterPosition) 99 | 100 | if(Build.VERSION.SDK_INT >= 21) 101 | { 102 | // lower selected row 103 | ViewCompat.setElevation(viewHolder.itemView, 0.0f) 104 | } 105 | else 106 | { 107 | // reset the original background color 108 | val tv = TypedValue() 109 | recyclerView.context.theme.resolveAttribute(android.R.attr.colorBackground, tv, true) 110 | viewHolder.itemView.setBackgroundColor(tv.data) 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /app/src/main/res/layout-land/about_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 26 | 29 | 30 | 36 | 46 | 47 | 60 | 61 | 70 | 71 | 80 | 81 | 91 | 92 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /app/src/main/res/layout/about_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 26 | 29 | 30 | 36 | 48 | 49 | 62 | 63 | 73 | 74 | 83 | 84 | 94 | 95 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /app/src/main/res/layout/single_progress_bar_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 46 | 47 | 64 | 65 | 82 | 83 | 96 | 97 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /app/src/main/java/settings/listeners.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Matthew Chandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.mattvchandler.progressbars.settings 23 | 24 | import android.view.View 25 | import android.widget.EditText 26 | import android.widget.Toast 27 | import org.mattvchandler.progressbars.R 28 | import org.mattvchandler.progressbars.db.Data 29 | import org.mattvchandler.progressbars.settings.Settings.Companion.get_date_format 30 | import org.mattvchandler.progressbars.settings.Settings.Companion.get_time_format 31 | import java.text.ParsePosition 32 | import java.util.* 33 | 34 | // listen for changes to date text 35 | internal class Date_listener(private val data: Data): View.OnFocusChangeListener 36 | { 37 | override fun onFocusChange(v: View, hasFocus: Boolean) 38 | { 39 | // check format when date entry loses focus 40 | if(!hasFocus) 41 | { 42 | // attempt to parse current text 43 | var new_date = (v as EditText).text.toString() 44 | val df = get_date_format(v.context) 45 | 46 | val date = df.parse(new_date, ParsePosition(0)) 47 | if(date == null) 48 | { 49 | // couldn't parse, show message 50 | Toast.makeText(v.context, v.getContext().resources.getString(R.string.invalid_date, 51 | new_date, df.toLocalizedPattern()), Toast.LENGTH_LONG).show() 52 | 53 | // replace with old value, so field contains valid data 54 | if(v.id == R.id.start_date_sel) 55 | { 56 | df.timeZone = TimeZone.getTimeZone(data.start_tz) 57 | v.setText(df.format(Date(data.start_time * 1000))) 58 | } 59 | else if(v.id == R.id.end_date_sel) 60 | { 61 | df.timeZone = TimeZone.getTimeZone(data.end_tz) 62 | v.setText(df.format(Date(data.end_time * 1000))) 63 | } 64 | } 65 | else 66 | { 67 | // new value is valid, set it. 68 | new_date = df.format(date) 69 | v.setText(new_date) 70 | } 71 | } 72 | } 73 | } 74 | 75 | // listen for changes to time text 76 | internal class Time_listener(private val data: Data): View.OnFocusChangeListener 77 | { 78 | override fun onFocusChange(v: View, hasFocus: Boolean) 79 | { 80 | // check format when time entry loses focus 81 | if(!hasFocus) 82 | { 83 | // attempt to parse current text 84 | var new_time = (v as EditText).text.toString() 85 | val df = get_time_format() 86 | 87 | val time = df.parse(new_time, ParsePosition(0)) 88 | if(time == null) 89 | { 90 | // couldn't parse, show message 91 | Toast.makeText(v.getContext(), v.getContext().resources.getString(R.string.invalid_time, 92 | new_time, df.toLocalizedPattern()), 93 | Toast.LENGTH_LONG).show() 94 | 95 | // replace with old value, so field contains valid data 96 | if(v.id == R.id.start_time_sel) 97 | { 98 | df.timeZone = TimeZone.getTimeZone(data.start_tz) 99 | v.setText(df.format(Date(data.start_time * 1000))) 100 | } 101 | else if(v.id == R.id.end_time_sel) 102 | { 103 | df.timeZone = TimeZone.getTimeZone(data.end_tz) 104 | v.setText(df.format(Date(data.end_time * 1000))) 105 | } 106 | } 107 | else 108 | { 109 | // new value is valid, set it. 110 | new_time = df.format(time) 111 | v.setText(new_time) 112 | } 113 | } 114 | } 115 | } 116 | 117 | // listen for changes to repeat count & units 118 | internal class Repeat_count_listener(private val data: Data): View.OnFocusChangeListener 119 | { 120 | override fun onFocusChange(v: View, hasFocus: Boolean) 121 | { 122 | // check value when count loses focus 123 | if(!hasFocus) 124 | { 125 | var count = 0 126 | try 127 | { 128 | count = Integer.parseInt((v as EditText).text.toString()) 129 | } 130 | catch(ignored: NumberFormatException) {} 131 | 132 | if(count <= 0) 133 | { 134 | (v as EditText).setText(data.repeat_count.toString()) 135 | Toast.makeText(v.getContext(), R.string.invalid_repeat_count, Toast.LENGTH_LONG).show() 136 | } 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /app/src/main/res/layout/progress_bar_widget.xml: -------------------------------------------------------------------------------- 1 | 21 | 22 | 33 | 34 | 43 | 44 | 50 | 58 | 65 | 66 | 72 | 73 | 79 | 80 | 81 | 90 | 96 | 106 | 107 | 108 | 116 | 123 | 129 | 135 | 136 | 137 | 138 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /app/src/main/res/values/values.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | date_format 26 | 27 | 28 | locale 29 | yyyy-MM-dd 30 | 31 | 32 | @string/pref_date_format_value_locale 33 | @string/pref_date_format_value_ISO8601 34 | 35 | 36 | locale 37 | 38 | first_day_of_wk 39 | 40 | 41 | 0 42 | 7 43 | 1 44 | 2 45 | 46 | 47 | @string/pref_first_day_of_wk_value_locale 48 | @string/pref_first_day_of_wk_value_sat 49 | @string/pref_first_day_of_wk_value_sun 50 | @string/pref_first_day_of_wk_value_mon 51 | 52 | 53 | @string/pref_first_day_of_wk_value_locale 54 | 55 | system_notifications 56 | 57 | widget_refresh 58 | 59 | 60 | 61 | 5000 62 | 15000 63 | 30000 64 | 60000 65 | 100000 66 | 1800000 67 | 3600000 68 | 86400000 69 | 70 | 71 | @string/pref_widget_refresh_value_5s 72 | @string/pref_widget_refresh_value_15s 73 | @string/pref_widget_refresh_value_30s 74 | @string/pref_widget_refresh_value_1m 75 | @string/pref_widget_refresh_value_15m 76 | @string/pref_widget_refresh_value_30m 77 | @string/pref_widget_refresh_value_1h 78 | @string/pref_widget_refresh_value_1d 79 | 80 | @string/pref_widget_refresh_value_30s 81 | 82 | widget_text_color 83 | white 84 | black 85 | 86 | 87 | @string/pref_widget_text_color_white 88 | @string/pref_widget_text_color_black 89 | 90 | 91 | @string/pref_widget_text_color_white 92 | 93 | widget_bg 94 | false 95 | 96 | theme 97 | system 98 | day 99 | night 100 | auto 101 | auto_batt 102 | 103 | 104 | @string/pref_theme_value_system 105 | @string/pref_theme_value_day 106 | @string/pref_theme_value_night 107 | @string/pref_theme_value_auto 108 | 109 | 110 | @string/pref_theme_value_system 111 | 112 | next_id 113 | 114 | prompt_exact_alarm 115 | 116 | 117 | MIN 118 | LOW 119 | DEFAULT 120 | HIGH 121 | MAX 122 | 123 | 124 | -------------------------------------------------------------------------------- /app/src/main/java/util/Dynamic_theme_activity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Matthew Chandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.mattvchandler.progressbars.util 23 | 24 | import android.content.Context 25 | import android.os.Build 26 | import android.os.Bundle 27 | import android.os.Handler 28 | import android.util.TypedValue 29 | import android.view.View 30 | import android.view.ViewGroup 31 | import androidx.appcompat.app.AppCompatActivity 32 | import androidx.appcompat.app.AppCompatDelegate 33 | import androidx.core.view.ViewCompat 34 | import androidx.core.view.updatePadding 35 | import androidx.preference.PreferenceManager 36 | import com.google.android.material.appbar.AppBarLayout 37 | import org.mattvchandler.progressbars.R 38 | 39 | // extend AppCompatActivity to call recreate when the "dark_theme" preference changes 40 | // and to set the correct theme when create is called 41 | abstract class Dynamic_theme_activity: AppCompatActivity() 42 | { 43 | private lateinit var theme: String 44 | override fun onCreate(savedInstanceState: Bundle?) 45 | { 46 | val (night_mode, new_theme) = get_current_night_mode(this) 47 | 48 | theme = new_theme 49 | AppCompatDelegate.setDefaultNightMode(night_mode) 50 | 51 | super.onCreate(savedInstanceState) 52 | } 53 | 54 | override fun onResume() 55 | { 56 | super.onResume() 57 | val new_theme = PreferenceManager.getDefaultSharedPreferences(this).getString(resources.getString(R.string.pref_theme_key), resources.getString(R.string.pref_theme_default))!! 58 | 59 | // has the theme changed? recreate this activity 60 | if(new_theme != theme) 61 | Handler().postDelayed({recreate()}, 0) 62 | 63 | if(Build.VERSION.SDK_INT in 26..28) 64 | { 65 | window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or 66 | if(resources.getBoolean(R.bool.is_theme_light)) View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR else 0 67 | 68 | } 69 | else if(Build.VERSION.SDK_INT >= 21) 70 | { 71 | window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 72 | } 73 | } 74 | 75 | companion object 76 | { 77 | fun get_current_night_mode(context: Context): Pair 78 | { 79 | val theme = PreferenceManager.getDefaultSharedPreferences(context).getString(context.resources.getString(R.string.pref_theme_key), context.resources.getString(R.string.pref_theme_default))!! 80 | 81 | var night_mode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM 82 | 83 | when(theme) 84 | { 85 | context.resources.getString(R.string.pref_theme_value_system) -> night_mode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM 86 | context.resources.getString(R.string.pref_theme_value_day) -> night_mode = AppCompatDelegate.MODE_NIGHT_NO 87 | context.resources.getString(R.string.pref_theme_value_night) -> night_mode = AppCompatDelegate.MODE_NIGHT_YES 88 | context.resources.getString(R.string.pref_theme_value_auto) -> night_mode = AppCompatDelegate.MODE_NIGHT_AUTO // I know this is deprecated, but I don't care. There isn't a better alternative before Android Pie 89 | context.resources.getString(R.string.pref_theme_value_auto_batt) -> night_mode = AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY 90 | } 91 | 92 | return Pair(night_mode, theme) 93 | } 94 | 95 | fun consume_insets(context: Context, view: View, appbar_layout: AppBarLayout) 96 | { 97 | if(Build.VERSION.SDK_INT >= 21) 98 | { 99 | ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets -> 100 | var toolbar_height = 0 101 | val tv = TypedValue() 102 | if(context.theme.resolveAttribute(android.R.attr.actionBarSize, tv, true)) 103 | toolbar_height = TypedValue.complexToDimensionPixelSize(tv.data, context.resources.displayMetrics) 104 | 105 | v.setPadding( 106 | insets.systemWindowInsetLeft, 107 | insets.systemWindowInsetTop + toolbar_height, 108 | insets.systemWindowInsetRight, 109 | insets.systemWindowInsetBottom 110 | ) 111 | insets 112 | } 113 | 114 | ViewCompat.setOnApplyWindowInsetsListener(appbar_layout) { v, insets -> 115 | val margins= v.layoutParams as ViewGroup.MarginLayoutParams 116 | margins.topMargin = insets.systemWindowInsetTop 117 | 118 | if(insets.displayCutout != null && insets.systemWindowInsetLeft == insets.displayCutout!!.safeInsetLeft) 119 | { 120 | margins.leftMargin = 0 121 | (v as AppBarLayout).getChildAt(0).updatePadding(left = insets.systemWindowInsetLeft) 122 | } 123 | else 124 | { 125 | margins.leftMargin = insets.systemWindowInsetLeft 126 | } 127 | 128 | if(insets.displayCutout != null && insets.systemWindowInsetRight == insets.displayCutout!!.safeInsetRight) 129 | { 130 | margins.rightMargin = 0 131 | (v as AppBarLayout).getChildAt(0).updatePadding(right = insets.systemWindowInsetRight) 132 | } 133 | else 134 | { 135 | margins.rightMargin = insets.systemWindowInsetRight 136 | } 137 | 138 | insets 139 | } 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /app/src/main/java/util/Preferences.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Matthew Chandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.mattvchandler.progressbars.util 23 | 24 | import android.Manifest 25 | import android.app.Dialog 26 | import android.content.Context 27 | import android.content.Intent 28 | import android.content.SharedPreferences 29 | import android.content.pm.PackageManager 30 | import android.net.Uri 31 | import android.os.Build 32 | import android.os.Bundle 33 | import android.view.MenuItem 34 | import androidx.appcompat.app.AlertDialog 35 | import androidx.appcompat.widget.Toolbar 36 | import androidx.core.app.ActivityCompat 37 | import androidx.core.content.ContextCompat 38 | import androidx.databinding.DataBindingUtil 39 | import androidx.fragment.app.DialogFragment 40 | import androidx.preference.ListPreference 41 | import androidx.preference.Preference 42 | import androidx.preference.PreferenceFragmentCompat 43 | import androidx.preference.PreferenceManager 44 | import org.mattvchandler.progressbars.R 45 | import org.mattvchandler.progressbars.databinding.ActivityPreferencesBinding 46 | 47 | // application settings screen 48 | class Preferences: Dynamic_theme_activity() 49 | { 50 | companion object 51 | { 52 | private const val LOCATION_PERMISSION_RESPONSE = 1 53 | } 54 | class Progress_bar_prefs_frag: PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener 55 | { 56 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) 57 | { 58 | setPreferencesFromResource(R.xml.preferences, rootKey) 59 | PreferenceManager.setDefaultValues(requireActivity(), R.xml.preferences, false) 60 | findPreference(resources.getString(R.string.pref_date_format_key))?.summaryProvider = ListPreference.SimpleSummaryProvider.getInstance() 61 | findPreference(resources.getString(R.string.pref_first_day_of_wk_key))?.summaryProvider = ListPreference.SimpleSummaryProvider.getInstance() 62 | findPreference(resources.getString(R.string.pref_widget_refresh_key))?.summaryProvider = ListPreference.SimpleSummaryProvider.getInstance() 63 | findPreference(resources.getString(R.string.pref_widget_text_color_key))?.summaryProvider = ListPreference.SimpleSummaryProvider.getInstance() 64 | findPreference(resources.getString(R.string.pref_theme_key))?.summaryProvider = ListPreference.SimpleSummaryProvider.getInstance() 65 | } 66 | 67 | // register / unregister listener 68 | override fun onResume() 69 | { 70 | super.onResume() 71 | preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) 72 | 73 | } 74 | 75 | override fun onPause() 76 | { 77 | super.onPause() 78 | preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this) 79 | } 80 | 81 | override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) 82 | { 83 | if(key == resources.getString(R.string.pref_theme_key)) 84 | { 85 | // request location permission for local sunset / sunrise times 86 | if(sharedPreferences?.getString(resources.getString(R.string.pref_theme_key), "") == resources.getString(R.string.pref_theme_value_auto)) 87 | { 88 | if (ContextCompat.checkSelfPermission(activity as Context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) 89 | { 90 | if(ActivityCompat.shouldShowRequestPermissionRationale(requireActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) 91 | { 92 | class Location_frag: DialogFragment() 93 | { 94 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = 95 | AlertDialog.Builder(requireContext()) 96 | .setTitle(R.string.loc_perm_title) 97 | .setMessage(R.string.loc_perm_msg) 98 | .setPositiveButton(android.R.string.ok) { _, _ -> ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_PERMISSION_RESPONSE)} 99 | .setNegativeButton(android.R.string.cancel, null) 100 | .create() 101 | } 102 | Location_frag().show(requireActivity().supportFragmentManager, "location_permission_dialog") 103 | } 104 | else 105 | { 106 | ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_PERMISSION_RESPONSE) 107 | } 108 | } 109 | } 110 | // recreate this activity to apply the new theme 111 | activity?.recreate() 112 | } 113 | } 114 | 115 | override fun onPreferenceTreeClick(preference: Preference): Boolean 116 | { 117 | when(preference.key) 118 | { 119 | resources.getString(R.string.pref_system_notifications_key) -> 120 | { 121 | val intent = Intent() 122 | when 123 | { 124 | Build.VERSION.SDK_INT in 21..25 -> 125 | { 126 | intent.action = "android.settings.APP_NOTIFICATION_SETTINGS" 127 | intent.putExtra("app_package", requireContext().packageName) 128 | intent.putExtra("app_uid", requireContext().applicationInfo.uid) 129 | } 130 | Build.VERSION.SDK_INT >= 26 -> 131 | { 132 | intent.action = android.provider.Settings.ACTION_APP_NOTIFICATION_SETTINGS 133 | intent.putExtra(android.provider.Settings.EXTRA_APP_PACKAGE, requireContext().packageName) 134 | } 135 | else -> 136 | { 137 | intent.action = android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS 138 | intent.addCategory(Intent.CATEGORY_DEFAULT) 139 | intent.data = Uri.parse("package:${requireContext().packageName}") 140 | } 141 | } 142 | 143 | startActivity(intent) 144 | return false 145 | } 146 | 147 | else -> return super.onPreferenceTreeClick(preference) 148 | } 149 | } 150 | } 151 | 152 | override fun onCreate(savedInstanceState: Bundle?) 153 | { 154 | super.onCreate(savedInstanceState) 155 | 156 | val binding = DataBindingUtil.setContentView(this, R.layout.activity_preferences) 157 | setSupportActionBar(binding.toolbar as Toolbar) // Note: not a useless cast. It wont build without it. Google needs to get their type-checking together for bindings 158 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 159 | 160 | consume_insets(this, binding.preferences, binding.appbarLayout) 161 | 162 | // put settings content into frame layout 163 | supportFragmentManager.beginTransaction().replace(R.id.preferences, Progress_bar_prefs_frag()).commit() 164 | } 165 | 166 | override fun onOptionsItemSelected(item: MenuItem) = when(item.itemId) 167 | { 168 | // make home button go back 169 | android.R.id.home -> { finish(); true } 170 | else -> super.onOptionsItemSelected(item) 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /app/src/main/java/settings/TimeZone_disp.kt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Matthew Chandler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.mattvchandler.progressbars.settings 23 | 24 | import android.content.Context 25 | import android.content.Intent 26 | import android.os.Bundle 27 | import android.view.LayoutInflater 28 | import android.view.Menu 29 | import android.view.View 30 | import android.view.ViewGroup 31 | import androidx.appcompat.app.AppCompatActivity 32 | import androidx.appcompat.widget.SearchView 33 | import androidx.appcompat.widget.Toolbar 34 | import androidx.databinding.DataBindingUtil 35 | import androidx.recyclerview.widget.LinearLayoutManager 36 | import androidx.recyclerview.widget.RecyclerView 37 | import androidx.recyclerview.widget.SortedList 38 | import org.mattvchandler.progressbars.R 39 | import org.mattvchandler.progressbars.databinding.ActivityTimezoneBinding 40 | import org.mattvchandler.progressbars.databinding.TimezoneItemBinding 41 | import org.mattvchandler.progressbars.util.Dynamic_theme_activity 42 | import java.io.Serializable 43 | import java.util.* 44 | 45 | class TimeZone_disp(val id: String, context: Context?, date: Date?): Serializable 46 | { 47 | val name = id.replace('_', ' ') 48 | val subtitle:String? 49 | val search_kwds: List 50 | 51 | init 52 | { 53 | val tz = TimeZone.getTimeZone(id) 54 | 55 | val is_daylight = if(date != null) tz.inDaylightTime(date) else false 56 | 57 | val disp_name = tz.getDisplayName(is_daylight, TimeZone.LONG) 58 | val short_name = tz.getDisplayName(is_daylight, TimeZone.SHORT) 59 | 60 | subtitle = context?.resources?.getString(R.string.tz_list_subtitle, disp_name, short_name) 61 | 62 | search_kwds = listOf(name, id, disp_name, short_name).map{ it.lowercase(Locale.getDefault()) }.distinct() 63 | } 64 | 65 | override fun toString() = name 66 | override fun equals(other: Any?): Boolean 67 | { 68 | if(this === other) return true 69 | if(javaClass != other?.javaClass) return false 70 | 71 | other as TimeZone_disp 72 | 73 | if(id != other.id) return false 74 | 75 | return true 76 | } 77 | 78 | override fun hashCode(): Int 79 | { 80 | return id.hashCode() 81 | } 82 | } 83 | 84 | private class TimeZone_adapter(private val activity: TimeZone_activity, private val date: Date): RecyclerView.Adapter() 85 | { 86 | inner class Holder(private val binding: TimezoneItemBinding, view: View): RecyclerView.ViewHolder(view), View.OnClickListener 87 | { 88 | lateinit var tz: TimeZone_disp 89 | fun set() 90 | { 91 | val position = adapterPosition 92 | if(position == RecyclerView.NO_POSITION) 93 | return 94 | 95 | tz = timezones[position] 96 | binding.tz = tz 97 | } 98 | override fun onClick(v: View?) 99 | { 100 | val intent = Intent() 101 | 102 | intent.putExtra(TimeZone_activity.EXTRA_SELECTED_TZ, tz) 103 | activity.setResult(AppCompatActivity.RESULT_OK, intent) 104 | activity.finish() 105 | } 106 | } 107 | 108 | private var timezones = SortedList(TimeZone_disp::class.java, object: SortedList.Callback() 109 | { 110 | override fun onMoved(from_pos: Int, to_pos: Int) { notifyItemMoved(from_pos, to_pos) } 111 | override fun onChanged(pos: Int, count: Int) { notifyItemRangeChanged(pos, count) } 112 | override fun onInserted(pos: Int, count: Int) { notifyItemRangeInserted(pos, count) } 113 | override fun onRemoved(pos: Int, count: Int) { notifyItemRangeRemoved(pos, count) } 114 | 115 | override fun compare(a: TimeZone_disp, b: TimeZone_disp) = compareBy{ it.id }.compare(a, b) 116 | override fun areItemsTheSame(a: TimeZone_disp, b: TimeZone_disp) = a == b 117 | override fun areContentsTheSame(a: TimeZone_disp, b: TimeZone_disp) = a == b 118 | }) 119 | 120 | private val inflater = LayoutInflater.from(activity) 121 | 122 | init 123 | { 124 | filter("") 125 | } 126 | 127 | fun filter(search: String) 128 | { 129 | val all_tzs = TimeZone.getAvailableIDs().map{ TimeZone_disp(it, activity, date) } 130 | if(search == "") 131 | replace_all(all_tzs) 132 | else 133 | replace_all(all_tzs.filter{ tz -> tz.search_kwds.any{it.contains(search.lowercase(Locale.getDefault()))} }) 134 | } 135 | 136 | fun replace_all(tzs: List) 137 | { 138 | timezones.beginBatchedUpdates() 139 | 140 | for(i in timezones.size() - 1 downTo 0) 141 | { 142 | val tz = timezones.get(i) 143 | if(tz !in tzs) 144 | timezones.remove(tz) 145 | } 146 | 147 | timezones.addAll(tzs) 148 | 149 | timezones.endBatchedUpdates() 150 | } 151 | 152 | override fun getItemCount(): Int 153 | { 154 | return timezones.size() 155 | } 156 | 157 | override fun onCreateViewHolder(parent_in: ViewGroup, viewType: Int): Holder 158 | { 159 | val binding = TimezoneItemBinding.inflate(inflater, parent_in, false) 160 | val holder = Holder(binding, binding.root) 161 | binding.root.setOnClickListener(holder) 162 | return holder 163 | } 164 | 165 | override fun onBindViewHolder(holder: Holder, position: Int) 166 | { 167 | holder.set() 168 | } 169 | } 170 | 171 | class TimeZone_activity: Dynamic_theme_activity() 172 | { 173 | private lateinit var adapter: TimeZone_adapter 174 | private var saved_search: String? = null 175 | private lateinit var binding: ActivityTimezoneBinding 176 | 177 | override fun onCreate(savedInstanceState: Bundle?) 178 | { 179 | super.onCreate(savedInstanceState) 180 | 181 | binding = DataBindingUtil.setContentView(this, R.layout.activity_timezone) 182 | setSupportActionBar(binding.toolbar as Toolbar) 183 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 184 | 185 | consume_insets(this, binding.timezoneList, binding.appbarLayout) 186 | 187 | val date = intent.getSerializableExtra(EXTRA_DATE) as Date 188 | 189 | adapter = TimeZone_adapter(this, date) 190 | binding.timezoneList.adapter = adapter 191 | binding.timezoneList.layoutManager = LinearLayoutManager(this) 192 | 193 | if(savedInstanceState != null) 194 | saved_search = savedInstanceState.getString(SAVE_SEARCH) 195 | } 196 | 197 | override fun onSaveInstanceState(outState: Bundle) 198 | { 199 | super.onSaveInstanceState(outState) 200 | outState.putString(SAVE_SEARCH, saved_search) 201 | } 202 | 203 | override fun onCreateOptionsMenu(menu: Menu?): Boolean 204 | { 205 | menuInflater.inflate(R.menu.timezone_action_bar, menu) 206 | val search_item = menu?.findItem(R.id.timezone_search)!! 207 | val search = search_item.actionView as SearchView 208 | 209 | search.maxWidth = Int.MAX_VALUE 210 | 211 | search.setOnQueryTextListener(object: SearchView.OnQueryTextListener 212 | { 213 | override fun onQueryTextSubmit(query: String?): Boolean 214 | { 215 | search.clearFocus() 216 | return true 217 | } 218 | 219 | override fun onQueryTextChange(newText: String?): Boolean 220 | { 221 | saved_search = newText 222 | adapter.filter(newText?: "") 223 | binding.timezoneList.scrollToPosition(0) 224 | return true 225 | } 226 | }) 227 | 228 | if(saved_search != null) 229 | { 230 | search.setQuery(saved_search, true) 231 | search.clearFocus() 232 | } 233 | else 234 | search.requestFocus() 235 | 236 | return super.onCreateOptionsMenu(menu) 237 | } 238 | 239 | companion object 240 | { 241 | const val EXTRA_DATE = "org.mattvchandler.progressbars.EXTRA_DATE" 242 | const val EXTRA_SELECTED_TZ = "org.mattvchandler.progressbars.EXTRA_SELECTED_ID" 243 | 244 | private const val SAVE_SEARCH = "search" 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_countdown_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 40 | 41 | 48 | 54 | 61 | 69 | 70 | 71 | 78 | 86 | 87 | 88 | 95 | 103 | 104 | 105 | 112 | 120 | 121 | 122 | 129 | 137 | 138 | 139 | 146 | 154 | 155 | 156 | 163 | 171 | 172 | 173 | 180 | 188 | 189 | 190 | 191 | 198 | 202 | 203 | 204 | 205 | --------------------------------------------------------------------------------