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 |
--------------------------------------------------------------------------------
/library/src/main/java/com/baronzhang/android/library/util/system/MIUIHelper.java:
--------------------------------------------------------------------------------
1 | package com.baronzhang.android.library.util.system;
2 |
3 | import android.app.Activity;
4 | import android.view.Window;
5 |
6 | import java.lang.reflect.Field;
7 | import java.lang.reflect.Method;
8 |
9 | /**
10 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com)
11 | * 2017/6/2
12 | */
13 | class MIUIHelper implements SystemHelper {
14 |
15 | /**
16 | * 设置状态栏字体图标为深色,需要MIUI6以上
17 | *
18 | * @param isFontColorDark 是否把状态栏字体及图标颜色设置为深色
19 | * @return boolean 成功执行返回true
20 | */
21 | @Override
22 | public boolean setStatusBarLightMode(Activity activity, boolean isFontColorDark) {
23 |
24 | Window window = activity.getWindow();
25 | boolean result = false;
26 | if (window != null) {
27 | Class clazz = window.getClass();
28 | try {
29 | int darkModeFlag = 0;
30 | Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
31 | Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
32 | darkModeFlag = field.getInt(layoutParams);
33 | Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
34 | if (isFontColorDark) {
35 | extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//状态栏透明且黑色字体
36 | } else {
37 | extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体
38 | }
39 | result = true;
40 | } catch (Exception e) {
41 | e.printStackTrace();
42 | }
43 | }
44 | return result;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/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/res/layout/item_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
20 |
26 |
27 |
35 |
36 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/library/src/main/java/com/baronzhang/android/library/util/system/FlymeHelper.java:
--------------------------------------------------------------------------------
1 | package com.baronzhang.android.library.util.system;
2 |
3 | import android.app.Activity;
4 | import android.view.Window;
5 | import android.view.WindowManager;
6 |
7 | import java.lang.reflect.Field;
8 |
9 | /**
10 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com)
11 | * 2017/6/2
12 | */
13 | class FlymeHelper implements SystemHelper {
14 |
15 | /**
16 | * 设置状态栏图标为深色和魅族特定的文字风格
17 | * 可以用来判断是否为 Flyme 用户
18 | *
19 | * @param isFontColorDark 是否把状态栏字体及图标颜色设置为深色
20 | * @return boolean 成功执行返回 true
21 | */
22 | @Override
23 | public boolean setStatusBarLightMode(Activity activity, boolean isFontColorDark) {
24 | Window window = activity.getWindow();
25 | boolean result = false;
26 | if (window != null) {
27 | try {
28 | WindowManager.LayoutParams lp = window.getAttributes();
29 | Field darkFlag = WindowManager.LayoutParams.class
30 | .getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
31 | Field flymeFlags = WindowManager.LayoutParams.class
32 | .getDeclaredField("meizuFlags");
33 | darkFlag.setAccessible(true);
34 | flymeFlags.setAccessible(true);
35 | int bit = darkFlag.getInt(null);
36 | int value = flymeFlags.getInt(lp);
37 | if (isFontColorDark) {
38 | value |= bit;
39 | } else {
40 | value &= ~bit;
41 | }
42 | flymeFlags.setInt(lp, value);
43 | window.setAttributes(lp);
44 | result = true;
45 | } catch (Exception e) {
46 | e.printStackTrace();
47 | }
48 | }
49 | return result;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/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/res/layout/item_life_index.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
26 |
27 |
36 |
37 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/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/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/src/main/res/drawable/ic_index_car_wash.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_drawer_menu_head.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
20 |
21 |
28 |
29 |
39 |
40 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.android.compileSdkVersion
5 | defaultConfig {
6 | minSdkVersion rootProject.ext.android.minSdkVersion
7 | targetSdkVersion rootProject.ext.android.targetSdkVersion
8 | versionCode 1
9 | versionName "1.0"
10 | }
11 | buildTypes {
12 | release {
13 | minifyEnabled false
14 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
15 | }
16 | }
17 |
18 | compileOptions {
19 | sourceCompatibility JavaVersion.VERSION_1_8
20 | targetCompatibility JavaVersion.VERSION_1_8
21 | }
22 |
23 | lintOptions {
24 | abortOnError false
25 | }
26 |
27 | publishNonDefault true
28 | }
29 |
30 | dependencies {
31 | implementation fileTree(dir: 'libs', include: ['*.jar'])
32 | testImplementation 'junit:junit:4.12'
33 | api rootProject.ext.dependencies["support-v4"]
34 | api rootProject.ext.dependencies["appcompat-v7"]
35 | api rootProject.ext.dependencies["design"]
36 | api rootProject.ext.dependencies["recyclerview"]
37 | api rootProject.ext.dependencies["cardview"]
38 | api rootProject.ext.dependencies["rxandroid"]
39 | api rootProject.ext.dependencies["rxbinding"]
40 | api rootProject.ext.dependencies["rxbinding-support-v4"]
41 | api rootProject.ext.dependencies["rxbinding-appcompat-v7"]
42 | api rootProject.ext.dependencies["rxbinding-design"]
43 | api rootProject.ext.dependencies["rxbinding-recyclerview-v7"]
44 | api rootProject.ext.dependencies["ormlite-android"]
45 | api rootProject.ext.dependencies["stream"]
46 | api rootProject.ext.dependencies["retrofit"]
47 | api rootProject.ext.dependencies["adapter-rxjava"]
48 | api rootProject.ext.dependencies["fastjson"]
49 | api rootProject.ext.dependencies["okhttp3-logging-interceptor"]
50 |
51 | debugApi rootProject.ext.dependencies["stetho"]
52 | debugApi rootProject.ext.dependencies["stetho-okhttp3"]
53 |
54 | api rootProject.ext.dependencies["SmartRefreshLayout"]
55 | api rootProject.ext.dependencies["SmartRefreshHeader"]
56 |
57 | api 'org.glassfish:javax.annotation:10.0-b28'
58 | }
59 |
--------------------------------------------------------------------------------
/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/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/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/res/drawable/ic_index_sunscreen.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
18 |
21 |
24 |
27 |
30 |
33 |
34 |
--------------------------------------------------------------------------------
/library/src/main/java/com/baronzhang/android/library/util/system/StatusBarHelper.java:
--------------------------------------------------------------------------------
1 | package com.baronzhang.android.library.util.system;
2 |
3 | import android.app.Activity;
4 | import android.os.Build;
5 | import androidx.annotation.IntDef;
6 |
7 | import java.lang.annotation.Retention;
8 | import java.lang.annotation.RetentionPolicy;
9 |
10 | /**
11 | * 适配4.4以上版本 MIUI6、Flyme 和其他 Android6.0 及以上版本状态栏字体颜色
12 | *
13 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com)
14 | * 2017/6/2
15 | */
16 | public class StatusBarHelper {
17 |
18 | private static final int MIUI = 1;
19 | private static final int FLYME = 2;
20 | private static final int ANDROID_M = 3;
21 | private static final int OTHER = 4;
22 |
23 | @IntDef({MIUI, FLYME, ANDROID_M, OTHER})
24 | @Retention(RetentionPolicy.SOURCE)
25 | @interface SystemType {
26 | }
27 |
28 | public static void statusBarLightMode(Activity activity) {
29 | statusMode(activity, true);
30 | }
31 |
32 | public static int statusBarDarkMode(Activity activity) {
33 | return statusMode(activity, false);
34 | }
35 |
36 | private static int statusMode(Activity activity, boolean isFontColorDark) {
37 | @SystemType int result = 0;
38 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
39 | if (new MIUIHelper().setStatusBarLightMode(activity, isFontColorDark)) {
40 | result = MIUI;
41 | } else if (new FlymeHelper().setStatusBarLightMode(activity, isFontColorDark)) {
42 | result = FLYME;
43 | }
44 | // else if (new AndroidMHelper().setStatusBarLightMode(activity, isFontColorDark)) {
45 | // result = ANDROID_M;
46 | // }
47 | }
48 | return result;
49 | }
50 |
51 |
52 | public static void statusBarLightMode(Activity activity, @SystemType int type) {
53 | statusBarMode(activity, type, true);
54 |
55 | }
56 |
57 | public static void statusBarDarkMode(Activity activity, @SystemType int type) {
58 | statusBarMode(activity, type, false);
59 | }
60 |
61 | private static void statusBarMode(Activity activity, @SystemType int type, boolean isFontColorDark) {
62 | if (type == MIUI) {
63 | new MIUIHelper().setStatusBarLightMode(activity, isFontColorDark);
64 | } else if (type == FLYME) {
65 | new FlymeHelper().setStatusBarLightMode(activity, isFontColorDark);
66 | }
67 | // else if (type == ANDROID_M) {
68 | // new AndroidMHelper().setStatusBarLightMode(activity, isFontColorDark);
69 | // }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/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/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/res/layout-v21/item_city_manager.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
18 |
19 |
26 |
27 |
35 |
36 |
43 |
44 |
53 |
54 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/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 | 
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 | ## 三. 开源许可 [](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 |
--------------------------------------------------------------------------------
/widget/src/main/java/com/baronzhang/android/widget/TitleView.java:
--------------------------------------------------------------------------------
1 | package com.baronzhang.android.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import androidx.annotation.Nullable;
6 | import android.util.AttributeSet;
7 | import android.util.DisplayMetrics;
8 | import android.util.TypedValue;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.widget.LinearLayout;
12 | import android.widget.TextView;
13 |
14 | /**
15 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com)
16 | * 2017/8/3
17 | */
18 | public class TitleView extends LinearLayout {
19 |
20 | private int defaultTitleTextSize = 7;// 默认文字大小
21 | private int defaultTitleBackgroundColorId = R.color.default_title_background_color;// 默认背景颜色
22 | private int defaultTitleTextColorId = R.color.default_title_text_color;// 默认文字颜色
23 | private int defaultTitleLineColorId = R.color.default_title_line_color;// 默认底部线条颜色
24 |
25 | private String title;
26 | private int titleTextSize;
27 | private int titleTextColor;
28 | private int titleBackgroundColor;
29 | private int titleLineColor;
30 |
31 | public TitleView(Context context, @Nullable AttributeSet attrs) {
32 | super(context, attrs);
33 |
34 | initAttrs(context, attrs);
35 |
36 | initView();
37 | }
38 |
39 | private void initAttrs(Context context, @Nullable AttributeSet attrs) {
40 |
41 | DisplayMetrics dm = getResources().getDisplayMetrics();
42 | this.defaultTitleTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, defaultTitleTextSize, dm);
43 |
44 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TitleView);
45 | title = typedArray.getString(R.styleable.TitleView_titleViewText);
46 | titleTextSize = typedArray.getDimensionPixelSize(R.styleable.TitleView_titleViewTextSize, defaultTitleTextSize);
47 | titleTextColor = typedArray.getColor(R.styleable.TitleView_titleViewTextColor, getResources().getColor(defaultTitleTextColorId));
48 | titleBackgroundColor = typedArray.getColor(R.styleable.TitleView_titleViewBackground, getResources().getColor(defaultTitleBackgroundColorId));
49 | titleLineColor = typedArray.getColor(R.styleable.TitleView_titleViewLineColor, getResources().getColor(defaultTitleLineColorId));
50 | typedArray.recycle();
51 | }
52 |
53 | private void initView() {
54 |
55 | LayoutInflater.from(getContext()).inflate(R.layout.layout_title_view, this, true);
56 | this.setOrientation(VERTICAL);
57 | this.setBackgroundColor(titleBackgroundColor);
58 |
59 | TextView titleTextView = (TextView) findViewById(R.id.title_text_view);
60 | titleTextView.setText(title);
61 | // titleTextView.setTextSize(titleTextSize);
62 | titleTextView.setTextColor(titleTextColor);
63 |
64 | View view = findViewById(R.id.title_line_view);
65 | view.setBackgroundColor(titleLineColor);
66 | }
67 | }
68 |
69 |
70 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/library/src/main/java/com/baronzhang/android/library/view/DividerItemDecoration.java:
--------------------------------------------------------------------------------
1 | package com.baronzhang.android.library.view;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Rect;
7 | import android.graphics.drawable.Drawable;
8 | import androidx.recyclerview.widget.LinearLayoutManager;
9 | import androidx.recyclerview.widget.RecyclerView;
10 | import android.view.View;
11 |
12 | /**
13 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com)
14 | * 16/3/21
15 | */
16 | public class DividerItemDecoration extends RecyclerView.ItemDecoration {
17 |
18 | private static final int[] ATTRS = new int[]{
19 | android.R.attr.listDivider
20 | };
21 |
22 | public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
23 |
24 | public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
25 |
26 | private Drawable mDivider;
27 |
28 | private int mOrientation;
29 |
30 | public DividerItemDecoration(Context context, int orientation) {
31 | final TypedArray a = context.obtainStyledAttributes(ATTRS);
32 | mDivider = a.getDrawable(0);
33 | a.recycle();
34 | setOrientation(orientation);
35 | }
36 |
37 | public void setOrientation(int orientation) {
38 | if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
39 | throw new IllegalArgumentException("invalid orientation");
40 | }
41 | mOrientation = orientation;
42 | }
43 |
44 | @Override
45 | public void onDraw(Canvas c, RecyclerView parent) {
46 |
47 | if (mOrientation == VERTICAL_LIST) {
48 | drawVertical(c, parent);
49 | } else {
50 | drawHorizontal(c, parent);
51 | }
52 |
53 | }
54 |
55 |
56 | public void drawVertical(Canvas c, RecyclerView parent) {
57 | final int left = parent.getPaddingLeft();
58 | final int right = parent.getWidth() - parent.getPaddingRight();
59 |
60 | final int childCount = parent.getChildCount();
61 | for (int i = 0; i < childCount; i++) {
62 | final View child = parent.getChildAt(i);
63 | RecyclerView v = new RecyclerView(parent.getContext());
64 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
65 | .getLayoutParams();
66 | final int top = child.getBottom() + params.bottomMargin;
67 | final int bottom = top + mDivider.getIntrinsicHeight();
68 | mDivider.setBounds(left, top, right, bottom);
69 | mDivider.draw(c);
70 | }
71 | }
72 |
73 | public void drawHorizontal(Canvas c, RecyclerView parent) {
74 | final int top = parent.getPaddingTop();
75 | final int bottom = parent.getHeight() - parent.getPaddingBottom();
76 |
77 | final int childCount = parent.getChildCount();
78 | for (int i = 0; i < childCount; i++) {
79 | final View child = parent.getChildAt(i);
80 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
81 | .getLayoutParams();
82 | final int left = child.getRight() + params.rightMargin;
83 | final int right = left + mDivider.getIntrinsicHeight();
84 | mDivider.setBounds(left, top, right, bottom);
85 | mDivider.draw(c);
86 | }
87 | }
88 |
89 | @Override
90 | public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
91 | if (mOrientation == VERTICAL_LIST) {
92 | outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
93 | } else {
94 | outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/dependencies.gradle:
--------------------------------------------------------------------------------
1 | //def supportVersion = "28.0.0"
2 | def rxBindingVersion = "0.4.0"
3 | def retrofitVersion = "2.5.0"
4 | def okHttpVersion = "3.14.0"
5 | def stethoVersion = "1.5.0"
6 | def butterKnifeVersion = "10.1.0"
7 | def daggerVersion = "2.8"
8 | def ormLiteVersion = "4.48"
9 | def fastJsonVersion = "1.1.60.android"
10 |
11 | project.ext {
12 | android = [
13 | compileSdkVersion: 28,
14 | applicationId : "com.baronzhang.android.weather",
15 | minSdkVersion : 19,
16 | targetSdkVersion : 28,
17 | versionCode : 1,
18 | versionName : "1.0"
19 | ]
20 |
21 | dependencies = [
22 | //android-support
23 | // "support-v4" : "com.android.support:support-v4:${supportVersion}",
24 | // "appcompat-v7" : "com.android.support:appcompat-v7:${supportVersion}",
25 | // "design" : "com.android.support:design:${supportVersion}",
26 | // "recyclerview" : "com.android.support:recyclerview-v7:${supportVersion}",
27 | // "cardview" : "com.android.support:cardview-v7:${supportVersion}",
28 |
29 | "support-v4" : "androidx.legacy:legacy-support-v4:1.0.0",
30 | "appcompat-v7" : "androidx.appcompat:appcompat:1.0.0",
31 | "design" : "com.google.android.material:material:1.0.0-rc01",
32 | "recyclerview" : "androidx.recyclerview:recyclerview:1.0.0",
33 | "cardview" : "androidx.cardview:cardview:1.0.0",
34 |
35 | //java8-support
36 | "stream" : "com.annimon:stream:1.0.8",
37 |
38 | //rx
39 | "rxandroid" : "io.reactivex:rxandroid:1.2.1",
40 | "rxbinding" : "com.jakewharton.rxbinding:rxbinding:${rxBindingVersion}",
41 | "rxbinding-support-v4" : "com.jakewharton.rxbinding:rxbinding-support-v4:${rxBindingVersion}",
42 | "rxbinding-appcompat-v7" : "com.jakewharton.rxbinding:rxbinding-appcompat-v7:${rxBindingVersion}",
43 | "rxbinding-design" : "com.jakewharton.rxbinding:rxbinding-design:${rxBindingVersion}",
44 | "rxbinding-recyclerview-v7" : "com.jakewharton.rxbinding:rxbinding-recyclerview-v7:${rxBindingVersion}",
45 |
46 | //retrofit
47 | "retrofit" : "com.squareup.retrofit2:retrofit:${retrofitVersion}",
48 | "adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava:${retrofitVersion}",
49 |
50 | //dagger
51 | "dagger" : "com.google.dagger:dagger:${daggerVersion}",
52 | "dagger-compiler" : "com.google.dagger:dagger-compiler:${daggerVersion}",
53 |
54 | //facebook
55 | "stetho" : "com.facebook.stetho:stetho:${stethoVersion}",
56 | "stetho-okhttp3" : "com.facebook.stetho:stetho-okhttp3:${stethoVersion}",
57 |
58 | //others
59 | "okhttp3-logging-interceptor" : "com.squareup.okhttp3:logging-interceptor:${okHttpVersion}",
60 | "ormlite-android" : "com.j256.ormlite:ormlite-android:${ormLiteVersion}",
61 | "fastjson" : "com.alibaba:fastjson:${fastJsonVersion}",
62 | "butterknife" : "com.jakewharton:butterknife:${butterKnifeVersion}",
63 | "butterknife-compiler" : "com.jakewharton:butterknife-compiler:${butterKnifeVersion}",
64 | "retrofit2-fastjson-converter": "com.github.BaronZ88:Retrofit2-FastJson-Converter:1.2",
65 | "SmartRefreshLayout" : "com.scwang.smartrefresh:SmartRefreshLayout:1.0.3",
66 | "SmartRefreshHeader" : "com.scwang.smartrefresh:SmartRefreshHeader:1.0.3"
67 | ]
68 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/baronzhang/android/library/util/DateConvertUtils.java:
--------------------------------------------------------------------------------
1 | package com.baronzhang.android.library.util;
2 |
3 | import java.text.ParseException;
4 | import java.text.SimpleDateFormat;
5 | import java.util.Calendar;
6 | import java.util.Date;
7 | import java.util.Locale;
8 |
9 | /**
10 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com)
11 | * 2016/12/12
12 | */
13 | public final class DateConvertUtils {
14 |
15 | public static final String DATA_FORMAT_PATTEN_YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
16 | public static final String DATA_FORMAT_PATTEN_YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
17 | public static final String DATA_FORMAT_PATTEN_YYYY_MM_DD = "yyyy-MM-dd";
18 |
19 | /**
20 | * 将时间转换为时间戳
21 | *
22 | * @param data 待转换的日期
23 | * @param dataFormatPatten 待转换日期格式
24 | */
25 | public static long dateToTimeStamp(String data, String dataFormatPatten) {
26 |
27 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dataFormatPatten, Locale.CHINA);
28 | Date date = null;
29 | try {
30 | date = simpleDateFormat.parse(data);
31 | } catch (ParseException e) {
32 | e.printStackTrace();
33 | }
34 | assert date != null;
35 | return date.getTime();
36 | }
37 |
38 | /**
39 | * 将时间戳转换为日期
40 | *
41 | * @param time 待转换的时间戳
42 | * @param dataFormatPatten 转换出的日期格式
43 | */
44 | public static String timeStampToDate(long time, String dataFormatPatten) {
45 |
46 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dataFormatPatten, Locale.CHINA);
47 | Date date = new Date(time);
48 | return simpleDateFormat.format(date);
49 | }
50 |
51 | /**
52 | * 日期转星期
53 | *
54 | * @param dateString 日期
55 | * @return 周一 周二 周三 ...
56 | */
57 | public static String convertDataToWeek(String dateString) {
58 |
59 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATA_FORMAT_PATTEN_YYYY_MM_DD, Locale.CHINA);
60 | Date date = null;
61 | try {
62 | date = simpleDateFormat.parse(dateString);
63 | } catch (ParseException e) {
64 | e.printStackTrace();
65 | }
66 | if (isNow(date))
67 | return "今天";
68 |
69 | String[] weekDaysName = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"};
70 | Calendar calendar = Calendar.getInstance();
71 | calendar.setTime(date);
72 | int intWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1;
73 | return weekDaysName[intWeek];
74 | }
75 |
76 | /**
77 | * 日期转换
78 | *
79 | * @return 08.07
80 | */
81 | public static String convertDataToString(String dateString) {
82 |
83 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATA_FORMAT_PATTEN_YYYY_MM_DD, Locale.CHINA);
84 | Date date = null;
85 | try {
86 | date = simpleDateFormat.parse(dateString);
87 | } catch (ParseException e) {
88 | e.printStackTrace();
89 | }
90 | if (date == null)
91 | return "";
92 | return (String.valueOf(date.getMonth()).length() == 1 ? "0" + date.getMonth() : String.valueOf(date.getMonth()))
93 | + "." + (String.valueOf(date.getDay()).length() == 1 ? "0" + date.getDay() : String.valueOf(date.getDay()));
94 | }
95 |
96 | /**
97 | * 判断时间是不是今天
98 | *
99 | * @return 是返回true,不是返回false
100 | */
101 | private static boolean isNow(Date date) {
102 | //当前时间
103 | Date now = new Date();
104 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATA_FORMAT_PATTEN_YYYY_MM_DD, Locale.CHINA);
105 | //获取今天的日期
106 | String nowDay = simpleDateFormat.format(now);
107 | //对比的时间
108 | String day = simpleDateFormat.format(date);
109 | return day.equals(nowDay);
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/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/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/repository/WeatherDataRepository.java:
--------------------------------------------------------------------------------
1 | package com.baronzhang.android.weather.data.repository;
2 |
3 | import android.content.Context;
4 | import android.text.TextUtils;
5 |
6 | import com.baronzhang.android.library.util.NetworkUtils;
7 | import com.baronzhang.android.weather.data.db.dao.WeatherDao;
8 | import com.baronzhang.android.weather.data.db.entities.adapter.CloudWeatherAdapter;
9 | import com.baronzhang.android.weather.data.db.entities.adapter.KnowWeatherAdapter;
10 | import com.baronzhang.android.weather.data.db.entities.adapter.MiWeatherAdapter;
11 | import com.baronzhang.android.weather.data.db.entities.minimalist.Weather;
12 | import com.baronzhang.android.weather.data.http.ApiClient;
13 | import com.baronzhang.android.weather.data.http.ApiConstants;
14 | import com.baronzhang.android.weather.data.http.entity.envicloud.EnvironmentCloudCityAirLive;
15 | import com.baronzhang.android.weather.data.http.entity.envicloud.EnvironmentCloudForecast;
16 | import com.baronzhang.android.weather.data.http.entity.envicloud.EnvironmentCloudWeatherLive;
17 |
18 | import java.sql.SQLException;
19 |
20 | import rx.Observable;
21 | import rx.exceptions.Exceptions;
22 | import rx.schedulers.Schedulers;
23 |
24 | /**
25 | * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com)
26 | * 2016/12/10
27 | */
28 | public class WeatherDataRepository {
29 |
30 | public static Observable getWeather(Context context, String cityId, WeatherDao weatherDao, boolean refreshNow) {
31 |
32 | //从数据库获取天气数据
33 | Observable observableForGetWeatherFromDB = Observable.create(subscriber -> {
34 | try {
35 | Weather weather = weatherDao.queryWeather(cityId);
36 | subscriber.onNext(weather);
37 | subscriber.onCompleted();
38 | } catch (SQLException e) {
39 | throw Exceptions.propagate(e);
40 | }
41 | });
42 |
43 | if (!NetworkUtils.isNetworkConnected(context))
44 | return observableForGetWeatherFromDB;
45 |
46 | //从服务端获取天气数据
47 | Observable observableForGetWeatherFromNetWork = null;
48 | switch (ApiClient.configuration.getDataSourceType()) {
49 | case ApiConstants.WEATHER_DATA_SOURCE_TYPE_KNOW:
50 | observableForGetWeatherFromNetWork = ApiClient.weatherService.getKnowWeather(cityId)
51 | .map(knowWeather -> new KnowWeatherAdapter(knowWeather).getWeather());
52 | break;
53 | case ApiConstants.WEATHER_DATA_SOURCE_TYPE_MI:
54 | observableForGetWeatherFromNetWork = ApiClient.weatherService.getMiWeather(cityId)
55 | .map(miWeather -> new MiWeatherAdapter(miWeather).getWeather());
56 | break;
57 | case ApiConstants.WEATHER_DATA_SOURCE_TYPE_ENVIRONMENT_CLOUD:
58 |
59 | Observable weatherLiveObservable = ApiClient.environmentCloudWeatherService.getWeatherLive(cityId);
60 | Observable forecastObservable = ApiClient.environmentCloudWeatherService.getWeatherForecast(cityId);
61 | Observable airLiveObservable = ApiClient.environmentCloudWeatherService.getAirLive(cityId);
62 |
63 | observableForGetWeatherFromNetWork = Observable.combineLatest(weatherLiveObservable, forecastObservable, airLiveObservable,
64 | (weatherLive, forecast, airLive) -> new CloudWeatherAdapter(weatherLive, forecast, airLive).getWeather());
65 |
66 | break;
67 | }
68 | assert observableForGetWeatherFromNetWork != null;
69 | observableForGetWeatherFromNetWork = observableForGetWeatherFromNetWork.doOnNext(weather -> Schedulers.io().createWorker().schedule(() -> {
70 | try {
71 | weatherDao.insertOrUpdateWeather(weather);
72 | } catch (SQLException e) {
73 | throw Exceptions.propagate(e);
74 | }
75 | }));
76 |
77 | return Observable.concat(observableForGetWeatherFromDB, observableForGetWeatherFromNetWork)
78 | .filter(weather -> weather != null && !TextUtils.isEmpty(weather.getCityId()))
79 | .distinct(weather -> weather.getWeatherLive().getTime())
80 | .takeUntil(weather -> !refreshNow && System.currentTimeMillis() - weather.getWeatherLive().getTime() <= 15 * 60 * 1000);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------