├── .gitignore ├── .travis.yml ├── CHANGELOG ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── org │ │ └── asdtm │ │ └── goodweather │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── fonts │ │ │ ├── Roboto-Light.ttf │ │ │ ├── Roboto-Thin.ttf │ │ │ └── weathericons-regular-webfont.ttf │ │ └── licenses │ │ │ ├── Gson │ │ │ ├── JeepTrail │ │ │ ├── MPAndroidChart │ │ │ ├── OpenWeatherMap │ │ │ ├── Roboto │ │ │ └── WeatherIcons │ ├── java │ │ └── org │ │ │ └── asdtm │ │ │ └── goodweather │ │ │ ├── AppCompatPreferenceActivity.java │ │ │ ├── BaseActivity.java │ │ │ ├── BitcoinDonationDialog.java │ │ │ ├── ConnectionDetector.java │ │ │ ├── GoodWeatherApp.java │ │ │ ├── GraphsActivity.java │ │ │ ├── LicenseActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── SearchActivity.java │ │ │ ├── SettingsActivity.java │ │ │ ├── WeatherForecastActivity.java │ │ │ ├── WeatherJSONParser.java │ │ │ ├── WeatherRequest.java │ │ │ ├── adapter │ │ │ ├── WeatherForecastAdapter.java │ │ │ └── WeatherForecastViewHolder.java │ │ │ ├── fragment │ │ │ └── ForecastBottomSheetDialogFragment.java │ │ │ ├── model │ │ │ ├── CitySearch.java │ │ │ ├── Weather.java │ │ │ └── WeatherForecast.java │ │ │ ├── receiver │ │ │ └── StartupReceiver.java │ │ │ ├── service │ │ │ ├── CurrentWeatherService.java │ │ │ ├── LocationUpdateService.java │ │ │ └── NotificationService.java │ │ │ ├── utils │ │ │ ├── ApiKeys.java │ │ │ ├── AppPreference.java │ │ │ ├── AppWidgetProviderAlarm.java │ │ │ ├── CityParser.java │ │ │ ├── Constants.java │ │ │ ├── CustomValueFormatter.java │ │ │ ├── LanguageUtil.java │ │ │ ├── PermissionUtil.java │ │ │ ├── PreferenceUtil.java │ │ │ ├── Utils.java │ │ │ ├── XAxisValueFormatter.java │ │ │ └── YAxisValueFormatter.java │ │ │ └── widget │ │ │ ├── LessWidgetProvider.java │ │ │ ├── LessWidgetService.java │ │ │ ├── MoreWidgetProvider.java │ │ │ └── MoreWidgetService.java │ └── res │ │ ├── drawable-hdpi │ │ ├── background_nav_header.png │ │ ├── ic_barometer_light_32dp.png │ │ ├── ic_current_weather_black_24dp.png │ │ ├── ic_daily_forecast_dark_24dp.png │ │ ├── ic_feedback.png │ │ ├── ic_graphs_black_32dp.png │ │ ├── ic_humidity_light_32dp.png │ │ ├── ic_menu_settings.png │ │ ├── ic_my_location_white_24dp.png │ │ ├── ic_rain_light_32dp.png │ │ ├── ic_refresh_white_18dp.png │ │ ├── ic_refresh_white_24dp.png │ │ ├── ic_search_white_24dp.png │ │ ├── ic_snow_light_32dp.png │ │ ├── ic_thumb_up_black_24dp.png │ │ ├── ic_wind_light_32dp.png │ │ └── small_icon.png │ │ ├── drawable-mdpi │ │ ├── background_nav_header.png │ │ ├── ic_barometer_light_32dp.png │ │ ├── ic_current_weather_black_24dp.png │ │ ├── ic_daily_forecast_dark_24dp.png │ │ ├── ic_feedback.png │ │ ├── ic_graphs_black_32dp.png │ │ ├── ic_humidity_light_32dp.png │ │ ├── ic_menu_settings.png │ │ ├── ic_my_location_white_24dp.png │ │ ├── ic_rain_light_32dp.png │ │ ├── ic_refresh_white_18dp.png │ │ ├── ic_refresh_white_24dp.png │ │ ├── ic_search_white_24dp.png │ │ ├── ic_snow_light_32dp.png │ │ ├── ic_thumb_up_black_24dp.png │ │ ├── ic_wind_light_32dp.png │ │ └── small_icon.png │ │ ├── drawable-nodpi │ │ ├── background_appbar.png │ │ ├── preview_less_widget.png │ │ └── preview_more_widget.png │ │ ├── drawable-xhdpi │ │ ├── background_nav_header.png │ │ ├── bitcoin_address_qr.png │ │ ├── ic_barometer_light_32dp.png │ │ ├── ic_current_weather_black_24dp.png │ │ ├── ic_daily_forecast_dark_24dp.png │ │ ├── ic_feedback.png │ │ ├── ic_graphs_black_32dp.png │ │ ├── ic_humidity_light_32dp.png │ │ ├── ic_menu_settings.png │ │ ├── ic_my_location_white_24dp.png │ │ ├── ic_rain_light_32dp.png │ │ ├── ic_refresh_white_18dp.png │ │ ├── ic_refresh_white_24dp.png │ │ ├── ic_search_white_24dp.png │ │ ├── ic_snow_light_32dp.png │ │ ├── ic_thumb_up_black_24dp.png │ │ ├── ic_wind_light_32dp.png │ │ └── small_icon.png │ │ ├── drawable-xxhdpi │ │ ├── background_nav_header.png │ │ ├── ic_barometer_light_32dp.png │ │ ├── ic_current_weather_black_24dp.png │ │ ├── ic_daily_forecast_dark_24dp.png │ │ ├── ic_feedback.png │ │ ├── ic_graphs_black_32dp.png │ │ ├── ic_humidity_light_32dp.png │ │ ├── ic_menu_settings.png │ │ ├── ic_my_location_white_24dp.png │ │ ├── ic_rain_light_32dp.png │ │ ├── ic_refresh_white_18dp.png │ │ ├── ic_refresh_white_24dp.png │ │ ├── ic_search_white_24dp.png │ │ ├── ic_snow_light_32dp.png │ │ ├── ic_thumb_up_black_24dp.png │ │ ├── ic_wind_light_32dp.png │ │ └── small_icon.png │ │ ├── drawable-xxxhdpi │ │ ├── background_nav_header.png │ │ ├── ic_current_weather_black_24dp.png │ │ ├── ic_daily_forecast_dark_24dp.png │ │ ├── ic_feedback.png │ │ ├── ic_menu_settings.png │ │ ├── ic_my_location_white_24dp.png │ │ ├── ic_rain_light_32dp.png │ │ ├── ic_refresh_white_18dp.png │ │ ├── ic_refresh_white_24dp.png │ │ ├── ic_search_white_24dp.png │ │ ├── ic_snow_light_32dp.png │ │ ├── ic_thumb_up_black_24dp.png │ │ └── small_icon.png │ │ ├── drawable │ │ ├── ic_android_black_24dp.xml │ │ └── ic_share.xml │ │ ├── layout │ │ ├── activity_graphs.xml │ │ ├── activity_license.xml │ │ ├── activity_main.xml │ │ ├── activity_search.xml │ │ ├── activity_settings.xml │ │ ├── activity_weather_forecast.xml │ │ ├── city_item.xml │ │ ├── dialog_donation_bitcoin.xml │ │ ├── forecast_item.xml │ │ ├── fragment_forecast_bottom_sheet.xml │ │ ├── main_content.xml │ │ ├── nav_header.xml │ │ ├── widget_less_3x1.xml │ │ └── widget_more_3x3.xml │ │ ├── menu │ │ ├── activity_main_menu.xml │ │ ├── draw_menu.xml │ │ ├── menu_graphs.xml │ │ ├── search.xml │ │ └── weather_forecast_menu.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-ar │ │ └── strings.xml │ │ ├── values-be-rBY │ │ └── strings.xml │ │ ├── values-be │ │ └── strings.xml │ │ ├── values-ca │ │ └── strings.xml │ │ ├── values-cs │ │ └── strings.xml │ │ ├── values-cy │ │ └── strings.xml │ │ ├── values-de │ │ └── strings.xml │ │ ├── values-el │ │ └── strings.xml │ │ ├── values-es │ │ └── strings.xml │ │ ├── values-et │ │ └── strings.xml │ │ ├── values-eu │ │ └── strings.xml │ │ ├── values-fi │ │ └── strings.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-hi │ │ └── strings.xml │ │ ├── values-hr │ │ └── strings.xml │ │ ├── values-hu │ │ └── strings.xml │ │ ├── values-id │ │ └── strings.xml │ │ ├── values-it │ │ └── strings.xml │ │ ├── values-ja │ │ └── strings.xml │ │ ├── values-kab │ │ └── strings.xml │ │ ├── values-nb-rNO │ │ └── strings.xml │ │ ├── values-nl │ │ └── strings.xml │ │ ├── values-ny │ │ └── strings.xml │ │ ├── values-pa │ │ └── strings.xml │ │ ├── values-pl │ │ └── strings.xml │ │ ├── values-ps │ │ └── strings.xml │ │ ├── values-pt-rBR │ │ └── strings.xml │ │ ├── values-pt │ │ └── strings.xml │ │ ├── values-ro │ │ └── strings.xml │ │ ├── values-ru │ │ └── strings.xml │ │ ├── values-si │ │ └── strings.xml │ │ ├── values-sk │ │ └── strings.xml │ │ ├── values-sl │ │ └── strings.xml │ │ ├── values-sw │ │ └── strings.xml │ │ ├── values-ta │ │ └── strings.xml │ │ ├── values-tr │ │ └── strings.xml │ │ ├── values-uk │ │ └── strings.xml │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-vi │ │ └── strings.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ ├── values-zh-rTW │ │ └── strings.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── licenses.xml │ │ ├── nontranslatable.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ ├── pref_about.xml │ │ ├── pref_general.xml │ │ ├── pref_headers.xml │ │ ├── pref_widget.xml │ │ ├── searchable.xml │ │ ├── widget_provider_less.xml │ │ └── widget_provider_more.xml │ └── test │ └── java │ └── org │ └── asdtm │ └── goodweather │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle/ 3 | .DS_Store 4 | build/ 5 | captures/ 6 | out/ 7 | .idea/ 8 | *.apk 9 | release.properties 10 | gradle.properties 11 | local.properties 12 | /.nb-gradle/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | os: linux 4 | sudo: required 5 | 6 | android: 7 | components: 8 | - tools 9 | - tools 10 | - platfom-tools 11 | - build-tools-25.0.2 12 | - android-25 13 | - extra-android-m2repository 14 | licenses: 15 | - 'android-sdk-preview-license-.+' 16 | - 'android-sdk-license-.+' 17 | 18 | # Accept ConstraintLayout license 19 | before_install: 20 | - mkdir "$ANDROID_HOME/licenses" || true 21 | - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" 22 | - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license" 23 | - chmod +x gradlew 24 | - ./gradlew dependencies || true 25 | 26 | script: ./gradlew build --stacktrace -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | v4.4 2 | 3 | - Added option to change app theme 4 | - Added dark theme 5 | 6 | v4.3 7 | 8 | - Added option to change language 9 | - Added transparent widget theme 10 | - Added option to change widget theme 11 | - Added translations: Czech, French 12 | - Updated translations 13 | 14 | v4.2.1 15 | 16 | - Fix bug when updated forecast 17 | 18 | v4.2 19 | 20 | - Fixed small bugs 21 | 22 | v4.1 23 | 24 | - Support runtime permissions for Android 6.0 25 | - Added translations: Basque, Belarusian, Japanese, Spanish 26 | - Minor bugfixes 27 | 28 | v4.0 29 | 30 | - Improved main screen 31 | - Added forecast 32 | - Added graphs 33 | - Fixed small bugs 34 | 35 | v3.0 36 | 37 | - Added widgets 38 | - Produced by the redesign of the main screen 39 | - Fix bug: in some cases the app crashed when it updates the weather 40 | - Fix bug: the app is not closed by pressing the back button 41 | 42 | v2.1 43 | 44 | - Support for Android 3.0 (Honeycomb) has ended 45 | 46 | v2.0 47 | 48 | - Add notifications 49 | - Update Settings screen 50 | - Update translations 51 | 52 | v1.3 53 | 54 | - Change the settings screen 55 | - Added search location screen 56 | - Update translations 57 | 58 | v1.2 59 | 60 | - Add Polish and German translations -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/qqq3/good-weather.svg?branch=master)](https://travis-ci.org/qqq3/good-weather) 2 | [![Release](https://img.shields.io/github/release/qqq3/good-weather.svg)](https://github.com/qqq3/good-weather/releases) 3 | [![License](https://img.shields.io/badge/license-GNU_GPLv3-orange.svg)](https://raw.githubusercontent.com/qqq3/good-weather/HEAD/LICENSE) 4 | 5 | # Good Weather 6 | Open source weather app for Android. 7 | 8 | [Get it on F-Droid](https://f-droid.org/repository/browse/?fdid=org.asdtm.goodweather) 9 | [Get it on Google Play](https://play.google.com/store/apps/details?id=org.asdtm.goodweather) 10 | 11 | ## Features 12 | * Different languages: Basque, Belarusian, Czech, English, French, German, Japanese, Spanish, Polish, Russian 13 | * Current weather 14 | * 7 day forecast 15 | * Many locations 16 | * Notifications 17 | * Support different measuring units 18 | * Ad-free 19 | 20 | ## Screenshots 21 | [Good Weather preview](http://i.imgur.com/FntbN8S.png) 22 | [Good Weather preview](http://i.imgur.com/WVFXEoo.png) 23 | [Good Weather preview](http://i.imgur.com/pDjCCqo.png) 24 | [Good Weather preview](http://i.imgur.com/lQKKBYY.png) 25 | 26 | ## To-Do 27 | - [ ] Add weather map 28 | - [ ] Improve accessibility for people with disabilities 29 | 30 | # Translations 31 | [https://hosted.weblate.org/projects/good-weather/strings/](https://hosted.weblate.org/projects/good-weather/strings/) 32 | 33 | The strings are translated using [Weblate](https://weblate.org/en/). Follow 34 | [these instructions](https://hosted.weblate.org/engage/good-weather/) if you would like to contribute 35 | [here](https://hosted.weblate.org/projects/good-weather/strings/). 36 | 37 | ## Donations 38 | If you would like to help, you can donate Bitcoin on ```1FV8m1MKqZ9ZKB8YNwpsjsuubHTznJSiT8``` address. 39 | Thanks! 40 | 41 | ## List of contributors 42 | [berian](https://github.com/beriain), [mahula](https://github.com/mahula), [naofum](https://github.com/naofum), 43 | [thuryn](https://github.com/thuryn), [monolifed](https://github.com/monolifed), [marcoM32](https://github.com/marcoM32), 44 | [Zagur](https://github.com/Zagur) 45 | 46 | ## License 47 | ``` 48 | Good Weather. App displays weather information. 49 | Copyright (C) 2015-2017 Eugene Kislyakov 50 | 51 | This program is free software: you can redistribute it and/or modify 52 | it under the terms of the GNU General Public License as published by 53 | the Free Software Foundation, either version 3 of the License, or 54 | (at your option) any later version. 55 | 56 | This program is distributed in the hope that it will be useful, 57 | but WITHOUT ANY WARRANTY; without even the implied warranty of 58 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 59 | GNU General Public License for more details. 60 | 61 | You should have received a copy of the GNU General Public License 62 | along with this program. If not, see . 63 | ``` 64 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion '25.0.2' 6 | defaultConfig { 7 | applicationId "org.asdtm.goodweather" 8 | minSdkVersion 14 9 | targetSdkVersion 25 10 | versionCode 13 11 | versionName "4.4" 12 | vectorDrawables.useSupportLibrary = true 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled true 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | applicationVariants.all { variant -> 19 | variant.outputs.each { output -> 20 | output.outputFile = new File( 21 | output.outputFile.parent, 22 | output.outputFile.name.replace("app-release.apk", "GoodWeather-release-${variant.versionName}.apk")) 23 | } 24 | } 25 | } 26 | debug { 27 | minifyEnabled false 28 | applicationVariants.all { variant -> 29 | variant.outputs.each { output -> 30 | output.outputFile = new File( 31 | output.outputFile.parent, 32 | output.outputFile.name.replace("app-debug.apk", "GoodWeather-debug-${variant.versionName}.apk")) 33 | } 34 | } 35 | } 36 | } 37 | lintOptions { 38 | disable 'GoogleAppIndexingWarning' 39 | abortOnError false 40 | } 41 | } 42 | 43 | dependencies { 44 | compile fileTree(include: ['*.jar'], dir: 'libs') 45 | compile 'com.android.support:appcompat-v7:25.3.1' 46 | compile 'com.android.support:design:25.3.1' 47 | compile 'com.android.support:cardview-v7:25.3.1' 48 | compile 'com.android.support:support-v4:25.3.1' 49 | testCompile 'junit:junit:4.12' 50 | compile 'com.google.code.gson:gson:2.6.2' 51 | compile 'com.github.PhilJay:MPAndroidChart:v3.0.0-beta1' 52 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 53 | } 54 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/qqq3/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | -keep class org.asdtm.goodweather.model.WeatherForecast { *; } -------------------------------------------------------------------------------- /app/src/androidTest/java/org/asdtm/goodweather/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase 10 | { 11 | public ApplicationTest() 12 | { 13 | super(Application.class); 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 39 | 40 | 45 | 46 | 47 | 48 | 49 | 52 | 55 | 56 | 60 | 63 | 64 | 68 | 71 | 72 | 76 | 77 | 78 | 79 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/assets/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/assets/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/weathericons-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/assets/fonts/weathericons-regular-webfont.ttf -------------------------------------------------------------------------------- /app/src/main/assets/licenses/Gson: -------------------------------------------------------------------------------- 1 | Copyright 2008 Google Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /app/src/main/assets/licenses/JeepTrail: -------------------------------------------------------------------------------- 1 | JEEP TRAIL IN LAKE CITY, by DEBRA DANIEL 2 | 3 | Under Creative Commons Attribution-ShareAlike 2.0 Generic (CC BY-SA 2.0) license: https://creativecommons.org/licenses/by-sa/2.0/legalcode 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/assets/licenses/MPAndroidChart: -------------------------------------------------------------------------------- 1 | Copyright 2016 Philipp Jahoda 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /app/src/main/assets/licenses/WeatherIcons: -------------------------------------------------------------------------------- 1 | Copyright (c) , (), 2 | with Reserved Font Name . 3 | Copyright (c) , (), 4 | with Reserved Font Name . 5 | Copyright (c) , (). 6 | 7 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 8 | This license is copied below, and is also available with a FAQ at: 9 | http://scripts.sil.org/OFL 10 | 11 | 12 | ----------------------------------------------------------- 13 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 14 | ----------------------------------------------------------- 15 | 16 | PREAMBLE 17 | The goals of the Open Font License (OFL) are to stimulate worldwide 18 | development of collaborative font projects, to support the font creation 19 | efforts of academic and linguistic communities, and to provide a free and 20 | open framework in which fonts may be shared and improved in partnership 21 | with others. 22 | 23 | The OFL allows the licensed fonts to be used, studied, modified and 24 | redistributed freely as long as they are not sold by themselves. The 25 | fonts, including any derivative works, can be bundled, embedded, 26 | redistributed and/or sold with any software provided that any reserved 27 | names are not used by derivative works. The fonts and derivatives, 28 | however, cannot be released under any other type of license. The 29 | requirement for fonts to remain under this license does not apply 30 | to any document created using the fonts or their derivatives. 31 | 32 | DEFINITIONS 33 | "Font Software" refers to the set of files released by the Copyright 34 | Holder(s) under this license and clearly marked as such. This may 35 | include source files, build scripts and documentation. 36 | 37 | "Reserved Font Name" refers to any names specified as such after the 38 | copyright statement(s). 39 | 40 | "Original Version" refers to the collection of Font Software components as 41 | distributed by the Copyright Holder(s). 42 | 43 | "Modified Version" refers to any derivative made by adding to, deleting, 44 | or substituting -- in part or in whole -- any of the components of the 45 | Original Version, by changing formats or by porting the Font Software to a 46 | new environment. 47 | 48 | "Author" refers to any designer, engineer, programmer, technical 49 | writer or other person who contributed to the Font Software. 50 | 51 | PERMISSION & CONDITIONS 52 | Permission is hereby granted, free of charge, to any person obtaining 53 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 54 | redistribute, and sell modified and unmodified copies of the Font 55 | Software, subject to the following conditions: 56 | 57 | 1) Neither the Font Software nor any of its individual components, 58 | in Original or Modified Versions, may be sold by itself. 59 | 60 | 2) Original or Modified Versions of the Font Software may be bundled, 61 | redistributed and/or sold with any software, provided that each copy 62 | contains the above copyright notice and this license. These can be 63 | included either as stand-alone text files, human-readable headers or 64 | in the appropriate machine-readable metadata fields within text or 65 | binary files as long as those fields can be easily viewed by the user. 66 | 67 | 3) No Modified Version of the Font Software may use the Reserved Font 68 | Name(s) unless explicit written permission is granted by the corresponding 69 | Copyright Holder. This restriction only applies to the primary font name as 70 | presented to the users. 71 | 72 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 73 | Software shall not be used to promote, endorse or advertise any 74 | Modified Version, except to acknowledge the contribution(s) of the 75 | Copyright Holder(s) and the Author(s) or with their explicit written 76 | permission. 77 | 78 | 5) The Font Software, modified or unmodified, in part or in whole, 79 | must be distributed entirely under this license, and must not be 80 | distributed under any other license. The requirement for fonts to 81 | remain under this license does not apply to any document created 82 | using the Font Software. 83 | 84 | TERMINATION 85 | This license becomes null and void if any of the above conditions are 86 | not met. 87 | 88 | DISCLAIMER 89 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 90 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 91 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 92 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 93 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 94 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 95 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 96 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 97 | OTHER DEALINGS IN THE FONT SOFTWARE. 98 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/AppCompatPreferenceActivity.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather; 2 | 3 | import android.content.res.Configuration; 4 | import android.os.Bundle; 5 | import android.preference.PreferenceActivity; 6 | import android.support.annotation.LayoutRes; 7 | import android.support.annotation.Nullable; 8 | import android.support.v7.app.ActionBar; 9 | import android.support.v7.app.AppCompatDelegate; 10 | import android.support.v7.widget.Toolbar; 11 | import android.view.MenuInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | 15 | /** 16 | * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls 17 | * to be used with AppCompat. 18 | */ 19 | public abstract class AppCompatPreferenceActivity extends PreferenceActivity { 20 | 21 | private AppCompatDelegate mDelegate; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | getDelegate().installViewFactory(); 26 | getDelegate().onCreate(savedInstanceState); 27 | super.onCreate(savedInstanceState); 28 | } 29 | 30 | @Override 31 | protected void onPostCreate(Bundle savedInstanceState) { 32 | super.onPostCreate(savedInstanceState); 33 | getDelegate().onPostCreate(savedInstanceState); 34 | } 35 | 36 | public ActionBar getSupportActionBar() { 37 | return getDelegate().getSupportActionBar(); 38 | } 39 | 40 | public void setSupportActionBar(@Nullable Toolbar toolbar) { 41 | getDelegate().setSupportActionBar(toolbar); 42 | } 43 | 44 | @Override 45 | public MenuInflater getMenuInflater() { 46 | return getDelegate().getMenuInflater(); 47 | } 48 | 49 | @Override 50 | public void setContentView(@LayoutRes int layoutResID) { 51 | getDelegate().setContentView(layoutResID); 52 | } 53 | 54 | @Override 55 | public void setContentView(View view) { 56 | getDelegate().setContentView(view); 57 | } 58 | 59 | @Override 60 | public void setContentView(View view, ViewGroup.LayoutParams params) { 61 | getDelegate().setContentView(view, params); 62 | } 63 | 64 | @Override 65 | public void addContentView(View view, ViewGroup.LayoutParams params) { 66 | getDelegate().addContentView(view, params); 67 | } 68 | 69 | @Override 70 | protected void onPostResume() { 71 | super.onPostResume(); 72 | getDelegate().onPostResume(); 73 | } 74 | 75 | @Override 76 | protected void onTitleChanged(CharSequence title, int color) { 77 | super.onTitleChanged(title, color); 78 | getDelegate().setTitle(title); 79 | } 80 | 81 | @Override 82 | public void onConfigurationChanged(Configuration newConfig) { 83 | super.onConfigurationChanged(newConfig); 84 | getDelegate().onConfigurationChanged(newConfig); 85 | } 86 | 87 | @Override 88 | protected void onStop() { 89 | super.onStop(); 90 | getDelegate().onStop(); 91 | } 92 | 93 | @Override 94 | protected void onDestroy() { 95 | super.onDestroy(); 96 | getDelegate().onDestroy(); 97 | } 98 | 99 | public void invalidateOptionsMenu() { 100 | getDelegate().invalidateOptionsMenu(); 101 | } 102 | 103 | private AppCompatDelegate getDelegate() { 104 | if (mDelegate == null) { 105 | mDelegate = AppCompatDelegate.create(this, null); 106 | } 107 | return mDelegate; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/BitcoinDonationDialog.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather; 2 | 3 | import android.app.Dialog; 4 | import android.app.DialogFragment; 5 | import android.content.ActivityNotFoundException; 6 | import android.content.DialogInterface; 7 | import android.content.Intent; 8 | import android.net.Uri; 9 | import android.os.Bundle; 10 | import android.support.v7.app.AlertDialog; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.widget.Button; 14 | import android.widget.TextView; 15 | import android.widget.Toast; 16 | 17 | import org.asdtm.goodweather.utils.Constants; 18 | import org.asdtm.goodweather.utils.Utils; 19 | 20 | public class BitcoinDonationDialog extends DialogFragment { 21 | 22 | public static BitcoinDonationDialog newInstance() { 23 | return new BitcoinDonationDialog(); 24 | } 25 | 26 | @Override 27 | public Dialog onCreateDialog(Bundle savedInstanceState) { 28 | LayoutInflater inflater = getActivity().getLayoutInflater(); 29 | View v = inflater.inflate(R.layout.dialog_donation_bitcoin, null); 30 | TextView bitcoinAddress = (TextView) v.findViewById(R.id.bitcoin_address); 31 | Button copyBitcoinAddress = (Button) v.findViewById(R.id.copy_bitcoin_address_button); 32 | Button openBitcoinApp = (Button) v.findViewById(R.id.open_bitcoin_app_button); 33 | 34 | bitcoinAddress.setText(Constants.BITCOIN_ADDRESS); 35 | copyBitcoinAddress.setOnClickListener(new View.OnClickListener() { 36 | @Override 37 | public void onClick(View view) { 38 | Utils.copyToClipboard(getActivity(), Constants.BITCOIN_ADDRESS); 39 | Toast.makeText(getActivity(), 40 | R.string.donation_bitcoin_copy_message, 41 | Toast.LENGTH_SHORT).show(); 42 | } 43 | }); 44 | openBitcoinApp.setOnClickListener(new View.OnClickListener() { 45 | @Override 46 | public void onClick(View view) { 47 | try { 48 | Intent intent = new Intent(Intent.ACTION_VIEW); 49 | intent.setData(Uri.parse("bitcoin:" + Constants.BITCOIN_ADDRESS)); 50 | startActivity(intent); 51 | } catch (ActivityNotFoundException e) { 52 | Toast.makeText(getActivity(), 53 | R.string.donation_bitcoin_copy_message, 54 | Toast.LENGTH_SHORT).show(); 55 | } 56 | } 57 | }); 58 | 59 | AlertDialog.Builder alertDialog = new AlertDialog.Builder(getActivity()); 60 | alertDialog.setTitle("Bitcoin"); 61 | alertDialog.setView(v); 62 | alertDialog.setNegativeButton(android.R.string.cancel, 63 | new DialogInterface.OnClickListener() { 64 | @Override 65 | public void onClick(DialogInterface dialogInterface, 66 | int i) { 67 | dialogInterface.cancel(); 68 | } 69 | }); 70 | return alertDialog.create(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/ConnectionDetector.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo; 6 | 7 | public class ConnectionDetector { 8 | 9 | private Context mContext; 10 | 11 | public ConnectionDetector(Context context) { 12 | mContext = context; 13 | } 14 | 15 | public boolean isNetworkAvailableAndConnected() { 16 | ConnectivityManager connectivityManager 17 | = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 18 | 19 | NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); 20 | 21 | return networkInfo != null && networkInfo.isConnected(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/GoodWeatherApp.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.content.res.Configuration; 6 | 7 | import org.asdtm.goodweather.utils.LanguageUtil; 8 | import org.asdtm.goodweather.utils.PreferenceUtil; 9 | import org.asdtm.goodweather.utils.PreferenceUtil.Theme; 10 | 11 | public class GoodWeatherApp extends Application { 12 | 13 | private static final String TAG = "GoodWeatherApp"; 14 | 15 | private static Theme sTheme = Theme.light; 16 | 17 | @Override 18 | public void onCreate() { 19 | super.onCreate(); 20 | LanguageUtil.setLanguage(this, PreferenceUtil.getLanguage(this)); 21 | 22 | sTheme = PreferenceUtil.getTheme(this); 23 | } 24 | 25 | @Override 26 | public void onConfigurationChanged(Configuration newConfig) { 27 | super.onConfigurationChanged(newConfig); 28 | LanguageUtil.setLanguage(this, PreferenceUtil.getLanguage(this)); 29 | } 30 | 31 | public void reloadTheme() { 32 | sTheme = PreferenceUtil.getTheme(this); 33 | } 34 | 35 | public void applyTheme(Activity activity) { 36 | activity.setTheme(getThemeResId()); 37 | } 38 | 39 | public static int getThemeResId() { 40 | switch (sTheme) { 41 | case light: 42 | return R.style.AppThemeLight; 43 | case dark: 44 | return R.style.AppThemeDark; 45 | default: 46 | return R.style.AppThemeLight; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/LicenseActivity.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather; 2 | 3 | import android.os.Build; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.ActionBar; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.Toolbar; 9 | import android.text.Html; 10 | import android.view.MenuItem; 11 | import android.widget.TextView; 12 | 13 | import java.io.BufferedReader; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.io.InputStreamReader; 17 | 18 | public class LicenseActivity extends AppCompatActivity { 19 | 20 | @Override 21 | protected void onCreate(@Nullable Bundle savedInstanceState) { 22 | ((GoodWeatherApp) getApplication()).applyTheme(this); 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_license); 25 | setupActionBar(); 26 | 27 | final String path = getIntent().getData().getPath(); 28 | 29 | setTitle(getString(R.string.title_activity_license, path.substring(24))); 30 | 31 | try { 32 | TextView licenseTextView = (TextView) findViewById(R.id.license_license_text); 33 | final String licenseText = readLicense(getAssets().open(path.substring(15))); 34 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 35 | licenseTextView.setText(Html.fromHtml(licenseText.replace("\n\n", "

