├── .github
├── FUNDING.yml
└── images
│ ├── Screenshot_20240809_113408_RomanDigital.png
│ ├── Screenshot_20240809_161416_RomanDigital.png
│ ├── Screenshot_20240809_205721_RomanDigital.png
│ ├── Screenshot_20240910_174429_One_UI_Home_scaled.jpg
│ ├── Screenshot_20241008_143851_One_UI_Home_scaled.jpg
│ ├── Screenshot_20250219_115336_One_UI_Home_scaled.jpg
│ ├── Screenshot_20250219_120411_One_UI_Home_scaled.jpg
│ ├── Torn_Screenshot_20240913_171548_One_UI_Home.png
│ ├── clearpixel.gif
│ └── get-the-latest-apk-on-github.png
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── net
│ │ └── diffengine
│ │ └── romandigitalclock
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── net
│ │ │ └── diffengine
│ │ │ └── romandigitalclock
│ │ │ ├── AboutActivity.java
│ │ │ ├── AppClass.java
│ │ │ ├── BootCompletedBroadcastReceiver.java
│ │ │ ├── ColorDialogPreference.java
│ │ │ ├── ColorSeekBarView.java
│ │ │ ├── MainActivity.java
│ │ │ ├── SettingsActivity.java
│ │ │ ├── SettingsButtonBarFragment.java
│ │ │ ├── TimeDisplayWidget.java
│ │ │ ├── TimeDisplayWidgetConfigActivity.java
│ │ │ └── romantime.java
│ ├── rd_launcher-playstore.png
│ └── res
│ │ ├── color
│ │ ├── ab_switch_thumb.xml
│ │ └── ab_switch_track.xml
│ │ ├── drawable-v21
│ │ ├── app_widget_background.xml
│ │ ├── app_widget_inner_view_background.xml
│ │ ├── appwidget_bkgnd_0.xml
│ │ ├── appwidget_bkgnd_10.xml
│ │ ├── appwidget_bkgnd_100.xml
│ │ ├── appwidget_bkgnd_20.xml
│ │ ├── appwidget_bkgnd_30.xml
│ │ ├── appwidget_bkgnd_40.xml
│ │ ├── appwidget_bkgnd_50.xml
│ │ ├── appwidget_bkgnd_60.xml
│ │ ├── appwidget_bkgnd_70.xml
│ │ ├── appwidget_bkgnd_80.xml
│ │ └── appwidget_bkgnd_90.xml
│ │ ├── drawable
│ │ ├── ic_info_outline_24.xml
│ │ ├── ic_settings_24dp.xml
│ │ ├── romandigital_3x1_appwidget_preview.png
│ │ └── seekbar_track_material.xml
│ │ ├── layout
│ │ ├── a_b_switch_layout.xml
│ │ ├── about_activity.xml
│ │ ├── activity_main.xml
│ │ ├── activity_time_display_widget_config.xml
│ │ ├── color_dialog_layout.xml
│ │ ├── color_seekbar_layout.xml
│ │ ├── fragment_settings_button_bar.xml
│ │ ├── separator_layout.xml
│ │ ├── settings_activity.xml
│ │ ├── time_display_widget.xml
│ │ ├── time_display_widget_hi_label.xml
│ │ └── time_display_widget_lo_label.xml
│ │ ├── menu
│ │ └── main_menu.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── rd_launcher.xml
│ │ └── rd_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── rd_launcher.webp
│ │ ├── rd_launcher_foreground.webp
│ │ └── rd_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── rd_launcher.webp
│ │ ├── rd_launcher_foreground.webp
│ │ └── rd_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── rd_launcher.webp
│ │ ├── rd_launcher_foreground.webp
│ │ └── rd_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── rd_launcher.webp
│ │ ├── rd_launcher_foreground.webp
│ │ └── rd_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── rd_launcher.webp
│ │ ├── rd_launcher_foreground.webp
│ │ └── rd_launcher_round.webp
│ │ ├── values-night-v31
│ │ └── themes.xml
│ │ ├── values-night
│ │ ├── colors.xml
│ │ └── themes.xml
│ │ ├── values-v21
│ │ └── styles.xml
│ │ ├── values-v31
│ │ ├── styles.xml
│ │ └── themes.xml
│ │ ├── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── rd_launcher_background.xml
│ │ ├── strings.xml
│ │ ├── styles.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── display_color_prefs.xml
│ │ ├── screen_preferences.xml
│ │ ├── time_display_widget_info.xml
│ │ └── widget_bkgnd_prefs.xml
│ └── test
│ └── java
│ └── net
│ └── diffengine
│ └── romandigitalclock
│ └── ExampleUnitTest.java
├── build.gradle.kts
├── fastlane
└── metadata
│ └── android
│ └── en-US
│ ├── changelogs
│ ├── 1.txt
│ ├── 2.txt
│ ├── 3.txt
│ ├── 4.txt
│ ├── 5.txt
│ ├── 6.txt
│ ├── 7.txt
│ ├── 8.txt
│ └── 9.txt
│ ├── full_description.txt
│ ├── images
│ ├── featureGraphic.png
│ ├── icon.png
│ ├── phoneScreenshots
│ │ ├── p1.png
│ │ ├── p10.jpg
│ │ ├── p11.jpg
│ │ ├── p2.png
│ │ ├── p3.png
│ │ ├── p4.png
│ │ ├── p5.png
│ │ ├── p6.png
│ │ ├── p7.png
│ │ ├── p8.png
│ │ └── p9.png
│ └── tenInchScreenshots
│ │ ├── pA.png
│ │ ├── pB.png
│ │ ├── pC.png
│ │ └── pD.png
│ ├── short_description.txt
│ └── title.txt
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # RomanDigital funding platforms
2 |
3 | liberapay: nichedev
4 | custom: ['https://www.paypal.com/donate/?business=7J9528994S6QU&no_recurring=0¤cy_code=USD']
--------------------------------------------------------------------------------
/.github/images/Screenshot_20240809_113408_RomanDigital.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/.github/images/Screenshot_20240809_113408_RomanDigital.png
--------------------------------------------------------------------------------
/.github/images/Screenshot_20240809_161416_RomanDigital.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/.github/images/Screenshot_20240809_161416_RomanDigital.png
--------------------------------------------------------------------------------
/.github/images/Screenshot_20240809_205721_RomanDigital.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/.github/images/Screenshot_20240809_205721_RomanDigital.png
--------------------------------------------------------------------------------
/.github/images/Screenshot_20240910_174429_One_UI_Home_scaled.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/.github/images/Screenshot_20240910_174429_One_UI_Home_scaled.jpg
--------------------------------------------------------------------------------
/.github/images/Screenshot_20241008_143851_One_UI_Home_scaled.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/.github/images/Screenshot_20241008_143851_One_UI_Home_scaled.jpg
--------------------------------------------------------------------------------
/.github/images/Screenshot_20250219_115336_One_UI_Home_scaled.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/.github/images/Screenshot_20250219_115336_One_UI_Home_scaled.jpg
--------------------------------------------------------------------------------
/.github/images/Screenshot_20250219_120411_One_UI_Home_scaled.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/.github/images/Screenshot_20250219_120411_One_UI_Home_scaled.jpg
--------------------------------------------------------------------------------
/.github/images/Torn_Screenshot_20240913_171548_One_UI_Home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/.github/images/Torn_Screenshot_20240913_171548_One_UI_Home.png
--------------------------------------------------------------------------------
/.github/images/clearpixel.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/.github/images/clearpixel.gif
--------------------------------------------------------------------------------
/.github/images/get-the-latest-apk-on-github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/.github/images/get-the-latest-apk-on-github.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Gradle files
2 | .gradle/
3 | build/
4 |
5 | # Local configuration file (sdk path, etc)
6 | local.properties
7 |
8 | # Log/OS Files
9 | *.log
10 |
11 | # Android Studio generated files and folders
12 | captures/
13 | .externalNativeBuild/
14 | .cxx/
15 | *.apk
16 | output.json
17 |
18 | # IntelliJ
19 | *.iml
20 | .idea/
21 | misc.xml
22 | deploymentTargetDropDown.xml
23 | render.experimental.xml
24 |
25 | # Keystore files
26 | *.jks
27 | *.keystore
28 |
29 | # Google Services (e.g. APIs or Firebase)
30 | google-services.json
31 |
32 | # Android Profiling
33 | *.hprof
34 |
35 | # OS X generated file
36 | .DS_Store
37 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | Significant changes to the RomanDigital project will be documented here.
4 |
5 | The format of this changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6 |
7 | This project aims to adhere to [Semantic Versioning](https://server.org).
8 |
9 | Regarding project commits: As of 2024-08-23, this project aims to adhere to the [Conventional Commits](https://www.conventionalcommits.org) standard. While the standard makes recommendations, it does not limit commit type or scope; consequently, neither type nor scope is limited to those recommendations in the project commits.
10 |
11 | ## [2.0.2] - 2025-03-21
12 |
13 | ### Fixed
14 |
15 | * Failure of widget preferences being applied after a device's launcher has reset. Fixed by allowing update of preference-related features with every receipt of a MINUTE_TICK intent, negating a change in [2.0.0].
16 | * Accuracy of preview in the widget picker on device's running Android 12 or later.
17 |
18 | ## [2.0.1] - 2025-03-07
19 |
20 | ### Fixed
21 |
22 | * Failure of widgets to restart at device boot.
23 |
24 | ## [2.0.0] - 2025-02-20
25 |
26 | ### Added
27 |
28 | * Ability to configure settings independently for app and for each of one or more widgets. Settings are distinguished by a postfix widget id added to preference keys corresponding to a given widget, and by app settings having no postfix. __[This addition may require the User to reset settings for any widgets currently in use.]__
29 | * Ability to select a different time zone for each widget.
30 | * Selectable widget layouts enabling addition of a time zone label above or below the time display.
31 | * A custom preference that has no function except to provide a separator line in the arrangement of preferences on a settings activity.
32 | * The id of the widget currently being configured to the widget settings activity title when the project is built for debugging.
33 | * Commit type 'cleanup' for chores limited to removal of unused files, unused code, unneeded comments (or portions thereof), and/or superfluous whitespace.
34 |
35 | ### Changed
36 |
37 | * XML implementation of time and opacity preferences to code implementation to enable programmatic change of preference keys so the same hierarchy could be used to independently set different preference values for app and widget.
38 | * Tick intent (action MINUTE_TICK) so as to eliminate its unnecessary duplication.
39 | * Frequency of updating widget features by determining when to update such features based on the action of a received intent, especially preventing these features from being updated with every receipt of a MINUTE_TICK intent.
40 |
41 | ### Deprecated
42 |
43 | * The back arrow on the about activity, to be replaced with an "Ok" or similarly labeled button in the style of the buttons now provided on settings activities.
44 |
45 | ### Removed
46 |
47 | * The back arrow from the title bar of each settings activity.
48 |
49 | ### Fixed
50 |
51 | * Issues with widget time display text size being incorrect for the width of the widget (3 or >4 tiles wide) by adding setting of text size to be updated along with other widget features.
52 |
53 | ## [1.3.0] - 2024-11-18
54 |
55 | ### Added
56 |
57 | * Variable widget background opacity, with opacity being selectable from 0% to 100% by tens.
58 | * A slider — technically, a SeekBarPreference — to the widget settings activity so as to enable the user to select the desired opacity.
59 | * Automatic setting of widget text color in light mode to white (#FAFAFA) for opacity < 50% and to black (#040404) for opacity >= 50% to improve contrast for very white backgrounds. Text color is set to white (#FAFAFA) in dark mode regardless of background opacity.
60 | * Intent SETTINGS_KICK and modifications to the widget so SETTINGS_KICK is treated identically to MINUTE_TICK with respect to time updating and additionally used to enable update of background opacity when changed by the user or update is initiated by the widget itself.
61 |
62 | ### Changed
63 |
64 | * Widget corners for all Android versions to the same degree of curvature (8dp radius).
65 | * The broadcast of a kickstart intent in the widget config activity onPause method and calls to update the widget in the widget itself to include SETTINGS_KICK instead of MINUTE_TICK.
66 |
67 | ## [1.2.0] - 2024-10-29
68 |
69 | ### Added
70 |
71 | * `Cancel` and `Save` buttons to both widget and app settings activities (addresses Issue #12).
72 |
73 | ## [1.1.2] - 2024-10-19
74 |
75 | ### Fixed
76 |
77 | * App clock text being too large (Issue #14) when user has selected a font in their Android settings that effects app fonts and that does not provide a monospace font. (Note: While this fix enables use of variable-width fonts, the "Align to Divider" option functionality does not work correctly for such fonts.)
78 |
79 | ## [1.1.1] - 2024-10-15
80 |
81 | ### Changed
82 |
83 | * Location of versionName definition from strings.xml to app/build.gradle.kts; this facilitates building with systems (e.g. F-Droid) for which definition from a string reference is problematic.
84 | * AboutActivity to fetch versionName for display from app/build.gradle.kts rather than from a string reference.
85 | * Fastlane full_description to improve readability of reference to the README.md file.
86 |
87 | ## [1.1.0] - 2024-10-09
88 |
89 | ### Added
90 |
91 | * App metadata in a fastlane file structure for use in generating an app description page on [F-Droid](https://f-droid.org)
92 | * distributionSha256Sum value in gradle-wrapper.properties matching the SHA256SUM of file gradle-8.2-bin.zip referred to by the distributionUrl (see https://gradle.org/release-checksums/) to improve app security
93 | * FUNDING.yml containing funding platform information for accepting donations
94 | * Commit types 'meta' for metadata-related commits and 'improve' as short for 'improvement' (type 'improvement' is recommended in Conventional Commits beta versions 2-4).
95 | * This CHANGELOG file
96 |
97 | ### Changed
98 |
99 | * The default size of the app widget to its smallest size to facilitate installation of the widget on a crowded Home screen
100 | * The preview image used in widget pickers to one with a size matching the new default size
101 | * Display of the version on the About activity so it's set in the activity's onCreate method; this facilitates setting the versionName in app/build.gradle.kts, by way of a string reference, to just be a dotted number sequence
102 | * The version of gradle-wrapper.jar to match the version of gradle-8.2-bin.zip (i.e. 8.2) to improve app security
103 | * The README file
104 |
105 | ### Refactored
106 |
107 | * Deleted unneeded and/or unused matter from AndroidManifest.xml
108 |
109 | ## [1.0.0] - 2024-09-13
110 |
111 | ### Added
112 |
113 | * Everything. This is the first release.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # RomanDigital
4 |
5 | Android Digital Clock App and Widget with Roman Numeral Display
6 |
7 | [
](https://f-droid.org/packages/net.diffengine.romandigitalclock/)
10 | 
11 |
12 |
13 | [
](https://github.com/dfyockey/RomanDigital/releases/latest)
16 | 
17 |
18 |
19 | ## Description
20 |
21 | RomanDigital is a digital clock app that displays the current time in
22 | Roman numerals. It can be used to repurpose an old Android device that's
23 | sitting around collecting dust, or to provide a clock of unique style on
24 | a current phone or tablet.
25 |
26 | 
27 |
28 | Further, RomanDigital includes a widget that can be added to a device's Home screen.
29 |
30 | 
31 |
32 | ## Features
33 |
34 | RomanDigital includes several common clock app features, including:
35 |
36 | * Choice of 12 or 24 hour display
37 | * Centered display
38 | * Option to keep display on when app is in foreground
39 | * Display in either portrait or landscape
40 | * Tap on screen displays app and system controls:
41 |
42 | 
43 |
44 | RomanDigital further includes:
45 |
46 | * Choice between centered display and display aligned with a fixed divider
47 | * AM/PM indicator integrated into the time display, such that the divider is displayed as "·" for AM and ":" for PM:
48 |
49 | 
50 |
51 | * Option to only keep display on when device is charging
52 | * Adaptive display providing the largest possible monospace text for the device screen width (excepting a narrow margin)
53 | * A widget for providing a Roman digital clock display on a device's Home screen:
54 |
55 | 
56 |
57 | * Independent setting of widget background transparency, time zone, and other settings:
58 |
59 |  
60 |
61 | * And... RomanDigital is Apache-2.0-licensed open source :slightly_smiling_face:
62 |
63 | ## Requirements
64 |
65 | RomanDigital requires Android 5.0 or greater and is designed to run on a phone or tablet.
66 |
67 | ## Widget Settings
68 |
69 | When the widget is added to the Home screen, an activity is displayed to allow selection of
70 | settings. This activity can be accessed later in one of two ways depending on the Android version.
71 | On Android 11 and earlier, a tap on the widget brings up the activity. On Android 12 and later, the
72 | activity is accessed by a long press on the widget and a tap on the settings or reconfigure button
73 | in the normal manner for the particular Android version.
74 |
75 | ## Permissions
76 |
77 | The USE_EXACT_ALARM permission is set by this app. This permission is
78 | necessary on Android 13 and greater to enable the widget to provide an
79 | accurate time display without inconveniencing the user by asking for the permission.
80 | It cannot be disabled without modifying the source code.
81 |
82 | The SCHEDULE_EXACT_ALARM permission is also set by this app and is
83 | necessary on Android 12 for the same reason as the USE_EXACT_ALARM
84 | permission discussed above. This permission can be disabled in the
85 | _Alarms & Reminders_ section of the system settings. Disabling this
86 | permission will cause inaccuracy in the widget's time display.
87 |
88 | Android versions 11 and lower allow setting of exact alarms by default.
89 |
90 | In addition, net.diffengine.romandigitalclock.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION
91 | is set by this app. This was set during the app build process by use of a particular Android
92 | library required to enable the app to function as expected on versions of Android 12 and earlier
93 | (i.e. prior to API 33). It cannot be disabled without modifying the source code. As noted in
94 | an fdroidserver commit, "it's basically just an internal hack, rather than a real permission." For
95 | a more technical explanation of this "permission", see the full commit message at
96 | https://gitlab.com/fdroid/fdroidserver/-/merge_requests/1336/diffs?commit_id=71697f9c88ec73980f63be5955f36cdc3ba7a02c
97 |
98 | ## Known Issues
99 |
100 | After building and updating the app in Android Studio, a widget that had previously been added to the home screen may stop updating. It may be 'kickstarted' by simply opening and then closing the widget's settings screen.
101 |
102 | The 'Align to Divider' option does not correctly align the display when a variable width font is used.
103 |
104 | The RomanDigital app will run on a 5th Generation Amazon Kindle Fire, which is based on Android 5.1, but the widget will not. RomanDigital has not been tested on other Fire versions.
105 |
106 | ## FAQ (Foremost Anticipated Questions)
107 |
108 | > Q: "I just updated RomanDigital / updated Android / restarted my device, and now the widget doesn't work! How do I get it running again?"
109 | >
110 | > A: As of version 2.0.1, this should only happen when running RomanDigital on a device through Android Studio. The workaround to get the widget going again is to "kickstart" it by simply opening and then canceling a settings screen from any widget or from the app.
111 |
112 | > Q: "Why does the position of the divider change when 'Align to Divider' is selected? Isn't it supposed to stay in one place?"
113 | >
114 | > A: Yes, it's supposed to stay in one place, but the positioning was designed with the expectation of using a monospace font. Shortsighted of me, I know, but implementation for variable width fonts would be _really hard_. If you've changed a device setting effecting the font used, e.g. your system-wide font, to something that doesn't provide for monospace, then the calculated display position based on expected equal-width characters, and thus the divider position, will unfortunately be off.
115 |
116 | > Q: "There's no alarm feature, so why the need to set exact alarms? What's this got to do with an accurate time display?"
117 | >
118 | > A: Android doesn't enable widgets to receive the ACTION_TIME_TICK intent broadcast through the system each minute. Setting an alarm for the exact time of each next minute at the end of a minute is the only straightforward way (that I know of) for the app's widget to know when to update the time display.
119 |
120 | > Q: "Will there ever be an alarm feature?"
121 | >
122 | > A: Maybe, but it's quite low on the priority list at this point.
123 |
124 | > Q: "Why can't I change the font/text color/background color/widget corner curvature/etc?"
125 | >
126 | > A: Background _transparency_ can now be changed, with background fully white/black for 100% opacity in light/dark mode. As for other stylistic changes, I haven't gotten to them yet. (_Spoiler Alert!_ Variable app text color is planned for version 2.1.0.)
127 |
128 | > Q: "Why no seconds?"
129 | >
130 | > A: They would crowd the display and look inelegant. Besides, most people (myself included) would likely have trouble reading a lengthy Roman numeral within a second.
131 |
132 | > Q: "Why no date?"
133 | >
134 | > A: Because it's just a simple clock. At least for now.
135 |
136 | > Q: "I want to change widget time zone labels to particular place names. Is there any way to do this?"
137 | >
138 | > A: Not right now, but it should be added pretty soon since I want that option myself and it should be easy to implement.
139 |
140 | > Q: "Can I put the widget on my phone's lock screen?"
141 | >
142 | > A: Maybe. I was able to on my Samsung Galaxy A14 5G by purchasing the awkwardly-named [Lockscreen Widgets and Drawer](https://play.google.com/store/apps/details?id=tk.zwander.lockscreenwidgets) app for the low, low price of $1.49. YMMV. Here's what my lock screen looks like:
143 |
144 | 
145 |
146 | > Note:
147 | >
148 | > * I get nothing if you click on the "Lock Screen Widgets and Drawer" link and/or buy the app, and my purchase and use of it are not meant as an endorsement. There may be other such apps that would work as well or better.
149 |
150 | > Q: "Why no version specifically for a watch?"
151 | >
152 | > A: Because I don't have one to test on. Yet. I'd rather not rely *entirely* on virtual devices for testing.
153 |
154 | > Q: "Will it work on a watch or a TV?"
155 | >
156 | > A: It might — haven't tried — but if it did, it would likely result in a time display that's "unoptimized for the given device's screen size." In other words, "too large" or "too small".
157 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | }
4 |
5 | android {
6 | namespace = "net.diffengine.romandigitalclock"
7 | compileSdk = 34
8 |
9 | defaultConfig {
10 | applicationId = "net.diffengine.romandigitalclock"
11 | minSdk = 21
12 | targetSdk = 34
13 | versionCode = 9
14 | versionName = "2.0.2"
15 |
16 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | isMinifyEnabled = false
22 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
23 | }
24 | }
25 | compileOptions {
26 | sourceCompatibility = JavaVersion.VERSION_1_8
27 | targetCompatibility = JavaVersion.VERSION_1_8
28 | }
29 | buildFeatures {
30 | viewBinding = true
31 | buildConfig = true
32 | }
33 | }
34 |
35 | dependencies {
36 | val acraVersion = "5.11.3"
37 |
38 | implementation("androidx.appcompat:appcompat:1.6.1")
39 | implementation("com.google.android.material:material:1.11.0")
40 | implementation("androidx.constraintlayout:constraintlayout:2.1.4")
41 | implementation("androidx.preference:preference:1.2.1")
42 | implementation("ch.acra:acra-mail:$acraVersion")
43 | implementation("ch.acra:acra-dialog:$acraVersion")
44 | testImplementation("junit:junit:4.13.2")
45 | androidTestImplementation("androidx.test.ext:junit:1.1.5")
46 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
47 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/net/diffengine/romandigitalclock/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package net.diffengine.romandigitalclock;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 | assertEquals("net.diffengine.romandigitalclock", appContext.getPackageName());
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
39 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
53 |
54 |
55 |
58 |
59 |
60 |
61 |
62 |
63 |
70 |
71 |
72 |
73 |
74 |
80 |
85 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/app/src/main/java/net/diffengine/romandigitalclock/AboutActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * AboutActivity.java
3 | * - This file is part of the Android app RomanDigital
4 | *
5 | * Copyright 2024 David Yockey
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | */
20 |
21 | package net.diffengine.romandigitalclock;
22 |
23 | import androidx.appcompat.app.ActionBar;
24 | import androidx.appcompat.app.AppCompatActivity;
25 |
26 | import android.os.Bundle;
27 | import android.widget.TextView;
28 |
29 | public class AboutActivity extends AppCompatActivity {
30 |
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | setContentView(R.layout.about_activity);
35 |
36 | TextView tv = findViewById(R.id.tvVersion);
37 |
38 | // BuildConfig full package name is to prevent any possible future confusion with org.acra.BuildConfig
39 | String appversion = net.diffengine.romandigitalclock.BuildConfig.VERSION_NAME;
40 |
41 | tv.setText(getString(R.string.app_version_label, appversion));
42 |
43 | ActionBar actionBar = getSupportActionBar();
44 | if (actionBar != null) {
45 | actionBar.setDisplayHomeAsUpEnabled(true);
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/diffengine/romandigitalclock/AppClass.java:
--------------------------------------------------------------------------------
1 | /*
2 | * AppClass.java
3 | * - This file is part of the Android app RomanDigital
4 | *
5 | * Copyright 2024 David Yockey
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | */
20 |
21 | package net.diffengine.romandigitalclock;
22 |
23 | import android.app.Application;
24 | import android.content.Context;
25 |
26 | import org.acra.ACRA;
27 | import org.acra.config.CoreConfigurationBuilder;
28 | import org.acra.config.DialogConfigurationBuilder;
29 | import org.acra.config.MailSenderConfigurationBuilder;
30 | import org.acra.data.StringFormat;
31 | import org.acra.mail.BuildConfig;
32 |
33 | public class AppClass extends Application {
34 | @Override
35 | protected void attachBaseContext(Context base) {
36 | super.attachBaseContext(base);
37 |
38 | ACRA.init(this, new CoreConfigurationBuilder()
39 | //core configuration:
40 | .withBuildConfigClass(BuildConfig.class)
41 | .withReportFormat(StringFormat.KEY_VALUE_LIST)
42 | .withPluginConfigurations(
43 | new MailSenderConfigurationBuilder()
44 | .withMailTo("appissues@diffengine.net")
45 | .withReportAsFile(true)
46 | .withReportFileName("RomanDigital_Issue.txt")
47 | .withSubject(this.getPackageName() + " bug report")
48 | .build(),
49 | new DialogConfigurationBuilder()
50 | .withText(getString(R.string.dialog_text))
51 | .withTitle(getString(R.string.app_name) + " Crashed")
52 | .build()
53 | )
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/net/diffengine/romandigitalclock/BootCompletedBroadcastReceiver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * BootCompletedBroadcastReceiver.java
3 | * - This file is part of the Android app RomanDigital
4 | *
5 | * Copyright 2025 David Yockey
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | */
20 |
21 | package net.diffengine.romandigitalclock;
22 |
23 | import static android.content.Intent.ACTION_BOOT_COMPLETED;
24 |
25 | import android.content.BroadcastReceiver;
26 | import android.content.Context;
27 | import android.content.Intent;
28 |
29 | public class BootCompletedBroadcastReceiver extends BroadcastReceiver {
30 |
31 | @Override
32 | public void onReceive(Context context, Intent intent) {
33 | String action = intent.getAction();
34 |
35 | if (action != null && action.equals(ACTION_BOOT_COMPLETED)) {
36 | Intent kickstart = new Intent(context, TimeDisplayWidget.class);
37 | kickstart.setAction(TimeDisplayWidget.MINUTE_TICK);
38 | kickstart.setPackage(context.getPackageName());
39 | context.sendBroadcast(kickstart);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/net/diffengine/romandigitalclock/ColorSeekBarView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ColorSeekBarView.java
3 | * - This file is part of the Android app RomanDigital
4 | *
5 | * Copyright 2025 David Yockey
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | */
20 |
21 | package net.diffengine.romandigitalclock;
22 |
23 | import android.content.Context;
24 | import android.content.res.ColorStateList;
25 | import android.content.res.TypedArray;
26 | import android.graphics.PorterDuff;
27 | import android.os.Build;
28 | import android.util.AttributeSet;
29 | import android.widget.LinearLayout;
30 | import android.widget.SeekBar;
31 | import android.widget.TextView;
32 |
33 | import androidx.annotation.Nullable;
34 | import androidx.core.content.res.ResourcesCompat;
35 |
36 | public class ColorSeekBarView extends LinearLayout {
37 |
38 | CharSequence mtext;
39 | ColorStateList mcolor;
40 | ColorStateList mbkgnd;
41 |
42 | TextView barLabel;
43 | SeekBar barColor;
44 |
45 | public ColorSeekBarView(Context context) {
46 | super(context);
47 | init(context, null);
48 | }
49 |
50 | public ColorSeekBarView(Context context, @Nullable AttributeSet attrs) {
51 | super(context, attrs);
52 | init(context, attrs);
53 | }
54 |
55 | public ColorSeekBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
56 | super(context, attrs, defStyleAttr);
57 | init(context, attrs);
58 | }
59 |
60 | @SuppressWarnings({"UnusedDeclaration"})
61 | public ColorSeekBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
62 | super(context, attrs, defStyleAttr, defStyleRes);
63 | init(context, attrs);
64 |
65 | }
66 |
67 | private void init(Context context, @Nullable AttributeSet attrs) {
68 | inflate(context, R.layout.color_seekbar_layout, this);
69 |
70 | if (attrs != null) {
71 | TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ColorSeekBarView, 0, 0);
72 |
73 | try {
74 | mtext = a.getText(R.styleable.ColorSeekBarView_barLabel);
75 | mcolor = a.getColorStateList(R.styleable.ColorSeekBarView_barColor);
76 | mbkgnd = a.getColorStateList(R.styleable.ColorSeekBarView_barBackgroundColor);
77 | } finally {
78 | a.recycle();
79 | }
80 | }
81 | }
82 |
83 | @Override
84 | protected void onFinishInflate() {
85 | super.onFinishInflate();
86 | setupSeekBar();
87 | }
88 |
89 | private void setupSeekBar() {
90 | barLabel = findViewById(R.id.colorLabel);
91 | barLabel.setText(mtext);
92 |
93 | barColor = findViewById(R.id.colorBar);
94 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
95 | barColor.setMin(0);
96 | }
97 | barColor.setMax(255);
98 |
99 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
100 | barColor.setProgressDrawable(ResourcesCompat.getDrawable(getContext().getResources(), R.drawable.seekbar_track_material, null));
101 | }
102 |
103 | barColor.setProgressBackgroundTintMode(PorterDuff.Mode.SRC_IN);
104 | barColor.setProgressBackgroundTintList(mbkgnd);
105 | barColor.setProgressTintMode(PorterDuff.Mode.SRC_IN);
106 | barColor.setProgressTintList(mcolor);
107 | barColor.setThumbTintMode(PorterDuff.Mode.SRC_IN);
108 | barColor.setThumbTintList(mcolor);
109 | }
110 |
111 | public void setProgress(int progress) {
112 | barColor.setProgress(progress);
113 | }
114 | public int getProgress() { return barColor.getProgress(); }
115 |
116 | public void setOnColorSeekBarChangeListener(SeekBar.OnSeekBarChangeListener listener) {
117 | barColor.setOnSeekBarChangeListener(listener);
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/app/src/main/java/net/diffengine/romandigitalclock/SettingsActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SettingsActivity.java
3 | * - This file is part of the Android app RomanDigital
4 | *
5 | * Copyright 2024-2025 David Yockey
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | */
20 |
21 | package net.diffengine.romandigitalclock;
22 |
23 | import android.app.Dialog;
24 | import android.appwidget.AppWidgetManager;
25 | import android.content.BroadcastReceiver;
26 | import android.content.Context;
27 | import android.content.Intent;
28 | import android.content.IntentFilter;
29 | import android.os.Bundle;
30 |
31 | import androidx.annotation.NonNull;
32 | import androidx.annotation.Nullable;
33 | import androidx.appcompat.app.AppCompatActivity;
34 | import androidx.fragment.app.FragmentManager;
35 | import androidx.preference.ListPreference;
36 | import androidx.preference.Preference;
37 | import androidx.preference.PreferenceCategory;
38 | import androidx.preference.PreferenceFragmentCompat;
39 | import androidx.preference.PreferenceManager;
40 | import androidx.preference.PreferenceScreen;
41 | import androidx.preference.SwitchPreferenceCompat;
42 |
43 | import java.util.TimeZone;
44 |
45 | public class SettingsActivity extends AppCompatActivity {
46 |
47 | DisplayColorFragment displayColorFragment;
48 |
49 | @Override
50 | protected void onCreate(Bundle savedInstanceState) {
51 | super.onCreate(savedInstanceState);
52 | setContentView(R.layout.settings_activity);
53 | if (savedInstanceState == null) {
54 | displayColorFragment = new DisplayColorFragment();
55 | FragmentManager supportFragmentManager = getSupportFragmentManager();
56 | supportFragmentManager
57 | .beginTransaction()
58 | .setReorderingAllowed(true)
59 | .add(R.id.app_settings_frame, new SettingsFragment(true, AppWidgetManager.INVALID_APPWIDGET_ID))
60 | .add(R.id.display_color_frame, displayColorFragment)
61 | .add(R.id.screen_settings_frame, new ScreenSettingsFragment())
62 | .add(R.id.button_bar_2, new SettingsButtonBarFragment())
63 | .commit();
64 | } else {
65 | FragmentManager supportFragmentManager = getSupportFragmentManager();
66 | displayColorFragment = (DisplayColorFragment) supportFragmentManager.findFragmentById(R.id.display_color_frame);
67 | }
68 | }
69 |
70 | public static class SettingsFragment extends PreferenceFragmentCompat {
71 |
72 | static String postfix;
73 |
74 | public SettingsFragment () {
75 | }
76 |
77 | public SettingsFragment (Boolean isApp, int appWidgetId) {
78 | final String appPostfix = "";
79 | final String widgetPostfix = String.valueOf(appWidgetId);
80 | postfix = ( (isApp) ? appPostfix : widgetPostfix );
81 | }
82 |
83 | private void setSeparatorEnableState(SwitchPreferenceCompat pFormat) {
84 | SwitchPreferenceCompat pSeparator = findPreference("switch_separator" + postfix);
85 | if (pFormat.isChecked() == MainActivity.right) {
86 | //noinspection DataFlowIssue
87 | pSeparator.setChecked(MainActivity.left);
88 | pSeparator.setEnabled(false);
89 | } else {
90 | //noinspection DataFlowIssue
91 | pSeparator.setEnabled(true);
92 | }
93 | }
94 |
95 | Context prefManagerContext;
96 | PreferenceCategory category;
97 |
98 | private void addABSwitchPreference (String key, String aText, String bText) {
99 | SwitchPreferenceCompat pref = new SwitchPreferenceCompat(prefManagerContext);
100 | pref.setLayoutResource(R.layout.a_b_switch_layout);
101 | pref.setKey(key + postfix);
102 | pref.setDefaultValue(false);
103 | pref.setTitle(aText);
104 | pref.setSummary(bText);
105 | category.addPreference(pref);
106 | }
107 |
108 | private void addSeparator (String key) {
109 | Preference pref = new Preference(prefManagerContext);
110 | pref.setLayoutResource(R.layout.separator_layout);
111 | pref.setKey(key);
112 | category.addPreference(pref);
113 | }
114 |
115 | private void addListPreference (String key, String title, String[] entries, String[] entryValues, String defaultValue) {
116 | ListPreference pref = new ListPreference(prefManagerContext);
117 | pref.setIconSpaceReserved(true); // Required for some devices that default this to false
118 | pref.setKey(key + postfix);
119 | pref.setTitle(title);
120 |
121 | // "%s" is documented in the doc for the deprecated android.preference.ListPreference at
122 | // https://developer.android.com/reference/android/preference/ListPreference.html#setSummary(java.lang.CharSequence),
123 | // but not in the doc for its replacement androidx.preference.ListPreference at
124 | // https://developer.android.com/reference/androidx/preference/ListPreference#setSummary(java.lang.CharSequence).
125 | //
126 | // Consequently, while it's EXTREMELY useful, it should be considered deprecated unless
127 | // and until it is documented in the androidx.preference.ListPreference documentation.
128 | pref.setSummary("%s");
129 |
130 | pref.setEntries(entries);
131 | pref.setEntryValues(entryValues);
132 | pref.setValue(defaultValue);
133 |
134 | category.addPreference(pref);
135 | }
136 |
137 | @Override
138 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
139 | PreferenceManager manager = getPreferenceManager();
140 | prefManagerContext = manager.getContext();
141 | PreferenceScreen screen = manager.createPreferenceScreen(prefManagerContext);
142 |
143 | category = new PreferenceCategory(prefManagerContext);
144 | category.setTitle("Time");
145 | category.setIconSpaceReserved(false);
146 | screen.addPreference(category);
147 |
148 | addABSwitchPreference("switch_format", "12 Hour", "24 Hour");
149 | addABSwitchPreference("switch_alignment", "Align to Center", "Align to Divider");
150 | addABSwitchPreference("switch_separator", ": for All", "· for AM\n: for PM");
151 |
152 | if (!postfix.equals("")) {
153 | addSeparator("S1");
154 |
155 | String[] timezoneIds = TimeZone.getAvailableIDs();
156 | addListPreference("list_timezone", "Time Zone", timezoneIds, timezoneIds, TimeZone.getDefault().getID() );
157 |
158 | String[] layoutEntries = {getString(R.string.tz_above_time), getString(R.string.time_only), getString(R.string.tz_below_time)};
159 | String[] layoutValues = {"hi_label", "no_label", "lo_label"};
160 |
161 | addListPreference("list_widget_layout", "Display Layout", layoutEntries, layoutValues, layoutValues[1] );
162 | }
163 |
164 | setPreferenceScreen(screen);
165 |
166 | // At start of the activity, ensure that the separator switch is disabled and set to
167 | // left if the format switch is set to right (i.e. 24 hour format).
168 | //
169 | SwitchPreferenceCompat pFormat = findPreference("switch_format" + postfix);
170 | //noinspection DataFlowIssue
171 | setSeparatorEnableState(pFormat);
172 | }
173 |
174 | @Override
175 | public boolean onPreferenceTreeClick(@NonNull Preference preference) {
176 | if (preference.getKey().equals("switch_format" + postfix)) {
177 | //
178 | // Set separator switch enable and check states based on whether format switch state
179 | // is left or right (i.e. whether format is 12 or 24 hour). Implementation in code
180 | // of the enable/disable operation is needed because it is opposite to that provided
181 | // by the normal preference dependency attribute.
182 | //
183 | SwitchPreferenceCompat pFormat = (SwitchPreferenceCompat)preference;
184 | setSeparatorEnableState(pFormat);
185 | }
186 | return super.onPreferenceTreeClick(preference);
187 | }
188 | }
189 |
190 | public static class ScreenSettingsFragment extends PreferenceFragmentCompat {
191 | @Override
192 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
193 | setPreferencesFromResource(R.xml.screen_preferences, rootKey);
194 | }
195 | }
196 |
197 | public static boolean isHexColor(String hex) {
198 | return !( hex == null || !hex.matches("[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]") );
199 | }
200 |
201 | public static class DisplayColorFragment extends PreferenceFragmentCompat {
202 | Context prefManagerContext;
203 | PreferenceCategory category;
204 |
205 | @Override
206 | public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
207 | PreferenceManager manager = getPreferenceManager();
208 | prefManagerContext = manager.getContext();
209 | PreferenceScreen screen = manager.createPreferenceScreen(prefManagerContext);
210 |
211 | category = new PreferenceCategory(prefManagerContext);
212 | category.setIconSpaceReserved(false);
213 | category.setTitle("Style");
214 | screen.addPreference(category);
215 |
216 | ColorDialogPreference colorPref = new ColorDialogPreference(prefManagerContext, getChildFragmentManager());
217 | colorPref.setTitle("Time Color");
218 | colorPref.setKey("hexcolor");
219 | category.addPreference(colorPref);
220 |
221 | setPreferenceScreen(screen);
222 | }
223 |
224 | public void updateDialogTimeDisplayPreview() {
225 | ColorDialogPreference colorDialogPreference = category.findPreference("hexcolor"); //colorPref; //DisplayColorFragment.getColorDialogPreference();
226 | ColorDialogPreference.ColorDialogFragment colorDialogFragment = colorDialogPreference.getColorDialogFragment();
227 |
228 | if (colorDialogFragment != null) {
229 | Dialog dialog = colorDialogFragment.getDialog();
230 | if (dialog != null && dialog.isShowing()) {
231 | colorDialogFragment.updatePreviewTime();
232 | }
233 | }
234 | }
235 | }
236 |
237 | private class BroadcastReceiverEx extends BroadcastReceiver {
238 | @Override
239 | public void onReceive(Context context, Intent intent) {
240 | if (displayColorFragment != null) {
241 | displayColorFragment.updateDialogTimeDisplayPreview();
242 | }
243 | }
244 | }
245 |
246 | // Receiver instance to be registered as exported for receiving system-broadcast ACTION_TIME_TICK intent
247 | private final BroadcastReceiverEx broadcastReceiver = new BroadcastReceiverEx();
248 |
249 | @Override
250 | protected void onPause() {
251 | super.onPause();
252 |
253 | // Kick the widgets so they'll update time immediately in case some broadcast intent has
254 | // been missed. This may be unnecessary; I tend to think of intents as similar to messages
255 | // used in controlling other systems' GUIs, which may not be the case.
256 | Intent kickstart = new Intent(this, TimeDisplayWidget.class);
257 | kickstart.setAction(TimeDisplayWidget.MINUTE_TICK);
258 | kickstart.setPackage(this.getPackageName());
259 | this.sendBroadcast(kickstart);
260 |
261 | unregisterReceiver(broadcastReceiver);
262 | }
263 |
264 | @Override
265 | protected void onResume() {
266 | super.onResume();
267 |
268 | registerReceiver(broadcastReceiver, new IntentFilter(Intent.ACTION_TIME_TICK));
269 | }
270 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/diffengine/romandigitalclock/SettingsButtonBarFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SettingsButtonBarFragment.java
3 | * - This file is part of the Android app RomanDigital
4 | *
5 | * Copyright 2024-2025 David Yockey
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | */
20 |
21 | package net.diffengine.romandigitalclock;
22 |
23 | import static android.app.Activity.RESULT_OK;
24 |
25 | import android.annotation.SuppressLint;
26 | import android.app.Activity;
27 | import android.appwidget.AppWidgetManager;
28 | import android.content.Intent;
29 | import android.content.SharedPreferences;
30 | import android.os.Bundle;
31 |
32 | import androidx.fragment.app.Fragment;
33 | import androidx.preference.PreferenceManager;
34 |
35 | import android.view.LayoutInflater;
36 | import android.view.View;
37 | import android.view.ViewGroup;
38 | import android.widget.Button;
39 |
40 | import java.util.Map;
41 | import java.util.Objects;
42 |
43 | public class SettingsButtonBarFragment extends Fragment implements View.OnClickListener {
44 | Activity parentActivity;
45 | Button btnCancel;
46 | Button btnSave;
47 |
48 | Map origprefs; // Storage for backup of original preference values
49 | SharedPreferences prefs;
50 |
51 | public SettingsButtonBarFragment() {
52 | // If the layout isn't provided to the superclass, it's necessary to use a form of
53 | // fragmentTransaction.add or .replace that takes the fragment class rather than an
54 | // instance thereof to instantiate the fragment in an activity's onCreate method.
55 | // Otherwise, onCreateView will never be called.
56 | super(R.layout.fragment_settings_button_bar);
57 | }
58 |
59 | @Override
60 | public void onCreate(Bundle savedInstanceState) {
61 | super.onCreate(savedInstanceState);
62 |
63 | parentActivity = requireActivity();
64 | prefs = PreferenceManager.getDefaultSharedPreferences(requireContext());
65 | origprefs = prefs.getAll();
66 | }
67 |
68 | @SuppressLint("ApplySharedPref")
69 | public void onClick (View v) {
70 |
71 | // Get the appWidgetId if we're in a widget config activity
72 | //
73 | //noinspection ReassignedVariable
74 | int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
75 | String cls = parentActivity.getComponentName().getClassName();
76 | if (cls.equals("net.diffengine.romandigitalclock.TimeDisplayWidgetConfigActivity")) {
77 | TimeDisplayWidgetConfigActivity widgetConfigActivity = (TimeDisplayWidgetConfigActivity) parentActivity;
78 | appWidgetId = widgetConfigActivity.appWidgetId;
79 | }
80 |
81 | if (v == btnCancel) {
82 | // Set preferences back to original values
83 | SharedPreferences.Editor spEditor = prefs.edit();
84 |
85 | for (String key : origprefs.keySet()) {
86 | Object value = origprefs.get(key);
87 | String prefType = Objects.requireNonNull(value).getClass().getSimpleName();
88 |
89 | switch (prefType) {
90 | case "Boolean":
91 | spEditor.putBoolean(key, (boolean) value);
92 | break;
93 | case "Integer":
94 | spEditor.putInt(key, (int) value);
95 | break;
96 | case "String":
97 | spEditor.putString(key, (String) value);
98 | break;
99 | default:
100 | break;
101 | }
102 | }
103 |
104 | spEditor.commit();
105 |
106 | } else if (v == btnSave) {
107 | if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
108 | // Provision of appwidget id in the extra data should prevent crash of some UIs
109 | // (e.g. TouchWiz on old Samsung devices) at activity destruction.
110 | // See https://stackoverflow.com/a/40709721
111 | Intent result = new Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
112 | parentActivity.setResult(RESULT_OK, result);
113 | }
114 | } else {
115 | // In case of some shortsighted modification... :)
116 | return;
117 | }
118 |
119 | parentActivity.finish();
120 | }
121 |
122 | @Override
123 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
124 | Bundle savedInstanceState) {
125 | View v = inflater.inflate(R.layout.fragment_settings_button_bar, container, false);
126 |
127 | btnCancel = v.findViewById(R.id.buttonCancel);
128 | btnCancel.setOnClickListener(this);
129 |
130 | btnSave = v.findViewById(R.id.buttonSave);
131 | btnSave.setOnClickListener(this);
132 |
133 | return v;
134 | }
135 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/diffengine/romandigitalclock/TimeDisplayWidgetConfigActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * TimeDisplayWidgetConfigActivity.java
3 | * - This file is part of the Android app RomanDigital
4 | *
5 | * Copyright 2024-2025 David Yockey
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | */
20 |
21 | package net.diffengine.romandigitalclock;
22 |
23 | import androidx.annotation.NonNull;
24 | import androidx.annotation.Nullable;
25 | import androidx.appcompat.app.AppCompatActivity;
26 | import androidx.fragment.app.FragmentTransaction;
27 | import androidx.preference.Preference;
28 | import androidx.preference.PreferenceCategory;
29 | import androidx.preference.PreferenceFragmentCompat;
30 | import androidx.preference.PreferenceManager;
31 | import androidx.preference.PreferenceScreen;
32 | import androidx.preference.SeekBarPreference;
33 |
34 | import android.appwidget.AppWidgetManager;
35 | import android.content.Context;
36 | import android.content.Intent;
37 | import android.os.Bundle;
38 |
39 | import java.util.Objects;
40 |
41 | public class TimeDisplayWidgetConfigActivity extends AppCompatActivity {
42 | int appWidgetId;
43 |
44 | public TimeDisplayWidgetConfigActivity() {
45 | super(R.layout.activity_time_display_widget_config);
46 | }
47 |
48 | private void setResultCanceled() {
49 | // Enable cancellation of the configuration and,
50 | // if it's being added, app widget addition to home screen.
51 | // See https://developer.android.com/develop/ui/views/appwidgets/configuration#java
52 | // Provision of appwidget id in the extra data should also
53 | // prevent crash of TouchWiz on old Samsung devices at activity destruction.
54 | // See https://stackoverflow.com/a/40709721
55 | Intent intent = getIntent();
56 | Bundle extras = intent.getExtras();
57 | appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
58 | if (extras != null) {
59 | appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
60 | }
61 | Intent result = new Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
62 | setResult(RESULT_CANCELED, result);
63 | }
64 |
65 | @Override
66 | protected void onCreate(Bundle savedInstanceState) {
67 | super.onCreate(savedInstanceState);
68 | setResultCanceled();
69 | setContentView(R.layout.activity_time_display_widget_config);
70 |
71 | if(BuildConfig.DEBUG) {
72 | String activityTitle = (String) getTitle();
73 | setTitle(activityTitle + " - " + appWidgetId);
74 | }
75 |
76 | if (savedInstanceState == null) {
77 | FragmentTransaction fragmentTransaction = getSupportFragmentManager()
78 | .beginTransaction()
79 | .setReorderingAllowed(true);
80 |
81 | fragmentTransaction.add(R.id.widget_settings, new SettingsActivity.SettingsFragment(false, appWidgetId));
82 | fragmentTransaction.add(R.id.widget_bkgnd, new WidgetBkgndSettingsFragment(appWidgetId));
83 | fragmentTransaction.add(R.id.button_bar, new SettingsButtonBarFragment()).commit();
84 | }
85 | }
86 |
87 | public static class WidgetBkgndSettingsFragment extends PreferenceFragmentCompat {
88 |
89 | static String postfix;
90 |
91 | public WidgetBkgndSettingsFragment () {
92 | }
93 |
94 | public WidgetBkgndSettingsFragment (int appWidgetId) {
95 | postfix = String.valueOf(appWidgetId);
96 | }
97 |
98 | String buildOpacityLabel(int rawvalue) {
99 | int percentage = rawvalue * 10;
100 | return "Opacity: " + percentage + "%";
101 | }
102 |
103 | Context prefManagerContext;
104 | PreferenceCategory category;
105 |
106 | @SuppressWarnings("SameParameterValue") // From https://stackoverflow.com/a/48734923/
107 | private void addSeekBarPreference (String key) {
108 | SeekBarPreference pref = new SeekBarPreference(prefManagerContext);
109 | pref.setKey(key + postfix);
110 | pref.setMax(10);
111 | pref.setShowSeekBarValue(false);
112 | pref.setUpdatesContinuously(true);
113 | pref.setSummary("Opacity: %");
114 |
115 | // Required for some devices that default this to false
116 | pref.setIconSpaceReserved(true);
117 |
118 | category.addPreference(pref);
119 | }
120 |
121 | @Override
122 | public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
123 | PreferenceManager manager = getPreferenceManager();
124 | prefManagerContext = manager.getContext();
125 | PreferenceScreen screen = manager.createPreferenceScreen(prefManagerContext);
126 |
127 | category = new PreferenceCategory(prefManagerContext);
128 | category.setTitle("Background");
129 | category.setIconSpaceReserved(false);
130 | screen.addPreference(category);
131 |
132 | addSeekBarPreference("seekbar_opacity");
133 |
134 | setPreferenceScreen(screen);
135 |
136 | SeekBarPreference seekBarPreference = findPreference("seekbar_opacity" + postfix);
137 | Objects.requireNonNull(seekBarPreference).setSummary( buildOpacityLabel(seekBarPreference.getValue()) );
138 |
139 | seekBarPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
140 | @Override
141 | public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
142 | SeekBarPreference seekBarPref = (SeekBarPreference) preference;
143 | seekBarPref.setSummary( buildOpacityLabel((int)newValue) );
144 | return true;
145 | }
146 | });
147 | }
148 | }
149 |
150 | @Override
151 | protected void onResume() {
152 | super.onResume();
153 | }
154 |
155 | @Override
156 | protected void onPause() {
157 | super.onPause();
158 |
159 | // Since the widget's alarms may have been canceled on pause,
160 | // broadcast an intent to kickstart the widget when it resumes
161 | // along with immediately updating the widget
162 | Intent kickstart = new Intent(this, TimeDisplayWidget.class);
163 | kickstart.setAction(TimeDisplayWidget.SETTINGS_KICK);
164 | kickstart.setPackage(this.getPackageName());
165 | this.sendBroadcast(kickstart);
166 | }
167 |
168 | @Override
169 | protected void onDestroy() {
170 | super.onDestroy();
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/app/src/main/java/net/diffengine/romandigitalclock/romantime.java:
--------------------------------------------------------------------------------
1 | /*
2 | * romantime.java
3 | * - This file is part of the Android app RomanDigital
4 | *
5 | * Copyright 2024-2025 David Yockey
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | */
20 |
21 | package net.diffengine.romandigitalclock;
22 |
23 | import java.util.Calendar;
24 | import java.util.TimeZone;
25 |
26 | /** @noinspection SpellCheckingInspection*/
27 | public class romantime {
28 | private static String itor(int i) {
29 | String[] R = {
30 | "","I","II","III","IV","V","VI","VII","VIII","IX",
31 | "","X","XX","XXX","XL","L"
32 | };
33 | return R[(i/10)+10] + R[i%10];
34 | }
35 |
36 | private static String getHours (Calendar cal, boolean ampm) {
37 | int h = (ampm) ? cal.get(Calendar.HOUR) : cal.get(Calendar.HOUR_OF_DAY);
38 | return (ampm) ? itor( (h>0)?h:12 ) : itor(h);
39 | }
40 |
41 | /** @noinspection ReassignedVariable*/
42 | private static String getSeparator (Calendar cal, boolean ampm, boolean ampmSeparator) {
43 | String separator = ":";
44 | if (ampm) {
45 | separator = ( (ampmSeparator && (cal.get(Calendar.AM_PM) == Calendar.AM) ) ? "·" : ":");
46 | }
47 | return separator;
48 | }
49 |
50 | public static String now(boolean ampm, boolean ampmSeparator, boolean center, String tzId) {
51 |
52 | // ampm ampmSeparator
53 | // T T 12 hr / ampm separator
54 | // T F 12 hr / constant separator
55 | // F - 24 hr / constant separator
56 |
57 | /* GET TIME */
58 | Calendar cal = Calendar.getInstance();
59 | cal.setTimeZone(TimeZone.getTimeZone(tzId));
60 | String rHours = getHours(cal, ampm);
61 | String rMinutes = itor(cal.get(Calendar.MINUTE));
62 | String separator = getSeparator(cal, ampm, ampmSeparator);
63 | //noinspection ReassignedVariable
64 | String rtime = rHours + separator + rMinutes;
65 |
66 | if (!center) {
67 | /* ADD PADDING */
68 | // Each `NBSP` in the following is a "U+00A0 NO-BREAK SPACE".
69 | // These are treated by a TextView as visible chars rather than whitespace.
70 | //
71 | // Note also that four padding spaces are needed for the ampm (i.e. 12-hour) format,
72 | // even though none will then be used for VIII o'clock, because the substring method to
73 | // retrieve the appropriate length of padding will throw an exception due to reading
74 | // past the end of the string if only three are used. As is, it returns "" for VIII.
75 | String lpad = (" " + ((ampm) ? "" : " ")).substring(rHours.length());
76 | String rpad = " ".substring(rMinutes.length());
77 | rtime = lpad + rtime + rpad;
78 | }
79 |
80 | return rtime;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/rd_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/rd_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/res/color/ab_switch_thumb.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
27 |
29 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/color/ab_switch_track.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
27 |
29 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/app_widget_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
30 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
30 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/appwidget_bkgnd_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/appwidget_bkgnd_10.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/appwidget_bkgnd_100.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/appwidget_bkgnd_20.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/appwidget_bkgnd_30.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/appwidget_bkgnd_40.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/appwidget_bkgnd_50.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/appwidget_bkgnd_60.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/appwidget_bkgnd_70.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/appwidget_bkgnd_80.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/appwidget_bkgnd_90.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_info_outline_24.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_settings_24dp.xml:
--------------------------------------------------------------------------------
1 |
16 |
21 |
24 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/romandigital_3x1_appwidget_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/drawable/romandigital_3x1_appwidget_preview.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/seekbar_track_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | -
21 |
23 |
24 |
25 |
26 |
27 |
28 | -
30 |
31 |
32 |
34 | -
35 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | -
47 |
48 |
49 |
51 | -
52 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/a_b_switch_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
30 |
31 |
45 |
46 |
58 |
59 |
71 |
72 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/about_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
32 |
33 |
40 |
41 |
44 |
45 |
46 |
47 |
60 |
61 |
73 |
74 |
86 |
87 |
95 |
96 |
97 |
98 |
105 |
106 |
118 |
119 |
136 |
137 |
146 |
147 |
163 |
164 |
182 |
183 |
192 |
193 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
35 |
36 |
47 |
48 |
72 |
73 |
92 |
93 |
106 |
107 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_time_display_widget_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
32 |
33 |
43 |
44 |
47 |
48 |
55 |
56 |
63 |
64 |
65 |
66 |
67 |
68 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/color_dialog_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
32 |
33 |
51 |
52 |
64 |
65 |
83 |
84 |
92 |
93 |
96 |
97 |
110 |
111 |
123 |
124 |
136 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/color_seekbar_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
30 |
31 |
39 |
40 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_settings_button_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
33 |
34 |
44 |
45 |
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/separator_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
30 |
31 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/settings_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
32 |
33 |
43 |
44 |
47 |
48 |
55 |
56 |
63 |
64 |
71 |
72 |
73 |
74 |
75 |
83 |
84 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/time_display_widget.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
34 |
35 |
63 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/time_display_widget_hi_label.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
35 |
36 |
42 |
43 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/time_display_widget_lo_label.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
35 |
36 |
53 |
54 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/rd_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/rd_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/rd_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-hdpi/rd_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/rd_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-hdpi/rd_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/rd_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-hdpi/rd_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/rd_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-mdpi/rd_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/rd_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-mdpi/rd_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/rd_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-mdpi/rd_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/rd_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-xhdpi/rd_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/rd_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-xhdpi/rd_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/rd_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-xhdpi/rd_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/rd_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-xxhdpi/rd_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/rd_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-xxhdpi/rd_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/rd_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-xxhdpi/rd_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/rd_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-xxxhdpi/rd_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/rd_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-xxxhdpi/rd_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/rd_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/app/src/main/res/mipmap-xxxhdpi/rd_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/values-night-v31/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
28 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 | #F3C2BF
26 | #686868
27 | #F44336
28 | #484848
29 | #26000000
30 | #FAFAFA
31 | #FAFAFA
32 | @color/abswitch_thumb_disabled
33 |
34 | #FF000000
35 | #E6000000
36 | #CC000000
37 | #B3000000
38 | #99000000
39 | #80000000
40 | #66000000
41 | #4D000000
42 | #33000000
43 | #1A000000
44 | #00000000
45 |
46 | @color/white
47 | @color/black
48 | @color/fragment_title
49 |
50 |
51 | #585858
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
26 |
30 |
31 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
29 |
30 |
35 |
36 |
37 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v31/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
31 |
32 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v31/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
29 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 | #FF000000
26 | #FFFFFFFF
27 | #F44336
28 | #F44336
29 | #B9B9B9
30 | #F3C2BF
31 | #DEDEDE
32 | #F3EDF7
33 | #282828
34 | #26FFFFFF
35 | #FAFAFA
36 | #040404
37 | @color/abswitch_thumb_disabled
38 |
39 | #FFFFFFFF
40 | #E6FFFFFF
41 | #CCFFFFFF
42 | #B3FFFFFF
43 | #99FFFFFF
44 | #80FFFFFF
45 | #66FFFFFF
46 | #4DFFFFFF
47 | #33FFFFFF
48 | #1AFFFFFF
49 | #00FFFFFF
50 |
51 | @color/black
52 | @color/white
53 | @color/fragment_title
54 |
55 | #DCDCDC
56 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 | 30dp
26 | 3dp
27 | 2dp
28 | 2dp
29 | 24sp
30 |
31 | 25sp
32 | 14sp
33 |
34 | 18dp
35 |
36 | 20sp
37 | 20sp
38 |
39 | 15dp
40 | 300sp
41 | 3
42 |
43 | 0dp
44 |
45 |
49 | 0dp
50 |
51 |
52 | 2dp
53 | 2dp
54 | 4dp
55 | 0dp
56 |
57 |
--------------------------------------------------------------------------------
/app/src/main/res/values/rd_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 | #000000
26 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 | RomanDigital
26 | A Digital Clock with Roman Numeral Display
27 | Version %1$s
28 |
29 | RomanDigital App Settings
30 | About
31 |
32 | Format
33 | 24-hour clock
34 | 12-hour clock
35 |
36 | Alignment
37 | Align to Fixed Separator
38 | Center
39 |
40 | Separator
41 | Always show as \':\'
42 | Show as \'·\' for AM and \':\' for PM
43 |
44 | Keep screen on
45 | Only when charging
46 |
47 | XXXXXXXXXXXX
48 | XXXXXXXXXXXXX
49 |
50 | Settings
51 | About
52 |
53 | Copyright © 2024-2025 David Yockey
54 |
55 |
56 | "Licensed under the Apache License, Version 2.0 (the "License"); you may only use this software in compliance with the License. You may obtain a copy of the License at
57 |
58 | https://www.apache.org/licenses/LICENSE-2.0
59 |
60 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License."
61 |
62 |
63 |
64 | "RomanDigital includes the following dependency:"
65 |
66 |
67 | "Application Crash Reporting for Android
68 | Copyright (c) 2013 Kevin Gaudin
69 | http://acra.ch
70 |
71 | This product is licensed under the terms of the Apache Software License 2.0. See the LICENSE file or
72 |
73 | https://www.apache.org/licenses/LICENSE-2.0
74 |
75 | for the full license text."
76 |
77 |
78 | "You may obtain RomanDigital in source form at
79 |
80 | https://github.com/dfyockey/RomanDigital"
81 |
82 |
83 |
84 | XIII:XXXVIII
85 | Digital Clock with Roman Numeral Display
86 | RomanDigital Widget Settings
87 | Time zone label above time
88 | Time only
89 | Time zone label below time
90 |
91 |
92 | false
93 | true
94 | Option a
95 | Option b
96 | App Settings Scrollview
97 | Widget Settings Scrollview
98 |
99 |
100 | An error has caused RomanDigital to stop running. The developer can work to fix it, but only if he finds out about it.\n\nIf you have an email app set up then you can help!\n\nClick OK to automatically generate an email with bug report attached that you can then send.\n\nYou can add any comments regarding the crash that you think might be helpful to the autogenerated email.\n\nYour email address won\'t be kept or otherwise used unless you ask a question or provide a comment necessitating a reply.\n\nThanks in advance!
101 |
102 |
103 | Cancel
104 | Save
105 |
106 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
29 |
30 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
26 |
31 |
32 |
33 |
34 |
37 |
43 |
44 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/display_color_prefs.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
26 |
27 |
30 |
31 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/screen_preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
26 |
27 |
30 |
35 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/time_display_widget_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/widget_bkgnd_prefs.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
26 |
27 |
33 |
34 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/test/java/net/diffengine/romandigitalclock/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package net.diffengine.romandigitalclock;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | id("com.android.application") version "8.2.2" apply false
4 | }
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/1.txt:
--------------------------------------------------------------------------------
1 | * First release
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/2.txt:
--------------------------------------------------------------------------------
1 | Changed:
2 | * Default size of the app widget to its smallest size to facilitate installation of the widget on a crowded Home screen
3 | * Preview image used in widget pickers to one with a size matching the new default size
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/3.txt:
--------------------------------------------------------------------------------
1 | Changed:
2 | * Internal location of versionName definition to facilitate inclusion on F-Droid.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/4.txt:
--------------------------------------------------------------------------------
1 | Fixed:
2 | * App clock text being too large when user has selected a font in their Android settings that effects app fonts and that does not provide a monospace font. (Note: While this fix enables use of variable-width fonts, the "Align to Divider" option functionality does not work correctly for such fonts.)
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/5.txt:
--------------------------------------------------------------------------------
1 | Added:
2 | * `Cancel` and `Save` buttons to both widget and app settings activities.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/6.txt:
--------------------------------------------------------------------------------
1 | Added:
2 | * Variable widget background opacity selectable with a slider added to the widget settings, where the opacity can be set from 0% to 100% by tens.
3 | * Automatic setting of widget text color in light mode to white for opacity < 50% and to black for opacity >= 50% to improve contrast for very white backgrounds.
4 |
5 | Changed:
6 | * Widget corners for all Android versions to the same degree of curvature.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/7.txt:
--------------------------------------------------------------------------------
1 | Added:
2 | * Ability to configure settings independently for app and for each of one or more widgets.
3 | * Ability to select a different time zone for each widget.
4 | * Selectable widget layouts enabling addition of a time zone label above or below the time display.
5 |
6 | Changed:
7 | * Internal details related to updating of widgets' time and features, to improve efficiency.
8 |
9 | Removed:
10 | * The back arrow from the title bar of each Settings screen, since the arrow performs the same function as the Save button,
11 |
12 | Fixed:
13 | * Issues with widget time display text size being incorrect for the width of the widget (3 or >=4 tiles wide).
14 |
15 | NOTE: Installing this update will likely require resetting settings for any widgets currently in use.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/8.txt:
--------------------------------------------------------------------------------
1 | Fixed:
2 | * Failure of widgets to restart at device boot.
3 |
4 | _In version 2.0.0_
5 |
6 | Added:
7 | * Independent configuration app and each widget.
8 | * Time zone selection for each widget.
9 | * Selectable widget layouts adding a time zone label.
10 |
11 | Changed:
12 | * Internal details to improve efficiency.
13 |
14 | Removed:
15 | * Settings screens' back arrow from title bar.
16 |
17 | Fixed:
18 | * Inappropriate widget time display text size for particular widget widths.
19 |
20 | _Update to v2.0.0 may require reset of settings for currently installed widgets._
21 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/9.txt:
--------------------------------------------------------------------------------
1 | Fixed:
2 | * Failure of widget preferences being applied after a device's launcher has reset.
3 | * Accuracy of preview in the widget picker on device's running Android 12 or later.
4 |
5 | In version 2.0.1
6 |
7 | Fixed:
8 | * Failure of widgets to restart at device boot.
9 |
10 | Update from any v1.x.x to v2.x.x may require reset of settings for currently installed widgets.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/full_description.txt:
--------------------------------------------------------------------------------
1 | The RomanDigital app provides a clock of unique style that can be used to repurpose an old device or to display time on a current model.
2 |
3 | For more personal use, the RomanDigital widget makes a distinctive addition to a device's Home screen.
4 |
5 | RomanDigital includes features expected in an ordinary digital clock, such as:
6 | * Choice of 12 or 24 hour format
7 | * Optional AM/PM indicator (uniquely integrated into the time display)
8 | * Option to keep display on when app is in the foreground
9 | * Display in either portrait or landscape
10 |
11 | The RomanDigital also includes:
12 | * Independent configuration of app and widgets.
13 | * Variable widget background opacity that can be set from 0% to 100% by tens.
14 | * Selectable widget time zone.
15 | * Selectable widget layouts to add a time zone label.
16 |
17 | For more detailed information including a FAQ list, please see the README.md file accompanying the RomanDigital source code.
18 |
19 | Source code for RomanDigital is available at:
20 | https://github.com/dfyockey/RomanDigital
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/featureGraphic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/featureGraphic.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/icon.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/p1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/phoneScreenshots/p1.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/p10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/phoneScreenshots/p10.jpg
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/p11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/phoneScreenshots/p11.jpg
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/p2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/phoneScreenshots/p2.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/p3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/phoneScreenshots/p3.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/p4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/phoneScreenshots/p4.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/p5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/phoneScreenshots/p5.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/p6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/phoneScreenshots/p6.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/p7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/phoneScreenshots/p7.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/p8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/phoneScreenshots/p8.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/p9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/phoneScreenshots/p9.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/tenInchScreenshots/pA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/tenInchScreenshots/pA.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/tenInchScreenshots/pB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/tenInchScreenshots/pB.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/tenInchScreenshots/pC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/tenInchScreenshots/pC.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/tenInchScreenshots/pD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/fastlane/metadata/android/en-US/images/tenInchScreenshots/pD.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/short_description.txt:
--------------------------------------------------------------------------------
1 | Digital Clock App and Widget with Roman Numeral Display
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/title.txt:
--------------------------------------------------------------------------------
1 | RomanDigital
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Enables namespacing of each library's R class so that its R class includes only the
19 | # resources declared in the library itself and none from the library's dependencies,
20 | # thereby reducing the size of the R class for that library
21 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dfyockey/RomanDigital/98594a64b34e3391e30f2eda14ce3056590f98ad/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionSha256Sum=38f66cd6eef217b4c35855bb11ea4e9fbc53594ccccb5fb82dfd317ef8c2c5a3
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
5 | networkTimeout=10000
6 | validateDistributionUrl=true
7 | zipStoreBase=GRADLE_USER_HOME
8 | zipStorePath=wrapper/dists
9 |
--------------------------------------------------------------------------------
/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/HEAD/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 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
87 |
88 | # Use the maximum available, or set MAX_FD != -1 to use that value.
89 | MAX_FD=maximum
90 |
91 | warn () {
92 | echo "$*"
93 | } >&2
94 |
95 | die () {
96 | echo
97 | echo "$*"
98 | echo
99 | exit 1
100 | } >&2
101 |
102 | # OS specific support (must be 'true' or 'false').
103 | cygwin=false
104 | msys=false
105 | darwin=false
106 | nonstop=false
107 | case "$( uname )" in #(
108 | CYGWIN* ) cygwin=true ;; #(
109 | Darwin* ) darwin=true ;; #(
110 | MSYS* | MINGW* ) msys=true ;; #(
111 | NONSTOP* ) nonstop=true ;;
112 | esac
113 |
114 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
115 |
116 |
117 | # Determine the Java command to use to start the JVM.
118 | if [ -n "$JAVA_HOME" ] ; then
119 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
120 | # IBM's JDK on AIX uses strange locations for the executables
121 | JAVACMD=$JAVA_HOME/jre/sh/java
122 | else
123 | JAVACMD=$JAVA_HOME/bin/java
124 | fi
125 | if [ ! -x "$JAVACMD" ] ; then
126 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
127 |
128 | Please set the JAVA_HOME variable in your environment to match the
129 | location of your Java installation."
130 | fi
131 | else
132 | JAVACMD=java
133 | if ! command -v java >/dev/null 2>&1
134 | then
135 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
136 |
137 | Please set the JAVA_HOME variable in your environment to match the
138 | location of your Java installation."
139 | fi
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 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
147 | # shellcheck disable=SC3045
148 | MAX_FD=$( ulimit -H -n ) ||
149 | warn "Could not query maximum file descriptor limit"
150 | esac
151 | case $MAX_FD in #(
152 | '' | soft) :;; #(
153 | *)
154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
155 | # shellcheck disable=SC3045
156 | ulimit -n "$MAX_FD" ||
157 | warn "Could not set maximum file descriptor limit to $MAX_FD"
158 | esac
159 | fi
160 |
161 | # Collect all arguments for the java command, stacking in reverse order:
162 | # * args from the command line
163 | # * the main class name
164 | # * -classpath
165 | # * -D...appname settings
166 | # * --module-path (only if needed)
167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
168 |
169 | # For Cygwin or MSYS, switch paths to Windows format before running java
170 | if "$cygwin" || "$msys" ; then
171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
173 |
174 | JAVACMD=$( cygpath --unix "$JAVACMD" )
175 |
176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
177 | for arg do
178 | if
179 | case $arg in #(
180 | -*) false ;; # don't mess with options #(
181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
182 | [ -e "$t" ] ;; #(
183 | *) false ;;
184 | esac
185 | then
186 | arg=$( cygpath --path --ignore --mixed "$arg" )
187 | fi
188 | # Roll the args list around exactly as many times as the number of
189 | # args, so each arg winds up back in the position where it started, but
190 | # possibly modified.
191 | #
192 | # NB: a `for` loop captures its iteration list before it begins, so
193 | # changing the positional parameters here affects neither the number of
194 | # iterations, nor the values presented in `arg`.
195 | shift # remove old arg
196 | set -- "$@" "$arg" # push replacement arg
197 | done
198 | fi
199 |
200 |
201 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
202 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
203 |
204 | # Collect all arguments for the java command;
205 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
206 | # shell script including quotes and variable substitutions, so put them in
207 | # double quotes to make sure that they get re-expanded; and
208 | # * put everything else in single quotes, so that it's not re-expanded.
209 |
210 | set -- \
211 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
212 | -classpath "$CLASSPATH" \
213 | org.gradle.wrapper.GradleWrapperMain \
214 | "$@"
215 |
216 | # Stop when "xargs" is not available.
217 | if ! command -v xargs >/dev/null 2>&1
218 | then
219 | die "xargs is not available"
220 | fi
221 |
222 | # Use "xargs" to parse quoted args.
223 | #
224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
225 | #
226 | # In Bash we could simply go:
227 | #
228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
229 | # set -- "${ARGS[@]}" "$@"
230 | #
231 | # but POSIX shell has neither arrays nor command substitution, so instead we
232 | # post-process each arg (as a line of input to sed) to backslash-escape any
233 | # character that might be a shell metacharacter, then use eval to reverse
234 | # that process (while maintaining the separation between arguments), and wrap
235 | # the whole thing up as a single "set" statement.
236 | #
237 | # This will of course break if any of these variables contains a newline or
238 | # an unmatched quote.
239 | #
240 |
241 | eval "set -- $(
242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
243 | xargs -n1 |
244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
245 | tr '\n' ' '
246 | )" '"$@"'
247 |
248 | exec "$JAVACMD" "$@"
249 |
--------------------------------------------------------------------------------
/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 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 |
16 | rootProject.name = "RomanDigitalClock"
17 | include(":app")
18 |
--------------------------------------------------------------------------------