├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── fabric.properties ├── proguard-rules.pro └── src │ ├── debug │ └── java │ │ └── com │ │ └── baronzhang │ │ └── android │ │ └── weather │ │ └── DebugStethoHelper.java │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ └── com │ │ └── baronzhang │ │ └── android │ │ └── weather │ │ ├── AppConstants.java │ │ ├── WeatherApplication.java │ │ ├── WelcomeActivity.java │ │ ├── base │ │ ├── BaseActivity.java │ │ ├── BaseFragment.java │ │ ├── BasePresenter.java │ │ ├── BaseRecyclerViewAdapter.java │ │ └── BaseView.java │ │ ├── data │ │ ├── WeatherDetail.java │ │ ├── db │ │ │ ├── CityDatabaseHelper.java │ │ │ ├── WeatherDatabaseHelper.java │ │ │ ├── dao │ │ │ │ ├── CityDao.java │ │ │ │ └── WeatherDao.java │ │ │ └── entities │ │ │ │ ├── City.java │ │ │ │ ├── HotCity.java │ │ │ │ ├── adapter │ │ │ │ ├── CloudWeatherAdapter.java │ │ │ │ ├── KnowWeatherAdapter.java │ │ │ │ ├── MiWeatherAdapter.java │ │ │ │ └── WeatherAdapter.java │ │ │ │ └── minimalist │ │ │ │ ├── AirQualityLive.java │ │ │ │ ├── LifeIndex.java │ │ │ │ ├── Weather.java │ │ │ │ ├── WeatherForecast.java │ │ │ │ └── WeatherLive.java │ │ ├── http │ │ │ ├── ApiClient.java │ │ │ ├── ApiConstants.java │ │ │ ├── configuration │ │ │ │ └── ApiConfiguration.java │ │ │ ├── entity │ │ │ │ ├── envicloud │ │ │ │ │ ├── EnvironmentCloudCityAirLive.java │ │ │ │ │ ├── EnvironmentCloudForecast.java │ │ │ │ │ └── EnvironmentCloudWeatherLive.java │ │ │ │ ├── know │ │ │ │ │ └── KnowWeather.java │ │ │ │ └── mi │ │ │ │ │ ├── MiAQI.java │ │ │ │ │ ├── MiForecast.java │ │ │ │ │ ├── MiIndex.java │ │ │ │ │ ├── MiRealTime.java │ │ │ │ │ ├── MiToday.java │ │ │ │ │ └── MiWeather.java │ │ │ ├── interceptor │ │ │ │ └── HttpRequestInterceptor.java │ │ │ └── service │ │ │ │ ├── EnvironmentCloudWeatherService.java │ │ │ │ └── WeatherService.java │ │ ├── preference │ │ │ ├── ConfigurationListener.java │ │ │ ├── PreferenceHelper.java │ │ │ └── WeatherSettings.java │ │ └── repository │ │ │ └── WeatherDataRepository.java │ │ ├── di │ │ ├── component │ │ │ ├── ApplicationComponent.java │ │ │ └── PresenterComponent.java │ │ ├── module │ │ │ └── ApplicationModule.java │ │ └── scope │ │ │ └── ActivityScoped.java │ │ ├── feature │ │ ├── home │ │ │ ├── DetailAdapter.java │ │ │ ├── ForecastAdapter.java │ │ │ ├── HomePageComponent.java │ │ │ ├── HomePageContract.java │ │ │ ├── HomePageFragment.java │ │ │ ├── HomePageModule.java │ │ │ ├── HomePagePresenter.java │ │ │ ├── LifeIndexAdapter.java │ │ │ ├── MainActivity.java │ │ │ └── drawer │ │ │ │ ├── CityManagerAdapter.java │ │ │ │ ├── DrawerContract.java │ │ │ │ ├── DrawerMenuFragment.java │ │ │ │ ├── DrawerMenuModule.java │ │ │ │ └── DrawerMenuPresenter.java │ │ └── selectcity │ │ │ ├── CityListAdapter.java │ │ │ ├── SelectCityActivity.java │ │ │ ├── SelectCityComponent.java │ │ │ ├── SelectCityContract.java │ │ │ ├── SelectCityFragment.java │ │ │ ├── SelectCityModule.java │ │ │ └── SelectCityPresenter.java │ │ └── util │ │ ├── StethoHelper.java │ │ └── stetho │ │ └── ReleaseStethoHelper.java │ └── res │ ├── drawable-v21 │ ├── ic_menu_camera.xml │ ├── ic_menu_gallery.xml │ ├── ic_menu_manage.xml │ ├── ic_menu_send.xml │ ├── ic_menu_share.xml │ └── ic_menu_slideshow.xml │ ├── drawable-xhdpi │ └── ic_menu_search.png │ ├── drawable-xxhdpi │ ├── ic_menu_search.png │ ├── weather_bg_1.jpg │ └── weather_bg_2.jpg │ ├── drawable │ ├── bg_launch_window.xml │ ├── ic_index_car_wash.xml │ ├── ic_index_clod.xml │ ├── ic_index_dance.xml │ ├── ic_index_dress.xml │ ├── ic_index_shopping.xml │ ├── ic_index_sport.xml │ ├── ic_index_sun_cure.xml │ ├── ic_index_sunscreen.xml │ ├── ic_vector_weather.xml │ ├── ic_vector_weather2.xml │ ├── shape_card_bg.xml │ └── side_nav_bar.xml │ ├── layout-v21 │ ├── fragment_home_page.xml │ └── item_city_manager.xml │ ├── layout │ ├── activity_city_manager.xml │ ├── activity_main.xml │ ├── activity_select_city.xml │ ├── activity_welcome.xml │ ├── fragment_drawer_menu.xml │ ├── fragment_home_page.xml │ ├── fragment_select_city.xml │ ├── item_city.xml │ ├── item_city_manager.xml │ ├── item_detail.xml │ ├── item_forecast.xml │ ├── item_life_index.xml │ ├── layout_app_bar.xml │ ├── layout_app_bar_main.xml │ └── layout_drawer_menu_head.xml │ ├── menu │ ├── activity_main_drawer.xml │ ├── main.xml │ └── search_city.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 │ ├── raw │ └── city.db │ ├── values-v19 │ └── styles.xml │ ├── values-v21 │ └── styles.xml │ ├── values-v23 │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ ├── values │ ├── colors.xml │ ├── dimens.xml │ ├── drawables.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── network_security_config.xml ├── build.gradle ├── dependencies.gradle ├── framework_minimalist_weather.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── baronzhang │ │ └── android │ │ └── library │ │ ├── util │ │ ├── ActivityUtils.java │ │ ├── DateConvertUtils.java │ │ ├── NetworkUtils.java │ │ ├── RxSchedulerUtils.java │ │ ├── lifecycle │ │ │ ├── ActivityLifecycleEvent.java │ │ │ └── FragmentLifecycleEvent.java │ │ └── system │ │ │ ├── AndroidMHelper.java │ │ │ ├── FlymeHelper.java │ │ │ ├── MIUIHelper.java │ │ │ ├── StatusBarHelper.java │ │ │ └── SystemHelper.java │ │ └── view │ │ ├── DividerItemDecoration.java │ │ └── OnRecyclerViewItemClickListener.java │ └── res │ └── values │ └── strings.xml ├── settings.gradle ├── widget ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── baronzhang │ │ └── android │ │ └── widget │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── baronzhang │ │ │ └── android │ │ │ └── widget │ │ │ ├── IndicatorValueChangeListener.java │ │ │ ├── IndicatorView.java │ │ │ └── TitleView.java │ └── res │ │ ├── drawable-xxxhdpi │ │ └── housekeeper_hotdegree_mark.png │ │ ├── drawable │ │ ├── ic_vector_indicator_down.xml │ │ └── ic_vector_indicator_location.xml │ │ ├── layout │ │ └── layout_title_view.xml │ │ └── values │ │ ├── arrays.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── baronzhang │ └── android │ └── widget │ └── ExampleUnitTest.java └── 扫码关注.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MinimalistWeather 2 | 3 | > 欢迎关注微信公众号:**BaronTalk** 4 | 5 | 6 | ## 一. 前言 7 | 8 | 推荐阅读: 9 | 10 | * [安居客 Android 项目架构演进](https://mp.weixin.qq.com/s?__biz=MzU4ODM2MjczNA==&mid=2247483731&idx=1&sn=76bd5612ba723171b6ebac69aaf039f8&chksm=fddca7d2caab2ec4eec8736cf4005615c401984e2218a0cfc71dddfe3a204495c4e8a7312b4a&scene=38#wechat_redirect) 11 | * [Android 模块化探索与实践](https://mp.weixin.qq.com/s?__biz=MzU4ODM2MjczNA==&mid=2247483732&idx=1&sn=b7ee1151b2c8ad2e997b8db39adf3267&chksm=fddca7d5caab2ec33905cc3350f31c0c98794774b0d04a01845565e3989b1f20205c7f432cb9&scene=38#wechat_redirect) 12 | 13 | **MinimalistWeather 是 Android 平台上一款开源天气 App ,目前还在开发中。项目基于 MVP 架构,采用各主流开源库实现。开发此项目主要是为展示各种开源库的使用方式以及 Android 项目的设计方案,并作为团队项目开发规范的一部分。** 14 | 15 | 采用的开源库包括: 16 | 17 | * RxJava 18 | * Retrofit2 19 | * OKHttp3 20 | * ORMLite 21 | * Dagger2 22 | * ButterKnife 23 | * RetroLambda 24 | * Stetho 25 | 26 | **本项目还展示了:** 27 | 28 | * MVP+RxJava在实际项目中的应用,MVP中RxJava生命周期的管理...; 29 | * 上述罗列的各种开源框架的使用方法; 30 | * Java8 Lambda表达式和Stream API的用法; 31 | * 怎样适配Material Design; 32 | * ToolBar、RecycleView、CardView、CoordinatorLayout等新控件的用法; 33 | * Gradle的基本配置(包括签名打包、项目依赖等等); 34 | * 如何更好的管理Gradle依赖库的版本; 35 | * 代码混淆配置; 36 | * 如何快速开发一款结构清晰、可扩展性强的Android Application。 37 | 38 | ## 二. 项目结构设计图 39 | 40 | ![架构设计图](framework_minimalist_weather.png) 41 | 42 | ## 三. 项目包结构介绍 43 | 44 | **App Module包结构** 45 | 46 | ```Java 47 | -com.baronzhang.android.weather 48 | + base // MVP 各组件的基类及相关基础类 49 | + data // MVP 中所有 Model 层的数据处理都在这里 50 | - feature // 业务 feature,feature 内按页面划分,如果是大型项目可以按业务模块划分,对于特大型项目建议走模块化(组件化)方案,每个业务模块再按照 MinimalistWeather 的分包规则来分包 51 | + home 52 | - selectcity 53 | - xxActivity.java // Activity 作为全局的控制者,用来负责创建 View 和 Presenter 的实例 54 | - xxFragment.java 55 | - xxPresenter.java 56 | - xxContract.java // 契约类,用来统一管理 View 和 Presenter 的接口 57 | + util 58 | - AppConstants.java // App 全局常量 59 | - WeatherApplication.java // Application 类 60 | - WelcomeActivity.java // 放在这里是为了便于查找应用程序入口 61 | ``` 62 | 63 | **欢迎扫码关注公众号交流** 64 | 65 |
66 | 67 | ## 三. 开源许可 [![Hex.pm](https://img.shields.io/hexpm/l/plug.svg)](https://www.apache.org/licenses/LICENSE-2.0) 68 | 69 | ``` 70 | Copyright 2017 Baron Zhang 71 | 72 | Licensed under the Apache License, Version 2.0 (the "License"); 73 | you may not use this file except in compliance with the License. 74 | You may obtain a copy of the License at 75 | 76 | http://www.apache.org/licenses/LICENSE-2.0 77 | 78 | Unless required by applicable law or agreed to in writing, software 79 | distributed under the License is distributed on an "AS IS" BASIS, 80 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 81 | See the License for the specific language governing permissions and 82 | limitations under the License. 83 | ``` 84 | 85 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | 5 | compileOptions { 6 | sourceCompatibility JavaVersion.VERSION_1_8 7 | targetCompatibility JavaVersion.VERSION_1_8 8 | } 9 | 10 | compileSdkVersion rootProject.ext.android.compileSdkVersion 11 | defaultConfig { 12 | applicationId rootProject.ext.android.applicationId 13 | minSdkVersion rootProject.ext.android.minSdkVersion 14 | targetSdkVersion rootProject.ext.android.targetSdkVersion 15 | versionCode rootProject.ext.android.versionCode 16 | versionName rootProject.ext.android.versionName 17 | 18 | vectorDrawables.useSupportLibrary = true 19 | } 20 | buildTypes { 21 | debug { 22 | buildConfigField("boolean", "LOG_DEBUG", "true") 23 | buildConfigField 'com.baronzhang.android.weather.util.StethoHelper', 'STETHO', 'new com.baronzhang.android.weather.DebugStethoHelper()' 24 | } 25 | 26 | release { 27 | buildConfigField("boolean", "LOG_DEBUG", "false") 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 30 | buildConfigField 'com.baronzhang.android.weather.util.StethoHelper', 'STETHO', 'new com.baronzhang.android.weather.util.stetho.ReleaseStethoHelper()' 31 | } 32 | } 33 | 34 | lintOptions { 35 | abortOnError false 36 | } 37 | } 38 | 39 | dependencies { 40 | implementation fileTree(include: ['*.jar'], dir: 'libs') 41 | testImplementation 'junit:junit:4.12' 42 | 43 | implementation project(':library') 44 | implementation project(':widget') 45 | 46 | implementation rootProject.ext.dependencies["butterknife"] 47 | annotationProcessor rootProject.ext.dependencies["butterknife-compiler"] 48 | 49 | implementation rootProject.ext.dependencies["dagger"] 50 | annotationProcessor rootProject.ext.dependencies["dagger-compiler"] 51 | 52 | implementation rootProject.ext.dependencies["retrofit2-fastjson-converter"] 53 | } 54 | -------------------------------------------------------------------------------- /app/fabric.properties: -------------------------------------------------------------------------------- 1 | #Contains API Secret used to validate your application. Commit to internal source control; avoid making secret public. 2 | #Fri Jul 22 11:51:10 CST 2016 3 | apiSecret=e86a9bd81d553715a7b0b66668a872664323d6b52a6a1cd519437557c494909e 4 | -------------------------------------------------------------------------------- /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 /Users/baron/develop/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 public class * extends com.baronzhang.android.weather.util.StethoHelper 20 | 21 | # Retrofit 22 | -dontwarn okio.** 23 | -dontwarn javax.annotation.* -------------------------------------------------------------------------------- /app/src/debug/java/com/baronzhang/android/weather/DebugStethoHelper.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather; 2 | 3 | import android.content.Context; 4 | 5 | import com.baronzhang.android.weather.util.StethoHelper; 6 | import com.facebook.stetho.Stetho; 7 | import com.facebook.stetho.okhttp3.StethoInterceptor; 8 | 9 | import okhttp3.OkHttpClient; 10 | 11 | /** 12 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 13 | * 2017/7/25 14 | */ 15 | public class DebugStethoHelper implements StethoHelper { 16 | 17 | @Override 18 | public void init(Context context) { 19 | Stetho.initializeWithDefaults(context); 20 | } 21 | 22 | @Override 23 | public OkHttpClient.Builder addNetworkInterceptor(OkHttpClient.Builder builder) { 24 | return builder.addNetworkInterceptor(new StethoInterceptor()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BaronZ88/MinimalistWeather/879abc57e8a1d448b0675affcf936b084b7a7ae4/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/AppConstants.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather; 2 | 3 | /** 4 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 5 | * 16/3/13 6 | */ 7 | public class AppConstants { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/WeatherApplication.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.os.StrictMode; 6 | import android.util.Log; 7 | 8 | import com.baronzhang.android.weather.data.http.ApiClient; 9 | import com.baronzhang.android.weather.data.http.ApiConstants; 10 | import com.baronzhang.android.weather.data.http.configuration.ApiConfiguration; 11 | import com.baronzhang.android.weather.di.component.ApplicationComponent; 12 | import com.baronzhang.android.weather.di.component.DaggerApplicationComponent; 13 | import com.baronzhang.android.weather.di.module.ApplicationModule; 14 | 15 | /** 16 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 17 | * 16/2/4 18 | */ 19 | public class WeatherApplication extends Application { 20 | 21 | private static final String TAG = "WeatherApp"; 22 | 23 | private ApplicationComponent applicationComponent; 24 | 25 | private static WeatherApplication weatherApplicationInstance; 26 | 27 | public static WeatherApplication getInstance() { 28 | 29 | return weatherApplicationInstance; 30 | } 31 | 32 | @Override 33 | protected void attachBaseContext(Context base) { 34 | super.attachBaseContext(base); 35 | Log.d(TAG, "attachBaseContext"); 36 | } 37 | 38 | @Override 39 | public void onCreate() { 40 | super.onCreate(); 41 | Log.d(TAG, "onCreate start"); 42 | if (BuildConfig.DEBUG) { 43 | StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); 44 | StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build()); 45 | } 46 | 47 | applicationComponent = DaggerApplicationComponent.builder() 48 | .applicationModule(new ApplicationModule(this)) 49 | .build(); 50 | 51 | //初始化Stetho 52 | BuildConfig.STETHO.init(this.getApplicationContext()); 53 | 54 | weatherApplicationInstance = this; 55 | 56 | //初始化ApiClient 57 | ApiConfiguration apiConfiguration = ApiConfiguration.builder() 58 | // .dataSourceType(ApiConstants.WEATHER_DATA_SOURCE_TYPE_MI) 59 | // .dataSourceType(ApiConstants.WEATHER_DATA_SOURCE_TYPE_KNOW) 60 | .dataSourceType(ApiConstants.WEATHER_DATA_SOURCE_TYPE_ENVIRONMENT_CLOUD) 61 | .build(); 62 | ApiClient.init(apiConfiguration); 63 | Log.d(TAG, "onCreate end"); 64 | } 65 | 66 | 67 | public ApplicationComponent getApplicationComponent() { 68 | 69 | return applicationComponent; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/WelcomeActivity.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | 6 | import com.baronzhang.android.weather.base.BaseActivity; 7 | import com.baronzhang.android.library.util.system.StatusBarHelper; 8 | import com.baronzhang.android.weather.feature.home.MainActivity; 9 | import com.baronzhang.android.weather.data.db.CityDatabaseHelper; 10 | import com.baronzhang.android.weather.data.preference.PreferenceHelper; 11 | import com.baronzhang.android.weather.data.preference.WeatherSettings; 12 | 13 | import java.io.InvalidClassException; 14 | 15 | import rx.Observable; 16 | import rx.android.schedulers.AndroidSchedulers; 17 | import rx.schedulers.Schedulers; 18 | 19 | /** 20 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 21 | */ 22 | public class WelcomeActivity extends BaseActivity { 23 | 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | StatusBarHelper.statusBarLightMode(this); 29 | 30 | Observable.just(initAppData()) 31 | .subscribeOn(Schedulers.io()) 32 | .observeOn(AndroidSchedulers.mainThread()) 33 | .subscribe(result -> gotoMainPage()); 34 | 35 | } 36 | 37 | private void gotoMainPage() { 38 | Intent intent = new Intent(this, MainActivity.class); 39 | startActivity(intent); 40 | // 修复 Android 9.0 下 Activity 跳转动画导致的启动页闪屏的问题 41 | overridePendingTransition(0, 0); 42 | finish(); 43 | } 44 | 45 | /** 46 | * 初始化应用数据 47 | */ 48 | private String initAppData() { 49 | PreferenceHelper.loadDefaults(); 50 | //TODO 测试,待删除 51 | if (PreferenceHelper.getSharedPreferences().getBoolean(WeatherSettings.SETTINGS_FIRST_USE.getId(), false)) { 52 | try { 53 | PreferenceHelper.savePreference(WeatherSettings.SETTINGS_CURRENT_CITY_ID, "101020100"); 54 | PreferenceHelper.savePreference(WeatherSettings.SETTINGS_FIRST_USE, false); 55 | } catch (InvalidClassException e) { 56 | e.printStackTrace(); 57 | } 58 | } 59 | CityDatabaseHelper.importCityDB(); 60 | return null; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.base; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.appcompat.app.AppCompatActivity; 6 | import androidx.appcompat.app.AppCompatDelegate; 7 | import androidx.appcompat.widget.SearchView; 8 | import android.view.Menu; 9 | import android.view.MenuItem; 10 | 11 | /** 12 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 13 | */ 14 | public class BaseActivity extends AppCompatActivity { 15 | 16 | /* 17 | * 解决Vector兼容性问题 18 | * 19 | * First up, this functionality was originally released in 23.2.0, 20 | * but then we found some memory usage and Configuration updating 21 | * issues so we it removed in 23.3.0. In 23.4.0 (technically a fix 22 | * release) we’ve re-added the same functionality but behind a flag 23 | * which you need to manually enable. 24 | * 25 | * http://www.jianshu.com/p/e3614e7abc03 26 | */ 27 | static { 28 | AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); 29 | } 30 | 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | } 35 | 36 | @Override 37 | protected void onDestroy() { 38 | super.onDestroy(); 39 | } 40 | 41 | 42 | @Override 43 | public boolean onCreateOptionsMenu(Menu menu) { 44 | return super.onCreateOptionsMenu(menu); 45 | } 46 | 47 | @Override 48 | public boolean onOptionsItemSelected(MenuItem item) { 49 | int id = item.getItemId(); 50 | if (id == android.R.id.home) { 51 | finish(); 52 | return true; 53 | } 54 | return super.onOptionsItemSelected(item); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/base/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.base; 2 | 3 | 4 | import android.os.Bundle; 5 | import androidx.annotation.Nullable; 6 | import androidx.fragment.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | /** 12 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 13 | */ 14 | public class BaseFragment extends Fragment { 15 | 16 | // private PublishSubject fragmentLifecycleSubject = PublishSubject.create(); 17 | 18 | @Override 19 | public void onCreate(@Nullable Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | } 22 | 23 | @Override 24 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 25 | Bundle savedInstanceState) { 26 | return super.onCreateView(inflater, container, savedInstanceState); 27 | } 28 | 29 | @Override 30 | public void onDestroyView() { 31 | // fragmentLifecycleSubject.onNext(FragmentLifecycleEvent.DESTROY_VIEW); 32 | super.onDestroyView(); 33 | } 34 | 35 | @Override 36 | public void onDestroy() { 37 | super.onDestroy(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/base/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.base; 2 | 3 | /** 4 | * presenter interface,所有Presenter必须实现此接口 5 | * 6 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 7 | */ 8 | public interface BasePresenter { 9 | 10 | void subscribe(); 11 | 12 | void unSubscribe(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/base/BaseRecyclerViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.base; 2 | 3 | import androidx.recyclerview.widget.RecyclerView; 4 | import android.widget.AdapterView; 5 | 6 | /** 7 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 8 | * 16/4/15 9 | */ 10 | public abstract class BaseRecyclerViewAdapter extends RecyclerView.Adapter{ 11 | 12 | protected AdapterView.OnItemClickListener onItemClickListener; 13 | 14 | public void setOnItemClickListener(AdapterView.OnItemClickListener onItemClickListener) { 15 | 16 | this.onItemClickListener = onItemClickListener; 17 | } 18 | 19 | protected void onItemHolderClick(RecyclerView.ViewHolder itemHolder) { 20 | if (onItemClickListener != null) { 21 | onItemClickListener.onItemClick(null, itemHolder.itemView, 22 | itemHolder.getAdapterPosition(), itemHolder.getItemId()); 23 | } else { 24 | throw new IllegalStateException("Please call setOnItemClickListener method set the click event listeners"); 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/base/BaseView.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.base; 2 | 3 | /** 4 | * view interface,所有View(此项目中的View主要是Fragment和自定义的ViewGroup)必须实现此接口 5 | * 6 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 7 | */ 8 | public interface BaseView { 9 | 10 | void setPresenter(T presenter); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/WeatherDetail.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data; 2 | 3 | /** 4 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 5 | * 2017/7/6 6 | */ 7 | public class WeatherDetail { 8 | 9 | private int iconResourceId; 10 | private String key; 11 | private String value; 12 | 13 | public WeatherDetail(int iconResourceId, String key, String value) { 14 | this.iconResourceId = iconResourceId; 15 | this.key = key; 16 | this.value = value; 17 | } 18 | 19 | public int getIconResourceId() { 20 | return iconResourceId; 21 | } 22 | 23 | public void setIconResourceId(int iconResourceId) { 24 | this.iconResourceId = iconResourceId; 25 | } 26 | 27 | public String getKey() { 28 | return key; 29 | } 30 | 31 | public void setKey(String key) { 32 | this.key = key; 33 | } 34 | 35 | public String getValue() { 36 | return value; 37 | } 38 | 39 | public void setValue(String value) { 40 | this.value = value; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/db/CityDatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.db; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.util.Log; 6 | 7 | import com.baronzhang.android.weather.R; 8 | import com.baronzhang.android.weather.WeatherApplication; 9 | import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; 10 | import com.j256.ormlite.dao.Dao; 11 | import com.j256.ormlite.dao.DaoManager; 12 | import com.j256.ormlite.support.ConnectionSource; 13 | 14 | import java.io.File; 15 | import java.io.FileOutputStream; 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.sql.SQLException; 19 | 20 | /** 21 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 22 | * 16/3/13 23 | */ 24 | public final class CityDatabaseHelper extends OrmLiteSqliteOpenHelper { 25 | 26 | private static final String TAG = "CityDatabaseHelper"; 27 | 28 | private static final String DATABASE_NAME = "city.db"; 29 | private static final int DATABASE_VERSION = 1; 30 | 31 | private static volatile CityDatabaseHelper instance; 32 | 33 | public CityDatabaseHelper(Context context) { 34 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 35 | } 36 | 37 | @Override 38 | public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) { 39 | //由于城市数据库是由外部导入的,故不需要创建执行创建表的操作 40 | } 41 | 42 | @Override 43 | public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) { 44 | 45 | } 46 | 47 | /** 48 | * 单例获取OpenHelper实例 49 | * 50 | * @param context application context 51 | * @return instance 52 | */ 53 | public static CityDatabaseHelper getInstance(Context context) { 54 | 55 | context = context.getApplicationContext(); 56 | if (instance == null) { 57 | synchronized (CityDatabaseHelper.class) { 58 | if (instance == null) { 59 | instance = new CityDatabaseHelper(context); 60 | } 61 | } 62 | } 63 | return instance; 64 | } 65 | 66 | @Override 67 | public void close() { 68 | super.close(); 69 | DaoManager.clearCache(); 70 | } 71 | 72 | public , T> D getCityDao(Class clazz) { 73 | try { 74 | return getDao(clazz); 75 | } catch (SQLException e) { 76 | Log.e(TAG, e.getMessage()); 77 | } 78 | return null; 79 | } 80 | 81 | /** 82 | * 导入城市数据库 83 | */ 84 | public static void importCityDB() { 85 | 86 | // 判断保持城市的数据库文件是否存在 87 | File file = new File(WeatherApplication.getInstance().getDatabasePath(DATABASE_NAME).getAbsolutePath()); 88 | if (!file.exists()) {// 如果不存在,则导入数据库文件 89 | //数据库文件 90 | File dbFile = WeatherApplication.getInstance().getDatabasePath(DATABASE_NAME); 91 | try { 92 | if (!dbFile.getParentFile().exists()) { 93 | dbFile.getParentFile().mkdir(); 94 | } 95 | if (!dbFile.exists()) { 96 | dbFile.createNewFile(); 97 | } 98 | //加载欲导入的数据库 99 | InputStream is = WeatherApplication.getInstance().getResources().openRawResource(R.raw.city); 100 | FileOutputStream fos = new FileOutputStream(dbFile); 101 | byte[] buffer = new byte[is.available()]; 102 | is.read(buffer); 103 | fos.write(buffer); 104 | is.close(); 105 | fos.close(); 106 | 107 | } catch (IOException e) { 108 | e.printStackTrace(); 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/db/WeatherDatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.db; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.util.Log; 6 | 7 | import com.baronzhang.android.weather.data.db.entities.minimalist.AirQualityLive; 8 | import com.baronzhang.android.weather.data.db.entities.minimalist.WeatherForecast; 9 | import com.baronzhang.android.weather.data.db.entities.minimalist.LifeIndex; 10 | import com.baronzhang.android.weather.data.db.entities.minimalist.WeatherLive; 11 | import com.baronzhang.android.weather.data.db.entities.minimalist.Weather; 12 | import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; 13 | import com.j256.ormlite.dao.Dao; 14 | import com.j256.ormlite.dao.DaoManager; 15 | import com.j256.ormlite.support.ConnectionSource; 16 | import com.j256.ormlite.table.TableUtils; 17 | 18 | import java.sql.SQLException; 19 | 20 | /** 21 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 22 | * 16/3/13 23 | */ 24 | public final class WeatherDatabaseHelper extends OrmLiteSqliteOpenHelper { 25 | 26 | private static final String TAG = "WeatherDatabaseHelper"; 27 | 28 | private static final String DATABASE_NAME = "weather.db"; 29 | private static final int DATABASE_VERSION = 1; 30 | 31 | private static volatile WeatherDatabaseHelper instance; 32 | 33 | public WeatherDatabaseHelper(Context context) { 34 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 35 | } 36 | 37 | @Override 38 | public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) { 39 | 40 | try { 41 | TableUtils.createTableIfNotExists(connectionSource, AirQualityLive.class); 42 | TableUtils.createTableIfNotExists(connectionSource, WeatherForecast.class); 43 | TableUtils.createTableIfNotExists(connectionSource, LifeIndex.class); 44 | TableUtils.createTableIfNotExists(connectionSource, WeatherLive.class); 45 | TableUtils.createTableIfNotExists(connectionSource, Weather.class); 46 | 47 | String weatherTrigger = "CREATE TRIGGER trigger_delete AFTER DELETE " + 48 | "ON Weather " + 49 | "FOR EACH ROW " + 50 | "BEGIN " + 51 | "DELETE FROM AirQuality WHERE cityId = OLD.cityId; " + 52 | "DELETE FROM WeatherLive WHERE cityId = OLD.cityId; " + 53 | "DELETE FROM WeatherForecast WHERE cityId = OLD.cityId; " + 54 | "DELETE FROM LifeIndex WHERE cityId = OLD.cityId; " + 55 | "END;"; 56 | database.execSQL(weatherTrigger); 57 | 58 | } catch (SQLException e) { 59 | e.printStackTrace(); 60 | } 61 | } 62 | 63 | @Override 64 | public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) { 65 | 66 | onCreate(database, connectionSource); 67 | } 68 | 69 | /** 70 | * 单例获取OpenHelper实例 71 | * 72 | * @param context application context 73 | * @return instance 74 | */ 75 | public static WeatherDatabaseHelper getInstance(Context context) { 76 | 77 | context = context.getApplicationContext(); 78 | if (instance == null) { 79 | synchronized (WeatherDatabaseHelper.class) { 80 | if (instance == null) { 81 | instance = new WeatherDatabaseHelper(context); 82 | } 83 | } 84 | } 85 | return instance; 86 | } 87 | 88 | @Override 89 | public void close() { 90 | super.close(); 91 | DaoManager.clearCache(); 92 | } 93 | 94 | public , T> D getWeatherDao(Class clazz) { 95 | try { 96 | return getDao(clazz); 97 | } catch (SQLException e) { 98 | Log.e(TAG, e.getMessage()); 99 | } 100 | return null; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/db/dao/CityDao.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.db.dao; 2 | 3 | import android.content.Context; 4 | 5 | import com.baronzhang.android.weather.data.db.entities.City; 6 | import com.j256.ormlite.dao.Dao; 7 | import com.j256.ormlite.stmt.QueryBuilder; 8 | 9 | import java.sql.SQLException; 10 | import java.util.List; 11 | 12 | import javax.inject.Inject; 13 | 14 | import com.baronzhang.android.weather.data.db.CityDatabaseHelper; 15 | import com.baronzhang.android.weather.data.db.entities.HotCity; 16 | 17 | /** 18 | * City表操作类 19 | * 20 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 21 | * 16/3/13 22 | */ 23 | public class CityDao { 24 | 25 | private Dao cityDaoOperation; 26 | private Dao hotCityDaoOperation; 27 | 28 | @Inject 29 | CityDao(Context context) { 30 | 31 | this.cityDaoOperation = CityDatabaseHelper.getInstance(context).getCityDao(City.class); 32 | this.hotCityDaoOperation = CityDatabaseHelper.getInstance(context).getCityDao(HotCity.class); 33 | } 34 | 35 | /** 36 | * 查询表中的所有城市 37 | * 38 | * @return 城市列表数据 39 | */ 40 | public List queryCityList() { 41 | 42 | try { 43 | return cityDaoOperation.queryForAll(); 44 | } catch (SQLException e) { 45 | e.printStackTrace(); 46 | } 47 | return null; 48 | } 49 | 50 | /** 51 | * 根据城市查询城市信息 52 | * 53 | * @param cityId 城市ID 54 | * @return city 55 | * @throws SQLException 56 | */ 57 | public City queryCityById(String cityId) throws SQLException { 58 | 59 | QueryBuilder queryBuilder = cityDaoOperation.queryBuilder(); 60 | queryBuilder.where().eq(City.CITY_ID_FIELD_NAME, cityId); 61 | 62 | return queryBuilder.queryForFirst(); 63 | } 64 | 65 | /** 66 | * 查询所有热门城市 67 | * 68 | * @return 热门城市列表 69 | */ 70 | public List queryAllHotCity() { 71 | try { 72 | return hotCityDaoOperation.queryForAll(); 73 | } catch (SQLException e) { 74 | e.printStackTrace(); 75 | } 76 | return null; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/db/entities/City.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.db.entities; 2 | 3 | import com.j256.ormlite.field.DatabaseField; 4 | import com.j256.ormlite.table.DatabaseTable; 5 | 6 | /** 7 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 8 | * 16/3/11 9 | */ 10 | @DatabaseTable(tableName = "City") 11 | public class City { 12 | 13 | public static final String ID_FIELD_NAME = "_id"; 14 | public static final String ROOT_FIELD_NAME = "root"; 15 | public static final String PARENT_FIELD_NAME = "parent"; 16 | public static final String CITY_NAME_FIELD_NAME = "name"; 17 | public static final String CITY_NAME_EN_FIELD_NAME = "pinyin"; 18 | public static final String LON_FIELD_NAME = "x"; 19 | public static final String LAT_FIELD_NAME = "y"; 20 | public static final String CITY_ID_FIELD_NAME = "posID"; 21 | 22 | @DatabaseField(columnName = ID_FIELD_NAME, generatedId = true) 23 | private int id; 24 | @DatabaseField(columnName = ROOT_FIELD_NAME) 25 | private String root; 26 | @DatabaseField(columnName = PARENT_FIELD_NAME) 27 | private String parent; 28 | @DatabaseField(columnName = CITY_ID_FIELD_NAME) 29 | private int cityId; 30 | @DatabaseField(columnName = CITY_NAME_FIELD_NAME) 31 | private String cityName; 32 | @DatabaseField(columnName = CITY_NAME_EN_FIELD_NAME) 33 | private String cityNameEn; 34 | @DatabaseField(columnName = LON_FIELD_NAME) 35 | private String lon; 36 | @DatabaseField(columnName = LAT_FIELD_NAME) 37 | private String lat; 38 | 39 | public String getRoot() { 40 | return root; 41 | } 42 | 43 | public void setRoot(String root) { 44 | this.root = root; 45 | } 46 | 47 | public String getParent() { 48 | return parent; 49 | } 50 | 51 | public void setParent(String parent) { 52 | this.parent = parent; 53 | } 54 | 55 | public int getCityId() { 56 | return cityId; 57 | } 58 | 59 | public void setCityId(int cityId) { 60 | this.cityId = cityId; 61 | } 62 | 63 | public String getCityName() { 64 | return cityName; 65 | } 66 | 67 | public void setCityName(String cityName) { 68 | this.cityName = cityName; 69 | } 70 | 71 | public String getCityNameEn() { 72 | return cityNameEn; 73 | } 74 | 75 | public void setCityNameEn(String cityNameEn) { 76 | this.cityNameEn = cityNameEn; 77 | } 78 | 79 | public String getLon() { 80 | return lon; 81 | } 82 | 83 | public void setLon(String lon) { 84 | this.lon = lon; 85 | } 86 | 87 | public String getLat() { 88 | return lat; 89 | } 90 | 91 | public void setLat(String lat) { 92 | this.lat = lat; 93 | } 94 | 95 | @Override 96 | public String toString() { 97 | return "CityInfo{" + 98 | "id=" + id + 99 | ", root='" + root + '\'' + 100 | ", parent='" + parent + '\'' + 101 | ", cityId='" + cityId + '\'' + 102 | ", cityName='" + cityName + '\'' + 103 | ", cityNameEn='" + cityNameEn + '\'' + 104 | ", lon='" + lon + '\'' + 105 | ", lat='" + lat + '\'' + 106 | '}'; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/db/entities/HotCity.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.db.entities; 2 | 3 | import com.j256.ormlite.field.DatabaseField; 4 | import com.j256.ormlite.table.DatabaseTable; 5 | 6 | /** 7 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 8 | * 16/6/21 9 | */ 10 | @DatabaseTable(tableName = "HotCity") 11 | public class HotCity { 12 | 13 | public static final String ID_FIELD_NAME = "_id"; 14 | public static final String CITY_NAME_FIELD_NAME = "name"; 15 | public static final String CITY_ID_FIELD_NAME = "posID"; 16 | 17 | @DatabaseField(columnName = ID_FIELD_NAME, generatedId = true) 18 | private int id; 19 | @DatabaseField(columnName = CITY_ID_FIELD_NAME) 20 | private int cityId; 21 | @DatabaseField(columnName = CITY_NAME_FIELD_NAME) 22 | private String cityName; 23 | 24 | public int getCityId() { 25 | return cityId; 26 | } 27 | 28 | public void setCityId(int cityId) { 29 | this.cityId = cityId; 30 | } 31 | 32 | public String getCityName() { 33 | return cityName; 34 | } 35 | 36 | public void setCityName(String cityName) { 37 | this.cityName = cityName; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/db/entities/adapter/WeatherAdapter.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.db.entities.adapter; 2 | 3 | import com.baronzhang.android.weather.data.db.entities.minimalist.AirQualityLive; 4 | import com.baronzhang.android.weather.data.db.entities.minimalist.LifeIndex; 5 | import com.baronzhang.android.weather.data.db.entities.minimalist.Weather; 6 | import com.baronzhang.android.weather.data.db.entities.minimalist.WeatherForecast; 7 | import com.baronzhang.android.weather.data.db.entities.minimalist.WeatherLive; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 13 | * 16/2/25 14 | */ 15 | public abstract class WeatherAdapter { 16 | 17 | public abstract String getCityId(); 18 | 19 | public abstract String getCityName(); 20 | 21 | public abstract String getCityNameEn(); 22 | 23 | public abstract WeatherLive getWeatherLive(); 24 | 25 | public abstract List getWeatherForecasts(); 26 | 27 | public abstract List getLifeIndexes(); 28 | 29 | public abstract AirQualityLive getAirQualityLive(); 30 | 31 | public Weather getWeather() { 32 | 33 | Weather weather = new Weather(); 34 | weather.setCityId(getCityId()); 35 | weather.setCityName(getCityName()); 36 | weather.setCityNameEn(getCityNameEn()); 37 | weather.setAirQualityLive(getAirQualityLive()); 38 | weather.setWeatherForecasts(getWeatherForecasts()); 39 | weather.setLifeIndexes(getLifeIndexes()); 40 | weather.setWeatherLive(getWeatherLive()); 41 | return weather; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/db/entities/minimalist/LifeIndex.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.db.entities.minimalist; 2 | 3 | import com.j256.ormlite.field.DatabaseField; 4 | import com.j256.ormlite.table.DatabaseTable; 5 | 6 | /** 7 | * @author baron (baronzhang[at]anjuke[dot]com) 8 | * 16/2/25 9 | */ 10 | @DatabaseTable(tableName = "LifeIndex") 11 | public class LifeIndex { 12 | 13 | public static final String ID_FIELD_NAME = "_id"; 14 | public static final String CITY_ID_FIELD_NAME = "cityId"; 15 | public static final String NAME_ID_FIELD_NAME = "name"; 16 | public static final String INDEX_ID_FIELD_NAME = "index"; 17 | public static final String DETAILS_ID_FIELD_NAME = "details"; 18 | 19 | @DatabaseField(columnName = ID_FIELD_NAME, generatedId = true) 20 | private long id;//数据库自增长ID 21 | @DatabaseField(columnName = CITY_ID_FIELD_NAME) 22 | private String cityId; 23 | @DatabaseField(columnName = NAME_ID_FIELD_NAME) 24 | private String name; 25 | @DatabaseField(columnName = INDEX_ID_FIELD_NAME) 26 | private String index; 27 | @DatabaseField(columnName = DETAILS_ID_FIELD_NAME) 28 | private String details; 29 | 30 | public LifeIndex() { 31 | } 32 | 33 | public LifeIndex(String cityId, String name, String index, String details) { 34 | 35 | this.cityId = cityId; 36 | this.name = name; 37 | this.index = index; 38 | this.details = details; 39 | } 40 | 41 | public long getId() { 42 | return id; 43 | } 44 | 45 | public void setId(long id) { 46 | this.id = id; 47 | } 48 | 49 | public String getCityId() { 50 | return cityId; 51 | } 52 | 53 | public void setCityId(String cityId) { 54 | this.cityId = cityId; 55 | } 56 | 57 | public String getDetails() { 58 | return details; 59 | } 60 | 61 | public void setDetails(String details) { 62 | this.details = details; 63 | } 64 | 65 | public String getIndex() { 66 | return index; 67 | } 68 | 69 | public void setIndex(String index) { 70 | this.index = index; 71 | } 72 | 73 | public String getName() { 74 | return name; 75 | } 76 | 77 | public void setName(String name) { 78 | this.name = name; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return "LifeIndex{" + 84 | "id=" + id + 85 | ", cityId=" + cityId + 86 | ", name='" + name + '\'' + 87 | ", index='" + index + '\'' + 88 | ", details='" + details + '\'' + 89 | '}'; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/db/entities/minimalist/Weather.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.db.entities.minimalist; 2 | 3 | import com.j256.ormlite.field.DatabaseField; 4 | import com.j256.ormlite.table.DatabaseTable; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 10 | * 16/2/25 11 | */ 12 | @DatabaseTable(tableName = "Weather") 13 | public class Weather { 14 | 15 | public static final String CITY_ID_FIELD_NAME = "cityId"; 16 | public static final String CITY_NAME_FIELD_NAME = "cityName"; 17 | public static final String CITY_NAME_EN_FIELD_NAME = "cityNameEn"; 18 | 19 | @DatabaseField(columnName = CITY_ID_FIELD_NAME, id = true) 20 | private String cityId; 21 | @DatabaseField(columnName = CITY_NAME_FIELD_NAME) 22 | private String cityName; 23 | @DatabaseField(columnName = CITY_NAME_EN_FIELD_NAME) 24 | private String cityNameEn; 25 | 26 | private WeatherLive weatherLive; 27 | 28 | private List weatherForecasts; 29 | 30 | private AirQualityLive airQualityLive; 31 | 32 | private List lifeIndexes; 33 | 34 | public AirQualityLive getAirQualityLive() { 35 | return airQualityLive; 36 | } 37 | 38 | public void setAirQualityLive(AirQualityLive airQualityLive) { 39 | this.airQualityLive = airQualityLive; 40 | } 41 | 42 | public String getCityId() { 43 | return cityId; 44 | } 45 | 46 | public void setCityId(String cityId) { 47 | this.cityId = cityId; 48 | } 49 | 50 | public String getCityName() { 51 | return cityName; 52 | } 53 | 54 | public void setCityName(String cityName) { 55 | this.cityName = cityName; 56 | } 57 | 58 | public String getCityNameEn() { 59 | return cityNameEn; 60 | } 61 | 62 | public void setCityNameEn(String cityNameEn) { 63 | this.cityNameEn = cityNameEn; 64 | } 65 | 66 | public List getWeatherForecasts() { 67 | return weatherForecasts; 68 | } 69 | 70 | public void setWeatherForecasts(List weatherForecasts) { 71 | this.weatherForecasts = weatherForecasts; 72 | } 73 | 74 | public List getLifeIndexes() { 75 | return lifeIndexes; 76 | } 77 | 78 | public void setLifeIndexes(List lifeIndexes) { 79 | this.lifeIndexes = lifeIndexes; 80 | } 81 | 82 | public WeatherLive getWeatherLive() { 83 | return weatherLive; 84 | } 85 | 86 | public void setWeatherLive(WeatherLive weatherLive) { 87 | this.weatherLive = weatherLive; 88 | } 89 | 90 | @Override 91 | public String toString() { 92 | return "WeatherData{" + 93 | "aqi=" + airQualityLive + 94 | ", cityId='" + cityId + '\'' + 95 | ", cityName='" + cityName + '\'' + 96 | ", cityNameEn='" + cityNameEn + '\'' + 97 | ", realTime=" + weatherLive + 98 | ", forecasts=" + weatherForecasts + 99 | ", lifeIndexes=" + lifeIndexes + 100 | '}'; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/http/ApiClient.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.http; 2 | 3 | import com.baronzhang.android.weather.BuildConfig; 4 | import com.baronzhang.android.weather.data.http.configuration.ApiConfiguration; 5 | import com.baronzhang.android.weather.data.http.service.EnvironmentCloudWeatherService; 6 | import com.baronzhang.android.weather.data.http.service.WeatherService; 7 | import com.baronzhang.retrofit2.converter.FastJsonConverterFactory; 8 | 9 | import okhttp3.OkHttpClient; 10 | import okhttp3.logging.HttpLoggingInterceptor; 11 | import retrofit2.Retrofit; 12 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 13 | 14 | /** 15 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 16 | * 16/2/25 17 | */ 18 | public final class ApiClient { 19 | 20 | public static WeatherService weatherService; 21 | public static EnvironmentCloudWeatherService environmentCloudWeatherService; 22 | 23 | public static ApiConfiguration configuration; 24 | 25 | public static void init(ApiConfiguration apiConfiguration) { 26 | 27 | configuration = apiConfiguration; 28 | String weatherApiHost; 29 | switch (configuration.getDataSourceType()) { 30 | case ApiConstants.WEATHER_DATA_SOURCE_TYPE_KNOW: 31 | weatherApiHost = ApiConstants.KNOW_WEATHER_API_HOST; 32 | weatherService = initWeatherService(weatherApiHost, WeatherService.class); 33 | break; 34 | case ApiConstants.WEATHER_DATA_SOURCE_TYPE_MI: 35 | weatherApiHost = ApiConstants.MI_WEATHER_API_HOST; 36 | weatherService = initWeatherService(weatherApiHost, WeatherService.class); 37 | break; 38 | case ApiConstants.WEATHER_DATA_SOURCE_TYPE_ENVIRONMENT_CLOUD: 39 | weatherApiHost = ApiConstants.ENVIRONMENT_CLOUD_WEATHER_API_HOST; 40 | environmentCloudWeatherService = initWeatherService(weatherApiHost, EnvironmentCloudWeatherService.class); 41 | break; 42 | } 43 | } 44 | 45 | private static T initWeatherService(String baseUrl, Class clazz) { 46 | 47 | OkHttpClient.Builder builder = new OkHttpClient().newBuilder(); 48 | if (BuildConfig.DEBUG) { 49 | HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(); 50 | httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 51 | builder.addInterceptor(httpLoggingInterceptor); 52 | // builder.addNetworkInterceptor(new StethoInterceptor()); 53 | BuildConfig.STETHO.addNetworkInterceptor(builder); 54 | } 55 | OkHttpClient client = builder.build(); 56 | 57 | Retrofit retrofit = new Retrofit.Builder() 58 | .baseUrl(baseUrl) 59 | .addConverterFactory(FastJsonConverterFactory.create()) 60 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 61 | .client(client) 62 | .build(); 63 | 64 | return retrofit.create(clazz); 65 | } 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/http/ApiConstants.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.http; 2 | 3 | /** 4 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 5 | * 16/3/8 6 | */ 7 | public final class ApiConstants { 8 | 9 | static final String MI_WEATHER_API_HOST = "http://weatherapi.market.xiaomi.com/wtr-v2/"; 10 | static final String KNOW_WEATHER_API_HOST = "http://knowweather.duapp.com/"; 11 | static final String ENVIRONMENT_CLOUD_WEATHER_API_HOST = "http://service.envicloud.cn:8082/"; 12 | 13 | public static final int WEATHER_DATA_SOURCE_TYPE_KNOW = 1; 14 | public static final int WEATHER_DATA_SOURCE_TYPE_MI = 2; 15 | public static final int WEATHER_DATA_SOURCE_TYPE_ENVIRONMENT_CLOUD = 3; 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/http/configuration/ApiConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.http.configuration; 2 | 3 | 4 | import com.baronzhang.android.weather.data.http.ApiConstants; 5 | 6 | /** 7 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 8 | * 2016/12/10 9 | */ 10 | public class ApiConfiguration { 11 | 12 | private int dataSourceType; 13 | 14 | private ApiConfiguration(Builder builder) { 15 | initialize(builder); 16 | } 17 | 18 | public static Builder builder() { 19 | return new Builder(); 20 | } 21 | 22 | private void initialize(final Builder builder) { 23 | this.dataSourceType = builder.dataSourceType; 24 | } 25 | 26 | public int getDataSourceType() { 27 | return dataSourceType; 28 | } 29 | 30 | public static final class Builder { 31 | 32 | private int dataSourceType; 33 | 34 | private Builder() { 35 | } 36 | 37 | public ApiConfiguration build() { 38 | if (dataSourceType != ApiConstants.WEATHER_DATA_SOURCE_TYPE_KNOW 39 | && dataSourceType != ApiConstants.WEATHER_DATA_SOURCE_TYPE_MI 40 | && dataSourceType != ApiConstants.WEATHER_DATA_SOURCE_TYPE_ENVIRONMENT_CLOUD) { 41 | throw new IllegalStateException("The dataSourceType does not support!"); 42 | } 43 | return new ApiConfiguration(this); 44 | } 45 | 46 | public Builder dataSourceType(int dataSourceType) { 47 | this.dataSourceType = dataSourceType; 48 | return this; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/http/entity/envicloud/EnvironmentCloudCityAirLive.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.http.entity.envicloud; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | /** 6 | * 城市实时空气质量 7 | * 8 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 9 | * 2017/2/16 10 | */ 11 | public class EnvironmentCloudCityAirLive { 12 | 13 | 14 | /** 15 | * citycode : 101020100 16 | * PM25 : 33 17 | * time : 2017021614 18 | * rdesc : Success 19 | * PM10 : 43 20 | * SO2 : 12.25 21 | * o3 : 51.58 22 | * NO2 : 53.17 23 | * primary : 颗粒物(PM10) 24 | * rcode : 200 25 | * CO : 0.77 26 | * AQI : 46 27 | */ 28 | 29 | @JSONField(name = "rcode") 30 | private int requestCode;//结果吗 31 | 32 | @JSONField(name = "rdesc") 33 | private String requestDesc;//结果描述 34 | 35 | @JSONField(name = "citycode") 36 | private String cityId;//城市ID 37 | 38 | private String time;//时间(yyyyMMddHH) 39 | 40 | @JSONField(name = "AQI") 41 | private String aqi;//空气质量指数 42 | 43 | @JSONField(name = "PM25") 44 | private String pm25;//PM2.5浓度(μg/m3) 45 | 46 | @JSONField(name = "PM10") 47 | private String pm10;//PM10浓度(μg/m3) 48 | 49 | @JSONField(name = "CO") 50 | private String co;//一氧化碳浓度(mg/m3) 51 | 52 | @JSONField(name = "SO2") 53 | private String so2;//二氧化硫浓度(μg/m3) 54 | 55 | @JSONField(name = "NO2") 56 | private String no2;//二氧化氮浓度(μg/m3) 57 | 58 | private String o3;//臭氧浓度(μg/m3) 59 | 60 | private String primary;//首要污染物 61 | 62 | public int getRequestCode() { 63 | return requestCode; 64 | } 65 | 66 | public void setRequestCode(int requestCode) { 67 | this.requestCode = requestCode; 68 | } 69 | 70 | public String getRequestDesc() { 71 | return requestDesc; 72 | } 73 | 74 | public void setRequestDesc(String requestDesc) { 75 | this.requestDesc = requestDesc; 76 | } 77 | 78 | public String getCityId() { 79 | return cityId; 80 | } 81 | 82 | public void setCityId(String cityId) { 83 | this.cityId = cityId; 84 | } 85 | 86 | public String getTime() { 87 | return time; 88 | } 89 | 90 | public void setTime(String time) { 91 | this.time = time; 92 | } 93 | 94 | public String getAqi() { 95 | return aqi; 96 | } 97 | 98 | public void setAqi(String aqi) { 99 | this.aqi = aqi; 100 | } 101 | 102 | public String getPm25() { 103 | return pm25; 104 | } 105 | 106 | public void setPm25(String pm25) { 107 | this.pm25 = pm25; 108 | } 109 | 110 | public String getPm10() { 111 | return pm10; 112 | } 113 | 114 | public void setPm10(String pm10) { 115 | this.pm10 = pm10; 116 | } 117 | 118 | public String getCo() { 119 | return co; 120 | } 121 | 122 | public void setCo(String co) { 123 | this.co = co; 124 | } 125 | 126 | public String getSo2() { 127 | return so2; 128 | } 129 | 130 | public void setSo2(String so2) { 131 | this.so2 = so2; 132 | } 133 | 134 | public String getNo2() { 135 | return no2; 136 | } 137 | 138 | public void setNo2(String no2) { 139 | this.no2 = no2; 140 | } 141 | 142 | public String getO3() { 143 | return o3; 144 | } 145 | 146 | public void setO3(String o3) { 147 | this.o3 = o3; 148 | } 149 | 150 | public String getPrimary() { 151 | return primary; 152 | } 153 | 154 | public void setPrimary(String primary) { 155 | this.primary = primary; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/http/entity/envicloud/EnvironmentCloudWeatherLive.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.http.entity.envicloud; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | /** 6 | * 天气实况 7 | * 8 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 9 | * 2017/2/16 10 | */ 11 | public class EnvironmentCloudWeatherLive { 12 | 13 | 14 | /** 15 | * airpressure : 1016.0 16 | * rain : 0.0 17 | * windpower : 微风 18 | * rcode : 200 19 | * feelst : 17.7 20 | * citycode : 101020100 21 | * rdesc : Success 22 | * winddirect : 西北风 23 | * temperature : 17.8 24 | * humidity : 50.0 25 | * windspeed : 0.9 26 | * updatetime : 2017-02-16 14:06 27 | * phenomena : 阵雨 28 | */ 29 | 30 | @JSONField(name = "rcode") 31 | private int requestCode;//结果吗 32 | 33 | @JSONField(name = "rdesc") 34 | private String requestDesc;//结果描述 35 | 36 | @JSONField(name = "updatetime") 37 | private String updateTime;//更新时间 38 | 39 | private String phenomena;//天气现象 40 | 41 | private String temperature;//气温(℃) 42 | 43 | @JSONField(name = "feelst") 44 | private String feelsTemperature;//体感温度(℃) 45 | 46 | @JSONField(name = "airpressure") 47 | private String airPressure;//气压(hPa) 48 | 49 | private String humidity;//相对湿度(%) 50 | 51 | private String rain;//降雨量(mm) 52 | 53 | @JSONField(name = "winddirect") 54 | private String windDirect;//风向 55 | 56 | @JSONField(name = "windpower") 57 | private String windPower;//风力 58 | 59 | @JSONField(name = "windspeed") 60 | private String windSpeed;//风速(m/s) 61 | 62 | @JSONField(name = "citycode") 63 | private String cityId;//城市ID 64 | 65 | public int getRequestCode() { 66 | return requestCode; 67 | } 68 | 69 | public void setRequestCode(int requestCode) { 70 | this.requestCode = requestCode; 71 | } 72 | 73 | public String getRequestDesc() { 74 | return requestDesc; 75 | } 76 | 77 | public void setRequestDesc(String requestDesc) { 78 | this.requestDesc = requestDesc; 79 | } 80 | 81 | public String getUpdateTime() { 82 | return updateTime; 83 | } 84 | 85 | public void setUpdateTime(String updateTime) { 86 | this.updateTime = updateTime; 87 | } 88 | 89 | public String getPhenomena() { 90 | return phenomena; 91 | } 92 | 93 | public void setPhenomena(String phenomena) { 94 | this.phenomena = phenomena; 95 | } 96 | 97 | public String getTemperature() { 98 | return temperature; 99 | } 100 | 101 | public void setTemperature(String temperature) { 102 | this.temperature = temperature; 103 | } 104 | 105 | public String getFeelsTemperature() { 106 | return feelsTemperature; 107 | } 108 | 109 | public void setFeelsTemperature(String feelsTemperature) { 110 | this.feelsTemperature = feelsTemperature; 111 | } 112 | 113 | public String getAirPressure() { 114 | return airPressure; 115 | } 116 | 117 | public void setAirPressure(String airPressure) { 118 | this.airPressure = airPressure; 119 | } 120 | 121 | public String getHumidity() { 122 | return humidity; 123 | } 124 | 125 | public void setHumidity(String humidity) { 126 | this.humidity = humidity; 127 | } 128 | 129 | public String getRain() { 130 | return rain; 131 | } 132 | 133 | public void setRain(String rain) { 134 | this.rain = rain; 135 | } 136 | 137 | public String getWindDirect() { 138 | return windDirect; 139 | } 140 | 141 | public void setWindDirect(String windDirect) { 142 | this.windDirect = windDirect; 143 | } 144 | 145 | public String getWindPower() { 146 | return windPower; 147 | } 148 | 149 | public void setWindPower(String windPower) { 150 | this.windPower = windPower; 151 | } 152 | 153 | public String getWindSpeed() { 154 | return windSpeed; 155 | } 156 | 157 | public void setWindSpeed(String windSpeed) { 158 | this.windSpeed = windSpeed; 159 | } 160 | 161 | public String getCityId() { 162 | return cityId; 163 | } 164 | 165 | public void setCityId(String cityId) { 166 | this.cityId = cityId; 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/http/entity/mi/MiAQI.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.http.entity.mi; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | /** 6 | * 小米天气空气污染指数 7 | * 8 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 9 | * 16/2/25 10 | */ 11 | public class MiAQI { 12 | 13 | @JSONField(name = "city") 14 | private String cityName; 15 | @JSONField(name = "city_id") 16 | private int cityId; 17 | @JSONField(name = "pub_time") 18 | private String publishTime; 19 | private int aqi; 20 | private int pm25; 21 | private int pm10; 22 | private int so2; 23 | private int no3; 24 | private String src; 25 | private String spot; 26 | 27 | public int getAqi() { 28 | return aqi; 29 | } 30 | 31 | public void setAqi(int aqi) { 32 | this.aqi = aqi; 33 | } 34 | 35 | public int getCityId() { 36 | return cityId; 37 | } 38 | 39 | public void setCityId(int cityId) { 40 | this.cityId = cityId; 41 | } 42 | 43 | public String getCityName() { 44 | return cityName; 45 | } 46 | 47 | public void setCityName(String cityName) { 48 | this.cityName = cityName; 49 | } 50 | 51 | public int getNo3() { 52 | return no3; 53 | } 54 | 55 | public void setNo3(int no3) { 56 | this.no3 = no3; 57 | } 58 | 59 | public int getPm10() { 60 | return pm10; 61 | } 62 | 63 | public void setPm10(int pm10) { 64 | this.pm10 = pm10; 65 | } 66 | 67 | public int getPm25() { 68 | return pm25; 69 | } 70 | 71 | public void setPm25(int pm25) { 72 | this.pm25 = pm25; 73 | } 74 | 75 | public String getPublishTime() { 76 | return publishTime; 77 | } 78 | 79 | public void setPublishTime(String publishTime) { 80 | this.publishTime = publishTime; 81 | } 82 | 83 | public int getSo2() { 84 | return so2; 85 | } 86 | 87 | public void setSo2(int so2) { 88 | this.so2 = so2; 89 | } 90 | 91 | public String getSpot() { 92 | return spot; 93 | } 94 | 95 | public void setSpot(String spot) { 96 | this.spot = spot; 97 | } 98 | 99 | public String getSrc() { 100 | return src; 101 | } 102 | 103 | public void setSrc(String src) { 104 | this.src = src; 105 | } 106 | 107 | @Override 108 | public String toString() { 109 | return "MiAQI{" + 110 | "cityName='" + cityName + '\'' + 111 | ", cityId='" + cityId + '\'' + 112 | ", publishTime='" + publishTime + '\'' + 113 | ", aqi=" + aqi + 114 | ", pm25=" + pm25 + 115 | ", pm10=" + pm10 + 116 | ", so2=" + so2 + 117 | ", no3=" + no3 + 118 | ", src='" + src + '\'' + 119 | ", spot='" + spot + '\'' + 120 | '}'; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/http/entity/mi/MiIndex.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.http.entity.mi; 2 | 3 | /** 4 | * 小米天气生活指数 5 | * 6 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 7 | * 16/2/25 8 | */ 9 | public class MiIndex { 10 | 11 | private String code; 12 | private String details; 13 | private String index; 14 | private String name; 15 | 16 | public String getCode() { 17 | return code; 18 | } 19 | 20 | public void setCode(String code) { 21 | this.code = code; 22 | } 23 | 24 | public String getDetails() { 25 | return details; 26 | } 27 | 28 | public void setDetails(String details) { 29 | this.details = details; 30 | } 31 | 32 | public String getIndex() { 33 | return index; 34 | } 35 | 36 | public void setIndex(String index) { 37 | this.index = index; 38 | } 39 | 40 | public String getName() { 41 | return name; 42 | } 43 | 44 | public void setName(String name) { 45 | this.name = name; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "MiIndex{" + 51 | "code='" + code + '\'' + 52 | ", details='" + details + '\'' + 53 | ", index='" + index + '\'' + 54 | ", name='" + name + '\'' + 55 | '}'; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/http/entity/mi/MiRealTime.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.http.entity.mi; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | /** 6 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 7 | * 16/2/25 8 | */ 9 | public class MiRealTime { 10 | 11 | @JSONField(name = "SD") 12 | private String humidity;//湿度 13 | @JSONField(name = "WD") 14 | private String wind;//风向 15 | @JSONField(name = "WS") 16 | private String windSpeed;//风速 17 | @JSONField(name = "cityid") 18 | private String cityId; 19 | private String temp;//温度 20 | private String time;//发布时间 21 | private String weather;//天气情况 22 | 23 | public String getCityId() { 24 | return cityId; 25 | } 26 | 27 | public void setCityId(String cityId) { 28 | this.cityId = cityId; 29 | } 30 | 31 | public String getHumidity() { 32 | return humidity; 33 | } 34 | 35 | public void setHumidity(String humidity) { 36 | this.humidity = humidity; 37 | } 38 | 39 | public String getTemp() { 40 | return temp; 41 | } 42 | 43 | public void setTemp(String temp) { 44 | this.temp = temp; 45 | } 46 | 47 | public String getTime() { 48 | return time; 49 | } 50 | 51 | public void setTime(String time) { 52 | this.time = time; 53 | } 54 | 55 | public String getWeather() { 56 | return weather; 57 | } 58 | 59 | public void setWeather(String weather) { 60 | this.weather = weather; 61 | } 62 | 63 | public String getWind() { 64 | return wind; 65 | } 66 | 67 | public void setWind(String wind) { 68 | this.wind = wind; 69 | } 70 | 71 | public String getWindSpeed() { 72 | return windSpeed; 73 | } 74 | 75 | public void setWindSpeed(String windSpeed) { 76 | this.windSpeed = windSpeed; 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | return "MiRealTime{" + 82 | "humidity='" + humidity + '\'' + 83 | ", wind='" + wind + '\'' + 84 | ", windSpeed='" + windSpeed + '\'' + 85 | ", cityId='" + cityId + '\'' + 86 | ", temp='" + temp + '\'' + 87 | ", time='" + time + '\'' + 88 | ", weather='" + weather + '\'' + 89 | '}'; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/http/entity/mi/MiWeather.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.http.entity.mi; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 9 | * 16/2/25 10 | */ 11 | public class MiWeather { 12 | 13 | private MiForecast forecast; 14 | @JSONField(name = "realtime") 15 | private MiRealTime realTime; 16 | private MiAQI aqi; 17 | @JSONField(name = "index") 18 | private List indexList; 19 | private MiToday today; 20 | private MiToday yesterday; 21 | 22 | public MiAQI getAqi() { 23 | return aqi; 24 | } 25 | 26 | public void setAqi(MiAQI aqi) { 27 | this.aqi = aqi; 28 | } 29 | 30 | public MiForecast getForecast() { 31 | return forecast; 32 | } 33 | 34 | public void setForecast(MiForecast forecast) { 35 | this.forecast = forecast; 36 | } 37 | 38 | public List getIndexList() { 39 | return indexList; 40 | } 41 | 42 | public void setIndexList(List indexList) { 43 | this.indexList = indexList; 44 | } 45 | 46 | public MiRealTime getRealTime() { 47 | return realTime; 48 | } 49 | 50 | public void setRealTime(MiRealTime realTime) { 51 | this.realTime = realTime; 52 | } 53 | 54 | public MiToday getToday() { 55 | return today; 56 | } 57 | 58 | public void setToday(MiToday today) { 59 | this.today = today; 60 | } 61 | 62 | public MiToday getYesterday() { 63 | return yesterday; 64 | } 65 | 66 | public void setYesterday(MiToday yesterday) { 67 | this.yesterday = yesterday; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return "MiWeather{" + 73 | "forecast=" + forecast + 74 | ", realTime=" + realTime + 75 | ", aqi=" + aqi + 76 | ", indexList=" + indexList + 77 | ", today=" + today + 78 | ", yesterday=" + yesterday + 79 | '}'; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/http/interceptor/HttpRequestInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.http.interceptor; 2 | 3 | import java.io.IOException; 4 | 5 | import okhttp3.Interceptor; 6 | import okhttp3.Response; 7 | 8 | /** 9 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 10 | * 16/2/25 11 | */ 12 | public class HttpRequestInterceptor implements Interceptor { 13 | 14 | @Override 15 | public Response intercept(Chain chain) throws IOException { 16 | return null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/http/service/EnvironmentCloudWeatherService.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.http.service; 2 | 3 | import com.baronzhang.android.weather.data.http.entity.envicloud.EnvironmentCloudCityAirLive; 4 | import com.baronzhang.android.weather.data.http.entity.envicloud.EnvironmentCloudForecast; 5 | import com.baronzhang.android.weather.data.http.entity.envicloud.EnvironmentCloudWeatherLive; 6 | 7 | import retrofit2.http.GET; 8 | import retrofit2.http.Path; 9 | import rx.Observable; 10 | 11 | /** 12 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 13 | * 2017/2/16 14 | */ 15 | public interface EnvironmentCloudWeatherService { 16 | 17 | /** 18 | * 获取指定城市的实时天气 19 | *