"), Html.FROM_HTML_MODE_LEGACY)); 36 | } else { 37 | licenseTextView.setText(Html.fromHtml(licenseText.replace("\n\n", "

"))); 38 | } 39 | } catch (IOException e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | 44 | private void setupActionBar() { 45 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 46 | setSupportActionBar(toolbar); 47 | ActionBar actionBar = getSupportActionBar(); 48 | if (actionBar != null) { 49 | actionBar.setDisplayHomeAsUpEnabled(true); 50 | } 51 | } 52 | 53 | @Override 54 | public boolean onOptionsItemSelected(MenuItem item) { 55 | switch (item.getItemId()) { 56 | case android.R.id.home: 57 | this.onBackPressed(); 58 | return true; 59 | } 60 | return super.onOptionsItemSelected(item); 61 | } 62 | 63 | private String readLicense(final InputStream inputStream) throws IOException { 64 | InputStreamReader inputStreamReader = new InputStreamReader(inputStream); 65 | BufferedReader reader = new BufferedReader(inputStreamReader); 66 | StringBuilder builder = new StringBuilder(); 67 | try { 68 | String stringRead; 69 | while ((stringRead = reader.readLine()) != null) { 70 | builder.append(stringRead).append("\n"); 71 | } 72 | } catch (IOException e) { 73 | e.printStackTrace(); 74 | } finally { 75 | reader.close(); 76 | } 77 | return builder.toString(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/WeatherJSONParser.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather; 2 | 3 | import org.asdtm.goodweather.model.CitySearch; 4 | import org.asdtm.goodweather.model.Weather; 5 | import org.json.JSONArray; 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | public class WeatherJSONParser { 10 | 11 | private static final String TAG = "WeatherJSONParser"; 12 | 13 | public static Weather getWeather(String data) throws JSONException { 14 | Weather weather = new Weather(); 15 | CitySearch location = new CitySearch(); 16 | 17 | JSONObject weatherData = new JSONObject(data); 18 | 19 | JSONArray weatherArray = weatherData.getJSONArray("weather"); 20 | 21 | JSONObject weatherObj = weatherArray.getJSONObject(0); 22 | if (weatherObj.has("description")) { 23 | weather.currentWeather.setDescription(weatherObj.getString("description")); 24 | } 25 | if (weatherObj.has("icon")) { 26 | weather.currentWeather.setIdIcon(weatherObj.getString("icon")); 27 | } 28 | 29 | JSONObject mainObj = weatherData.getJSONObject("main"); 30 | if (mainObj.has("temp")) { 31 | weather.temperature.setTemp(Float.parseFloat(mainObj.getString("temp"))); 32 | } 33 | if (mainObj.has("pressure")) { 34 | weather.currentCondition.setPressure(Float.parseFloat(mainObj.getString("pressure"))); 35 | } 36 | if (mainObj.has("humidity")) { 37 | weather.currentCondition.setHumidity(mainObj.getInt("humidity")); 38 | } 39 | 40 | JSONObject windObj = weatherData.getJSONObject("wind"); 41 | if (windObj.has("speed")) { 42 | weather.wind.setSpeed(Float.parseFloat(windObj.getString("speed"))); 43 | } 44 | if (windObj.has("deg")) { 45 | weather.wind.setDirection(Float.parseFloat(windObj.getString("deg"))); 46 | } 47 | 48 | JSONObject cloudsObj = weatherData.getJSONObject("clouds"); 49 | if (cloudsObj.has("all")) { 50 | weather.cloud.setClouds(cloudsObj.getInt("all")); 51 | } 52 | 53 | if (weatherData.has("name")) { 54 | location.setCityName(weatherData.getString("name")); 55 | } 56 | 57 | JSONObject sysObj = weatherData.getJSONObject("sys"); 58 | if (sysObj.has("country")) { 59 | location.setCountryCode(sysObj.getString("country")); 60 | } 61 | weather.sys.setSunrise(sysObj.getLong("sunrise")); 62 | weather.sys.setSunset(sysObj.getLong("sunset")); 63 | 64 | weather.location = location; 65 | 66 | return weather; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/WeatherRequest.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.net.HttpURLConnection; 7 | import java.net.URL; 8 | import org.asdtm.goodweather.utils.Constants; 9 | import org.asdtm.goodweather.utils.Utils; 10 | 11 | public class WeatherRequest 12 | { 13 | private static final String TAG = "WeatherRequest"; 14 | 15 | byte[] getWeatherByte(URL url) throws IOException 16 | { 17 | // Вызов метода openConnection() создает объект подключения к заданному url-адресу 18 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 19 | 20 | try { 21 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 22 | InputStream inputStream = connection.getInputStream(); 23 | 24 | if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { 25 | return null; 26 | } 27 | 28 | int bytesRead = 0; 29 | byte[] buffer = new byte[1024]; 30 | 31 | while ((bytesRead = inputStream.read(buffer)) > 0) { 32 | outputStream.write(buffer, 0, bytesRead); 33 | } 34 | outputStream.close(); 35 | 36 | return outputStream.toByteArray(); 37 | } finally { 38 | connection.disconnect(); 39 | } 40 | } 41 | 42 | public String getResultAsString(URL url) throws IOException 43 | { 44 | return new String(getWeatherByte(url)); 45 | } 46 | 47 | public String getItems(String lat, String lon, String units, String lang) throws IOException 48 | { 49 | return getResultAsString(Utils.getWeatherForecastUrl(Constants.WEATHER_ENDPOINT, lat, lon, units, lang)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/adapter/WeatherForecastAdapter.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.adapter; 2 | 3 | import android.content.Context; 4 | import android.support.v4.app.FragmentManager; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import org.asdtm.goodweather.R; 11 | import org.asdtm.goodweather.model.WeatherForecast; 12 | 13 | import java.util.List; 14 | 15 | public class WeatherForecastAdapter extends RecyclerView.Adapter { 16 | 17 | private Context mContext; 18 | private List mWeatherList; 19 | private FragmentManager mFragmentManager; 20 | 21 | public WeatherForecastAdapter(Context context, List weather, FragmentManager fragmentManager) { 22 | mContext = context; 23 | mWeatherList = weather; 24 | mFragmentManager = fragmentManager; 25 | } 26 | 27 | @Override 28 | public WeatherForecastViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 29 | LayoutInflater inflater = LayoutInflater.from(parent.getContext()); 30 | View v = inflater.inflate(R.layout.forecast_item, parent, false); 31 | return new WeatherForecastViewHolder(v, mContext, mFragmentManager); 32 | } 33 | 34 | @Override 35 | public void onBindViewHolder(WeatherForecastViewHolder holder, int position) { 36 | WeatherForecast weather = mWeatherList.get(position); 37 | holder.bindWeather(weather); 38 | } 39 | 40 | @Override 41 | public int getItemCount() { 42 | return (mWeatherList != null ? mWeatherList.size() : 0); 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/adapter/WeatherForecastViewHolder.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.adapter; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.support.v4.app.FragmentManager; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.view.View; 8 | import android.widget.TextView; 9 | 10 | import org.asdtm.goodweather.R; 11 | import org.asdtm.goodweather.fragment.ForecastBottomSheetDialogFragment; 12 | import org.asdtm.goodweather.model.WeatherForecast; 13 | import org.asdtm.goodweather.utils.Utils; 14 | 15 | import java.text.SimpleDateFormat; 16 | import java.util.Date; 17 | import java.util.Locale; 18 | 19 | public class WeatherForecastViewHolder extends RecyclerView.ViewHolder implements 20 | View.OnClickListener { 21 | 22 | private final String TAG = "ForecastViewHolder"; 23 | 24 | private WeatherForecast mWeatherForecast; 25 | private Context mContext; 26 | private FragmentManager mFragmentManager; 27 | 28 | private TextView mDateTime; 29 | private TextView mIcon; 30 | private TextView mTemperatureMin; 31 | private TextView mTemperatureMax; 32 | 33 | public WeatherForecastViewHolder(View itemView, Context context, 34 | FragmentManager fragmentManager) { 35 | super(itemView); 36 | mContext = context; 37 | mFragmentManager = fragmentManager; 38 | itemView.setOnClickListener(this); 39 | 40 | mDateTime = (TextView) itemView.findViewById(R.id.forecast_date_time); 41 | mIcon = (TextView) itemView.findViewById(R.id.forecast_icon); 42 | mTemperatureMin = (TextView) itemView.findViewById(R.id.forecast_temperature_min); 43 | mTemperatureMax = (TextView) itemView.findViewById(R.id.forecast_temperature_max); 44 | } 45 | 46 | void bindWeather(WeatherForecast weather) { 47 | mWeatherForecast = weather; 48 | 49 | Typeface typeface = Typeface.createFromAsset(mContext.getAssets(), 50 | "fonts/weathericons-regular-webfont.ttf"); 51 | SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMMM", Locale.getDefault()); 52 | Date date = new Date(weather.getDateTime() * 1000); 53 | String temperatureMin = mContext.getString(R.string.temperature_with_degree, 54 | String.format(Locale.getDefault(), "%.0f", 55 | weather.getTemperatureMin())); 56 | String temperatureMax = mContext.getString(R.string.temperature_with_degree, 57 | String.format(Locale.getDefault(), "%.0f", 58 | weather.getTemperatureMax())); 59 | 60 | mDateTime.setText(format.format(date)); 61 | mIcon.setTypeface(typeface); 62 | mIcon.setText(Utils.getStrIcon(mContext, weather.getIcon())); 63 | if (weather.getTemperatureMin() > 0) { 64 | temperatureMin = "+" + temperatureMin; 65 | } 66 | mTemperatureMin.setText(temperatureMin); 67 | if (weather.getTemperatureMax() > 0) { 68 | temperatureMax = "+" + temperatureMax; 69 | } 70 | mTemperatureMax.setText(temperatureMax); 71 | } 72 | 73 | @Override 74 | public void onClick(View view) { 75 | new ForecastBottomSheetDialogFragment() 76 | .newInstance(mWeatherForecast) 77 | .show(mFragmentManager, "forecastBottomSheet"); 78 | } 79 | } -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/fragment/ForecastBottomSheetDialogFragment.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.fragment; 2 | 3 | import android.graphics.Typeface; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.design.widget.BottomSheetDialogFragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.TextView; 11 | 12 | import org.asdtm.goodweather.R; 13 | import org.asdtm.goodweather.model.WeatherForecast; 14 | import org.asdtm.goodweather.utils.Utils; 15 | 16 | import java.util.Locale; 17 | 18 | public class ForecastBottomSheetDialogFragment extends BottomSheetDialogFragment { 19 | 20 | private WeatherForecast mWeather; 21 | 22 | public ForecastBottomSheetDialogFragment newInstance(WeatherForecast weather) { 23 | ForecastBottomSheetDialogFragment fragment = new ForecastBottomSheetDialogFragment(); 24 | Bundle args = new Bundle(); 25 | args.putSerializable("weatherForecast", weather); 26 | fragment.setArguments(args); 27 | return fragment; 28 | } 29 | 30 | @Override 31 | public void onCreate(@Nullable Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | mWeather = (WeatherForecast) getArguments().getSerializable("weatherForecast"); 34 | } 35 | 36 | @Override 37 | public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { 38 | View v = inflater.inflate(R.layout.fragment_forecast_bottom_sheet, parent, false); 39 | 40 | String speedScale = Utils.getSpeedScale(getActivity()); 41 | String percentSign = getActivity().getString(R.string.percent_sign); 42 | String pressureMeasurement = getActivity().getString(R.string.pressure_measurement); 43 | String mmLabel = getString(R.string.millimetre_label); 44 | 45 | Float temperatureMorning = mWeather.getTemperatureMorning(); 46 | Float temperatureDay = mWeather.getTemperatureDay(); 47 | Float temperatureEvening = mWeather.getTemperatureEvening(); 48 | Float temperatureNight = mWeather.getTemperatureNight(); 49 | 50 | String description = mWeather.getDescription(); 51 | String temperatureMorningStr = getActivity().getString(R.string.temperature_with_degree, 52 | String.format(Locale.getDefault(), 53 | "%.0f", 54 | temperatureMorning)); 55 | String temperatureDayStr = getActivity().getString(R.string.temperature_with_degree, 56 | String.format(Locale.getDefault(), 57 | "%.0f", 58 | temperatureDay)); 59 | String temperatureEveningStr = getActivity().getString(R.string.temperature_with_degree, 60 | String.format(Locale.getDefault(), 61 | "%.0f", 62 | temperatureEvening)); 63 | String temperatureNightStr = getActivity().getString(R.string.temperature_with_degree, 64 | String.format(Locale.getDefault(), 65 | "%.0f", 66 | temperatureNight)); 67 | String wind = getActivity().getString(R.string.wind_label, mWeather.getWindSpeed(), speedScale); 68 | String windDegree = mWeather.getWindDegree(); 69 | String windDirection = Utils.windDegreeToDirections(getActivity(), 70 | Double.parseDouble(windDegree)); 71 | String rain = getString(R.string.rain_label, mWeather.getRain(), mmLabel); 72 | String snow = getString(R.string.snow_label, mWeather.getSnow(), mmLabel); 73 | String pressure = getActivity().getString(R.string.pressure_label, mWeather.getPressure(), pressureMeasurement); 74 | String humidity = getActivity().getString(R.string.humidity_label, mWeather.getHumidity(), percentSign); 75 | 76 | TextView descriptionView = (TextView) v.findViewById(R.id.forecast_description); 77 | TextView windView = (TextView) v.findViewById(R.id.forecast_wind); 78 | TextView rainView = (TextView) v.findViewById(R.id.forecast_rain); 79 | TextView snowView = (TextView) v.findViewById(R.id.forecast_snow); 80 | TextView humidityView = (TextView) v.findViewById(R.id.forecast_humidity); 81 | TextView pressureView = (TextView) v.findViewById(R.id.forecast_pressure); 82 | 83 | TextView temperatureMorningView = (TextView) v.findViewById( 84 | R.id.forecast_morning_temperature); 85 | TextView temperatureDayView = (TextView) v.findViewById( 86 | R.id.forecast_day_temperature); 87 | TextView temperatureEveningView = (TextView) v.findViewById( 88 | R.id.forecast_evening_temperature); 89 | TextView temperatureNightView = (TextView) v.findViewById( 90 | R.id.forecast_night_temperature); 91 | Typeface typeface = Typeface.createFromAsset(getActivity().getAssets(), 92 | "fonts/weathericons-regular-webfont.ttf"); 93 | 94 | descriptionView.setText(description); 95 | windView.setTypeface(typeface); 96 | windView.setText(wind + " " + windDirection); 97 | rainView.setText(rain); 98 | snowView.setText(snow); 99 | humidityView.setText(humidity); 100 | pressureView.setText(pressure); 101 | if (temperatureMorning > 0) { 102 | temperatureMorningStr = "+" + temperatureMorningStr; 103 | } 104 | if (temperatureDay > 0) { 105 | temperatureDayStr = "+" + temperatureDayStr; 106 | } 107 | if (temperatureEvening > 0) { 108 | temperatureEveningStr = "+" + temperatureEveningStr; 109 | } 110 | if (temperatureNight > 0) { 111 | temperatureNightStr = "+" + temperatureNightStr; 112 | } 113 | temperatureMorningView.setText(temperatureMorningStr); 114 | temperatureDayView.setText(temperatureDayStr); 115 | temperatureEveningView.setText(temperatureEveningStr); 116 | temperatureNightView.setText(temperatureNightStr); 117 | 118 | return v; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/model/CitySearch.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.model; 2 | 3 | public class CitySearch 4 | { 5 | private String mCityName; 6 | private String mCountry; 7 | private String mLatitude; 8 | private String mLongitude; 9 | private String mCountryCode; 10 | 11 | public CitySearch(){} 12 | public CitySearch(String cityName, String countryCode, String latitude, String longitude) 13 | { 14 | mCityName = cityName; 15 | mCountryCode = countryCode; 16 | mLatitude = latitude; 17 | mLongitude = longitude; 18 | } 19 | 20 | public String getCityName() 21 | { 22 | return mCityName; 23 | } 24 | 25 | public void setCityName(String cityName) 26 | { 27 | mCityName = cityName; 28 | } 29 | 30 | public String getCountry() 31 | { 32 | return mCountry; 33 | } 34 | 35 | public void setCountry(String country) 36 | { 37 | mCountry = country; 38 | } 39 | 40 | public String getLatitude() 41 | { 42 | return mLatitude; 43 | } 44 | 45 | public void setLatitude(String latitude) 46 | { 47 | mLatitude = latitude; 48 | } 49 | 50 | public String getLongitude() 51 | { 52 | return mLongitude; 53 | } 54 | 55 | public void setLongitude(String longitude) 56 | { 57 | mLongitude = longitude; 58 | } 59 | 60 | public String getCountryCode() 61 | { 62 | return mCountryCode; 63 | } 64 | 65 | public void setCountryCode(String countryCode) 66 | { 67 | mCountryCode = countryCode; 68 | } 69 | 70 | @Override 71 | public String toString() 72 | { 73 | return mCityName + ", " + mCountryCode; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/model/Weather.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.model; 2 | 3 | public class Weather { 4 | 5 | public CitySearch location; 6 | public Temperature temperature = new Temperature(); 7 | public Wind wind = new Wind(); 8 | public CurrentWeather currentWeather = new CurrentWeather(); 9 | public CurrentCondition currentCondition = new CurrentCondition(); 10 | public Cloud cloud = new Cloud(); 11 | public Sys sys = new Sys(); 12 | 13 | public class Temperature { 14 | 15 | private float mTemp; 16 | 17 | public float getTemp() { 18 | return mTemp; 19 | } 20 | 21 | public void setTemp(float temp) { 22 | mTemp = temp; 23 | } 24 | } 25 | 26 | public class Wind { 27 | 28 | private float mSpeed; 29 | private float mDirection; 30 | 31 | public float getSpeed() { 32 | return mSpeed; 33 | } 34 | 35 | public void setSpeed(float speed) { 36 | mSpeed = speed; 37 | } 38 | 39 | public float getDirection() { 40 | return mDirection; 41 | } 42 | 43 | public void setDirection(float direction) { 44 | mDirection = direction; 45 | } 46 | } 47 | 48 | public class CurrentWeather { 49 | 50 | private String mDescription; 51 | private String mIdIcon; 52 | 53 | public String getDescription() { 54 | return mDescription; 55 | } 56 | 57 | public void setDescription(String description) { 58 | mDescription = description.substring(0, 1).toUpperCase() + description.substring(1); 59 | } 60 | 61 | public String getIdIcon() { 62 | return mIdIcon; 63 | } 64 | 65 | public void setIdIcon(String idIcon) { 66 | mIdIcon = idIcon; 67 | } 68 | } 69 | 70 | public class CurrentCondition { 71 | 72 | private float mPressure; 73 | private int mHumidity; 74 | 75 | public float getPressure() { 76 | return mPressure; 77 | } 78 | 79 | public void setPressure(float pressure) { 80 | mPressure = pressure; 81 | } 82 | 83 | public int getHumidity() { 84 | return mHumidity; 85 | } 86 | 87 | public void setHumidity(int humidity) { 88 | mHumidity = humidity; 89 | } 90 | } 91 | 92 | public class Cloud { 93 | 94 | private int mClouds; 95 | 96 | public int getClouds() { 97 | return mClouds; 98 | } 99 | 100 | public void setClouds(int clouds) { 101 | mClouds = clouds; 102 | } 103 | } 104 | 105 | public class Sys { 106 | 107 | private long mSunrise; 108 | private long mSunset; 109 | 110 | public long getSunrise() { 111 | return mSunrise; 112 | } 113 | 114 | public void setSunrise(long sunrise) { 115 | mSunrise = sunrise; 116 | } 117 | 118 | public long getSunset() { 119 | return mSunset; 120 | } 121 | 122 | public void setSunset(long sunset) { 123 | mSunset = sunset; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/model/WeatherForecast.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.model; 2 | 3 | import java.io.Serializable; 4 | 5 | public class WeatherForecast implements Serializable { 6 | 7 | private long dateTime; 8 | private float temperatureMin; 9 | private float temperatureMax; 10 | private float temperatureMorning; 11 | private float temperatureDay; 12 | private float temperatureEvening; 13 | private float temperatureNight; 14 | private String pressure; 15 | private String humidity; 16 | private String icon; 17 | private String description; 18 | private String windSpeed; 19 | private String windDegree; 20 | private String cloudiness; 21 | private String rain; 22 | private String snow; 23 | 24 | public String getDescription() { 25 | return description; 26 | } 27 | 28 | public void setDescription(String description) { 29 | this.description = description.substring(0, 1).toUpperCase() + description.substring(1); 30 | } 31 | 32 | public long getDateTime() { 33 | return dateTime; 34 | } 35 | 36 | public void setDateTime(long dateTime) { 37 | this.dateTime = dateTime; 38 | } 39 | 40 | public String getHumidity() { 41 | return humidity; 42 | } 43 | 44 | public void setHumidity(String humidity) { 45 | this.humidity = humidity; 46 | } 47 | 48 | public String getIcon() { 49 | return icon; 50 | } 51 | 52 | public void setIcon(String icon) { 53 | this.icon = icon; 54 | } 55 | 56 | public String getPressure() { 57 | return pressure; 58 | } 59 | 60 | public void setPressure(String pressure) { 61 | this.pressure = pressure; 62 | } 63 | 64 | public String getRain() { 65 | return rain; 66 | } 67 | 68 | public void setRain(String rain) { 69 | this.rain = rain; 70 | } 71 | 72 | public String getSnow() { 73 | return snow; 74 | } 75 | 76 | public void setSnow(String snow) { 77 | this.snow = snow; 78 | } 79 | 80 | public String getWindDegree() { 81 | return windDegree; 82 | } 83 | 84 | public void setWindDegree(String windDegree) { 85 | this.windDegree = windDegree; 86 | } 87 | 88 | public String getWindSpeed() { 89 | return windSpeed; 90 | } 91 | 92 | public void setWindSpeed(String windSpeed) { 93 | this.windSpeed = windSpeed; 94 | } 95 | 96 | public String getCloudiness() { 97 | return cloudiness; 98 | } 99 | 100 | public void setCloudiness(String cloudiness) { 101 | this.cloudiness = cloudiness; 102 | } 103 | 104 | public float getTemperatureDay() { 105 | return temperatureDay; 106 | } 107 | 108 | public void setTemperatureDay(float temperatureDay) { 109 | this.temperatureDay = temperatureDay; 110 | } 111 | 112 | public float getTemperatureEvening() { 113 | return temperatureEvening; 114 | } 115 | 116 | public void setTemperatureEvening(float temperatureEvening) { 117 | this.temperatureEvening = temperatureEvening; 118 | } 119 | 120 | public float getTemperatureMax() { 121 | return temperatureMax; 122 | } 123 | 124 | public void setTemperatureMax(float temperatureMax) { 125 | this.temperatureMax = temperatureMax; 126 | } 127 | 128 | public float getTemperatureMin() { 129 | return temperatureMin; 130 | } 131 | 132 | public void setTemperatureMin(float temperatureMin) { 133 | this.temperatureMin = temperatureMin; 134 | } 135 | 136 | public float getTemperatureMorning() { 137 | return temperatureMorning; 138 | } 139 | 140 | public void setTemperatureMorning(float temperatureMorning) { 141 | this.temperatureMorning = temperatureMorning; 142 | } 143 | 144 | public float getTemperatureNight() { 145 | return temperatureNight; 146 | } 147 | 148 | public void setTemperatureNight(float temperatureNight) { 149 | this.temperatureNight = temperatureNight; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/receiver/StartupReceiver.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.receiver; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import org.asdtm.goodweather.service.NotificationService; 8 | import org.asdtm.goodweather.utils.AppPreference; 9 | 10 | public class StartupReceiver extends BroadcastReceiver { 11 | 12 | private static final String TAG = "StartupReceiver"; 13 | 14 | @Override 15 | public void onReceive(Context context, Intent intent) { 16 | boolean isNotificationEnabled = AppPreference.isNotificationEnabled(context); 17 | NotificationService.setNotificationServiceAlarm(context, isNotificationEnabled); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/service/CurrentWeatherService.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.service; 2 | 3 | import android.app.IntentService; 4 | import android.content.Intent; 5 | import android.content.SharedPreferences; 6 | import android.support.v4.content.LocalBroadcastManager; 7 | import android.util.Log; 8 | 9 | import org.asdtm.goodweather.ConnectionDetector; 10 | import org.asdtm.goodweather.utils.AppPreference; 11 | import org.asdtm.goodweather.utils.Constants; 12 | import org.asdtm.goodweather.utils.LanguageUtil; 13 | import org.asdtm.goodweather.utils.PreferenceUtil; 14 | import org.asdtm.goodweather.utils.Utils; 15 | import org.json.JSONArray; 16 | import org.json.JSONException; 17 | import org.json.JSONObject; 18 | 19 | import java.io.ByteArrayOutputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.net.HttpURLConnection; 23 | import java.net.URL; 24 | 25 | import static org.asdtm.goodweather.MainActivity.mCitySearch; 26 | import static org.asdtm.goodweather.MainActivity.mWeather; 27 | 28 | public class CurrentWeatherService extends IntentService { 29 | 30 | private static final String TAG = "WeatherService"; 31 | 32 | public static final String ACTION_WEATHER_UPDATE_OK = "org.asdtm.goodweather.action.WEATHER_UPDATE_OK"; 33 | public static final String ACTION_WEATHER_UPDATE_FAIL = "org.asdtm.goodweather.action.WEATHER_UPDATE_FAIL"; 34 | public static final String ACTION_WEATHER_UPDATE_RESULT = "org.asdtm.goodweather.action.WEATHER_UPDATE_RESULT"; 35 | 36 | public CurrentWeatherService() { 37 | super(TAG); 38 | } 39 | 40 | @Override 41 | protected void onHandleIntent(Intent intent) { 42 | ConnectionDetector connectionDetector = new ConnectionDetector(this); 43 | if (!connectionDetector.isNetworkAvailableAndConnected()) { 44 | return; 45 | } 46 | 47 | SharedPreferences preferences = getSharedPreferences(Constants.APP_SETTINGS_NAME, 0); 48 | String latitude = preferences.getString(Constants.APP_SETTINGS_LATITUDE, "51.51"); 49 | String longitude = preferences.getString(Constants.APP_SETTINGS_LONGITUDE, "-0.13"); 50 | String locale = LanguageUtil.getLanguageName(PreferenceUtil.getLanguage(this)); 51 | String units = AppPreference.getTemperatureUnit(this); 52 | 53 | String requestResult = ""; 54 | HttpURLConnection connection = null; 55 | try { 56 | URL url = Utils.getWeatherForecastUrl(Constants.WEATHER_ENDPOINT, latitude, longitude, units, locale); 57 | connection = (HttpURLConnection) url.openConnection(); 58 | 59 | if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { 60 | ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); 61 | InputStream inputStream = connection.getInputStream(); 62 | 63 | int bytesRead; 64 | byte[] buffer = new byte[1024]; 65 | while ((bytesRead = inputStream.read(buffer)) > 0) { 66 | byteArray.write(buffer, 0, bytesRead); 67 | } 68 | byteArray.close(); 69 | requestResult = byteArray.toString(); 70 | AppPreference.saveLastUpdateTimeMillis(this); 71 | } 72 | 73 | } catch (IOException e) { 74 | Log.e(TAG, "IOException: " + requestResult); 75 | 76 | } finally { 77 | if (connection != null) { 78 | connection.disconnect(); 79 | } 80 | } 81 | parseWeather(requestResult); 82 | } 83 | 84 | public void parseWeather(String data) { 85 | try { 86 | JSONObject jsonObject = new JSONObject(data); 87 | 88 | JSONArray weatherArray = jsonObject.getJSONArray("weather"); 89 | JSONObject weatherObject = weatherArray.getJSONObject(0); 90 | if (weatherObject.has("description")) { 91 | mWeather.currentWeather.setDescription(weatherObject.getString("description")); 92 | } 93 | if (weatherObject.has("icon")) { 94 | mWeather.currentWeather.setIdIcon(weatherObject.getString("icon")); 95 | } 96 | 97 | JSONObject mainObj = jsonObject.getJSONObject("main"); 98 | if (mainObj.has("temp")) { 99 | mWeather.temperature.setTemp(Float.parseFloat(mainObj.getString("temp"))); 100 | } 101 | if (mainObj.has("pressure")) { 102 | mWeather.currentCondition.setPressure( 103 | Float.parseFloat(mainObj.getString("pressure"))); 104 | } 105 | if (mainObj.has("humidity")) { 106 | mWeather.currentCondition.setHumidity(mainObj.getInt("humidity")); 107 | } 108 | 109 | JSONObject windObj = jsonObject.getJSONObject("wind"); 110 | if (windObj.has("speed")) { 111 | mWeather.wind.setSpeed(Float.parseFloat(windObj.getString("speed"))); 112 | } 113 | if (windObj.has("deg")) { 114 | mWeather.wind.setDirection(Float.parseFloat(windObj.getString("deg"))); 115 | } 116 | 117 | JSONObject cloudsObj = jsonObject.getJSONObject("clouds"); 118 | if (cloudsObj.has("all")) { 119 | mWeather.cloud.setClouds(cloudsObj.getInt("all")); 120 | } 121 | 122 | if (jsonObject.has("name")) { 123 | mCitySearch.setCityName(jsonObject.getString("name")); 124 | } 125 | 126 | JSONObject sysObj = jsonObject.getJSONObject("sys"); 127 | if (sysObj.has("country")) { 128 | mCitySearch.setCountryCode(sysObj.getString("country")); 129 | } 130 | mWeather.sys.setSunrise(sysObj.getLong("sunrise")); 131 | mWeather.sys.setSunset(sysObj.getLong("sunset")); 132 | 133 | mWeather.location = mCitySearch; 134 | sendResult(ACTION_WEATHER_UPDATE_OK); 135 | } catch (JSONException e) { 136 | Log.e(TAG, "Error parsing JSON"); 137 | sendResult(ACTION_WEATHER_UPDATE_FAIL); 138 | } 139 | } 140 | 141 | public void sendResult(String result) { 142 | Intent intent = new Intent(ACTION_WEATHER_UPDATE_RESULT); 143 | if (result.equals(ACTION_WEATHER_UPDATE_OK)) { 144 | intent.putExtra(ACTION_WEATHER_UPDATE_RESULT, ACTION_WEATHER_UPDATE_OK); 145 | } else if (result.equals(ACTION_WEATHER_UPDATE_FAIL)) { 146 | intent.putExtra(ACTION_WEATHER_UPDATE_RESULT, ACTION_WEATHER_UPDATE_FAIL); 147 | } 148 | LocalBroadcastManager.getInstance(this).sendBroadcast(intent); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/utils/ApiKeys.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.utils; 2 | 3 | public class ApiKeys { 4 | 5 | /** 6 | * API key for http://openweathermap.org/api 7 | */ 8 | public static final String OPEN_WEATHER_MAP_API_KEY = "1487dd8a93bfd85d278d9ac8dcfee94c"; 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/utils/AppWidgetProviderAlarm.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.utils; 2 | 3 | import android.app.AlarmManager; 4 | import android.app.PendingIntent; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.os.SystemClock; 8 | 9 | public class AppWidgetProviderAlarm { 10 | 11 | private static final String TAG = "AppWidgetProviderAlarm"; 12 | 13 | private Context mContext; 14 | private Class mCls; 15 | 16 | public AppWidgetProviderAlarm(Context context, Class cls) { 17 | this.mContext = context; 18 | this.mCls = cls; 19 | } 20 | 21 | public void setAlarm() { 22 | String updatePeriodStr = AppPreference.getWidgetUpdatePeriod(mContext); 23 | long updatePeriodMills = Utils.intervalMillisForAlarm(updatePeriodStr); 24 | AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 25 | alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, 26 | SystemClock.elapsedRealtime() + updatePeriodMills, 27 | updatePeriodMills, 28 | getPendingIntent(mCls)); 29 | } 30 | 31 | public void cancelAlarm() { 32 | AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 33 | alarmManager.cancel(getPendingIntent(mCls)); 34 | getPendingIntent(mCls).cancel(); 35 | } 36 | 37 | private PendingIntent getPendingIntent(Class cls) { 38 | Intent intent = new Intent(mContext, cls); 39 | intent.setAction(Constants.ACTION_FORCED_APPWIDGET_UPDATE); 40 | return PendingIntent.getBroadcast(mContext, 41 | 0, 42 | intent, 43 | PendingIntent.FLAG_CANCEL_CURRENT); 44 | } 45 | 46 | public boolean isAlarmOff() { 47 | Intent intent = new Intent(mContext, mCls); 48 | intent.setAction(Constants.ACTION_FORCED_APPWIDGET_UPDATE); 49 | PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 50 | 0, 51 | intent, 52 | PendingIntent.FLAG_NO_CREATE); 53 | return pendingIntent == null; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/utils/CityParser.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.utils; 2 | 3 | import android.net.Uri; 4 | 5 | import org.asdtm.goodweather.model.CitySearch; 6 | import org.json.JSONArray; 7 | import org.json.JSONException; 8 | import org.json.JSONObject; 9 | 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.net.HttpURLConnection; 14 | import java.net.MalformedURLException; 15 | import java.net.URL; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | public class CityParser { 20 | 21 | private static final String TAG = "CityParser"; 22 | private static final String ENDPOINT = "http://api.openweathermap.org/data/2.5/find"; 23 | private static final String APPID = ApiKeys.OPEN_WEATHER_MAP_API_KEY; 24 | 25 | public static List getCity(String query) { 26 | List citySearchList = new ArrayList<>(); 27 | CitySearch city; 28 | HttpURLConnection connection = null; 29 | 30 | try { 31 | URL url = getUrl(query); 32 | connection = (HttpURLConnection) url.openConnection(); 33 | 34 | if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { 35 | return null; 36 | } 37 | 38 | ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); 39 | InputStream inputStream = connection.getInputStream(); 40 | int bytesRead; 41 | byte[] buffer = new byte[1024]; 42 | 43 | while ((bytesRead = inputStream.read(buffer)) > 0) { 44 | byteArray.write(buffer, 0, bytesRead); 45 | } 46 | byteArray.close(); 47 | 48 | JSONObject jsonObject = new JSONObject(byteArray.toString()); 49 | JSONArray listArray = jsonObject.getJSONArray("list"); 50 | 51 | int listArrayCount = listArray.length(); 52 | 53 | for (int i = 0; i != listArrayCount; ++i) { 54 | city = new CitySearch(); 55 | JSONObject resultObject = listArray.getJSONObject(i); 56 | city.setCityName(resultObject.getString("name")); 57 | 58 | JSONObject coordObject = resultObject.getJSONObject("coord"); 59 | city.setLongitude(coordObject.getString("lon")); 60 | city.setLatitude(coordObject.getString("lat")); 61 | 62 | JSONObject sysObject = resultObject.getJSONObject("sys"); 63 | city.setCountryCode(sysObject.getString("country")); 64 | citySearchList.add(city); 65 | } 66 | 67 | } catch (JSONException | IOException e) { 68 | e.printStackTrace(); 69 | } finally { 70 | if (connection != null) { 71 | connection.disconnect(); 72 | } 73 | } 74 | 75 | return citySearchList; 76 | } 77 | 78 | private static URL getUrl(String query) throws MalformedURLException { 79 | String url = Uri.parse(ENDPOINT).buildUpon() 80 | .appendQueryParameter("q", query) 81 | .appendQueryParameter("type", "like") 82 | .appendQueryParameter("cnt", "15") 83 | .appendQueryParameter("APPID", APPID) 84 | .build().toString(); 85 | return new URL(url); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/utils/Constants.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.utils; 2 | 3 | public class Constants { 4 | 5 | /** 6 | * SharedPreference names 7 | */ 8 | public static final String APP_SETTINGS_NAME = "config"; 9 | public static final String PREF_WEATHER_NAME = "weather_pref"; 10 | public static final String PREF_FORECAST_NAME = "weather_forecast"; 11 | 12 | /** 13 | * Preferences constants 14 | */ 15 | public static final String APP_SETTINGS_LATITUDE = "latitude"; 16 | public static final String APP_SETTINGS_LONGITUDE = "longitude"; 17 | public static final String APP_SETTINGS_CITY = "city"; 18 | public static final String APP_SETTINGS_COUNTRY_CODE = "country_code"; 19 | public static final String APP_SETTINGS_GEO_COUNTRY_NAME = "geo_country_name"; 20 | public static final String APP_SETTINGS_GEO_DISTRICT_OF_CITY = "geo_district_name"; 21 | public static final String APP_SETTINGS_GEO_CITY = "geo_city_name"; 22 | public static final String LAST_UPDATE_TIME_IN_MS = "last_update"; 23 | 24 | public static final String KEY_PREF_IS_NOTIFICATION_ENABLED = "notification_pref_key"; 25 | public static final String KEY_PREF_TEMPERATURE = "temperature_pref_key"; 26 | public static final String KEY_PREF_HIDE_DESCRIPTION = "hide_desc_pref_key"; 27 | public static final String KEY_PREF_INTERVAL_NOTIFICATION = "notification_interval_pref_key"; 28 | public static final String KEY_PREF_VIBRATE = "notification_vibrate_pref_key"; 29 | public static final String KEY_PREF_WIDGET_UPDATE_LOCATION = "widget_update_location_pref_key"; 30 | public static final String KEY_PREF_WIDGET_USE_GEOCODER = "widget_use_geocoder_pref_key"; 31 | public static final String KEY_PREF_WIDGET_THEME = "widget_theme_pref_key"; 32 | public static final String KEY_PREF_WIDGET_UPDATE_PERIOD = "widget_update_period_pref_key"; 33 | public static final String PREF_LANGUAGE = "language_pref_key"; 34 | public static final String PREF_THEME = "theme_pref_key"; 35 | 36 | /** 37 | * About preference screen constants 38 | */ 39 | public static final String KEY_PREF_ABOUT_VERSION = "about_version_pref_key"; 40 | public static final String KEY_PREF_ABOUT_F_DROID = "about_f_droid_pref_key"; 41 | public static final String KEY_PREF_ABOUT_GOOGLE_PLAY = "about_google_play_pref_key"; 42 | public static final String KEY_PREF_ABOUT_OPEN_SOURCE_LICENSES = 43 | "about_open_source_licenses_pref_key"; 44 | 45 | public static final String WEATHER_DATA_TEMPERATURE = "temperature"; 46 | public static final String WEATHER_DATA_DESCRIPTION = "description"; 47 | public static final String WEATHER_DATA_PRESSURE = "pressure"; 48 | public static final String WEATHER_DATA_HUMIDITY = "humidity"; 49 | public static final String WEATHER_DATA_WIND_SPEED = "wind_speed"; 50 | public static final String WEATHER_DATA_CLOUDS = "clouds"; 51 | public static final String WEATHER_DATA_ICON = "icon"; 52 | public static final String WEATHER_DATA_SUNRISE = "sunrise"; 53 | public static final String WEATHER_DATA_SUNSET = "sunset"; 54 | 55 | /** 56 | * Widget action constants 57 | */ 58 | public static final String ACTION_FORCED_APPWIDGET_UPDATE = 59 | "org.asdtm.goodweather.action.FORCED_APPWIDGET_UPDATE"; 60 | public static final String ACTION_APPWIDGET_THEME_CHANGED = 61 | "org.asdtm.goodweather.action.APPWIDGET_THEME_CHANGED"; 62 | public static final String ACTION_APPWIDGET_UPDATE_PERIOD_CHANGED = 63 | "org.asdtm.goodweather.action.APPWIDGET_UPDATE_PERIOD_CHANGED"; 64 | 65 | /** 66 | * URIs constants 67 | */ 68 | public static final String SOURCE_CODE_URI = "https://github.com/qqq3/good-weather"; 69 | public static final String GOOGLE_PLAY_APP_URI = "market://details?id=%s"; 70 | public static final String GOOGLE_PLAY_WEB_URI = 71 | "http://play.google.com/store/apps/details?id=%s"; 72 | public static final String F_DROID_WEB_URI = "https://f-droid.org/repository/browse/?fdid=%s"; 73 | public static final String WEATHER_ENDPOINT = "http://api.openweathermap.org/data/2.5/weather"; 74 | public static final String WEATHER_FORECAST_ENDPOINT = "http://api.openweathermap.org/data/2.5/forecast/daily"; 75 | 76 | public static final String BITCOIN_ADDRESS = "1FV8m1MKqZ9ZKB8YNwpsjsuubHTznJSiT8"; 77 | 78 | public static final int PARSE_RESULT_SUCCESS = 0; 79 | public static final int TASK_RESULT_ERROR = -1; 80 | public static final int PARSE_RESULT_ERROR = -2; 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/utils/CustomValueFormatter.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.utils; 2 | 3 | import com.github.mikephil.charting.data.Entry; 4 | import com.github.mikephil.charting.formatter.ValueFormatter; 5 | import com.github.mikephil.charting.utils.ViewPortHandler; 6 | 7 | import java.text.DecimalFormat; 8 | 9 | public class CustomValueFormatter implements ValueFormatter { 10 | 11 | private DecimalFormat mFormat; 12 | 13 | public CustomValueFormatter() { 14 | mFormat = new DecimalFormat("#.##"); 15 | } 16 | 17 | @Override 18 | public String getFormattedValue(float value, Entry entry, int dataSetIndex, 19 | ViewPortHandler viewPortHandler) { 20 | return mFormat.format(value); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/utils/LanguageUtil.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.utils; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.content.ContextWrapper; 6 | import android.content.Intent; 7 | import android.content.res.Configuration; 8 | import android.content.res.Resources; 9 | import android.os.Build; 10 | import android.text.TextUtils; 11 | 12 | import java.util.Locale; 13 | 14 | public class LanguageUtil { 15 | 16 | private static final String TAG = LanguageUtil.class.getSimpleName(); 17 | 18 | @TargetApi(17) 19 | @SuppressWarnings("deprecation") 20 | public static void setLanguage(final ContextWrapper contextWrapper, String locale) { 21 | Locale sLocale; 22 | if (TextUtils.isEmpty(locale)) { 23 | sLocale = Locale.getDefault(); 24 | } else { 25 | String[] localeParts = locale.split("_"); 26 | if (localeParts.length > 1) { 27 | sLocale = new Locale(localeParts[0], localeParts[1]); 28 | } else { 29 | sLocale = new Locale(locale); 30 | } 31 | } 32 | 33 | Resources resources = contextWrapper.getBaseContext().getResources(); 34 | Configuration configuration = resources.getConfiguration(); 35 | Locale.setDefault(sLocale); 36 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 37 | configuration.setLocale(sLocale); 38 | } else { 39 | configuration.locale = sLocale; 40 | } 41 | 42 | resources.updateConfiguration(configuration, resources.getDisplayMetrics()); 43 | } 44 | 45 | public static void forceChangeLanguage(Activity activity) { 46 | Intent intent = activity.getIntent(); 47 | if (intent == null) { 48 | return; 49 | } 50 | intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); 51 | activity.finish(); 52 | activity.overridePendingTransition(0, 15); 53 | activity.startActivity(intent); 54 | activity.overridePendingTransition(0, 0); 55 | } 56 | 57 | public static String getLanguageName(String locale) { 58 | if (TextUtils.isEmpty(locale)) { 59 | locale = Locale.getDefault().toString(); 60 | } 61 | if (locale.contains("_")) { 62 | return locale.split("_")[0]; 63 | } 64 | return locale; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/utils/PermissionUtil.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.utils; 2 | 3 | import android.content.pm.PackageManager; 4 | 5 | public class PermissionUtil { 6 | 7 | public static boolean verifyPermissions(int[] grantResults) { 8 | if (grantResults.length < 1) { 9 | return false; 10 | } 11 | 12 | for (int result : grantResults) { 13 | if (result != PackageManager.PERMISSION_GRANTED) { 14 | return false; 15 | } 16 | } 17 | return true; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/utils/PreferenceUtil.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.utils; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.preference.PreferenceManager; 6 | 7 | public class PreferenceUtil { 8 | 9 | public enum Theme { 10 | light, 11 | dark, 12 | } 13 | 14 | public static SharedPreferences getDefaultSharedPrefs(Context context) { 15 | return PreferenceManager.getDefaultSharedPreferences(context); 16 | } 17 | 18 | public static String getLanguage(Context context) { 19 | return PreferenceManager.getDefaultSharedPreferences(context).getString(Constants.PREF_LANGUAGE, "en"); 20 | } 21 | 22 | public static Theme getTheme(Context context) { 23 | return Theme.valueOf(PreferenceManager.getDefaultSharedPreferences(context).getString(Constants.PREF_THEME, "light")); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/utils/XAxisValueFormatter.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.utils; 2 | 3 | import com.github.mikephil.charting.components.AxisBase; 4 | import com.github.mikephil.charting.formatter.AxisValueFormatter; 5 | 6 | public class XAxisValueFormatter implements AxisValueFormatter { 7 | 8 | private String[] mValues; 9 | 10 | public XAxisValueFormatter(String[] dates) { 11 | mValues = dates; 12 | } 13 | 14 | @Override 15 | public String getFormattedValue(float value, AxisBase axis) { 16 | return mValues[(int) value]; 17 | } 18 | 19 | @Override 20 | public int getDecimalDigits() { 21 | return 0; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/utils/YAxisValueFormatter.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.utils; 2 | 3 | import com.github.mikephil.charting.components.AxisBase; 4 | import com.github.mikephil.charting.formatter.AxisValueFormatter; 5 | 6 | import java.text.DecimalFormat; 7 | 8 | public class YAxisValueFormatter implements AxisValueFormatter { 9 | 10 | private DecimalFormat mFormat; 11 | 12 | public YAxisValueFormatter() { 13 | mFormat = new DecimalFormat("#.##"); 14 | } 15 | 16 | @Override 17 | public String getFormattedValue(float value, AxisBase axis) { 18 | return mFormat.format(value); 19 | } 20 | 21 | @Override 22 | public int getDecimalDigits() { 23 | return 1; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/widget/LessWidgetService.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.widget; 2 | 3 | import android.app.IntentService; 4 | import android.appwidget.AppWidgetManager; 5 | import android.content.ComponentName; 6 | import android.content.Intent; 7 | import android.content.SharedPreferences; 8 | import android.util.Log; 9 | import android.widget.RemoteViews; 10 | 11 | import org.asdtm.goodweather.ConnectionDetector; 12 | import org.asdtm.goodweather.R; 13 | import org.asdtm.goodweather.WeatherJSONParser; 14 | import org.asdtm.goodweather.WeatherRequest; 15 | import org.asdtm.goodweather.model.Weather; 16 | import org.asdtm.goodweather.utils.AppPreference; 17 | import org.asdtm.goodweather.utils.Constants; 18 | import org.asdtm.goodweather.utils.LanguageUtil; 19 | import org.asdtm.goodweather.utils.PreferenceUtil; 20 | import org.asdtm.goodweather.utils.Utils; 21 | import org.json.JSONException; 22 | 23 | import java.io.IOException; 24 | import java.util.Locale; 25 | 26 | public class LessWidgetService extends IntentService { 27 | 28 | private static final String TAG = "UpdateLessWidgetService"; 29 | 30 | public LessWidgetService() { 31 | super(TAG); 32 | } 33 | 34 | @Override 35 | protected void onHandleIntent(Intent intent) { 36 | ConnectionDetector checkNetwork = new ConnectionDetector(this); 37 | if (!checkNetwork.isNetworkAvailableAndConnected()) { 38 | return; 39 | } 40 | SharedPreferences preferences = getSharedPreferences(Constants.APP_SETTINGS_NAME, 0); 41 | String latitude = preferences.getString(Constants.APP_SETTINGS_LATITUDE, "51.51"); 42 | String longitude = preferences.getString(Constants.APP_SETTINGS_LONGITUDE, "-0.13"); 43 | String locale = LanguageUtil.getLanguageName(PreferenceUtil.getLanguage(this)); 44 | String units = AppPreference.getTemperatureUnit(this); 45 | try { 46 | String weatherRaw = new WeatherRequest().getItems(latitude, longitude, units, 47 | locale); 48 | Weather weather; 49 | weather = WeatherJSONParser.getWeather(weatherRaw); 50 | AppPreference.saveWeather(this, weather); 51 | updateWidget(weather); 52 | } catch (IOException | JSONException e) { 53 | Log.e(TAG, "Error get weather", e); 54 | stopSelf(); 55 | } 56 | } 57 | 58 | private void updateWidget(Weather weather) { 59 | AppWidgetManager widgetManager = AppWidgetManager.getInstance(this); 60 | ComponentName widgetComponent = new ComponentName(this, 61 | LessWidgetProvider.class); 62 | 63 | int[] widgetIds = widgetManager.getAppWidgetIds(widgetComponent); 64 | for (int appWidgetId : widgetIds) { 65 | RemoteViews remoteViews = new RemoteViews(this.getPackageName(), 66 | R.layout.widget_less_3x1); 67 | 68 | String iconId = weather.currentWeather.getIdIcon(); 69 | String weatherIcon = Utils.getStrIcon(this, iconId); 70 | String lastUpdate = Utils.setLastUpdateTime(this, AppPreference 71 | .saveLastUpdateTimeMillis(this)); 72 | 73 | String temperatureScale = Utils.getTemperatureScale(this); 74 | String temperature = String.format(Locale.getDefault(), "%.0f", 75 | weather.temperature.getTemp()); 76 | 77 | remoteViews.setTextViewText(R.id.widget_temperature, 78 | temperature + temperatureScale); 79 | if(!AppPreference.hideDescription(this)) 80 | remoteViews.setTextViewText(R.id.widget_description, 81 | weather.currentWeather.getDescription()); 82 | else remoteViews.setTextViewText(R.id.widget_description, " "); 83 | remoteViews.setTextViewText(R.id.widget_city, Utils.getCityAndCountry(this)); 84 | remoteViews.setTextViewText(R.id.widget_last_update, lastUpdate); 85 | remoteViews.setImageViewBitmap(R.id.widget_icon, 86 | Utils.createWeatherIcon(this, weatherIcon)); 87 | 88 | widgetManager.updateAppWidget(appWidgetId, remoteViews); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/org/asdtm/goodweather/widget/MoreWidgetService.java: -------------------------------------------------------------------------------- 1 | package org.asdtm.goodweather.widget; 2 | 3 | import android.app.IntentService; 4 | import android.appwidget.AppWidgetManager; 5 | import android.content.ComponentName; 6 | import android.content.Intent; 7 | import android.content.SharedPreferences; 8 | import android.util.Log; 9 | import android.widget.RemoteViews; 10 | import java.io.BufferedWriter; 11 | import java.io.File; 12 | import java.io.FileWriter; 13 | 14 | import org.asdtm.goodweather.ConnectionDetector; 15 | import org.asdtm.goodweather.R; 16 | import org.asdtm.goodweather.WeatherJSONParser; 17 | import org.asdtm.goodweather.WeatherRequest; 18 | import org.asdtm.goodweather.model.Weather; 19 | import org.asdtm.goodweather.utils.AppPreference; 20 | import org.asdtm.goodweather.utils.Constants; 21 | import org.asdtm.goodweather.utils.LanguageUtil; 22 | import org.asdtm.goodweather.utils.PreferenceUtil; 23 | import org.asdtm.goodweather.utils.Utils; 24 | import org.json.JSONException; 25 | 26 | import java.io.IOException; 27 | import java.util.Locale; 28 | 29 | public class MoreWidgetService extends IntentService { 30 | 31 | private static final String TAG = "UpdateMoreWidgetService"; 32 | 33 | public MoreWidgetService() { 34 | super(TAG); 35 | } 36 | 37 | @Override 38 | protected void onHandleIntent(Intent intent) { 39 | ConnectionDetector checkNetwork = new ConnectionDetector(this); 40 | if (!checkNetwork.isNetworkAvailableAndConnected()) { 41 | return; 42 | } 43 | 44 | SharedPreferences preferences = getSharedPreferences(Constants.APP_SETTINGS_NAME, 0); 45 | String latitude = preferences.getString(Constants.APP_SETTINGS_LATITUDE, "51.51"); 46 | String longitude = preferences.getString(Constants.APP_SETTINGS_LONGITUDE, "-0.13"); 47 | String locale = LanguageUtil.getLanguageName(PreferenceUtil.getLanguage(this)); 48 | String units = AppPreference.getTemperatureUnit(this); 49 | 50 | try { 51 | String weatherRaw = new WeatherRequest().getItems(latitude, longitude, units, 52 | locale); 53 | Weather weather; 54 | weather = WeatherJSONParser.getWeather(weatherRaw); 55 | AppPreference.saveWeather(this, weather); 56 | updateWidget(weather); 57 | } catch (IOException | JSONException e) { 58 | Log.e(TAG, "Error get weather", e); 59 | stopSelf(); 60 | } 61 | } 62 | 63 | private void updateWidget(Weather weather) { 64 | AppWidgetManager widgetManager = AppWidgetManager.getInstance(this); 65 | ComponentName widgetComponent = new ComponentName(this, MoreWidgetProvider.class); 66 | 67 | int[] widgetIds = widgetManager.getAppWidgetIds(widgetComponent); 68 | for (int appWidgetId : widgetIds) { 69 | String temperatureScale = Utils.getTemperatureScale(this); 70 | String speedScale = Utils.getSpeedScale(this); 71 | String percentSign = getString(R.string.percent_sign); 72 | String pressureMeasurement = getString(R.string.pressure_measurement); 73 | 74 | String temperature = String.format(Locale.getDefault(), "%.0f", 75 | weather.temperature.getTemp()); 76 | String wind = getString(R.string.wind_label, String.format(Locale.getDefault(), 77 | "%.1f", 78 | weather.wind.getSpeed()), 79 | speedScale); 80 | String humidity = getString(R.string.humidity_label, 81 | String.valueOf(weather.currentCondition.getHumidity()), 82 | percentSign); 83 | String pressure = getString(R.string.pressure_label, 84 | String.format(Locale.getDefault(), "%.1f", 85 | weather.currentCondition.getPressure()), 86 | pressureMeasurement); 87 | String cloudiness = getString(R.string.cloudiness_label, 88 | String.valueOf(weather.cloud.getClouds()), 89 | percentSign); 90 | String iconId = weather.currentWeather.getIdIcon(); 91 | String weatherIcon = Utils.getStrIcon(this, iconId); 92 | String lastUpdate = Utils.setLastUpdateTime(this, AppPreference 93 | .saveLastUpdateTimeMillis(this)); 94 | 95 | RemoteViews remoteViews = new RemoteViews(this.getPackageName(), 96 | R.layout.widget_more_3x3); 97 | remoteViews.setTextViewText(R.id.widget_city, Utils.getCityAndCountry(this)); 98 | remoteViews.setTextViewText(R.id.widget_temperature, temperature + temperatureScale); 99 | if(!AppPreference.hideDescription(this)) 100 | remoteViews.setTextViewText(R.id.widget_description, 101 | weather.currentWeather.getDescription()); 102 | else remoteViews.setTextViewText(R.id.widget_description, " "); 103 | remoteViews.setTextViewText(R.id.widget_wind, wind); 104 | remoteViews.setTextViewText(R.id.widget_humidity, humidity); 105 | remoteViews.setTextViewText(R.id.widget_pressure, pressure); 106 | remoteViews.setTextViewText(R.id.widget_clouds, cloudiness); 107 | remoteViews.setImageViewBitmap(R.id.widget_icon, 108 | Utils.createWeatherIcon(this, weatherIcon)); 109 | remoteViews.setTextViewText(R.id.widget_last_update, lastUpdate); 110 | 111 | widgetManager.updateAppWidget(appWidgetId, remoteViews); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/background_nav_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/background_nav_header.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_barometer_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_barometer_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_current_weather_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_current_weather_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_daily_forecast_dark_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_daily_forecast_dark_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_feedback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_feedback.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_graphs_black_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_graphs_black_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_humidity_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_humidity_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_menu_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_menu_settings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_my_location_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_my_location_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_rain_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_rain_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_refresh_white_18dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_refresh_white_18dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_search_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_search_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_snow_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_snow_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_thumb_up_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_thumb_up_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_wind_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/ic_wind_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/small_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-hdpi/small_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/background_nav_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/background_nav_header.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_barometer_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_barometer_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_current_weather_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_current_weather_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_daily_forecast_dark_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_daily_forecast_dark_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_feedback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_feedback.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_graphs_black_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_graphs_black_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_humidity_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_humidity_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_menu_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_menu_settings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_my_location_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_my_location_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_rain_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_rain_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_refresh_white_18dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_refresh_white_18dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_search_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_search_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_snow_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_snow_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_thumb_up_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_thumb_up_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_wind_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/ic_wind_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/small_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-mdpi/small_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/background_appbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-nodpi/background_appbar.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/preview_less_widget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-nodpi/preview_less_widget.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/preview_more_widget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-nodpi/preview_more_widget.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/background_nav_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/background_nav_header.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bitcoin_address_qr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/bitcoin_address_qr.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_barometer_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_barometer_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_current_weather_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_current_weather_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_daily_forecast_dark_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_daily_forecast_dark_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_feedback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_feedback.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_graphs_black_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_graphs_black_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_humidity_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_humidity_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_menu_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_menu_settings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_my_location_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_my_location_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_rain_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_rain_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_refresh_white_18dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_refresh_white_18dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_search_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_search_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_snow_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_snow_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_thumb_up_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_thumb_up_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_wind_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/ic_wind_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/small_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xhdpi/small_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/background_nav_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/background_nav_header.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_barometer_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_barometer_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_current_weather_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_current_weather_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_daily_forecast_dark_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_daily_forecast_dark_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_feedback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_feedback.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_graphs_black_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_graphs_black_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_humidity_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_humidity_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_menu_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_menu_settings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_my_location_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_my_location_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_rain_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_rain_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_refresh_white_18dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_refresh_white_18dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_snow_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_snow_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_thumb_up_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_thumb_up_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_wind_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/ic_wind_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/small_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxhdpi/small_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/background_nav_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxxhdpi/background_nav_header.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_current_weather_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxxhdpi/ic_current_weather_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_daily_forecast_dark_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxxhdpi/ic_daily_forecast_dark_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_feedback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxxhdpi/ic_feedback.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_menu_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxxhdpi/ic_menu_settings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_my_location_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxxhdpi/ic_my_location_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_rain_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxxhdpi/ic_rain_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_refresh_white_18dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxxhdpi/ic_refresh_white_18dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_snow_light_32dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxxhdpi/ic_snow_light_32dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_thumb_up_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxxhdpi/ic_thumb_up_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/small_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qqq3/good-weather/9356148c362aa9cc4a2a7d7c5acff64697a59f89/app/src/main/res/drawable-xxxhdpi/small_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_android_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_share.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_license.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 20 | 21 | 22 | 23 | 30 | 31 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 26 | 27 | 33 | 34 | 35 | 36 | 37 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_weather_forecast.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 21 | 22 | 29 | 30 | 31 | 32 | 39 | 40 | 41 | 47 | 48 | 49 | 57 | -------------------------------------------------------------------------------- /app/src/main/res/layout/city_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_donation_bitcoin.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 27 | 28 |