├── .gitignore ├── LICENSE.txt ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── de │ │ └── mm20 │ │ └── launcher2 │ │ └── plugin │ │ └── openweathermap │ │ ├── OpenWeatherMapWeatherProvider.kt │ │ ├── OwmApiClient.kt │ │ ├── SettingsActivity.kt │ │ ├── UserData.kt │ │ └── api │ │ ├── OwmForecast.kt │ │ ├── OwmGeo.kt │ │ └── OwmWeather.kt │ └── res │ ├── drawable │ ├── ic_launcher_background.xml │ ├── ic_launcher_foreground.xml │ └── ic_owm.xml │ ├── mipmap-anydpi-v26 │ └── ic_launcher.xml │ ├── mipmap-hdpi │ ├── ic_launcher_background.webp │ └── ic_launcher_foreground.webp │ ├── mipmap-mdpi │ ├── ic_launcher_background.webp │ └── ic_launcher_foreground.webp │ ├── mipmap-xhdpi │ ├── ic_launcher_background.webp │ └── ic_launcher_foreground.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher_background.webp │ └── ic_launcher_foreground.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher_background.webp │ └── ic_launcher_foreground.webp │ ├── values-ar │ └── strings.xml │ ├── values-arq │ └── strings.xml │ ├── values-az │ └── strings.xml │ ├── values-be │ └── strings.xml │ ├── values-bn │ └── strings.xml │ ├── values-ca │ └── strings.xml │ ├── values-cs │ └── strings.xml │ ├── values-da │ └── strings.xml │ ├── values-de │ └── strings.xml │ ├── values-el │ └── strings.xml │ ├── values-eo │ └── strings.xml │ ├── values-es │ └── strings.xml │ ├── values-eu │ └── strings.xml │ ├── values-fa │ └── strings.xml │ ├── values-fi │ └── strings.xml │ ├── values-fr │ └── strings.xml │ ├── values-hu │ └── strings.xml │ ├── values-ia │ └── strings.xml │ ├── values-in │ └── strings.xml │ ├── values-it │ └── strings.xml │ ├── values-ja │ └── strings.xml │ ├── values-ko │ └── strings.xml │ ├── values-lt │ └── strings.xml │ ├── values-ms │ └── strings.xml │ ├── values-nb-rNO │ └── strings.xml │ ├── values-nl │ └── strings.xml │ ├── values-pl │ └── strings.xml │ ├── values-pt-rBR │ └── strings.xml │ ├── values-pt │ └── strings.xml │ ├── values-ro │ └── strings.xml │ ├── values-ru │ └── strings.xml │ ├── values-sv │ └── strings.xml │ ├── values-th │ └── strings.xml │ ├── values-tr │ └── strings.xml │ ├── values-uk │ └── strings.xml │ ├── values-vi │ └── strings.xml │ ├── values-zh-rCN │ └── strings.xml │ ├── values-zh-rTW │ └── strings.xml │ ├── values │ └── strings.xml │ └── xml │ ├── backup_rules.xml │ └── data_extraction_rules.xml ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── readme.md └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/linux,macos,windows,android,androidstudio 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=linux,macos,windows,android,androidstudio 3 | 4 | ### Android ### 5 | # Gradle files 6 | .gradle/ 7 | build/ 8 | 9 | # Local configuration file (sdk path, etc) 10 | local.properties 11 | 12 | # Log/OS Files 13 | *.log 14 | 15 | # Android Studio generated files and folders 16 | captures/ 17 | .externalNativeBuild/ 18 | .cxx/ 19 | *.apk 20 | output.json 21 | 22 | # IntelliJ 23 | *.iml 24 | .idea/ 25 | misc.xml 26 | deploymentTargetDropDown.xml 27 | render.experimental.xml 28 | 29 | # Keystore files 30 | *.jks 31 | *.keystore 32 | 33 | # Google Services (e.g. APIs or Firebase) 34 | google-services.json 35 | 36 | # Android Profiling 37 | *.hprof 38 | 39 | ### Android Patch ### 40 | gen-external-apklibs 41 | 42 | # Replacement of .externalNativeBuild directories introduced 43 | # with Android Studio 3.5. 44 | 45 | ### Linux ### 46 | *~ 47 | 48 | # temporary files which can be created if a process still has a handle open of a deleted file 49 | .fuse_hidden* 50 | 51 | # KDE directory preferences 52 | .directory 53 | 54 | # Linux trash folder which might appear on any partition or disk 55 | .Trash-* 56 | 57 | # .nfs files are created when an open file is removed but is still being accessed 58 | .nfs* 59 | 60 | ### macOS ### 61 | # General 62 | .DS_Store 63 | .AppleDouble 64 | .LSOverride 65 | 66 | # Icon must end with two \r 67 | Icon 68 | 69 | 70 | # Thumbnails 71 | ._* 72 | 73 | # Files that might appear in the root of a volume 74 | .DocumentRevisions-V100 75 | .fseventsd 76 | .Spotlight-V100 77 | .TemporaryItems 78 | .Trashes 79 | .VolumeIcon.icns 80 | .com.apple.timemachine.donotpresent 81 | 82 | # Directories potentially created on remote AFP share 83 | .AppleDB 84 | .AppleDesktop 85 | Network Trash Folder 86 | Temporary Items 87 | .apdisk 88 | 89 | ### macOS Patch ### 90 | # iCloud generated files 91 | *.icloud 92 | 93 | ### Windows ### 94 | # Windows thumbnail cache files 95 | Thumbs.db 96 | Thumbs.db:encryptable 97 | ehthumbs.db 98 | ehthumbs_vista.db 99 | 100 | # Dump file 101 | *.stackdump 102 | 103 | # Folder config file 104 | [Dd]esktop.ini 105 | 106 | # Recycle Bin used on file shares 107 | $RECYCLE.BIN/ 108 | 109 | # Windows Installer files 110 | *.cab 111 | *.msi 112 | *.msix 113 | *.msm 114 | *.msp 115 | 116 | # Windows shortcuts 117 | *.lnk 118 | 119 | ### AndroidStudio ### 120 | # Covers files to be ignored for android development using Android Studio. 121 | 122 | # Built application files 123 | *.ap_ 124 | *.aab 125 | 126 | # Files for the ART/Dalvik VM 127 | *.dex 128 | 129 | # Java class files 130 | *.class 131 | 132 | # Generated files 133 | bin/ 134 | gen/ 135 | out/ 136 | 137 | # Gradle files 138 | .gradle 139 | 140 | # Signing files 141 | .signing/ 142 | 143 | # Local configuration file (sdk path, etc) 144 | 145 | # Proguard folder generated by Eclipse 146 | proguard/ 147 | 148 | # Log Files 149 | 150 | # Android Studio 151 | /*/build/ 152 | /*/local.properties 153 | /*/out 154 | /*/*/build 155 | /*/*/production 156 | .navigation/ 157 | *.ipr 158 | *.swp 159 | 160 | # Keystore files 161 | 162 | # Google Services (e.g. APIs or Firebase) 163 | # google-services.json 164 | 165 | # Android Patch 166 | 167 | # External native build folder generated in Android Studio 2.2 and later 168 | .externalNativeBuild 169 | 170 | # NDK 171 | obj/ 172 | 173 | # IntelliJ IDEA 174 | *.iws 175 | /out/ 176 | 177 | # User-specific configurations 178 | .idea/caches/ 179 | .idea/libraries/ 180 | .idea/shelf/ 181 | .idea/workspace.xml 182 | .idea/tasks.xml 183 | .idea/.name 184 | .idea/compiler.xml 185 | .idea/copyright/profiles_settings.xml 186 | .idea/encodings.xml 187 | .idea/misc.xml 188 | .idea/modules.xml 189 | .idea/scopes/scope_settings.xml 190 | .idea/dictionaries 191 | .idea/vcs.xml 192 | .idea/jsLibraryMappings.xml 193 | .idea/datasources.xml 194 | .idea/dataSources.ids 195 | .idea/sqlDataSources.xml 196 | .idea/dynamic.xml 197 | .idea/uiDesigner.xml 198 | .idea/assetWizardSettings.xml 199 | .idea/gradle.xml 200 | .idea/jarRepositories.xml 201 | .idea/navEditor.xml 202 | 203 | # Legacy Eclipse project files 204 | .classpath 205 | .project 206 | .cproject 207 | .settings/ 208 | 209 | # Mobile Tools for Java (J2ME) 210 | .mtj.tmp/ 211 | 212 | # Package Files # 213 | *.war 214 | *.ear 215 | 216 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) 217 | hs_err_pid* 218 | 219 | ## Plugin-specific files: 220 | 221 | # mpeltonen/sbt-idea plugin 222 | .idea_modules/ 223 | 224 | # JIRA plugin 225 | atlassian-ide-plugin.xml 226 | 227 | # Mongo Explorer plugin 228 | .idea/mongoSettings.xml 229 | 230 | # Crashlytics plugin (for Android Studio and IntelliJ) 231 | com_crashlytics_export_strings.xml 232 | crashlytics.properties 233 | crashlytics-build.properties 234 | fabric.properties 235 | 236 | ### AndroidStudio Patch ### 237 | 238 | !/gradle/wrapper/gradle-wrapper.jar 239 | 240 | # End of https://www.toptal.com/developers/gitignore/api/linux,macos,windows,android,androidstudio 241 | 242 | 243 | app/release 244 | .kotlin/sessions -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | */**/msal_auth_config.json -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.androidApplication) 3 | alias(libs.plugins.kotlin.android) 4 | alias(libs.plugins.kotlin.serialization) 5 | alias(libs.plugins.kotlin.compose) 6 | } 7 | 8 | android { 9 | namespace = "de.mm20.launcher2.plugin.openweathermap" 10 | compileSdk = 34 11 | 12 | defaultConfig { 13 | applicationId = "de.mm20.launcher2.plugin.openweathermap" 14 | minSdk = 26 15 | targetSdk = 34 16 | versionCode = 4 17 | versionName = "1.0.3" 18 | 19 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 20 | } 21 | 22 | buildTypes { 23 | release { 24 | postprocessing { 25 | isRemoveUnusedCode = true 26 | isObfuscate = false 27 | isOptimizeCode = true 28 | proguardFiles( 29 | "proguard-rules.pro" 30 | ) 31 | } 32 | } 33 | } 34 | compileOptions { 35 | sourceCompatibility = JavaVersion.VERSION_1_8 36 | targetCompatibility = JavaVersion.VERSION_1_8 37 | } 38 | kotlinOptions { 39 | jvmTarget = "1.8" 40 | } 41 | buildFeatures { 42 | compose = true 43 | } 44 | } 45 | 46 | dependencies { 47 | 48 | implementation(libs.androidx.core.ktx) 49 | implementation(libs.androidx.appcompat) 50 | implementation(libs.androidx.activitycompose) 51 | implementation(libs.androidx.compose.material3) 52 | implementation(libs.androidx.compose.materialicons) 53 | implementation(libs.androidx.lifecycle.compose) 54 | implementation(libs.androidx.datastore) 55 | implementation(libs.launchersdk) 56 | implementation(libs.kotlin.serialization) 57 | implementation(libs.ktor.client.android) 58 | implementation(libs.ktor.client.okhttp) 59 | implementation(libs.ktor.client.contentnegotiation) 60 | implementation(libs.ktor.serialization.json) 61 | } -------------------------------------------------------------------------------- /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 22 | 23 | -dontwarn org.slf4j.impl.StaticLoggerBinder -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 17 | 18 | 21 | 24 | 27 | 30 | 31 | 32 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 47 | 48 | 49 | 50 | 51 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/java/de/mm20/launcher2/plugin/openweathermap/OpenWeatherMapWeatherProvider.kt: -------------------------------------------------------------------------------- 1 | package de.mm20.launcher2.plugin.openweathermap 2 | 3 | import android.content.Intent 4 | import android.util.Log 5 | import de.mm20.launcher2.plugin.config.WeatherPluginConfig 6 | import de.mm20.launcher2.plugin.openweathermap.api.OwmForecast 7 | import de.mm20.launcher2.plugin.openweathermap.api.OwmForecastList 8 | import de.mm20.launcher2.plugin.openweathermap.api.OwmWeather 9 | import de.mm20.launcher2.sdk.PluginState 10 | import de.mm20.launcher2.sdk.weather.Forecast 11 | import de.mm20.launcher2.sdk.weather.K 12 | import de.mm20.launcher2.sdk.weather.WeatherIcon 13 | import de.mm20.launcher2.sdk.weather.WeatherLocation 14 | import de.mm20.launcher2.sdk.weather.WeatherProvider 15 | import de.mm20.launcher2.sdk.weather.hPa 16 | import de.mm20.launcher2.sdk.weather.m_s 17 | import de.mm20.launcher2.sdk.weather.mm 18 | import kotlinx.coroutines.flow.first 19 | import java.util.Locale 20 | import kotlin.math.roundToInt 21 | 22 | class OpenWeatherMapWeatherProvider : WeatherProvider( 23 | WeatherPluginConfig() 24 | ) { 25 | 26 | private lateinit var apiClient: OwmApiClient 27 | 28 | override fun onCreate(): Boolean { 29 | apiClient = OwmApiClient(context!!.applicationContext) 30 | return super.onCreate() 31 | } 32 | 33 | override suspend fun getWeatherData(location: WeatherLocation, lang: String?): List? { 34 | return when (location) { 35 | is WeatherLocation.LatLon -> getWeatherData( 36 | location.lat, 37 | location.lon, 38 | lang, 39 | location.name 40 | ) 41 | 42 | else -> { 43 | Log.e("OWMWeatherProvider", "Invalid location $location") 44 | null 45 | } 46 | } 47 | } 48 | 49 | override suspend fun getWeatherData(lat: Double, lon: Double, lang: String?): List? { 50 | return getWeatherData(lat, lon, lang, null) 51 | } 52 | 53 | private suspend fun getWeatherData( 54 | lat: Double, 55 | lon: Double, 56 | lang: String?, 57 | locationName: String? 58 | ): List? { 59 | val weather: OwmWeather = 60 | apiClient.weather(lat, lon, getLanguageCode(lang ?: Locale.getDefault().language)) 61 | 62 | val forecastList = mutableListOf() 63 | 64 | val city = weather.name 65 | val country = weather.sys?.country ?: return null 66 | val cityId = weather.id ?: return null 67 | val coords = weather.coord ?: return null 68 | if (coords.lat == null || coords.lon == null) return null 69 | val loc = locationName ?: "$city, $country" 70 | 71 | val currentForecast = owmToForecast(weather, loc) ?: return null 72 | forecastList.add(currentForecast) 73 | 74 | val forecasts: OwmForecast = apiClient.forecast( 75 | lat, 76 | lon, 77 | lang = getLanguageCode(lang ?: Locale.getDefault().language) 78 | ) 79 | 80 | if (forecasts.list == null) { 81 | Log.e("OWMWeatherProvider", "Forecast response has no forecasts") 82 | return null 83 | } 84 | 85 | for (forecast in forecasts.list) { 86 | forecastList += owmToForecast( 87 | forecast, 88 | loc, 89 | forecasts.city?.id 90 | ) ?: continue 91 | } 92 | 93 | return forecastList 94 | } 95 | 96 | private fun owmToForecast(weather: OwmWeather, location: String): Forecast? { 97 | val context = context ?: return null 98 | return Forecast( 99 | timestamp = (weather.dt ?: return null) * 1000, 100 | condition = weather.weather?.firstOrNull()?.description ?: return null, 101 | temperature = weather.main?.temp?.K ?: return null, 102 | minTemp = weather.main.tempMin?.K, 103 | maxTemp = weather.main.tempMax?.K, 104 | pressure = weather.main.pressure?.hPa, 105 | humidity = weather.main.humidity?.roundToInt(), 106 | precipitation = ((weather.rain?.`3h` ?: 0.0) + (weather.snow?.`3h` ?: 0.0)).mm, 107 | icon = iconForId(weather.weather.firstOrNull()?.id ?: return null), 108 | clouds = weather.clouds?.all?.roundToInt(), 109 | windSpeed = weather.wind?.speed?.m_s, 110 | windDirection = weather.wind?.deg ?: -1.0, 111 | night = run { 112 | val sunrise = weather.sys?.sunrise ?: 0 113 | val sunset = weather.sys?.sunset ?: 0 114 | weather.dt > sunset || weather.dt < sunrise 115 | }, 116 | location = location, 117 | provider = context.getString(R.string.plugin_name), 118 | providerUrl = weather.id?.let { "https://openweathermap.org/city/$it" }, 119 | createdAt = System.currentTimeMillis() 120 | ) 121 | } 122 | 123 | private fun owmToForecast( 124 | forecast: OwmForecastList, 125 | location: String, 126 | cityId: Int? 127 | ): Forecast? { 128 | val context = context ?: return null 129 | return Forecast( 130 | timestamp = (forecast.dt ?: return null) * 1000L, 131 | icon = iconForId(forecast.weather?.firstOrNull()?.id ?: return null), 132 | condition = forecast.weather.firstOrNull()?.description ?: return null, 133 | temperature = forecast.main?.temp?.K ?: return null, 134 | createdAt = System.currentTimeMillis(), 135 | provider = context.getString(R.string.plugin_name), 136 | providerUrl = cityId?.let { "https://openweathermap.org/city/$it" }, 137 | location = location, 138 | clouds = forecast.clouds?.all?.roundToInt(), 139 | windSpeed = forecast.wind?.speed?.m_s, 140 | windDirection = forecast.wind?.deg, 141 | night = forecast.sys?.pod == "n", 142 | humidity = forecast.main.humidity?.roundToInt(), 143 | pressure = forecast.main.pressure?.hPa, 144 | maxTemp = forecast.main.tempMax?.K, 145 | minTemp = forecast.main.tempMin?.K, 146 | precipitation = ((forecast.rain?.`3h` ?: 0.0) + (forecast.snow?.`3h` ?: 0.0)).mm, 147 | rainProbability = forecast.pop?.roundToInt(), 148 | ) 149 | } 150 | 151 | private fun iconForId(id: Int): WeatherIcon { 152 | return when (id) { 153 | 200, 201, in 230..232 -> WeatherIcon.ThunderstormWithRain 154 | 202 -> WeatherIcon.ThunderstormWithRain 155 | 210, 211 -> WeatherIcon.Thunderstorm 156 | 212, 221 -> WeatherIcon.HeavyThunderstorm 157 | in 300..302, in 310..312 -> WeatherIcon.Drizzle 158 | 313, 314, 321, in 500..504, 511, in 520..522, 531 -> WeatherIcon.Showers 159 | in 600..602 -> WeatherIcon.Snow 160 | 611, 612, 615, 616, in 620..622 -> WeatherIcon.Sleet 161 | 701, 711, 731, 741, 761, 762 -> WeatherIcon.Fog 162 | 721 -> WeatherIcon.Haze 163 | 771, 781, in 900..902, in 958..962 -> WeatherIcon.Storm 164 | 800 -> WeatherIcon.Clear 165 | 801 -> WeatherIcon.PartlyCloudy 166 | 802 -> WeatherIcon.MostlyCloudy 167 | 803 -> WeatherIcon.BrokenClouds 168 | 804, 951 -> WeatherIcon.Cloudy 169 | 903 -> WeatherIcon.Cold 170 | 904 -> WeatherIcon.Hot 171 | 905, in 952..957 -> WeatherIcon.Wind 172 | 906 -> WeatherIcon.Hail 173 | else -> WeatherIcon.Unknown 174 | } 175 | } 176 | 177 | /** 178 | * OWM incorrectly expects country codes instead of language codes for some languages 179 | * see https://openweathermap.org/current#multi 180 | */ 181 | private fun getLanguageCode(lang: String): String { 182 | when (lang) { 183 | "cs" -> return "cz" 184 | "al" -> return "sq" 185 | "kr" -> return "ko" 186 | "lv" -> return "la" 187 | else -> return lang 188 | } 189 | } 190 | 191 | override suspend fun findLocations(query: String, lang: String): List { 192 | val geo = apiClient.geo(q = query, limit = 5) 193 | 194 | // Here, OWM uses the correct language codes, so we don't need to map anything 195 | return geo.mapNotNull { 196 | val name = it.localNames?.get(lang) ?: it.name ?: return@mapNotNull null 197 | WeatherLocation.LatLon( 198 | name = "$name, ${it.country}", 199 | lat = it.lat ?: return@mapNotNull null, 200 | lon = it.lon ?: return@mapNotNull null, 201 | ) 202 | } 203 | } 204 | 205 | override suspend fun getPluginState(): PluginState { 206 | val context = context!! 207 | apiClient.apiKey.first() ?: return PluginState.SetupRequired( 208 | Intent(context, SettingsActivity::class.java), 209 | context.getString(R.string.plugin_state_setup_required) 210 | ) 211 | return PluginState.Ready() 212 | } 213 | 214 | } -------------------------------------------------------------------------------- /app/src/main/java/de/mm20/launcher2/plugin/openweathermap/OwmApiClient.kt: -------------------------------------------------------------------------------- 1 | package de.mm20.launcher2.plugin.openweathermap 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import de.mm20.launcher2.plugin.openweathermap.api.OwmForecast 6 | import de.mm20.launcher2.plugin.openweathermap.api.OwmGeo 7 | import de.mm20.launcher2.plugin.openweathermap.api.OwmWeather 8 | import io.ktor.client.HttpClient 9 | import io.ktor.client.call.body 10 | import io.ktor.client.engine.okhttp.OkHttp 11 | import io.ktor.client.plugins.contentnegotiation.ContentNegotiation 12 | import io.ktor.client.request.get 13 | import io.ktor.client.statement.bodyAsText 14 | import io.ktor.http.HttpStatusCode 15 | import io.ktor.http.URLProtocol 16 | import io.ktor.http.path 17 | import io.ktor.serialization.kotlinx.json.json 18 | import kotlinx.coroutines.flow.Flow 19 | import kotlinx.coroutines.flow.first 20 | import kotlinx.coroutines.flow.map 21 | import kotlinx.serialization.json.Json 22 | import java.io.IOException 23 | 24 | class OwmApiClient( 25 | private val context: Context 26 | ) { 27 | 28 | private val client = HttpClient(OkHttp) { 29 | install(ContentNegotiation) { 30 | json(Json { 31 | ignoreUnknownKeys = true 32 | explicitNulls = false 33 | }) 34 | } 35 | } 36 | 37 | suspend fun weather( 38 | lat: Double, 39 | lon: Double, 40 | lang: String? = null, 41 | appid: String? = null 42 | ): OwmWeather { 43 | val apiKey = appid ?: apiKey.first() ?: throw IllegalArgumentException("No API key provided") 44 | val response = client.get { 45 | url { 46 | protocol = URLProtocol.HTTPS 47 | host = "api.openweathermap.org" 48 | path("data", "2.5", "weather") 49 | parameters["lat"] = lat.toString() 50 | parameters["lon"] = lon.toString() 51 | parameters["appid"] = apiKey 52 | if (lang != null) parameters["lang"] = lang 53 | } 54 | } 55 | if (response.status == HttpStatusCode.Unauthorized) { 56 | throw IllegalArgumentException("Unauthorized. Invalid API key?; body ${response.bodyAsText()}") 57 | } else if (response.status != HttpStatusCode.OK) { 58 | throw IOException("API error: status ${response.status.value}; body ${response.bodyAsText()}") 59 | } 60 | return response.body() 61 | } 62 | 63 | suspend fun forecast( 64 | lat: Double, 65 | lon: Double, 66 | cnt: Int? = null, 67 | lang: String? = null, 68 | appid: String? = null 69 | ): OwmForecast { 70 | val apiKey = appid ?: apiKey.first() ?: throw IllegalArgumentException("No API key provided") 71 | val response = client.get { 72 | url { 73 | protocol = URLProtocol.HTTPS 74 | host = "api.openweathermap.org" 75 | path("data", "2.5", "forecast") 76 | parameters["lat"] = lat.toString() 77 | parameters["lon"] = lon.toString() 78 | parameters["appid"] = apiKey 79 | if (lang != null) parameters["lang"] = lang 80 | if (cnt != null) parameters["cnt"] = cnt.toString() 81 | } 82 | } 83 | if (response.status == HttpStatusCode.Unauthorized) { 84 | throw IllegalArgumentException("Unauthorized. Invalid API key?; body ${response.bodyAsText()}") 85 | } else if (response.status != HttpStatusCode.OK) { 86 | throw IOException("API error: status ${response.status.value}; body ${response.bodyAsText()}") 87 | } 88 | return response.body() 89 | } 90 | 91 | suspend fun geo( 92 | q: String, 93 | limit: Int? = null, 94 | appid: String? = null 95 | ): Array { 96 | val apiKey = appid ?: apiKey.first() ?: throw IllegalArgumentException("No API key provided") 97 | val response = client.get { 98 | url { 99 | protocol = URLProtocol.HTTPS 100 | host = "api.openweathermap.org" 101 | path("geo", "1.0", "direct") 102 | parameters["q"] = q 103 | parameters["appid"] = apiKey 104 | if (limit != null) parameters["limit"] = limit.toString() 105 | } 106 | } 107 | if (response.status == HttpStatusCode.Unauthorized) { 108 | throw IllegalArgumentException("Unauthorized. Invalid API key?; body ${response.bodyAsText()}") 109 | } else if (response.status != HttpStatusCode.OK) { 110 | throw IOException("API error: status ${response.status.value}; body ${response.bodyAsText()}") 111 | } 112 | return response.body() 113 | } 114 | 115 | suspend fun setApiKey(apiKey: String) { 116 | context.dataStore.updateData { 117 | it.copy(apiKey = apiKey) 118 | } 119 | } 120 | 121 | suspend fun testApiKey(apiKey: String): Boolean { 122 | return try { 123 | weather( 124 | lat = 51.5, 125 | lon = 0.0, 126 | appid = apiKey 127 | ) 128 | return true 129 | } catch (e: IllegalArgumentException) { 130 | Log.e("OwmApiClient", "Invalid API key", e) 131 | return false 132 | } 133 | } 134 | 135 | val apiKey: Flow = context.dataStore.data.map { it.apiKey } 136 | 137 | } -------------------------------------------------------------------------------- /app/src/main/java/de/mm20/launcher2/plugin/openweathermap/SettingsActivity.kt: -------------------------------------------------------------------------------- 1 | package de.mm20.launcher2.plugin.openweathermap 2 | 3 | import android.content.Intent 4 | import android.os.Build 5 | import android.os.Bundle 6 | import android.util.Log 7 | import androidx.activity.compose.setContent 8 | import androidx.activity.enableEdgeToEdge 9 | import androidx.appcompat.app.AppCompatActivity 10 | import androidx.compose.foundation.background 11 | import androidx.compose.foundation.clickable 12 | import androidx.compose.foundation.isSystemInDarkTheme 13 | import androidx.compose.foundation.layout.Arrangement 14 | import androidx.compose.foundation.layout.Box 15 | import androidx.compose.foundation.layout.Column 16 | import androidx.compose.foundation.layout.fillMaxSize 17 | import androidx.compose.foundation.layout.fillMaxWidth 18 | import androidx.compose.foundation.layout.padding 19 | import androidx.compose.foundation.layout.size 20 | import androidx.compose.foundation.layout.systemBarsPadding 21 | import androidx.compose.foundation.rememberScrollState 22 | import androidx.compose.foundation.verticalScroll 23 | import androidx.compose.material.icons.Icons 24 | import androidx.compose.material.icons.rounded.CheckCircle 25 | import androidx.compose.material.icons.rounded.Error 26 | import androidx.compose.material.icons.rounded.Save 27 | import androidx.compose.material3.Button 28 | import androidx.compose.material3.ButtonDefaults 29 | import androidx.compose.material3.CircularProgressIndicator 30 | import androidx.compose.material3.HorizontalDivider 31 | import androidx.compose.material3.Icon 32 | import androidx.compose.material3.LocalContentColor 33 | import androidx.compose.material3.MaterialTheme 34 | import androidx.compose.material3.OutlinedTextField 35 | import androidx.compose.material3.Text 36 | import androidx.compose.material3.darkColorScheme 37 | import androidx.compose.material3.dynamicDarkColorScheme 38 | import androidx.compose.material3.dynamicLightColorScheme 39 | import androidx.compose.material3.lightColorScheme 40 | import androidx.compose.runtime.CompositionLocalProvider 41 | import androidx.compose.runtime.LaunchedEffect 42 | import androidx.compose.runtime.collectAsState 43 | import androidx.compose.runtime.getValue 44 | import androidx.compose.runtime.mutableStateOf 45 | import androidx.compose.runtime.setValue 46 | import androidx.compose.ui.Alignment 47 | import androidx.compose.ui.Modifier 48 | import androidx.compose.ui.res.stringResource 49 | import androidx.compose.ui.text.style.TextDecoration 50 | import androidx.compose.ui.unit.dp 51 | import androidx.core.net.toUri 52 | import androidx.lifecycle.lifecycleScope 53 | import kotlinx.coroutines.launch 54 | 55 | class SettingsActivity : AppCompatActivity() { 56 | 57 | private var apiKey by mutableStateOf("") 58 | private var apiKeyState by mutableStateOf(null) 59 | private var saving by mutableStateOf(false) 60 | private val apiClient = OwmApiClient(this) 61 | private var savedApiKey = apiClient.apiKey 62 | 63 | override fun onCreate(savedInstanceState: Bundle?) { 64 | super.onCreate(savedInstanceState) 65 | 66 | enableEdgeToEdge() 67 | setContent { 68 | val savedApiKey by savedApiKey.collectAsState("") 69 | LaunchedEffect(savedApiKey) { 70 | apiKey = savedApiKey ?: "" 71 | } 72 | val darkMode = isSystemInDarkTheme() 73 | val theme = if (darkMode) { 74 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 75 | dynamicDarkColorScheme(this) 76 | } else { 77 | darkColorScheme() 78 | } 79 | } else { 80 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 81 | dynamicLightColorScheme(this) 82 | } else { 83 | lightColorScheme() 84 | } 85 | } 86 | MaterialTheme(theme) { 87 | CompositionLocalProvider( 88 | LocalContentColor provides MaterialTheme.colorScheme.onSurface 89 | ) { 90 | Column( 91 | modifier = Modifier 92 | .fillMaxSize() 93 | .background(MaterialTheme.colorScheme.surface) 94 | .systemBarsPadding() 95 | .verticalScroll(rememberScrollState()), 96 | horizontalAlignment = Alignment.Start, 97 | ) { 98 | 99 | Column( 100 | modifier = Modifier 101 | .fillMaxWidth() 102 | .padding(16.dp), 103 | horizontalAlignment = Alignment.Start, 104 | ) { 105 | Text( 106 | stringResource(R.string.setup_step_n, 1), 107 | style = MaterialTheme.typography.titleLarge 108 | ) 109 | Text( 110 | stringResource(R.string.instruction_register, "OpenWeatherMap"), 111 | style = MaterialTheme.typography.bodyMedium, 112 | modifier = Modifier.padding(top = 8.dp) 113 | ) 114 | Text( 115 | "https://home.openweathermap.org/api_keys", 116 | style = MaterialTheme.typography.bodyMedium, 117 | textDecoration = TextDecoration.Underline, 118 | color = MaterialTheme.colorScheme.primary, 119 | modifier = Modifier 120 | .padding(top = 8.dp) 121 | .clickable { 122 | startActivity(Intent(Intent.ACTION_VIEW).apply { 123 | data = 124 | "https://home.openweathermap.org/api_keys".toUri() 125 | }) 126 | } 127 | ) 128 | } 129 | 130 | HorizontalDivider() 131 | 132 | Column( 133 | modifier = Modifier 134 | .fillMaxWidth() 135 | .padding(16.dp), 136 | horizontalAlignment = Alignment.Start, 137 | ) { 138 | Text( 139 | stringResource(R.string.setup_step_n, 2), 140 | style = MaterialTheme.typography.titleLarge 141 | ) 142 | Text( 143 | stringResource(R.string.instruction_enter_key), 144 | style = MaterialTheme.typography.bodyMedium, 145 | modifier = Modifier.padding(top = 8.dp) 146 | ) 147 | 148 | Box( 149 | modifier = Modifier 150 | .padding(vertical = 16.dp) 151 | .background( 152 | MaterialTheme.colorScheme.secondaryContainer, 153 | MaterialTheme.shapes.medium 154 | ) 155 | ) { 156 | Text( 157 | stringResource(R.string.note_api_key), 158 | style = MaterialTheme.typography.bodySmall, 159 | modifier = Modifier.padding(16.dp), 160 | color = MaterialTheme.colorScheme.onSecondaryContainer, 161 | ) 162 | } 163 | 164 | Column( 165 | modifier = Modifier 166 | .fillMaxWidth() 167 | .padding(vertical = 8.dp), 168 | verticalArrangement = Arrangement.Center, 169 | horizontalAlignment = Alignment.CenterHorizontally, 170 | ) { 171 | OutlinedTextField( 172 | modifier = Modifier.fillMaxWidth(), 173 | enabled = apiKeyState != ApiKeyState.Saving, 174 | value = apiKey, 175 | onValueChange = { 176 | apiKey = it 177 | apiKeyState = null 178 | }, 179 | label = { 180 | Text(stringResource(id = R.string.input_api_key)) 181 | }, 182 | singleLine = true, 183 | supportingText = when (apiKeyState) { 184 | ApiKeyState.Saved -> { 185 | { Text(stringResource(R.string.api_key_state_saved)) } 186 | } 187 | 188 | ApiKeyState.Invalid -> { 189 | { Text(stringResource(R.string.api_key_state_invalid)) } 190 | } 191 | 192 | ApiKeyState.Error -> { 193 | { Text(stringResource(R.string.api_key_state_error)) } 194 | } 195 | 196 | else -> null 197 | }, 198 | isError = apiKeyState == ApiKeyState.Invalid || apiKeyState == ApiKeyState.Error, 199 | trailingIcon = when (apiKeyState) { 200 | ApiKeyState.Saved -> { 201 | { Icon(Icons.Rounded.CheckCircle, null) } 202 | } 203 | 204 | ApiKeyState.Invalid -> { 205 | { Icon(Icons.Rounded.Error, null) } 206 | } 207 | 208 | ApiKeyState.Error -> { 209 | { Icon(Icons.Rounded.Error, null) } 210 | } 211 | 212 | else -> null 213 | }, 214 | ) 215 | } 216 | Button( 217 | enabled = apiKeyState != ApiKeyState.Saving && apiKey.isNotBlank() && apiKey != savedApiKey, 218 | contentPadding = ButtonDefaults.ButtonWithIconContentPadding, 219 | onClick = { saveApiKey(apiKey) }, 220 | modifier = Modifier.align(Alignment.End) 221 | ) { 222 | if (apiKeyState == ApiKeyState.Saving) { 223 | CircularProgressIndicator( 224 | modifier = Modifier.size(ButtonDefaults.IconSize), 225 | color = LocalContentColor.current, 226 | strokeWidth = 2.dp, 227 | ) 228 | } else { 229 | Icon( 230 | Icons.Rounded.Save, 231 | null, 232 | modifier = Modifier.size(ButtonDefaults.IconSize) 233 | ) 234 | } 235 | Text( 236 | text = stringResource(R.string.api_key_save_button), 237 | modifier = Modifier.padding(start = ButtonDefaults.IconSpacing) 238 | ) 239 | } 240 | } 241 | } 242 | } 243 | } 244 | } 245 | } 246 | 247 | private fun saveApiKey(apiKey: String) { 248 | lifecycleScope.launch { 249 | apiKeyState = ApiKeyState.Saving 250 | try { 251 | val valid = apiClient.testApiKey(apiKey) 252 | if (valid) { 253 | apiClient.setApiKey(apiKey) 254 | apiKeyState = ApiKeyState.Saved 255 | } else { 256 | apiKeyState = ApiKeyState.Invalid 257 | } 258 | } catch (e: Exception) { 259 | apiKeyState = ApiKeyState.Error 260 | Log.e("SettingsActivity", "An error occured", e) 261 | } 262 | } 263 | } 264 | } 265 | 266 | enum class ApiKeyState { 267 | Saving, 268 | Saved, 269 | Invalid, 270 | Error, 271 | } -------------------------------------------------------------------------------- /app/src/main/java/de/mm20/launcher2/plugin/openweathermap/UserData.kt: -------------------------------------------------------------------------------- 1 | package de.mm20.launcher2.plugin.openweathermap 2 | 3 | import android.content.Context 4 | import androidx.datastore.core.Serializer 5 | import androidx.datastore.dataStore 6 | import kotlinx.serialization.Serializable 7 | import kotlinx.serialization.json.Json 8 | import kotlinx.serialization.json.decodeFromStream 9 | import kotlinx.serialization.json.encodeToStream 10 | import java.io.InputStream 11 | import java.io.OutputStream 12 | 13 | @Serializable 14 | data class UserData( 15 | val apiKey: String? = null 16 | ) 17 | 18 | val Context.dataStore by dataStore("userdata.json", UserDataSerializer) 19 | 20 | object UserDataSerializer: Serializer { 21 | override val defaultValue: UserData = UserData() 22 | 23 | override suspend fun readFrom(input: InputStream): UserData { 24 | return Json.decodeFromStream(input) 25 | } 26 | 27 | override suspend fun writeTo(t: UserData, output: OutputStream) { 28 | Json.encodeToStream(UserData.serializer(), t, output) 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/de/mm20/launcher2/plugin/openweathermap/api/OwmForecast.kt: -------------------------------------------------------------------------------- 1 | package de.mm20.launcher2.plugin.openweathermap.api 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | data class OwmForecast( 8 | val cod: Int?, 9 | val message: Int?, 10 | val cnt: Int?, 11 | val list: List?, 12 | val city: OwmForecastCity?, 13 | ) 14 | 15 | @Serializable 16 | data class OwmForecastList( 17 | val dt: Long?, 18 | val main: OwmForecastMain?, 19 | val weather: List?, 20 | val clouds: OwmForecastClouds?, 21 | val wind: OwmForecastWind?, 22 | val visibility: Double?, 23 | val pop: Double?, 24 | val rain: OwmForecastRain?, 25 | val snow: OwmForecastSnow?, 26 | val sys: OwmForecastSys?, 27 | @SerialName("dt_txt") val dtTxt: String?, 28 | ) 29 | 30 | @Serializable 31 | data class OwmForecastMain( 32 | val temp: Double?, 33 | @SerialName("feels_like")val feelsLike: Double?, 34 | val pressure: Double?, 35 | val humidity: Double?, 36 | @SerialName("temp_max") val tempMin: Double?, 37 | @SerialName("temp_min") val tempMax: Double?, 38 | @SerialName("sea_level") val seaLevel: Double?, 39 | @SerialName("grnd_level") val grndLevel: Double?, 40 | @SerialName("temp_kf") val tempKf: Double?, 41 | ) 42 | 43 | @Serializable 44 | data class OwmForecastWeather( 45 | val id: Int?, 46 | val main: String?, 47 | val description: String?, 48 | val icon: String?, 49 | ) 50 | 51 | @Serializable 52 | data class OwmForecastClouds( 53 | val all: Double?, 54 | ) 55 | 56 | @Serializable 57 | data class OwmForecastWind( 58 | val speed: Double?, 59 | val deg: Double?, 60 | val gust: Double?, 61 | ) 62 | 63 | @Serializable 64 | data class OwmForecastRain( 65 | val `3h`: Double?, 66 | ) 67 | 68 | @Serializable 69 | data class OwmForecastSnow( 70 | val `3h`: Double?, 71 | ) 72 | 73 | @Serializable 74 | data class OwmForecastSys( 75 | val pod: String?, 76 | ) 77 | 78 | @Serializable 79 | data class OwmForecastCity( 80 | val id: Int?, 81 | val name: String?, 82 | val coord: OwmForecastCoord?, 83 | val country: String?, 84 | val population: Int?, 85 | val timezone: Int?, 86 | val sunrise: Long?, 87 | val sunset: Long?, 88 | ) 89 | 90 | @Serializable 91 | data class OwmForecastCoord( 92 | val lon: Double?, 93 | val lat: Double?, 94 | ) -------------------------------------------------------------------------------- /app/src/main/java/de/mm20/launcher2/plugin/openweathermap/api/OwmGeo.kt: -------------------------------------------------------------------------------- 1 | package de.mm20.launcher2.plugin.openweathermap.api 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | data class OwmGeo( 8 | val name: String?, 9 | @SerialName("local_names") val localNames: Map?, 10 | val lat: Double?, 11 | val lon: Double?, 12 | val country: String?, 13 | val state: String?, 14 | ) -------------------------------------------------------------------------------- /app/src/main/java/de/mm20/launcher2/plugin/openweathermap/api/OwmWeather.kt: -------------------------------------------------------------------------------- 1 | package de.mm20.launcher2.plugin.openweathermap.api 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | data class OwmWeather( 8 | val coord: OwmWeatherCoord?, 9 | val weather: List?, 10 | val base: String?, 11 | val main: OwmWeatherMain?, 12 | val visibility: Double?, 13 | val wind: OwmWeatherWind?, 14 | val clouds: OwmWeatherClouds?, 15 | val rain: OwmWeatherRain?, 16 | val snow: OwmWeatherSnow?, 17 | val dt: Long?, 18 | val sys: OwmWeatherSys?, 19 | val timezone: Long?, 20 | val id: Int?, 21 | val name: String?, 22 | val cod: Int?, 23 | ) 24 | 25 | @Serializable 26 | data class OwmWeatherCoord( 27 | val lon: Double?, 28 | val lat: Double?, 29 | ) 30 | 31 | @Serializable 32 | data class OwmWeatherMain( 33 | val temp: Double?, 34 | @SerialName("feels_like")val feelsLike: Double?, 35 | val pressure: Double?, 36 | val humidity: Double?, 37 | @SerialName("temp_max") val tempMin: Double?, 38 | @SerialName("temp_min") val tempMax: Double?, 39 | @SerialName("sea_level") val seaLevel: Double?, 40 | @SerialName("grnd_level") val grndLevel: Double?, 41 | ) 42 | 43 | @Serializable 44 | data class OwmWeatherWeather( 45 | val id: Int?, 46 | val main: String?, 47 | val description: String?, 48 | val icon: String?, 49 | ) 50 | 51 | @Serializable 52 | data class OwmWeatherWind( 53 | val speed: Double?, 54 | val deg: Double?, 55 | val gust: Double?, 56 | ) 57 | 58 | @Serializable 59 | data class OwmWeatherClouds( 60 | val all: Double?, 61 | ) 62 | 63 | @Serializable 64 | data class OwmWeatherRain( 65 | val `1h`: Double?, 66 | val `3h`: Double?, 67 | ) 68 | 69 | @Serializable 70 | data class OwmWeatherSnow( 71 | val `1h`: Double?, 72 | val `3h`: Double?, 73 | ) 74 | 75 | @Serializable 76 | data class OwmWeatherSys( 77 | val type: Int?, 78 | val id: Int?, 79 | val country: String?, 80 | val sunrise: Long?, 81 | val sunset: Long?, 82 | ) 83 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_owm.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kvaesitso/Plugin-OpenWeatherMap/7bbf1d4ceca02013d8f1fdca15abbe7abc87cf85/app/src/main/res/mipmap-hdpi/ic_launcher_background.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kvaesitso/Plugin-OpenWeatherMap/7bbf1d4ceca02013d8f1fdca15abbe7abc87cf85/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kvaesitso/Plugin-OpenWeatherMap/7bbf1d4ceca02013d8f1fdca15abbe7abc87cf85/app/src/main/res/mipmap-mdpi/ic_launcher_background.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kvaesitso/Plugin-OpenWeatherMap/7bbf1d4ceca02013d8f1fdca15abbe7abc87cf85/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kvaesitso/Plugin-OpenWeatherMap/7bbf1d4ceca02013d8f1fdca15abbe7abc87cf85/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kvaesitso/Plugin-OpenWeatherMap/7bbf1d4ceca02013d8f1fdca15abbe7abc87cf85/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kvaesitso/Plugin-OpenWeatherMap/7bbf1d4ceca02013d8f1fdca15abbe7abc87cf85/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kvaesitso/Plugin-OpenWeatherMap/7bbf1d4ceca02013d8f1fdca15abbe7abc87cf85/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kvaesitso/Plugin-OpenWeatherMap/7bbf1d4ceca02013d8f1fdca15abbe7abc87cf85/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kvaesitso/Plugin-OpenWeatherMap/7bbf1d4ceca02013d8f1fdca15abbe7abc87cf85/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/values-ar/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | إضافة Kvaesitso ل ”OpenWeatherMap“ 4 | دمج OpenWeatherMap 5 | خريطة الطقس المفتوح OpenWeatherMap 6 | إضافة OpenWeatherMap كمزود خدمة الطقس 7 | أضف مفتاح OpenWeatherMap API لاستخدام هذه الإضافة. 8 | مفتاح واجهة برمجة التطبيقات (API) 9 | حفظ 10 | تم تحديث مفتاح API بنجاح. 11 | مفتاح API المقدم غير صالح. 12 | خطوة %1$d 13 | حدث خطأ، يرجى التحقق من اتصال الشبكة لديك. 14 | قم بالتسجيل في %1$s للحصول على مفتاح API: 15 | الصق مفتاح API أدناه: 16 | يرجى ملاحظة أن تفعيل مفتاحك قد يستغرق ما يصل إلى ساعتين. 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-arq/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-az/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-be/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Калі ласка, майце на ўвазе, что актывацыя ключа можа заняць да 2 гадзін. 4 | Плагін Kvaesitso для OpenWeatherMap 5 | Дадайце ключ API OpenWeatherMap, каб карыстацца гэтым плагінам. 6 | Паўстала памылка, калі ласка, праверце ваша падлучэнне. 7 | Інтэграцыя OpenWeatherMap 8 | OpenWeatherMap 9 | Дадае OpenWeatherMap як правайдара надвор\'я 10 | Ключ API 11 | Захаваць 12 | Ключ API быў абноўлены. 13 | Дадзены ключ API няправільны. 14 | Крок %1$d 15 | Зарэгіструйцеся на %1$s, каб атрымаць ключ API: 16 | Устаўце ключ API ніжэй: 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-bn/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-ca/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Connector Kvaesitso per a OpenWeatherMap 4 | Integració OpenWeatherMap 5 | OpenWeatherMap 6 | Afegeix OpenWeatherMap com a proveïdor de dades climatològiques 7 | La clau API proporcionada no és vàlida. 8 | Afegiu una clau API OpenWeatherMap per utilitzar aquest connector. 9 | Clau API 10 | Desa 11 | Clau API s\'ha actualitzat correctament. 12 | S\'ha produït un error, comproveu la vostra connexió de xarxa. 13 | Pas %1$d 14 | Registreu-vos a %1$s per obtenir una clau API: 15 | Enganxeu la clau de l\'API a continuació: 16 | Tingueu en compte que la clau pot tardar fins a 2 hores a activar-se. 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-cs/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Doplněk Kvaesitso pro OpenWeatherMap 4 | Pro použití tohoto doplňku přidejte klíč OpenWeatherMap API. 5 | Klíč API byl úspěšně aktualizován. 6 | Přidá OpenWeatherMap jako poskytovatele počasí 7 | Uložit 8 | Integrace OpenWeatherMap 9 | OpenWeatherMap 10 | Klíč API 11 | Zadaný klíč API je neplatný. 12 | Vyskytla se chyba, zkontrolujte prosím své připojení k síti. 13 | Krok %1$d 14 | Upozorňujeme, že aktivace klíče může trvat až 2 hodiny. 15 | Zaregistrujte se na %1$s pro získání klíče API: 16 | Vložte klíč API níže: 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-da/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Kvaesitso Udvidelse til OpenWeatherMap 4 | OpenWeatherMap 5 | Tilføj en OpenWeatherApp API-nøgle for at bruge denne udvidelse. 6 | API-nøgle 7 | Gem 8 | API-nøgle blev opdateret. 9 | Den angivne API-nøgle er ugyldig. 10 | En fejl opstod; tjek venligst din internetforbindelse. 11 | OpenWeatherMap-integration 12 | Tilføjer OpenWeatherApp som vejr-udbyder 13 | Trin %1$d 14 | Tilmeld dig på %1$s for at få en API-nøgle: 15 | Indsæt API-nøglen nedenfor: 16 | Bemærk, at det kan tage op til 2 timer, før din nøgle er aktiveret. 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Kvaesitso-Plugin für OpenWeatherMap 4 | OpenWeatherMap-Integration 5 | OpenWeatherMap 6 | Fügt OpenWeatherMap als Wetterdienst hinzu 7 | Fügen Sie einen OpenWeatherMap-API-Key hinzu, um dieses Plugin zu nutzen. 8 | API-Key 9 | Speichern 10 | API-Key wurde erfolgreich gespeichert. 11 | Ein Fehler ist aufgetreten, bitte prüfen Sie Ihre Internetverbindung. 12 | Der angegebene API-Key ist ungültig. 13 | Schritt %1$d 14 | Registrieren Sie sich unter %1$s, um einen API-Key zu erhalten: 15 | Fügen Sie den API-Key hier ein: 16 | Bitte beachten Sie, dass es bis zu zwei Stunden dauern kann, bis Ihr API-Key freigeschaltet ist. 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-el/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-eo/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-es/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Plugin de Kvaesitso para OpenWeatherMap 4 | Integración con OpenWeatherMap 5 | OpenWeatherMap 6 | Agregar OpenWeatherMap como proveedor de clima 7 | Agregar una clave del API de OpenWeatherMap para usar este plugin. 8 | Clave API 9 | Guardar 10 | La clave API ha sido actualizada correctamente. 11 | La clave API provista no es válida. 12 | Ha ocurrido un error, por favor comprueba tu conexión de red. 13 | -------------------------------------------------------------------------------- /app/src/main/res/values-eu/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OpenWeatherMap integrazioa 4 | OpenWeatherMap 5 | API gakoa 6 | Gorde 7 | %1$d. pausua 8 | API gakoa behean itsatsi: 9 | Kontuan izan 2 ordu arte igaro daitezkeela zure gakoa aktibatzeko. 10 | %1$s-(e)n erregistratu API gako bat lortzeko: 11 | OpenWeatherMap API gako bat konfiguratu gehigarri hau erabiltzeko. 12 | API gakoa ongi eguneratu da. 13 | Sartutako API gakoa ez da baliozkoa. 14 | Errore bat gertatu da, mesedez zure internet konexioa egiaztatu. 15 | Kvaesitso plugina OpenWeatherMap-erako 16 | OpenWeatherMap gehitzen du eguraldi hornitzaile gisa 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-fa/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | افزونه Kvaesitso برای OpenWeatherMap 4 | بکپارچگی نقشه آب و هوا آزاد 5 | نقشه آب و هوا آزاد(OpenWeatherMap) 6 | نقشه آب و هوا آزاد را در جایگاه ارائه دهنده آب و هوا می‌افزاید 7 | یک کلید OpenWeatherMap API برای به‌کارگیری این افزونه بیفزایید. 8 | کلید API 9 | نگه‌داری 10 | کلید API با موفقیت به‌روزرسانی شد. 11 | کلید API داده شده درست نیست. 12 | ایرادی رخ داده، لطفاً شبکه‌تان را بررسی کنید. 13 | گام %1$d 14 | برای گرفتن کلید AI، در %1$s ثبت‌نام کنید: 15 | کلید API را پایین جای‌گذاری کنید: 16 | توجه کنید که روشن شدن کلید شما می‌تواند تا 2 ساعت زمان ببرد. 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-fi/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Intégration OpenWeatherMap 4 | Ajouter une clé API OpenWeatherMap pour utiliser ce plugin. 5 | Clé API 6 | Enregistrer 7 | Clé API mise à jour avec succès. 8 | La clé API donnée n\'est pas valide. 9 | Une erreur est survenue, merci de vérifier votre connexion réseau. 10 | Plugin Kvaesitso pour OpenWeatherMap 11 | Ajoute OpenWeatherMap comme source météo 12 | OpenWeatherMap 13 | Inscrivez-vous sur %1$s pour obtenir une clé API : 14 | Collez la clé API ci-dessous : 15 | Veuillez noter que l\'activation de votre clé peut prendre jusqu\'à 2 heures. 16 | Étape %1$d 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-hu/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OpenWeatherMap integráció 4 | OpenWeatherMap 5 | Adjon hozzá egy OpenWeatherMap API-kulcsot a bővítmény használatához. 6 | API kulcs 7 | Mentés 8 | Az API-kulcs sikeresen frissült. 9 | A megadott API-kulcs nem érvényes. 10 | Kvaesitso bővítmény az OpenWeatherMaphez 11 | OpenWeatherMap hozzáadása, mint időjáráselőrejelzési szolgáltató 12 | Hiba történt, kérjük, ellenőrizd a hálózati kapcsolatot. 13 | Másolja be az API-kulcsot: 14 | Regisztráljon a(z) %1$s oldalon, hogy megkapja az API-kulcsot: 15 | Vegye figyelembe, hogy a kulcs aktiválása akár 2 órát is igénybe vehet. 16 | %1$d lépés 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-ia/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Salveguardar 4 | OpenWeatherMap 5 | Adde OpenWeatherMap como fornitor de tempore 6 | Clave API 7 | -------------------------------------------------------------------------------- /app/src/main/res/values-in/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Integrasi OpenWeatherMap 4 | OpenWeatherMap 5 | Menambahkan OpenWeatherMap sebagai penyedia cuaca 6 | kunci API 7 | Plugin Kvaesitso untuk OpenWeatherMap 8 | Tambahkan kunci API OpenWeatherMap untuk menggunakan plugin ini. 9 | -------------------------------------------------------------------------------- /app/src/main/res/values-it/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Plugin Kvaesitso per OpenWeatherMap 4 | Integrazione OpenWeatherMap 5 | OpenWeatherMap 6 | Aggiunge OpenWeatherMap come fornitore meteo 7 | Aggiungi una chiave API OpenWeatherMap per usare questo plugin. 8 | Chiave API 9 | Salva 10 | La chiave API fornita non è valida. 11 | Si è verificato un errore, controlla la connessione di rete. 12 | La chiave API è stata aggiornata correttamente. 13 | Registrati su %1$s per ottenere una chiave API: 14 | Incolla qui la chiave API: 15 | Passaggio %1$d 16 | Tieni presente che potrebbero essere necessarie fino a 2 ore per l\'attivazione della chiave. 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-ko/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-lt/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OpenWeatherMap integracija 4 | OpenWeatherMap 5 | Prideda OpenWeatherMap kaip oro duomenų tiekėją 6 | Kvaesito priedas skirtas OpenWeatherMap 7 | -------------------------------------------------------------------------------- /app/src/main/res/values-ms/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-nb-rNO/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Kvaesitso-tillegg for OpenWeatherMap 4 | OpenWeatherMap-integrasjon 5 | OpenWeatherMap 6 | Legger til OpenWeatherMap som en værvarseltilbyder 7 | Legg til en OpenWeatherMap-API-nøkkel for å bruke dette tillegget. 8 | API-nøkkel 9 | Lagre 10 | API-nøkkel er oppdatert. 11 | API-nøkkelen er ikke gyldig. 12 | En feil oppstod, sjekk din nettverkstilkobling. 13 | Steg %1$d 14 | Registrer på %1$s for å få en API-nøkkel: 15 | Lim inn API-nøkkel under: 16 | Merk at det kan ta opp mot 2 timer før nøkkelen din aktiveres. 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-nl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OpenWeatherMap-integratie 4 | OpenWeatherMap 5 | Voegt OpenWeatherMap toe als aanbieder van weergegevens 6 | Kvaesitso Plug-in voor OpenWeatherMap 7 | Voeg een OpenWeatherMap API-sleutel toe om deze plug-in te gebruiken. 8 | API-sleutel 9 | Opslaan 10 | API-sleutel is succesvol bijgewerkt. 11 | De opgegeven API-sleutel is niet geldig. 12 | Er is een fout opgetreden, controleer de netwerkverbinding. 13 | Stap %1$d 14 | Houd er rekening mee dat het tot 2 uur kan duren voordat uw sleutel is geactiveerd. 15 | Registreer bij %1$s om een API-sleutel te krijgen: 16 | Plak de onderstaande API-sleutel: 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-pl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Plugin Kvaesitso dla OpenWeatherMap 4 | Integracja OpenWeatherMap 5 | OpenWeatherMap 6 | Dodaje OpenWeatherMap jako dostawca danych pogodowych 7 | Dodaj klucz API OpenWeatherMap, aby korzystać z tej wtyczki. 8 | Klucz API 9 | Klucz API został pomyślnie zaktualizowany. 10 | Podany klucz API jest nieprawidłowy. 11 | Zapisz 12 | Wystąpił błąd, sprawdź połączenie sieciowe. 13 | Krok %1$d 14 | Zarejestruj się w %1$s by uzyskać klucz API: 15 | Wklej klucz API poniżej: 16 | -------------------------------------------------------------------------------- /app/src/main/res/values-pt-rBR/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Plugin do Kvaesitso para o OpenWeatherMap 4 | OpenWeatherMap 5 | Adicione uma chave API do OpenWeatherMap para utilizar esse plugin. 6 | Chave API 7 | Salvar 8 | A chave API foi atualizada com sucesso. 9 | Ocorreu um erro, por favor cheque sua conexão com a internet. 10 | Passo %1$d 11 | Registre-se em %1$s para conseguir uma chave API: 12 | Cole a chave API abaixo: 13 | A chave API provida não é válida. 14 | Adiciona o OpenWeatherMap como o provedor de clima 15 | Integração ao OpenWeatherMap 16 | Por favor note que pode levar até 2 horas para sua chave ser ativada. 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-pt/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Ocorreu um erro. Verifique a sua ligação de rede. 4 | Adicione a chave da API OpenWeatherMap para utilizar este plugin. 5 | Integração OpenWeatherMap 6 | Plugin OpenWeatherMap para Kvaesitso 7 | OpenWeatherMap 8 | Adicionar OpenWeatherMap como serviço de meteorologia 9 | Chave API 10 | Guardar 11 | A chave API foi atualizada com sucesso. 12 | A chave API indicada não é valida. 13 | Passo %1$d 14 | Registe-se em %1$s para obter uma chave API: 15 | Cole a chave API aqui: 16 | Tenha em atenção de que pode levar até 2 horas para a sua chave ficar ativa. 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-ro/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Plugin Kvaesitso pentru OpenWeatherMap 4 | Integrare OpenWeatherMap 5 | OpenWeatherMap 6 | Adaugă OpenWeatherMap ca un furnizor de vreme 7 | Adăugați o cheie API OpenWeatherMap pentru a utiliza acest plugin. 8 | Cheie API 9 | Salvați 10 | Cheia API a fost actualizată cu succes. 11 | Cheia API introdusā nu este validă. 12 | A apărut o eroare, vă rugăm verificați conexiunea la Internet. 13 | Înregistrați-vă în %1$s pentru a obține o cheie API: 14 | Pasul %1$d 15 | Lipiți cheia API mai jos: 16 | Țineți minte că poate dura până la două ore ca cheia dvs. să fie activată. 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-ru/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Плагин Kvaesitso для OpenWeatherMap 4 | Добавляет OpenWeatherMap в список поставщиков погоды 5 | API-ключ был успешно обновлён. 6 | Интеграция OpenWeatherMap 7 | OpenWeatherMap 8 | Чтобы использовать этот плагин, добавьте API-ключ от OpenWeatherMap. 9 | API-ключ 10 | Сохранить 11 | Предоставленный API-ключ недействителен. 12 | Произошла ошибка, проверьте интернет-соединение. 13 | Шаг %1$d 14 | Вставьте API-ключ ниже: 15 | Зарегистрируйтесь на %1$s для получения API-ключа: 16 | Пожалуйста, обратите внимание, что активация ключа может занимать до 2 часов. 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-sv/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-th/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ปลั๊กอิน Kvaesitso สำหรับ OpenWeatherMap 4 | ตัวเชื่อมต่อกับ OpenWeatherMap 5 | OpenWeatherMap 6 | เพิ่มข้อมูลสภาพอากาศจาก OpenWeatherMap 7 | ระบุ API key ของ OpenWeatherMap เพื่อใช้ปลั๊กอินนี้ 8 | API key 9 | บันทึก 10 | อัพเดต API key เรียบร้อยแล้ว 11 | API key ที่ระบุไม่ถูกต้อง 12 | เกิดข้อผิดพลาด กรุณาตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณ 13 | ขั้นตอนที่ %1$d 14 | ลงทะเบียนกับ %1$s เพื่อรับ API key: 15 | วาง API key ด้านล่าง: 16 | หมายเหตุ: API key ของคุณจะถูกเปิดใช้งานภายในเวลาไม่เกิน 2 ชั่วโมง 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-tr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OpenWeatherMap için Kvaesitso Eklentisi 4 | OpenWeatherMap entegrasyonu 5 | OpenWeatherMap 6 | OpenWeatherMap\'i hava durumu sağlayıcısı olarak ekler 7 | Bu eklentiyi kullanabilmek için bir OpenWeatherMap API anahtarı ekleyin. 8 | API anahtarı 9 | Kaydet 10 | Girdiğiniz API anahtarı geçersiz. 11 | Bir hata oluştu, lütfen internet bağlantınızı kontrol edin. 12 | API anahtarını aşağıya yapıştırın: 13 | API anahtarı başarıyla güncellendi. 14 | Bir API anahtarı almak için %1$s üzerinden kayıt olun: 15 | Adım %1$d 16 | API anahtarınızın aktifleştirilmesi 2 saate kadar sürebilir. 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-uk/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OpenWeatherMap 4 | API-ключ 5 | -------------------------------------------------------------------------------- /app/src/main/res/values-vi/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh-rCN/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 步骤 %1$d 4 | 在 %1$s 注册来获取API密钥: 5 | 在下方粘贴API密钥: 6 | Kvaesitso OpenWeatherMap插件 7 | OpenWeatherMap 集成 8 | OpenWeatherMap开源天气 9 | 添加为OpenWeatherMap作为天气提供商 10 | 请添加一个OpenWeatherMap API密钥来使用此插件。 11 | API密钥 12 | 保存 13 | API密钥更新成功. 14 | 提供的API密钥无效. 15 | 发生了一个错误,请检查你的网络连接。 16 | 请注意最多可能需要2小时来激活你的密钥。 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh-rTW/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 步驟 %1$d 4 | 在 %1$s 註冊以獲得 API 金鑰: 5 | 在下方貼上 API 金鑰: 6 | 適用於 OpenWeatherMap 的 Kvaesitso 外掛程式 7 | OpenWeatherMap 整合 8 | OpenWeatherMap 9 | 新增 OpenWeatherMap 作為天氣服務商 10 | 請注意,啟用您的金鑰可能最多需要 2 個小時。 11 | 新增一個 OpenWeatherMap API 金鑰來使用此外掛程式。 12 | API 金鑰 13 | 儲存 14 | API 金鑰已成功更新。 15 | 提供的 API 金鑰無效。 16 | 發生錯誤,請檢查您的網路連接。 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Kvaesitso Plugin for OpenWeatherMap 4 | OpenWeatherMap integration 5 | OpenWeatherMap 6 | Adds OpenWeatherMap as a weather provider 7 | Add an OpenWeatherMap API key to use this plugin. 8 | API key 9 | Save 10 | API key has been updated successfully. 11 | The provided API key is not valid. 12 | An error occurred, please check your network connection. 13 | Step %1$d 14 | Register at %1$s to get an API key: 15 | Paste the API key below: 16 | Please note that it can take up to 2 hours for your key to be activated. 17 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | alias(libs.plugins.androidApplication) apply false 4 | alias(libs.plugins.kotlin.android) apply false 5 | alias(libs.plugins.kotlin.serialization) apply false 6 | } -------------------------------------------------------------------------------- /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. For more details, visit 12 | # https://developer.android.com/r/tools/gradle-multi-project-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 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | agp = "8.4.2" 3 | kotlin = "2.0.0" 4 | 5 | kotlinSerialization = "1.6.2" 6 | 7 | coreKtx = "1.13.1" 8 | appcompat = "1.7.0" 9 | activity = "1.9.0" 10 | lifecycle = "2.8.2" 11 | datastore = "1.1.1" 12 | 13 | compose = "1.6.8" 14 | composeMaterial3 = "1.3.0-beta04" 15 | 16 | launcherSdk = "2.0.1" 17 | 18 | ktor = "2.3.7" 19 | 20 | 21 | [libraries] 22 | launchersdk = { group = "de.mm20.launcher2", name = "plugin-sdk", version.ref = "launcherSdk" } 23 | 24 | kotlin-serialization = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinSerialization" } 25 | 26 | androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } 27 | androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } 28 | androidx-activitycompose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity" } 29 | androidx-datastore = { group = "androidx.datastore", name = "datastore", version.ref = "datastore" } 30 | androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "composeMaterial3" } 31 | androidx-compose-materialicons = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "compose" } 32 | androidx-lifecycle-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" } 33 | ktor-client-android = { group = "io.ktor", name = "ktor-client-android", version.ref = "ktor" } 34 | ktor-client-okhttp = { group = "io.ktor", name = "ktor-client-okhttp", version.ref = "ktor" } 35 | ktor-client-contentnegotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktor" } 36 | ktor-serialization-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" } 37 | 38 | [plugins] 39 | androidApplication = { id = "com.android.application", version.ref = "agp" } 40 | kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } 41 | kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } 42 | kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } 43 | 44 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kvaesitso/Plugin-OpenWeatherMap/7bbf1d4ceca02013d8f1fdca15abbe7abc87cf85/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Dec 10 00:35:57 CET 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or 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 UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Kvaesitso Plugin for OpenWeatherMap 2 | 3 | This plugin adds OpenWeatherMap as a weather provider to Kvaesitso. 4 | 5 | ## License 6 | 7 | This plugin is licensed under the Apache License 2.0. 8 | 9 | ``` 10 | Copyright 2023 MM2-0 and the Kvaesitso contributors 11 | 12 | Licensed under the Apache License, Version 2.0 (the "License"); 13 | you may not use this file except in compliance with the License. 14 | You may obtain a copy of the License at 15 | 16 | http://www.apache.org/licenses/LICENSE-2.0 17 | 18 | Unless required by applicable law or agreed to in writing, software 19 | distributed under the License is distributed on an "AS IS" BASIS, 20 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | See the License for the specific language governing permissions and 22 | limitations under the License. 23 | ``` 24 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google { 4 | content { 5 | includeGroupByRegex("com\\.android.*") 6 | includeGroupByRegex("com\\.google.*") 7 | includeGroupByRegex("androidx.*") 8 | } 9 | } 10 | mavenCentral() 11 | gradlePluginPortal() 12 | } 13 | } 14 | dependencyResolutionManagement { 15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 16 | repositories { 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | rootProject.name = "Kvaesitso OpenWeatherMap Plugin" 23 | include(":app") 24 | --------------------------------------------------------------------------------