20 | * API地址:http://service.envicloud.cn:8082/v2/weatherlive/YMFYB256AGFUZZE0ODQ3MZM1MZE2NTU=/101020100 21 | * 22 | * @param cityId 城市id 23 | * @return Observable 24 | */ 25 | @GET("/v2/weatherlive/YMFYB256AGFUZZE0ODQ3MZM1MZE2NTU=/{cityId}") 26 | Observable getWeatherLive(@Path("cityId") String cityId); 27 | 28 | /** 29 | * 获取指定城市7日天气预报 30 | *

31 | * API地址:http://service.envicloud.cn:8082/v2/weatherforecast/YMFYB256AGFUZZE0ODQ3MZM1MZE2NTU=/101020100 32 | * 33 | * @param cityId 城市id 34 | * @return Observable 35 | */ 36 | @GET("/v2/weatherforecast/YMFYB256AGFUZZE0ODQ3MZM1MZE2NTU=/{cityId}") 37 | Observable getWeatherForecast(@Path("cityId") String cityId); 38 | 39 | /** 40 | * 获取指定城市的实时空气质量 41 | *

42 | * API地址:http://service.envicloud.cn:8082/v2/cityairlive/YMFYB256AGFUZZE0ODQ3MZM1MZE2NTU=/101020100 43 | * 44 | * @param cityId 城市id 45 | * @return Observable 46 | */ 47 | @GET("/v2/cityairlive/YMFYB256AGFUZZE0ODQ3MZM1MZE2NTU=/{cityId}") 48 | Observable getAirLive(@Path("cityId") String cityId); 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/http/service/WeatherService.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.http.service; 2 | 3 | import com.baronzhang.android.weather.data.http.entity.know.KnowWeather; 4 | import com.baronzhang.android.weather.data.http.entity.mi.MiWeather; 5 | import retrofit2.http.GET; 6 | import retrofit2.http.Path; 7 | import retrofit2.http.Query; 8 | import rx.Observable; 9 | 10 | /** 11 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 12 | * 16/2/25 13 | */ 14 | public interface WeatherService { 15 | 16 | /** 17 | * http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=101010100 18 | * 19 | * @param cityId 城市ID 20 | * @return 天气数据 21 | */ 22 | @GET("weather") 23 | Observable getMiWeather(@Query("cityId") String cityId); 24 | 25 | 26 | /** 27 | * http://knowweather.duapp.com/v1.0/weather/101010100 28 | * 29 | * @param cityId 城市ID 30 | * @return 天气数据 31 | */ 32 | @GET("v1.0/weather/{cityId}") 33 | Observable getKnowWeather(@Path("cityId") String cityId); 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/preference/ConfigurationListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 BaronZhang(BaronZ88) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.baronzhang.android.weather.data.preference; 17 | 18 | public interface ConfigurationListener { 19 | 20 | void onConfigurationChanged(WeatherSettings pref, Object newValue); 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/data/preference/WeatherSettings.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.data.preference; 2 | 3 | /** 4 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 5 | */ 6 | public enum WeatherSettings { 7 | 8 | /*默认配置项*/ 9 | SETTINGS_FIRST_USE("first_use", Boolean.TRUE), 10 | 11 | SETTINGS_CURRENT_CITY_ID("current_city_id", ""); 12 | 13 | private final String mId; 14 | private final Object mDefaultValue; 15 | 16 | WeatherSettings(String id, Object defaultValue) { 17 | this.mId = id; 18 | this.mDefaultValue = defaultValue; 19 | } 20 | 21 | public String getId() { 22 | return this.mId; 23 | } 24 | 25 | public Object getDefaultValue() { 26 | return this.mDefaultValue; 27 | } 28 | 29 | public static WeatherSettings fromId(String id) { 30 | WeatherSettings[] values = values(); 31 | for (WeatherSettings value : values) { 32 | if (value.mId.equals(id)) { 33 | return value; 34 | } 35 | } 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/di/component/ApplicationComponent.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.di.component; 2 | 3 | import android.content.Context; 4 | 5 | import com.baronzhang.android.weather.WeatherApplication; 6 | import com.baronzhang.android.weather.di.module.ApplicationModule; 7 | 8 | import javax.inject.Singleton; 9 | 10 | import dagger.Component; 11 | 12 | /** 13 | * @author 张磊 (baron[dot]zhanglei[at]gmail[dot]com) 14 | * 2016/11/30 15 | */ 16 | @Singleton 17 | @Component(modules = {ApplicationModule.class}) 18 | public interface ApplicationComponent { 19 | 20 | WeatherApplication getApplication(); 21 | 22 | Context getContext(); 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/di/component/PresenterComponent.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.di.component; 2 | 3 | import com.baronzhang.android.weather.di.module.ApplicationModule; 4 | import com.baronzhang.android.weather.feature.home.drawer.DrawerMenuPresenter; 5 | import com.baronzhang.android.weather.feature.selectcity.SelectCityPresenter; 6 | 7 | import javax.inject.Singleton; 8 | 9 | import dagger.Component; 10 | import com.baronzhang.android.weather.feature.home.HomePagePresenter; 11 | 12 | /** 13 | * @author 张磊 (baron[dot]zhanglei[at]gmail[dot]com) 14 | * 2016/12/2 15 | */ 16 | @Singleton 17 | @Component(modules = {ApplicationModule.class}) 18 | public interface PresenterComponent { 19 | 20 | void inject(HomePagePresenter presenter); 21 | 22 | void inject(SelectCityPresenter presenter); 23 | 24 | void inject(DrawerMenuPresenter presenter); 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/di/module/ApplicationModule.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.di.module; 2 | 3 | import android.content.Context; 4 | 5 | import com.baronzhang.android.weather.WeatherApplication; 6 | 7 | import javax.inject.Singleton; 8 | 9 | import dagger.Module; 10 | import dagger.Provides; 11 | 12 | /** 13 | * @author 张磊 (baronzhang[at]anjuke[dot]com) 14 | * 2016/11/30 15 | */ 16 | @Module 17 | public class ApplicationModule { 18 | 19 | private Context context; 20 | 21 | public ApplicationModule(Context context) { 22 | 23 | this.context = context; 24 | } 25 | 26 | @Provides 27 | @Singleton 28 | WeatherApplication provideApplication() { 29 | 30 | return (WeatherApplication) context.getApplicationContext(); 31 | } 32 | 33 | @Provides 34 | @Singleton 35 | Context provideContext() { 36 | 37 | return context; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/di/scope/ActivityScoped.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.di.scope; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | 7 | import javax.inject.Scope; 8 | 9 | /** 10 | * @author 张磊 (baronzhang[at]anjuke[dot]com) 11 | * 2016/12/1 12 | */ 13 | @Scope 14 | @Documented 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface ActivityScoped { 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/home/DetailAdapter.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.home; 2 | 3 | import androidx.recyclerview.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import com.baronzhang.android.weather.base.BaseRecyclerViewAdapter; 11 | import com.baronzhang.android.weather.R; 12 | import com.baronzhang.android.weather.data.WeatherDetail; 13 | 14 | import java.util.List; 15 | 16 | import butterknife.BindView; 17 | import butterknife.ButterKnife; 18 | 19 | /** 20 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 21 | * 2016/07/06 22 | */ 23 | public class DetailAdapter extends BaseRecyclerViewAdapter { 24 | 25 | private List details; 26 | 27 | public DetailAdapter(List details) { 28 | this.details = details; 29 | } 30 | 31 | @Override 32 | public DetailAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 33 | View itemView = LayoutInflater.from(parent.getContext()) 34 | .inflate(R.layout.item_detail, parent, false); 35 | return new ViewHolder(itemView, this); 36 | } 37 | 38 | @Override 39 | public void onBindViewHolder(DetailAdapter.ViewHolder holder, int position) { 40 | WeatherDetail detail = details.get(position); 41 | holder.detailIconImageView.setImageResource(detail.getIconResourceId()); 42 | holder.detailKeyTextView.setText(detail.getKey()); 43 | holder.detailValueTextView.setText(detail.getValue()); 44 | } 45 | 46 | @Override 47 | public int getItemCount() { 48 | return details == null ? 0 : details.size(); 49 | } 50 | 51 | static class ViewHolder extends RecyclerView.ViewHolder { 52 | 53 | @BindView(R.id.detail_icon_image_view) 54 | ImageView detailIconImageView; 55 | @BindView(R.id.detail_key_text_view) 56 | TextView detailKeyTextView; 57 | @BindView(R.id.detail_value_text_view) 58 | TextView detailValueTextView; 59 | 60 | ViewHolder(View itemView, DetailAdapter adapter) { 61 | super(itemView); 62 | ButterKnife.bind(this, itemView); 63 | itemView.setOnClickListener(v -> adapter.onItemHolderClick(DetailAdapter.ViewHolder.this)); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/home/ForecastAdapter.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.home; 2 | 3 | import android.annotation.SuppressLint; 4 | import androidx.recyclerview.widget.RecyclerView; 5 | import android.text.TextUtils; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.baronzhang.android.weather.base.BaseRecyclerViewAdapter; 13 | import com.baronzhang.android.weather.R; 14 | import com.baronzhang.android.weather.data.db.entities.minimalist.WeatherForecast; 15 | 16 | import java.util.List; 17 | 18 | import butterknife.BindView; 19 | import butterknife.ButterKnife; 20 | 21 | /** 22 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 23 | * 16/6/23 24 | */ 25 | public class ForecastAdapter extends BaseRecyclerViewAdapter { 26 | 27 | private List weatherForecasts; 28 | 29 | public ForecastAdapter(List weatherForecasts) { 30 | this.weatherForecasts = weatherForecasts; 31 | } 32 | 33 | @Override 34 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 35 | View itemView = LayoutInflater.from(parent.getContext()) 36 | .inflate(R.layout.item_forecast, parent, false); 37 | return new ViewHolder(itemView, this); 38 | } 39 | 40 | @SuppressLint("SetTextI18n") 41 | @Override 42 | public void onBindViewHolder(ForecastAdapter.ViewHolder holder, int position) { 43 | WeatherForecast weatherForecast = weatherForecasts.get(position); 44 | holder.weekTextView.setText(weatherForecast.getWeek()); 45 | holder.dateTextView.setText(weatherForecast.getDate()); 46 | holder.weatherIconImageView.setImageResource(R.mipmap.ic_launcher); 47 | holder.weatherTextView.setText(TextUtils.isEmpty(weatherForecast.getWeather()) ? 48 | (weatherForecast.getWeatherDay().equals(weatherForecast.getWeatherNight()) ? 49 | weatherForecast.getWeatherDay() : weatherForecast.getWeatherDay() + "转" + weatherForecast.getWeatherNight()) 50 | : weatherForecast.getWeather()); 51 | holder.tempMaxTextView.setText(weatherForecast.getTempMax() + "°"); 52 | holder.tempMinTextView.setText(weatherForecast.getTempMin() + "°"); 53 | } 54 | 55 | @Override 56 | public int getItemCount() { 57 | return weatherForecasts == null ? 0 : weatherForecasts.size(); 58 | } 59 | 60 | static class ViewHolder extends RecyclerView.ViewHolder { 61 | 62 | @BindView(R.id.week_text_view) 63 | TextView weekTextView; 64 | @BindView(R.id.date_text_view) 65 | TextView dateTextView; 66 | @BindView(R.id.weather_icon_image_view) 67 | ImageView weatherIconImageView; 68 | @BindView(R.id.weather_text_view) 69 | TextView weatherTextView; 70 | @BindView(R.id.temp_max_text_view) 71 | TextView tempMaxTextView; 72 | @BindView(R.id.temp_min_text_view) 73 | TextView tempMinTextView; 74 | 75 | ViewHolder(View itemView, ForecastAdapter adapter) { 76 | super(itemView); 77 | ButterKnife.bind(this, itemView); 78 | itemView.setOnClickListener(v -> adapter.onItemHolderClick(ViewHolder.this)); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/home/HomePageComponent.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.home; 2 | 3 | import com.baronzhang.android.weather.di.component.ApplicationComponent; 4 | import com.baronzhang.android.weather.di.scope.ActivityScoped; 5 | 6 | import dagger.Component; 7 | 8 | /** 9 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 10 | * 2016/11/29 11 | */ 12 | @ActivityScoped 13 | @Component(modules = HomePageModule.class, dependencies = ApplicationComponent.class) 14 | public interface HomePageComponent { 15 | 16 | void inject(MainActivity mainActivity); 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/home/HomePageContract.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.home; 2 | 3 | import com.baronzhang.android.weather.data.db.entities.minimalist.Weather; 4 | import com.baronzhang.android.weather.base.BasePresenter; 5 | import com.baronzhang.android.weather.base.BaseView; 6 | 7 | /** 8 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 9 | */ 10 | public interface HomePageContract { 11 | 12 | interface View extends BaseView { 13 | 14 | void displayWeatherInformation(Weather weather); 15 | } 16 | 17 | interface Presenter extends BasePresenter { 18 | 19 | void loadWeather(String cityId, boolean refreshNow); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/home/HomePageModule.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.home; 2 | 3 | import com.baronzhang.android.weather.di.scope.ActivityScoped; 4 | import com.baronzhang.android.weather.feature.home.HomePagePresenter; 5 | 6 | import dagger.Module; 7 | import dagger.Provides; 8 | 9 | import com.baronzhang.android.weather.feature.home.HomePageContract; 10 | 11 | /** 12 | * This is a Dagger module. We use this to pass in the View dependency to the 13 | * {@link HomePagePresenter} 14 | * 15 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 16 | * 2016/11/30 17 | */ 18 | @Module 19 | public class HomePageModule { 20 | 21 | private HomePageContract.View view; 22 | 23 | public HomePageModule(HomePageContract.View view) { 24 | 25 | this.view = view; 26 | } 27 | 28 | @Provides 29 | @ActivityScoped 30 | HomePageContract.View provideHomePageContractView() { 31 | return view; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/home/HomePagePresenter.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.home; 2 | 3 | import android.content.Context; 4 | import android.widget.Toast; 5 | 6 | import com.baronzhang.android.library.util.RxSchedulerUtils; 7 | import com.baronzhang.android.weather.data.db.dao.WeatherDao; 8 | import com.baronzhang.android.weather.data.preference.PreferenceHelper; 9 | import com.baronzhang.android.weather.data.preference.WeatherSettings; 10 | import com.baronzhang.android.weather.data.repository.WeatherDataRepository; 11 | import com.baronzhang.android.weather.di.component.DaggerPresenterComponent; 12 | import com.baronzhang.android.weather.di.module.ApplicationModule; 13 | import com.baronzhang.android.weather.di.scope.ActivityScoped; 14 | 15 | import javax.inject.Inject; 16 | 17 | import rx.Subscription; 18 | import rx.subscriptions.CompositeSubscription; 19 | 20 | /** 21 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 22 | */ 23 | @ActivityScoped 24 | public final class HomePagePresenter implements HomePageContract.Presenter { 25 | 26 | private final Context context; 27 | private final HomePageContract.View weatherView; 28 | 29 | private CompositeSubscription subscriptions; 30 | 31 | @Inject 32 | WeatherDao weatherDao; 33 | 34 | @Inject 35 | HomePagePresenter(Context context, HomePageContract.View view) { 36 | 37 | this.context = context; 38 | this.weatherView = view; 39 | this.subscriptions = new CompositeSubscription(); 40 | weatherView.setPresenter(this); 41 | 42 | DaggerPresenterComponent.builder() 43 | .applicationModule(new ApplicationModule(context)) 44 | .build().inject(this); 45 | } 46 | 47 | @Override 48 | public void subscribe() { 49 | String cityId = PreferenceHelper.getSharedPreferences().getString(WeatherSettings.SETTINGS_CURRENT_CITY_ID.getId(), ""); 50 | loadWeather(cityId, false); 51 | } 52 | 53 | @Override 54 | public void loadWeather(String cityId, boolean refreshNow) { 55 | 56 | Subscription subscription = WeatherDataRepository.getWeather(context, cityId, weatherDao, refreshNow) 57 | .compose(RxSchedulerUtils.normalSchedulersTransformer()) 58 | .subscribe(weatherView::displayWeatherInformation, throwable -> { 59 | Toast.makeText(context, throwable.getMessage(), Toast.LENGTH_LONG).show(); 60 | }); 61 | subscriptions.add(subscription); 62 | } 63 | 64 | @Override 65 | public void unSubscribe() { 66 | subscriptions.clear(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/home/LifeIndexAdapter.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.home; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import androidx.recyclerview.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.baronzhang.android.weather.base.BaseRecyclerViewAdapter; 13 | import com.baronzhang.android.weather.R; 14 | import com.baronzhang.android.weather.data.db.entities.minimalist.LifeIndex; 15 | 16 | import java.util.List; 17 | 18 | import butterknife.BindView; 19 | import butterknife.ButterKnife; 20 | 21 | import static com.baronzhang.android.weather.R.drawable.ic_index_sunscreen; 22 | 23 | /** 24 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 25 | * 2016/12/13 26 | */ 27 | public class LifeIndexAdapter extends BaseRecyclerViewAdapter { 28 | 29 | private Context context; 30 | private List indexList; 31 | 32 | public LifeIndexAdapter(Context context, List indexList) { 33 | this.context = context; 34 | this.indexList = indexList; 35 | } 36 | 37 | @Override 38 | public LifeIndexAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 39 | View itemView = LayoutInflater.from(parent.getContext()) 40 | .inflate(R.layout.item_life_index, parent, false); 41 | return new ViewHolder(itemView, this); 42 | } 43 | 44 | @Override 45 | public void onBindViewHolder(LifeIndexAdapter.ViewHolder holder, int position) { 46 | LifeIndex index = indexList.get(position); 47 | holder.indexIconImageView.setImageDrawable(getIndexDrawable(context, index.getName())); 48 | holder.indexLevelTextView.setText(index.getIndex()); 49 | holder.indexNameTextView.setText(index.getName()); 50 | } 51 | 52 | @Override 53 | public int getItemCount() { 54 | return indexList == null ? 0 : indexList.size(); 55 | } 56 | 57 | static class ViewHolder extends RecyclerView.ViewHolder { 58 | 59 | @BindView(R.id.index_icon_image_view) 60 | ImageView indexIconImageView; 61 | @BindView(R.id.index_level_text_view) 62 | TextView indexLevelTextView; 63 | @BindView(R.id.index_name_text_view) 64 | TextView indexNameTextView; 65 | 66 | ViewHolder(View itemView, LifeIndexAdapter adapter) { 67 | super(itemView); 68 | ButterKnife.bind(this, itemView); 69 | itemView.setOnClickListener(v -> adapter.onItemHolderClick(LifeIndexAdapter.ViewHolder.this)); 70 | } 71 | } 72 | 73 | private Drawable getIndexDrawable(Context context, String indexName) { 74 | 75 | 76 | int colorResourceId = ic_index_sunscreen; 77 | if (indexName.contains("防晒")) { 78 | colorResourceId = ic_index_sunscreen; 79 | } else if (indexName.contains("穿衣")) { 80 | colorResourceId = R.drawable.ic_index_dress; 81 | } else if (indexName.contains("运动")) { 82 | colorResourceId = R.drawable.ic_index_sport; 83 | } else if (indexName.contains("逛街")) { 84 | colorResourceId = R.drawable.ic_index_shopping; 85 | } else if (indexName.contains("晾晒")) { 86 | colorResourceId = R.drawable.ic_index_sun_cure; 87 | } else if (indexName.contains("洗车")) { 88 | colorResourceId = R.drawable.ic_index_car_wash; 89 | } else if (indexName.contains("感冒")) { 90 | colorResourceId = R.drawable.ic_index_clod; 91 | } else if (indexName.contains("广场舞")) { 92 | colorResourceId = R.drawable.ic_index_dance; 93 | } 94 | return context.getResources().getDrawable(colorResourceId); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/home/drawer/CityManagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.home.drawer; 2 | 3 | import androidx.recyclerview.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.AdapterView; 8 | import android.widget.ImageButton; 9 | import android.widget.TextView; 10 | 11 | import com.baronzhang.android.weather.base.BaseRecyclerViewAdapter; 12 | import com.baronzhang.android.library.util.DateConvertUtils; 13 | import com.baronzhang.android.weather.R; 14 | import com.baronzhang.android.weather.data.db.entities.minimalist.Weather; 15 | 16 | import java.util.List; 17 | 18 | import butterknife.BindView; 19 | import butterknife.ButterKnife; 20 | 21 | /** 22 | * 城市管理页面Adapter 23 | * 24 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 25 | * 16/3/16 26 | */ 27 | public class CityManagerAdapter extends BaseRecyclerViewAdapter { 28 | 29 | private final List weatherList; 30 | 31 | public CityManagerAdapter(List weatherList) { 32 | this.weatherList = weatherList; 33 | } 34 | 35 | @Override 36 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 37 | View itemView = LayoutInflater.from(parent.getContext()) 38 | .inflate(R.layout.item_city_manager, parent, false); 39 | return new ViewHolder(itemView, this); 40 | } 41 | 42 | @Override 43 | public void onBindViewHolder(final ViewHolder holder, int position) { 44 | Weather weather = weatherList.get(position); 45 | holder.city.setText(weather.getCityName()); 46 | holder.weather.setText(weather.getWeatherLive().getWeather()); 47 | holder.temp.setText(new StringBuilder().append(weather.getWeatherForecasts().get(0).getTempMin()).append("~").append(weather.getWeatherForecasts().get(0).getTempMax()).append("℃").toString()); 48 | holder.publishTime.setText("发布于 " + DateConvertUtils.timeStampToDate(weather.getWeatherLive().getTime(), DateConvertUtils.DATA_FORMAT_PATTEN_YYYY_MM_DD_HH_MM)); 49 | holder.deleteButton.setOnClickListener(v -> { 50 | Weather removeWeather = weatherList.get(holder.getAdapterPosition()); 51 | weatherList.remove(removeWeather); 52 | notifyItemRemoved(holder.getAdapterPosition()); 53 | 54 | if (onItemClickListener != null && onItemClickListener instanceof OnCityManagerItemClickListener) { 55 | ((OnCityManagerItemClickListener) onItemClickListener).onDeleteClick(removeWeather.getCityId()); 56 | } 57 | }); 58 | } 59 | 60 | @Override 61 | public int getItemCount() { 62 | return weatherList == null ? 0 : weatherList.size(); 63 | } 64 | 65 | static class ViewHolder extends RecyclerView.ViewHolder { 66 | 67 | @BindView(R.id.item_delete) 68 | ImageButton deleteButton; 69 | @BindView(R.id.item_tv_city) 70 | TextView city; 71 | @BindView(R.id.item_tv_publish_time) 72 | TextView publishTime; 73 | @BindView(R.id.item_tv_weather) 74 | TextView weather; 75 | @BindView(R.id.item_tv_temp) 76 | TextView temp; 77 | 78 | ViewHolder(View itemView, CityManagerAdapter adapter) { 79 | super(itemView); 80 | ButterKnife.bind(this, itemView); 81 | itemView.setOnClickListener(v -> adapter.onItemHolderClick(ViewHolder.this)); 82 | } 83 | } 84 | 85 | public interface OnCityManagerItemClickListener extends AdapterView.OnItemClickListener { 86 | 87 | void onDeleteClick(String cityId); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/home/drawer/DrawerContract.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.home.drawer; 2 | 3 | import com.baronzhang.android.weather.base.BasePresenter; 4 | import com.baronzhang.android.weather.base.BaseView; 5 | import com.baronzhang.android.weather.data.db.entities.minimalist.Weather; 6 | 7 | import java.io.InvalidClassException; 8 | import java.util.List; 9 | 10 | /** 11 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 12 | * 16/4/16 13 | */ 14 | public interface DrawerContract { 15 | 16 | interface View extends BaseView { 17 | 18 | void displaySavedCities(List weatherList); 19 | } 20 | 21 | interface Presenter extends BasePresenter { 22 | 23 | void loadSavedCities(); 24 | 25 | void deleteCity(String cityId); 26 | 27 | void saveCurrentCityToPreference(String cityId) throws InvalidClassException; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/home/drawer/DrawerMenuModule.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.home.drawer; 2 | 3 | import com.baronzhang.android.weather.di.scope.ActivityScoped; 4 | import com.baronzhang.android.weather.feature.home.drawer.DrawerContract; 5 | 6 | import dagger.Module; 7 | import dagger.Provides; 8 | 9 | /** 10 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 11 | * 2016/11/30 12 | */ 13 | @Module 14 | public class DrawerMenuModule { 15 | 16 | private DrawerContract.View view; 17 | 18 | public DrawerMenuModule(DrawerContract.View view) { 19 | this.view = view; 20 | } 21 | 22 | @Provides 23 | @ActivityScoped 24 | DrawerContract.View provideCityManagerContactView() { 25 | return view; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/home/drawer/DrawerMenuPresenter.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.home.drawer; 2 | 3 | import android.content.Context; 4 | 5 | import com.baronzhang.android.weather.data.db.dao.WeatherDao; 6 | import com.baronzhang.android.weather.data.db.entities.minimalist.Weather; 7 | import com.baronzhang.android.weather.data.preference.PreferenceHelper; 8 | import com.baronzhang.android.weather.data.preference.WeatherSettings; 9 | import com.baronzhang.android.weather.di.component.DaggerPresenterComponent; 10 | import com.baronzhang.android.weather.di.module.ApplicationModule; 11 | import com.baronzhang.android.weather.di.scope.ActivityScoped; 12 | 13 | import java.io.InvalidClassException; 14 | import java.sql.SQLException; 15 | import java.util.List; 16 | 17 | import javax.inject.Inject; 18 | 19 | import rx.Observable; 20 | import rx.Subscription; 21 | import rx.android.schedulers.AndroidSchedulers; 22 | import rx.schedulers.Schedulers; 23 | import rx.subscriptions.CompositeSubscription; 24 | 25 | /** 26 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 27 | * 16/4/16 28 | */ 29 | @ActivityScoped 30 | public final class DrawerMenuPresenter implements DrawerContract.Presenter { 31 | 32 | private DrawerContract.View view; 33 | 34 | 35 | private CompositeSubscription subscriptions; 36 | 37 | @Inject 38 | WeatherDao weatherDao; 39 | 40 | @Inject 41 | public DrawerMenuPresenter(Context context, DrawerContract.View view) { 42 | 43 | this.view = view; 44 | this.subscriptions = new CompositeSubscription(); 45 | view.setPresenter(this); 46 | 47 | DaggerPresenterComponent.builder() 48 | .applicationModule(new ApplicationModule(context)) 49 | .build().inject(this); 50 | } 51 | 52 | @Override 53 | public void subscribe() { 54 | loadSavedCities(); 55 | } 56 | 57 | @Override 58 | public void unSubscribe() { 59 | subscriptions.clear(); 60 | } 61 | 62 | @Override 63 | public void loadSavedCities() { 64 | 65 | try { 66 | Subscription subscription = Observable.just(weatherDao.queryAllSaveCity()) 67 | .subscribeOn(Schedulers.io()) 68 | .observeOn(AndroidSchedulers.mainThread()) 69 | .subscribe(weathers -> view.displaySavedCities(weathers)); 70 | subscriptions.add(subscription); 71 | } catch (SQLException e) { 72 | e.printStackTrace(); 73 | } 74 | 75 | } 76 | 77 | @Override 78 | public void deleteCity(String cityId) { 79 | 80 | Observable.just(deleteCityFromDBAndReturnCurrentCityId(cityId)) 81 | .subscribeOn(Schedulers.io()) 82 | .observeOn(AndroidSchedulers.mainThread()) 83 | .subscribe(currentCityId -> { 84 | if (currentCityId == null) 85 | return; 86 | try { 87 | PreferenceHelper.savePreference(WeatherSettings.SETTINGS_CURRENT_CITY_ID, currentCityId); 88 | } catch (InvalidClassException e) { 89 | e.printStackTrace(); 90 | } 91 | }); 92 | } 93 | 94 | @Override 95 | public void saveCurrentCityToPreference(String cityId) throws InvalidClassException{ 96 | PreferenceHelper.savePreference(WeatherSettings.SETTINGS_CURRENT_CITY_ID, cityId); 97 | } 98 | 99 | private String deleteCityFromDBAndReturnCurrentCityId(String cityId) { 100 | String currentCityId = PreferenceHelper.getSharedPreferences().getString(WeatherSettings.SETTINGS_CURRENT_CITY_ID.getId(), ""); 101 | try { 102 | weatherDao.deleteById(cityId); 103 | if (cityId.equals(currentCityId)) {//说明删除的是当前选择的城市,所以需要重新设置默认城市 104 | List weatherList = weatherDao.queryAllSaveCity(); 105 | if (weatherList != null && weatherList.size() > 0) { 106 | currentCityId = weatherList.get(0).getCityId(); 107 | } 108 | } 109 | } catch (SQLException e) { 110 | e.printStackTrace(); 111 | } 112 | return currentCityId; 113 | } 114 | 115 | 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/selectcity/SelectCityActivity.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.selectcity; 2 | 3 | import android.os.Bundle; 4 | import androidx.core.view.MenuItemCompat; 5 | import androidx.appcompat.widget.SearchView; 6 | import androidx.appcompat.widget.Toolbar; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | 10 | import com.baronzhang.android.weather.base.BaseActivity; 11 | import com.baronzhang.android.library.util.ActivityUtils; 12 | import com.baronzhang.android.weather.R; 13 | import com.baronzhang.android.weather.WeatherApplication; 14 | import com.jakewharton.rxbinding.support.v7.widget.RxSearchView; 15 | 16 | import java.util.concurrent.TimeUnit; 17 | 18 | import javax.inject.Inject; 19 | 20 | import butterknife.BindView; 21 | import butterknife.ButterKnife; 22 | import rx.android.schedulers.AndroidSchedulers; 23 | 24 | /** 25 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com ==>> baronzhang.com) 26 | */ 27 | public class SelectCityActivity extends BaseActivity { 28 | 29 | @BindView(R.id.toolbar) 30 | Toolbar toolbar; 31 | 32 | SelectCityFragment selectCityFragment; 33 | 34 | @Inject 35 | SelectCityPresenter selectCityPresenter; 36 | 37 | @Override 38 | protected void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | setContentView(R.layout.activity_select_city); 41 | ButterKnife.bind(this); 42 | 43 | setSupportActionBar(toolbar); 44 | if (getSupportActionBar() != null) { 45 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 46 | getSupportActionBar().setDisplayShowHomeEnabled(true); 47 | } 48 | selectCityFragment = (SelectCityFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_container); 49 | if (selectCityFragment == null) { 50 | selectCityFragment = SelectCityFragment.newInstance(); 51 | ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), selectCityFragment, R.id.fragment_container); 52 | } 53 | 54 | DaggerSelectCityComponent.builder() 55 | .applicationComponent(WeatherApplication.getInstance().getApplicationComponent()) 56 | .selectCityModule(new SelectCityModule(selectCityFragment)) 57 | .build().inject(this); 58 | } 59 | 60 | @Override 61 | public boolean onCreateOptionsMenu(Menu menu) { 62 | getMenuInflater().inflate(R.menu.search_city, menu); 63 | return super.onCreateOptionsMenu(menu); 64 | } 65 | 66 | @Override 67 | public boolean onOptionsItemSelected(MenuItem item) { 68 | int id = item.getItemId(); 69 | if (id == R.id.action_search) { 70 | SearchView searchView = (SearchView) MenuItemCompat.getActionView(item); 71 | RxSearchView.queryTextChanges(searchView) 72 | .map(charSequence -> charSequence == null ? null : charSequence.toString().trim()) 73 | .throttleLast(100, TimeUnit.MILLISECONDS) 74 | .debounce(100, TimeUnit.MILLISECONDS) 75 | .observeOn(AndroidSchedulers.mainThread()) 76 | .subscribe(searchText -> selectCityFragment.cityListAdapter.getFilter().filter(searchText)); 77 | return true; 78 | } 79 | return super.onOptionsItemSelected(item); 80 | } 81 | 82 | 83 | } -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/selectcity/SelectCityComponent.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.selectcity; 2 | 3 | import com.baronzhang.android.weather.di.component.ApplicationComponent; 4 | import com.baronzhang.android.weather.di.scope.ActivityScoped; 5 | 6 | import dagger.Component; 7 | 8 | /** 9 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 10 | * 2016/11/30 11 | */ 12 | @ActivityScoped 13 | @Component(modules = SelectCityModule.class, dependencies = ApplicationComponent.class) 14 | public interface SelectCityComponent { 15 | 16 | void inject(SelectCityActivity selectCityActivity); 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/selectcity/SelectCityContract.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.selectcity; 2 | 3 | import java.util.List; 4 | 5 | import com.baronzhang.android.weather.data.db.entities.City; 6 | import com.baronzhang.android.weather.base.BasePresenter; 7 | import com.baronzhang.android.weather.base.BaseView; 8 | 9 | /** 10 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 11 | */ 12 | public interface SelectCityContract { 13 | 14 | interface View extends BaseView { 15 | 16 | void displayCities(List cities); 17 | } 18 | 19 | interface Presenter extends BasePresenter { 20 | 21 | void loadCities(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/selectcity/SelectCityFragment.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.selectcity; 2 | 3 | import android.os.Bundle; 4 | import androidx.recyclerview.widget.DefaultItemAnimator; 5 | import androidx.recyclerview.widget.LinearLayoutManager; 6 | import androidx.recyclerview.widget.RecyclerView; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.Toast; 11 | 12 | import com.baronzhang.android.weather.base.BaseFragment; 13 | import com.baronzhang.android.weather.R; 14 | import com.baronzhang.android.weather.data.db.entities.City; 15 | import com.baronzhang.android.weather.data.preference.PreferenceHelper; 16 | import com.baronzhang.android.weather.data.preference.WeatherSettings; 17 | import com.baronzhang.android.library.view.DividerItemDecoration; 18 | 19 | import java.io.InvalidClassException; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import butterknife.BindView; 24 | import butterknife.ButterKnife; 25 | import butterknife.Unbinder; 26 | 27 | /** 28 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 29 | */ 30 | public class SelectCityFragment extends BaseFragment implements SelectCityContract.View { 31 | 32 | public List cities; 33 | public CityListAdapter cityListAdapter; 34 | 35 | @BindView(R.id.rv_city_list) 36 | RecyclerView recyclerView; 37 | private Unbinder unbinder; 38 | 39 | private SelectCityContract.Presenter presenter; 40 | 41 | public SelectCityFragment() { 42 | } 43 | 44 | public static SelectCityFragment newInstance() { 45 | return new SelectCityFragment(); 46 | } 47 | 48 | @Override 49 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 50 | View rootView = inflater.inflate(R.layout.fragment_select_city, container, false); 51 | unbinder = ButterKnife.bind(this, rootView); 52 | 53 | LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity()); 54 | recyclerView.setLayoutManager(linearLayoutManager); 55 | recyclerView.setItemAnimator(new DefaultItemAnimator()); 56 | recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST)); 57 | 58 | cities = new ArrayList<>(); 59 | cityListAdapter = new CityListAdapter(cities); 60 | cityListAdapter.setOnItemClickListener((parent, view, position, id) -> { 61 | try { 62 | City selectedCity = cityListAdapter.mFilterData.get(position); 63 | PreferenceHelper.savePreference(WeatherSettings.SETTINGS_CURRENT_CITY_ID, selectedCity.getCityId() + ""); 64 | Toast.makeText(this.getActivity(), selectedCity.getCityName(), Toast.LENGTH_LONG).show(); 65 | getActivity().finish(); 66 | } catch (InvalidClassException e) { 67 | e.printStackTrace(); 68 | } 69 | }); 70 | recyclerView.setAdapter(cityListAdapter); 71 | presenter.subscribe(); 72 | return rootView; 73 | } 74 | 75 | 76 | @Override 77 | public void onDestroyView() { 78 | super.onDestroyView(); 79 | unbinder.unbind(); 80 | presenter.unSubscribe(); 81 | } 82 | 83 | 84 | @Override 85 | public void displayCities(List cities) { 86 | this.cities.addAll(cities); 87 | cityListAdapter.notifyDataSetChanged(); 88 | } 89 | 90 | @Override 91 | public void setPresenter(SelectCityContract.Presenter presenter) { 92 | this.presenter = presenter; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/selectcity/SelectCityModule.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.selectcity; 2 | 3 | import dagger.Module; 4 | import dagger.Provides; 5 | 6 | import com.baronzhang.android.weather.di.scope.ActivityScoped; 7 | 8 | /** 9 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 10 | * 2016/11/30 11 | */ 12 | @Module 13 | public class SelectCityModule { 14 | 15 | private SelectCityContract.View view; 16 | 17 | public SelectCityModule(SelectCityContract.View view) { 18 | this.view = view; 19 | } 20 | 21 | @Provides 22 | @ActivityScoped 23 | SelectCityContract.View provideSelectCityContractView() { 24 | return view; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/feature/selectcity/SelectCityPresenter.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.feature.selectcity; 2 | 3 | import android.content.Context; 4 | 5 | import com.baronzhang.android.weather.data.db.dao.CityDao; 6 | import com.baronzhang.android.weather.di.component.DaggerPresenterComponent; 7 | import com.baronzhang.android.weather.di.module.ApplicationModule; 8 | import com.baronzhang.android.weather.di.scope.ActivityScoped; 9 | 10 | import javax.inject.Inject; 11 | 12 | import rx.Observable; 13 | import rx.Subscription; 14 | import rx.android.schedulers.AndroidSchedulers; 15 | import rx.schedulers.Schedulers; 16 | import rx.subscriptions.CompositeSubscription; 17 | 18 | /** 19 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 20 | */ 21 | @ActivityScoped 22 | public final class SelectCityPresenter implements SelectCityContract.Presenter { 23 | 24 | private final SelectCityContract.View cityListView; 25 | 26 | private CompositeSubscription subscriptions; 27 | 28 | @Inject 29 | CityDao cityDao; 30 | 31 | @Inject 32 | SelectCityPresenter(Context context, SelectCityContract.View view) { 33 | 34 | this.cityListView = view; 35 | this.subscriptions = new CompositeSubscription(); 36 | cityListView.setPresenter(this); 37 | 38 | DaggerPresenterComponent.builder() 39 | .applicationModule(new ApplicationModule(context)) 40 | .build().inject(this); 41 | } 42 | 43 | @Override 44 | public void loadCities() { 45 | Subscription subscription = Observable.just(cityDao.queryCityList()) 46 | .subscribeOn(Schedulers.io()) 47 | .observeOn(AndroidSchedulers.mainThread()) 48 | .subscribe(cityListView::displayCities); 49 | subscriptions.add(subscription); 50 | } 51 | 52 | @Override 53 | public void subscribe() { 54 | loadCities(); 55 | } 56 | 57 | @Override 58 | public void unSubscribe() { 59 | subscriptions.clear(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/util/StethoHelper.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.util; 2 | 3 | import android.content.Context; 4 | 5 | import okhttp3.OkHttpClient; 6 | 7 | /** 8 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 9 | * 2017/7/25 10 | */ 11 | public interface StethoHelper { 12 | 13 | void init(Context context); 14 | 15 | OkHttpClient.Builder addNetworkInterceptor(OkHttpClient.Builder builder); 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/baronzhang/android/weather/util/stetho/ReleaseStethoHelper.java: -------------------------------------------------------------------------------- 1 | package com.baronzhang.android.weather.util.stetho; 2 | 3 | import android.content.Context; 4 | 5 | import com.baronzhang.android.weather.util.StethoHelper; 6 | 7 | import okhttp3.OkHttpClient; 8 | 9 | /** 10 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) 11 | * 2017/7/25 12 | */ 13 | public class ReleaseStethoHelper implements StethoHelper { 14 | 15 | @Override 16 | public void init(Context context) { 17 | 18 | } 19 | 20 | @Override 21 | public OkHttpClient.Builder addNetworkInterceptor(OkHttpClient.Builder builder) { 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_camera.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_gallery.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_manage.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_send.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_share.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_slideshow.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_menu_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BaronZ88/MinimalistWeather/879abc57e8a1d448b0675affcf936b084b7a7ae4/app/src/main/res/drawable-xhdpi/ic_menu_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_menu_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BaronZ88/MinimalistWeather/879abc57e8a1d448b0675affcf936b084b7a7ae4/app/src/main/res/drawable-xxhdpi/ic_menu_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/weather_bg_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BaronZ88/MinimalistWeather/879abc57e8a1d448b0675affcf936b084b7a7ae4/app/src/main/res/drawable-xxhdpi/weather_bg_1.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/weather_bg_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BaronZ88/MinimalistWeather/879abc57e8a1d448b0675affcf936b084b7a7ae4/app/src/main/res/drawable-xxhdpi/weather_bg_2.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_launch_window.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_index_car_wash.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_index_clod.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_index_dress.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_index_shopping.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_index_sport.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_index_sun_cure.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_index_sunscreen.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_vector_weather.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_vector_weather2.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_card_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/side_nav_bar.xml: -------------------------------------------------------------------------------- 1 | 3 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout-v21/item_city_manager.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 18 | 19 | 26 | 27 | 35 | 36 | 43 | 44 | 53 | 54 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_city_manager.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_select_city.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_welcome.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_drawer_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_select_city.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_city.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_city_manager.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 17 | 18 | 24 | 25 | 32 | 33 | 41 | 42 | 49 | 50 | 59 | 60 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 26 | 27 | 35 | 36 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_forecast.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 29 | 30 | 39 | 40 | 52 | 53 | 63 | 64 | 73 | 74 | 84 | 85 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_life_index.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 26 | 27 | 36 | 37 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_app_bar.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_drawer_menu_head.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 20 | 21 | 28 | 29 | 39 | 40 |