commit 4818c1ad93d7460df5855dc76f5fb15504a43d36 Author: LvJia <2239513247@qq.com> Date: Thu Jun 1 21:15:38 2023 +0800 Final diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..56cc642 --- /dev/null +++ b/.gitignore @@ -0,0 +1,85 @@ +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c4134a4 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# FlamekingWeather +Android 天气预报App diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..74c0195 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,49 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.casflawed.flameking.weather" + minSdk 16 + targetSdk 32 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation fileTree(dir: 'libs', includes: ['*.jar']) + testImplementation 'junit:junit:4.13.2' + // litepal用于对数据库操作,类似JPA +// implementation 'org.litepal.guolindev:core:3.2.3' + // okhttp用于网络请求 + implementation 'com.squareup.okhttp3:okhttp:3.4.1' + // gson用于解析json数据 + implementation 'com.google.code.gson:gson:2.7' + // glide用于加载和显示图片 + implementation 'com.github.bumptech.glide:glide:3.7.0' + // 下拉刷新 + implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" + implementation 'androidx.appcompat:appcompat:1.3.0' + implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} \ No newline at end of file diff --git a/app/libs/litepal-1.4.1.jar b/app/libs/litepal-1.4.1.jar new file mode 100644 index 0000000..01a4a49 Binary files /dev/null and b/app/libs/litepal-1.4.1.jar differ diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/casflawed/flameking/weather/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/casflawed/flameking/weather/ExampleInstrumentedTest.java new file mode 100644 index 0000000..789e88b --- /dev/null +++ b/app/src/androidTest/java/com/casflawed/flameking/weather/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.casflawed.flameking.weather; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.casflawed.flameking.weather", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b247b1e --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/litepal.xml b/app/src/main/assets/litepal.xml new file mode 100644 index 0000000..a26383f --- /dev/null +++ b/app/src/main/assets/litepal.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casflawed/flameking/weather/ChooseAreaFragment.java b/app/src/main/java/com/casflawed/flameking/weather/ChooseAreaFragment.java new file mode 100644 index 0000000..abe9309 --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/ChooseAreaFragment.java @@ -0,0 +1,268 @@ +package com.casflawed.flameking.weather; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.fragment.app.Fragment; + +import com.casflawed.flameking.weather.db.City; +import com.casflawed.flameking.weather.db.County; +import com.casflawed.flameking.weather.db.Province; +import com.casflawed.flameking.weather.util.HttpUtil; +import com.casflawed.flameking.weather.util.Utility; + +import org.litepal.crud.DataSupport; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Response; + +public class ChooseAreaFragment extends Fragment { + public static final int LEVEL_PROVINCE = 0; + public static final int LEVEL_CITY = 1; + public static final int LEVEL_COUNTY = 2; + private ProgressDialog progressDialog; + private TextView titleText; + private Button backButton; + private ListView listView; + private ArrayAdapter adapter; + private List dataList = new ArrayList<>(); + /** + * 省列表 + */ + private List provinceList; + /** + * 市列表 + */ + private List cityList; + /** + * 县列表 + */ + private List countyList; + /** + * 选中的省份 + */ + private Province selectedProvince; + /** + * 选中的城市 + */ + private City selectedCity; + /** + * 当前选中的级别 + */ + private int currentLevel; + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // 加载choose_area.xml布局 + View view = inflater.inflate(R.layout.choose_area, container, false); + // 获取TextView控件 + titleText = (TextView) view.findViewById(R.id.title_text); + // 获取Button控件 + backButton = (Button) view.findViewById(R.id.back_button); + // 获取ListView控件 + listView = (ListView) view.findViewById(R.id.list_view); + // 初始化ArrayAdapter + adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, dataList); + // 将ArrayAdapter设置为ListView的适配器 + listView.setAdapter(adapter); + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, + long id) { + if (currentLevel == LEVEL_PROVINCE) { + selectedProvince = provinceList.get(position); + queryCities(); + } else if (currentLevel == LEVEL_CITY) { + selectedCity = cityList.get(position); + queryCounties(); + } else if (currentLevel == LEVEL_COUNTY) { + String weatherId = countyList.get(position).getWeatherId(); + if (getActivity() instanceof MainActivity) { + Intent intent = new Intent(getActivity(), WeatherActivity.class); + intent.putExtra("weather_id", weatherId); + startActivity(intent); + getActivity().finish(); + }else if (getActivity() instanceof WeatherActivity){ + // 如果在WeatherActivity活动中 + WeatherActivity activity = (WeatherActivity) getActivity(); + // 关闭菜单栏 + activity.drawerLayout.closeDrawers(); + // 打开下拉刷新进度条 + activity.swipeRefresh.setRefreshing(true); + // 请求天气信息 + activity.requestWeather(weatherId); + + } + + } + } + }); + backButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (currentLevel == LEVEL_COUNTY) { + queryCities(); + } else if (currentLevel == LEVEL_CITY) { + queryProvinces(); + } + } + }); + // 查询所有的省份信息 + queryProvinces(); + } + /** + * 查询全国所有的省,优先从数据库查询,如果没有查询到再去服务器上查询 + */ + private void queryProvinces() { + titleText.setText("中国"); + backButton.setVisibility(View.GONE); + provinceList = DataSupport.findAll(Province.class); + if (provinceList.size() > 0) { + dataList.clear(); + for (Province province : provinceList) { + dataList.add(province.getProvinceName()); + } + adapter.notifyDataSetChanged(); + listView.setSelection(0); + currentLevel = LEVEL_PROVINCE; + } else { + String address = "http://guolin.tech/api/china/"; + queryFromServer(address, "province"); + } + } + /** + * 查询选中省内所有的市,优先从数据库查询,如果没有查询到再去服务器上查询 + */ + private void queryCities() { + titleText.setText(selectedProvince.getProvinceName()); + backButton.setVisibility(View.VISIBLE); + cityList = DataSupport.where("provinceid = ? ", String.valueOf(selectedProvince.getId())).find(City.class); + if (cityList.size() > 0) { + dataList.clear(); + for (City city : cityList) { + dataList.add(city.getCityName()); + } + adapter.notifyDataSetChanged(); + listView.setSelection(0); + currentLevel = LEVEL_CITY; + } else { + int provinceCode = selectedProvince.getProvinceCode(); + String address = "http://guolin.tech/api/china/" + provinceCode; + queryFromServer(address, "city"); + } + } + /** + * 查询选中市内所有的县,优先从数据库查询,如果没有查询到再去服务器上查询 + */ + private void queryCounties() { + titleText.setText(selectedCity.getCityName()); + backButton.setVisibility(View.VISIBLE); + countyList = DataSupport.where("cityid = ? ", String.valueOf(selectedCity. + getId())).find(County.class); + if (countyList.size() > 0) { + dataList.clear(); + for (County county : countyList) { + dataList.add(county.getCountyName()); + } + adapter.notifyDataSetChanged(); + listView.setSelection(0); + currentLevel = LEVEL_COUNTY; + } else { + int provinceCode = selectedProvince.getProvinceCode(); + int cityCode = selectedCity.getCityCode(); + String address = "http://guolin.tech/api/china/" + provinceCode + "/" + + cityCode; + queryFromServer(address, "county"); + } + } + /** + * 根据传入的地址和类型从服务器上查询省市县数据 + */ + private void queryFromServer(String address, final String type) { + showProgressDialog(); + HttpUtil.sendOkHttpRequest(address, new Callback() { + @Override + public void onResponse(Call call, Response response) throws IOException { + String responseText = response.body().string(); + boolean result = false; + if ("province".equals(type)) { + result = Utility.handleProvinceResponse(responseText); + } else if ("city".equals(type)) { + result = Utility.handleCityResponse(responseText, + selectedProvince.getId()); + } else if ("county".equals(type)) { + result = Utility.handleCountyResponse(responseText, + selectedCity.getId()); + } + if (result) { + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + closeProgressDialog(); + if ("province".equals(type)) { + queryProvinces(); + } else if ("city".equals(type)) { + queryCities(); + } else if ("county".equals(type)) { + queryCounties(); + } + } + }); + } + } + @Override + public void onFailure(Call call, IOException e) { + // 通过runOnUiThread()方法回到主线程处理逻辑 + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + closeProgressDialog(); + Toast.makeText(getContext(), "加载失败", Toast.LENGTH_SHORT). + show(); + } + }); + } + }); + } + /** + * 显示进度对话框 + */ + private void showProgressDialog() { + if (progressDialog == null) { + progressDialog = new ProgressDialog(getActivity()); + progressDialog.setMessage("正在加载..."); + progressDialog.setCanceledOnTouchOutside(false); + } + progressDialog.show(); + } + /** + * 关闭进度对话框 + */ + private void closeProgressDialog() { + if (progressDialog != null) {progressDialog.dismiss(); + } + } +} + + diff --git a/app/src/main/java/com/casflawed/flameking/weather/MainActivity.java b/app/src/main/java/com/casflawed/flameking/weather/MainActivity.java new file mode 100644 index 0000000..ba12fcd --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/MainActivity.java @@ -0,0 +1,23 @@ +package com.casflawed.flameking.weather; + +import androidx.appcompat.app.AppCompatActivity; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + if (prefs.getString("weather", null) != null) { + Intent intent = new Intent(this, WeatherActivity.class); + startActivity(intent); + finish(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casflawed/flameking/weather/WeatherActivity.java b/app/src/main/java/com/casflawed/flameking/weather/WeatherActivity.java new file mode 100644 index 0000000..3d1b684 --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/WeatherActivity.java @@ -0,0 +1,251 @@ +package com.casflawed.flameking.weather; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.GravityCompat; +import androidx.drawerlayout.widget.DrawerLayout; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.os.Build; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; + +import com.bumptech.glide.Glide; +import com.casflawed.flameking.weather.gson.Forecast; +import com.casflawed.flameking.weather.gson.Weather; +import com.casflawed.flameking.weather.service.AutoUpdateService; +import com.casflawed.flameking.weather.util.HttpUtil; +import com.casflawed.flameking.weather.util.Utility; + +import java.io.IOException; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Response; + +public class WeatherActivity extends AppCompatActivity { + public DrawerLayout drawerLayout; + private Button navButton; + public SwipeRefreshLayout swipeRefresh; + private String mWeatherId; + private ScrollView weatherLayout; + private TextView titleCity; + private TextView titleUpdateTime; + private TextView degreeText; + private TextView weatherInfoText; + private LinearLayout forecastLayout; + private TextView aqiText; + private TextView pm25Text; + private TextView comfortText; + private TextView carWashText; + private TextView sportText; + private ImageView bingPicImg; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // 0.系统版本号判断,因为该功能Android 5.0以上才支持 + if (Build.VERSION.SDK_INT >= 21) { + View decorView = getWindow().getDecorView(); + // 0-1.改变系统UI的显示,让布局显示在状态栏上 + decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + // 0-2.将状态栏设置成透明的 + getWindow().setStatusBarColor(Color.TRANSPARENT); + } + // 1.加载布局文件activity_weather + setContentView(R.layout.activity_weather); + // 2.初始化各控件 + + weatherLayout = (ScrollView) findViewById(R.id.weather_layout); + titleCity = (TextView) findViewById(R.id.title_city); + titleUpdateTime = (TextView) findViewById(R.id.title_update_time); + degreeText = (TextView) findViewById(R.id.degree_text); + weatherInfoText = (TextView) findViewById(R.id.weather_info_text); + forecastLayout = (LinearLayout) findViewById(R.id.forecast_layout); + aqiText = (TextView) findViewById(R.id.aqi_text); + pm25Text = (TextView) findViewById(R.id.pm25_text); + comfortText = (TextView) findViewById(R.id.comfort_text); + carWashText = (TextView) findViewById(R.id.car_wash_text); + sportText = (TextView) findViewById(R.id.sport_text); + bingPicImg = (ImageView) findViewById(R.id.bing_pic_img); + swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh); + drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + navButton = (Button) findViewById(R.id.nav_button); + // 2-1.设置下拉刷新进度条的颜色 + swipeRefresh.setColorSchemeResources(com.google.android.material.R.color.design_default_color_primary); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + String weatherString = prefs.getString("weather", null); + + // 2-2.侧滑菜单按钮点击事件 + navButton.setOnClickListener(new View.OnClickListener(){ + @Override + public void onClick(View view) { + // 2-3.打开滑动菜单 + drawerLayout.openDrawer(GravityCompat.START); + } + }); + if (weatherString != null) { + // 3.有缓存时直接解析天气数据 + Weather weather = Utility.handleWeatherResponse(weatherString); + mWeatherId = weather.basic.weatherId; + showWeatherInfo(weather); + } else { + // 4.无缓存时去服务器查询天气 + mWeatherId = getIntent().getStringExtra("weather_id"); + weatherLayout.setVisibility(View.INVISIBLE); + requestWeather(mWeatherId); + } + // 4.1.设置下拉刷新的监听器 + swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + // 4.2.触发下拉刷新,调用onRefresh方法更新当前城市的天气信息 + @Override + public void onRefresh() { + requestWeather(mWeatherId); + } + }); + // 5.查询缓存的必应每日图片 + String bingPic = prefs.getString("bing_pic", null); + if (bingPic != null) { + // 6.Glide加载图片 + Glide.with(this).load(bingPic).into(bingPicImg); + }else { + loadBingPic(); + } + } + /** + * 根据天气id请求城市天气信息 + */ + public void requestWeather(final String weatherId) { + // 0.每次请求天气信息的同时,加载一张新的背景图片 + loadBingPic(); + // 1.请求url + String weatherUrl = "http://guolin.tech/api/weather?cityid=" + + weatherId + "&key=391a03e19a194caf97225f7cb4de51d9"; + // 2.发送请求 + HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() { + @Override + public void onResponse(Call call, Response response) throws IOException { + final String responseText = response.body().string(); + // 3.将天气json对象转换为Weather对象 + final Weather weather = Utility.handleWeatherResponse(responseText); + // 4.从当前线程切换到主线程 + runOnUiThread(new Runnable() { + @Override + public void run() { + // 5.请求天气信息成功 + if (weather != null && "ok".equals(weather.status)) { + SharedPreferences.Editor editor = PreferenceManager. + getDefaultSharedPreferences(WeatherActivity.this).edit(); + // 6.将返回的数据responseText缓存到SharedPreferences中 + editor.putString("weather", responseText); + editor.apply(); + mWeatherId = weather.basic.weatherId; + // 7.调用showWeatherInfo显示天气信息 + showWeatherInfo(weather); + } else { + Toast.makeText(WeatherActivity.this, "获取天气信息失败", + Toast.LENGTH_SHORT).show(); + } + // 8.请求结束,关闭下拉事件,隐藏刷新进度条 + swipeRefresh.setRefreshing(false); + } + }); + } + @Override + public void onFailure(Call call, IOException e) { + e.printStackTrace(); + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(WeatherActivity.this, "获取天气信息失败", + Toast.LENGTH_SHORT).show(); + swipeRefresh.setRefreshing(false); + } + }); + } + }); + } + /** + * 处理并展示Weather实体类中的数据 + */ + private void showWeatherInfo(Weather weather) { + String cityName = weather.basic.cityName; + String updateTime = weather.basic.update.updateTime.split(" ")[1]; + String degree = weather.now.temperature + "℃"; + String weatherInfo = weather.now.more.info; + titleCity.setText(cityName); + titleUpdateTime.setText(updateTime); + degreeText.setText(degree); + weatherInfoText.setText(weatherInfo); + forecastLayout.removeAllViews(); + for (Forecast forecast : weather.forecastList) { + View view = LayoutInflater.from(this).inflate(R.layout.forecast_item, forecastLayout, false); + TextView dateText = (TextView) view.findViewById(R.id.date_text); + ImageView infoIcon = (ImageView) view.findViewById(R.id.info_icon); +// TextView infoText = (TextView) view.findViewById(R.id.info_text); + TextView maxText = (TextView) view.findViewById(R.id.max_text); + TextView minText = (TextView) view.findViewById(R.id.min_text); + dateText.setText(forecast.date); + Integer iconCode = WeatherIconEnum.getIconCodeByIconName(forecast.more.info); + infoIcon.setImageResource(iconCode); +// infoText.setText(forecast.more.info); + maxText.setText(forecast.temperature.max); + minText.setText(forecast.temperature.min); + forecastLayout.addView(view); + } + if (weather.aqi != null) { + aqiText.setText(weather.aqi.city.aqi); + pm25Text.setText(weather.aqi.city.pm25); + } + String comfort = "舒适度:" + weather.suggestion.comfort.info; + String carWash = "洗车指数:" + weather.suggestion.carWash.info; + String sport = "运动建议:" + weather.suggestion.sport.info; + comfortText.setText(comfort); + carWashText.setText(carWash); + sportText.setText(sport); + weatherLayout.setVisibility(View.VISIBLE); + // 激活定时任务 + Intent intent = new Intent(this, AutoUpdateService.class); + startService(intent); + } + + /** + * 加载必应每日一图 + */ + private void loadBingPic(){ + String requestBingPic = "http://guolin.tech/api/bing_pic"; + HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() { + @Override + public void onResponse(Call call, Response response) throws IOException { + final String bingPic = response.body().string(); + SharedPreferences.Editor editor = PreferenceManager. + getDefaultSharedPreferences(WeatherActivity.this).edit(); + editor.putString("bing_pic", bingPic); + editor.apply(); + runOnUiThread(new Runnable() { + @Override + public void run() { + Glide.with(WeatherActivity.this).load(bingPic).into(bingPicImg); + } + }); + } + @Override + public void onFailure(Call call, IOException e) { + e.printStackTrace(); + } + }); + } + +} + + diff --git a/app/src/main/java/com/casflawed/flameking/weather/WeatherIconEnum.java b/app/src/main/java/com/casflawed/flameking/weather/WeatherIconEnum.java new file mode 100644 index 0000000..7a86452 --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/WeatherIconEnum.java @@ -0,0 +1,37 @@ +package com.casflawed.flameking.weather; + +public enum WeatherIconEnum { + SUNNY("晴", R.drawable.weather_100), + CLOUDY("多云", R.drawable.weather_101), + OVERCAST("阴", R.drawable.weather_104), + SHOWER_RAIN("阵雨", R.drawable.weather_300), + THUNDERSHOWER("雷阵雨", R.drawable.weather_302), + LIGHT_RAIN("小雨", R.drawable.weather_305), + MODERATE_RAIN("中雨", R.drawable.weather_306), + HEAVY_RAIN("大雨", R.drawable.weather_307); + + WeatherIconEnum(String weatherIconName, Integer weatherIconCode) { + this.weatherIconName = weatherIconName; + this.weatherIconCode = weatherIconCode; + } + + private String weatherIconName; + private Integer weatherIconCode; + + public String getWeatherIconName() { + return weatherIconName; + } + + public Integer getWeatherIconCode() { + return weatherIconCode; + } + + public static Integer getIconCodeByIconName(String name){ + for (WeatherIconEnum value : WeatherIconEnum.values()) { + if (value.getWeatherIconName().equals(name)) { + return value.weatherIconCode; + } + } + throw new RuntimeException("当前天气的图标还未收录!"); + } +} diff --git a/app/src/main/java/com/casflawed/flameking/weather/db/City.java b/app/src/main/java/com/casflawed/flameking/weather/db/City.java new file mode 100644 index 0000000..fd6d81b --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/db/City.java @@ -0,0 +1,52 @@ +package com.casflawed.flameking.weather.db; + +import org.litepal.crud.DataSupport; + +public class City extends DataSupport { + private Integer id; + private String cityName; + private Integer cityCode; + private Integer provinceId; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getCityName() { + return cityName; + } + + public void setCityName(String cityName) { + this.cityName = cityName; + } + + public Integer getCityCode() { + return cityCode; + } + + public void setCityCode(Integer cityCode) { + this.cityCode = cityCode; + } + + public Integer getProvinceId() { + return provinceId; + } + + public void setProvinceId(Integer provinceId) { + this.provinceId = provinceId; + } + + @Override + public String toString() { + return "City{" + + "id=" + id + + ", cityName='" + cityName + '\'' + + ", cityCode=" + cityCode + + ", provinceId=" + provinceId + + '}'; + } +} diff --git a/app/src/main/java/com/casflawed/flameking/weather/db/County.java b/app/src/main/java/com/casflawed/flameking/weather/db/County.java new file mode 100644 index 0000000..dbe4375 --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/db/County.java @@ -0,0 +1,52 @@ +package com.casflawed.flameking.weather.db; + +import org.litepal.crud.DataSupport; + +public class County extends DataSupport { + private Integer id; + private String countyName; + private String weatherId; + private Integer cityId; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getCountyName() { + return countyName; + } + + public void setCountyName(String countyName) { + this.countyName = countyName; + } + + public String getWeatherId() { + return weatherId; + } + + public void setWeatherId(String weatherId) { + this.weatherId = weatherId; + } + + public Integer getCityId() { + return cityId; + } + + public void setCityId(Integer cityId) { + this.cityId = cityId; + } + + @Override + public String toString() { + return "County{" + + "id=" + id + + ", countyName='" + countyName + '\'' + + ", weatherId=" + weatherId + + ", cityId=" + cityId + + '}'; + } +} diff --git a/app/src/main/java/com/casflawed/flameking/weather/db/Province.java b/app/src/main/java/com/casflawed/flameking/weather/db/Province.java new file mode 100644 index 0000000..7cc9d1f --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/db/Province.java @@ -0,0 +1,42 @@ +package com.casflawed.flameking.weather.db; + +import org.litepal.crud.DataSupport; + +public class Province extends DataSupport { + private Integer id; + private Integer provinceCode; + private String provinceName; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getProvinceCode() { + return provinceCode; + } + + public void setProvinceCode(Integer provinceCode) { + this.provinceCode = provinceCode; + } + + public String getProvinceName() { + return provinceName; + } + + public void setProvinceName(String provinceName) { + this.provinceName = provinceName; + } + + @Override + public String toString() { + return "Province{" + + "id=" + id + + ", provinceCode=" + provinceCode + + ", provinceName='" + provinceName + '\'' + + '}'; + } +} diff --git a/app/src/main/java/com/casflawed/flameking/weather/gson/AQI.java b/app/src/main/java/com/casflawed/flameking/weather/gson/AQI.java new file mode 100644 index 0000000..e56cd94 --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/gson/AQI.java @@ -0,0 +1,33 @@ +package com.casflawed.flameking.weather.gson; + +public class AQI { + public AQICity city; + public class AQICity{ + public String aqi; + public String pm25; + + public String getAqi() { + return aqi; + } + + public void setAqi(String aqi) { + this.aqi = aqi; + } + + public String getPm25() { + return pm25; + } + + public void setPm25(String pm25) { + this.pm25 = pm25; + } + } + + public AQICity getCity() { + return city; + } + + public void setCity(AQICity city) { + this.city = city; + } +} diff --git a/app/src/main/java/com/casflawed/flameking/weather/gson/Basic.java b/app/src/main/java/com/casflawed/flameking/weather/gson/Basic.java new file mode 100644 index 0000000..ef49dbe --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/gson/Basic.java @@ -0,0 +1,47 @@ +package com.casflawed.flameking.weather.gson; + +import com.google.gson.annotations.SerializedName; + +public class Basic { + @SerializedName("city") + public String cityName; + @SerializedName("cid") + public String weatherId; + public Update update; + public class Update{ + @SerializedName("loc") + public String updateTime; + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + } + + public String getCityName() { + return cityName; + } + + public void setCityName(String cityName) { + this.cityName = cityName; + } + + public String getWeatherId() { + return weatherId; + } + + public void setWeatherId(String weatherId) { + this.weatherId = weatherId; + } + + public Update getUpdate() { + return update; + } + + public void setUpdate(Update update) { + this.update = update; + } +} diff --git a/app/src/main/java/com/casflawed/flameking/weather/gson/Forecast.java b/app/src/main/java/com/casflawed/flameking/weather/gson/Forecast.java new file mode 100644 index 0000000..105f318 --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/gson/Forecast.java @@ -0,0 +1,67 @@ +package com.casflawed.flameking.weather.gson; + +import com.google.gson.annotations.SerializedName; + +public class Forecast { + public String date; + @SerializedName("tmp") + public Temperature temperature; + @SerializedName("cond") + public More more; + public class Temperature{ + public String max; + public String min; + + public String getMax() { + return max; + } + + public void setMax(String max) { + this.max = max; + } + + public String getMin() { + return min; + } + + public void setMin(String min) { + this.min = min; + } + } + public class More{ + @SerializedName("txt_d") + public String info; + + public String getInfo() { + return info; + } + + public void setInfo(String info) { + this.info = info; + } + } + + public String getDate() { + return date; + } + + public void setDate(String date) { + this.date = date; + } + + public Temperature getTemperature() { + return temperature; + } + + public void setTemperature(Temperature temperature) { + this.temperature = temperature; + } + + public More getMore() { + return more; + } + + public void setMore(More more) { + this.more = more; + } +} diff --git a/app/src/main/java/com/casflawed/flameking/weather/gson/Now.java b/app/src/main/java/com/casflawed/flameking/weather/gson/Now.java new file mode 100644 index 0000000..3120be5 --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/gson/Now.java @@ -0,0 +1,37 @@ +package com.casflawed.flameking.weather.gson; + +import com.google.gson.annotations.SerializedName; + +public class Now { + @SerializedName("tmp") + public String temperature; + @SerializedName("cond") + public More more; + public class More{ + public String info; + + public String getInfo() { + return info; + } + + public void setInfo(String info) { + this.info = info; + } + } + + public String getTemperature() { + return temperature; + } + + public void setTemperature(String temperature) { + this.temperature = temperature; + } + + public More getMore() { + return more; + } + + public void setMore(More more) { + this.more = more; + } +} diff --git a/app/src/main/java/com/casflawed/flameking/weather/gson/Suggestion.java b/app/src/main/java/com/casflawed/flameking/weather/gson/Suggestion.java new file mode 100644 index 0000000..7c732d3 --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/gson/Suggestion.java @@ -0,0 +1,71 @@ +package com.casflawed.flameking.weather.gson; + +import com.google.gson.annotations.SerializedName; + +public class Suggestion { + @SerializedName("comf") + public Comfort comfort; + public Sport sport; + @SerializedName("cw") + public CarWash carWash; + public class Comfort{ + @SerializedName("txt") + public String info; + + public String getInfo() { + return info; + } + + public void setInfo(String info) { + this.info = info; + } + } + public class Sport{ + @SerializedName("txt") + public String info; + + public String getInfo() { + return info; + } + + public void setInfo(String info) { + this.info = info; + } + } + public class CarWash{ + @SerializedName("txt") + public String info; + + public String getInfo() { + return info; + } + + public void setInfo(String info) { + this.info = info; + } + } + + public Comfort getComfort() { + return comfort; + } + + public void setComfort(Comfort comfort) { + this.comfort = comfort; + } + + public Sport getSport() { + return sport; + } + + public void setSport(Sport sport) { + this.sport = sport; + } + + public CarWash getCarWash() { + return carWash; + } + + public void setCarWash(CarWash carWash) { + this.carWash = carWash; + } +} diff --git a/app/src/main/java/com/casflawed/flameking/weather/gson/Weather.java b/app/src/main/java/com/casflawed/flameking/weather/gson/Weather.java new file mode 100644 index 0000000..b391e95 --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/gson/Weather.java @@ -0,0 +1,63 @@ +package com.casflawed.flameking.weather.gson; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +public class Weather { + public String status; + public Basic basic; + public AQI aqi; + public Now now; + public Suggestion suggestion; + @SerializedName("daily_forecast") + public List forecastList; + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Basic getBasic() { + return basic; + } + + public void setBasic(Basic basic) { + this.basic = basic; + } + + public AQI getAqi() { + return aqi; + } + + public void setAqi(AQI aqi) { + this.aqi = aqi; + } + + public Now getNow() { + return now; + } + + public void setNow(Now now) { + this.now = now; + } + + public Suggestion getSuggestion() { + return suggestion; + } + + public void setSuggestion(Suggestion suggestion) { + this.suggestion = suggestion; + } + + public List getForecastList() { + return forecastList; + } + + public void setForecastList(List forecastList) { + this.forecastList = forecastList; + } +} diff --git a/app/src/main/java/com/casflawed/flameking/weather/service/AutoUpdateService.java b/app/src/main/java/com/casflawed/flameking/weather/service/AutoUpdateService.java new file mode 100644 index 0000000..44eabc0 --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/service/AutoUpdateService.java @@ -0,0 +1,97 @@ +package com.casflawed.flameking.weather.service; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.IBinder; +import android.os.SystemClock; +import android.preference.PreferenceManager; + +import com.casflawed.flameking.weather.gson.Weather; +import com.casflawed.flameking.weather.util.HttpUtil; +import com.casflawed.flameking.weather.util.Utility; + +import java.io.IOException; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Response; + +public class AutoUpdateService extends Service { + @Override + public IBinder onBind(Intent intent) { + return null; + } + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // 1.更新天气信息 + updateWeather(); + // 2.更新图片信息 + updateBingPic(); + AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE); + // 3.每8小时自动更新,节省用户流量开销 + int anHour = 8 * 60 * 60 * 1000; // 这是8小时的毫秒数 + long triggerAtTime = SystemClock.elapsedRealtime() + anHour; + Intent i = new Intent(this, AutoUpdateService.class); + PendingIntent pi = PendingIntent.getService(this, 0, i, 0); + manager.cancel(pi); + manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi); + return super.onStartCommand(intent, flags, startId); + } + /** + * 更新天气信息 + */ + private void updateWeather(){ + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + String weatherString = prefs.getString("weather", null); + if (weatherString != null) { + // 有缓存时直接解析天气数据,只是获取weatherId + Weather weather = Utility.handleWeatherResponse(weatherString); + String weatherId = weather.basic.weatherId; + String weatherUrl = "http://guolin.tech/api/weather?cityid=" + + weatherId + "&key=391a03e19a194caf97225f7cb4de51d9"; + HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() { + @Override + public void onResponse(Call call, Response response) throws + IOException { + String responseText = response.body().string(); + Weather weather = Utility.handleWeatherResponse(responseText); + if (weather != null && "ok".equals(weather.status)) { + SharedPreferences.Editor editor = PreferenceManager. + getDefaultSharedPreferences(AutoUpdateService.this). + edit(); + // 更新缓存 + editor.putString("weather", responseText); + editor.apply(); + } + } + @Override + public void onFailure(Call call, IOException e) { + e.printStackTrace(); + } + }); + } + } + /** + * 更新必应每日一图 + */ + private void updateBingPic() { + String requestBingPic = "http://guolin.tech/api/bing_pic"; + HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() { + @Override + public void onResponse(Call call, Response response) throws IOException { + String bingPic = response.body().string(); + SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences( + AutoUpdateService.this).edit(); + editor.putString("bing_pic", bingPic); + editor.apply(); + } + @Override + public void onFailure(Call call, IOException e) { + e.printStackTrace(); + } + }); + } +} diff --git a/app/src/main/java/com/casflawed/flameking/weather/util/HttpUtil.java b/app/src/main/java/com/casflawed/flameking/weather/util/HttpUtil.java new file mode 100644 index 0000000..6b3562a --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/util/HttpUtil.java @@ -0,0 +1,16 @@ +package com.casflawed.flameking.weather.util; + +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; + +public class HttpUtil { + public static void sendOkHttpRequest(String address, Callback callback) { + OkHttpClient client = new OkHttpClient(); + // 1.传入url地址 + Request request = new Request.Builder().url(address).build(); + // 2.注册回调处理response + client.newCall(request).enqueue(callback); + } +} + diff --git a/app/src/main/java/com/casflawed/flameking/weather/util/Utility.java b/app/src/main/java/com/casflawed/flameking/weather/util/Utility.java new file mode 100644 index 0000000..517100d --- /dev/null +++ b/app/src/main/java/com/casflawed/flameking/weather/util/Utility.java @@ -0,0 +1,106 @@ +package com.casflawed.flameking.weather.util; + +import android.text.TextUtils; + +import com.casflawed.flameking.weather.db.City; +import com.casflawed.flameking.weather.db.County; +import com.casflawed.flameking.weather.db.Province; +import com.casflawed.flameking.weather.gson.Weather; +import com.google.gson.Gson; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class Utility { + /** + * 解析和处理服务器返回的省级数据 + */ + public static boolean handleProvinceResponse(String response) { + if (! TextUtils.isEmpty(response)) { + try { + // 1.转json数组 + JSONArray allProvinces = new JSONArray(response); + for (int i = 0; i < allProvinces.length(); i++) { + // 2.转json对象 + JSONObject provinceObject = allProvinces.getJSONObject(i); + // 3.初始化实例对象 + Province province = new Province(); + province.setProvinceName(provinceObject.getString("name")); + province.setProvinceCode(provinceObject.getInt("id")); + province.save(); + } + return true; + } catch (JSONException e) { + e.printStackTrace(); + } + } + return false; + } + /** + * 解析和处理服务器返回的市级数据 + */ + public static boolean handleCityResponse(String response, int provinceId) { + if (! TextUtils.isEmpty(response)) { + try { + JSONArray allCities = new JSONArray(response); + for (int i = 0; i < allCities.length(); i++) { + JSONObject cityObject= allCities.getJSONObject(i); + City city = new City(); + city.setCityName(cityObject.getString("name")); + city.setCityCode(cityObject.getInt("id")); + city.setProvinceId(provinceId); + city.save(); + } + return true; + } catch (JSONException e) { + e.printStackTrace(); + } + } + return false; + } + /** + * 解析和处理服务器返回的县级数据 + */ + public static boolean handleCountyResponse(String response, int cityId) { + if (! TextUtils.isEmpty(response)) { + try { + JSONArray allCounties = new JSONArray(response); + for (int i = 0; i < allCounties.length(); i++) { + JSONObject countyObject = allCounties.getJSONObject(i); + County county = new County(); + county.setCountyName(countyObject.getString("name")); + county.setWeatherId(countyObject.getString("weather_id")); + county.setCityId(cityId); + county.save(); + } + return true; + } catch (JSONException e) { + e.printStackTrace(); + } + } + return false; + } + + /** + * 将返回的JSON数据解析成Weather实体类 + */ + public static Weather handleWeatherResponse(String response) { + try { + // 1.获取响应对象数据 + JSONObject jsonObject = new JSONObject(response); + // 2.获取HeWeather数组对象 + JSONArray jsonArray = jsonObject.getJSONArray("HeWeather"); + // 3.获取HeWeather数组对象的第一个元素 + String weatherContent = jsonArray.getJSONObject(0).toString(); + // 4.gson转换json为Java对象 + return new Gson().fromJson(weatherContent, Weather.class); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + +} + + diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_back.png b/app/src/main/res/drawable/ic_back.png new file mode 100644 index 0000000..32a6d91 Binary files /dev/null and b/app/src/main/res/drawable/ic_back.png differ diff --git a/app/src/main/res/drawable/ic_home.png b/app/src/main/res/drawable/ic_home.png new file mode 100644 index 0000000..f7dc554 Binary files /dev/null and b/app/src/main/res/drawable/ic_home.png differ diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/weather_100.png b/app/src/main/res/drawable/weather_100.png new file mode 100644 index 0000000..ab36393 Binary files /dev/null and b/app/src/main/res/drawable/weather_100.png differ diff --git a/app/src/main/res/drawable/weather_101.png b/app/src/main/res/drawable/weather_101.png new file mode 100644 index 0000000..4b98813 Binary files /dev/null and b/app/src/main/res/drawable/weather_101.png differ diff --git a/app/src/main/res/drawable/weather_104.png b/app/src/main/res/drawable/weather_104.png new file mode 100644 index 0000000..cf75c85 Binary files /dev/null and b/app/src/main/res/drawable/weather_104.png differ diff --git a/app/src/main/res/drawable/weather_300.png b/app/src/main/res/drawable/weather_300.png new file mode 100644 index 0000000..39d7ae6 Binary files /dev/null and b/app/src/main/res/drawable/weather_300.png differ diff --git a/app/src/main/res/drawable/weather_302.png b/app/src/main/res/drawable/weather_302.png new file mode 100644 index 0000000..a565077 Binary files /dev/null and b/app/src/main/res/drawable/weather_302.png differ diff --git a/app/src/main/res/drawable/weather_305.png b/app/src/main/res/drawable/weather_305.png new file mode 100644 index 0000000..85ae414 Binary files /dev/null and b/app/src/main/res/drawable/weather_305.png differ diff --git a/app/src/main/res/drawable/weather_306.png b/app/src/main/res/drawable/weather_306.png new file mode 100644 index 0000000..54e224c Binary files /dev/null and b/app/src/main/res/drawable/weather_306.png differ diff --git a/app/src/main/res/drawable/weather_307.png b/app/src/main/res/drawable/weather_307.png new file mode 100644 index 0000000..f9baa36 Binary files /dev/null and b/app/src/main/res/drawable/weather_307.png differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..27c408a --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,21 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_weather.xml b/app/src/main/res/layout/activity_weather.xml new file mode 100644 index 0000000..cc3ebf4 --- /dev/null +++ b/app/src/main/res/layout/activity_weather.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/aqi.xml b/app/src/main/res/layout/aqi.xml new file mode 100644 index 0000000..26a5c7b --- /dev/null +++ b/app/src/main/res/layout/aqi.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/choose_area.xml b/app/src/main/res/layout/choose_area.xml new file mode 100644 index 0000000..53ba6e2 --- /dev/null +++ b/app/src/main/res/layout/choose_area.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + +