├── .gitignore
├── .idea
├── codeStyles
│ └── Project.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── LICENSE
├── Readme.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── release
│ └── output.json
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── java
│ │ └── wengjiayi
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── java
│ │ │ └── wengjiayi
│ │ │ ├── ItemNewsActivity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── MyWebViewClient.java
│ │ │ ├── NetworkAvail.java
│ │ │ ├── ShowDescriptionActivity.java
│ │ │ ├── WebContent.java
│ │ │ └── rss
│ │ │ ├── RssFeed.java
│ │ │ ├── RssFeed_SAXParser.java
│ │ │ ├── RssHandler.java
│ │ │ └── RssItem.java
│ ├── res
│ │ ├── drawable-v24
│ │ │ ├── ic_launcher_foreground.xml
│ │ │ ├── ic_menu_camera.xml
│ │ │ ├── ic_menu_gallery.xml
│ │ │ ├── ic_menu_manage.xml
│ │ │ ├── ic_menu_send.xml
│ │ │ ├── ic_menu_share.xml
│ │ │ ├── ic_menu_slideshow.xml
│ │ │ └── side_nav_bar.xml
│ │ ├── drawable
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── icon.png
│ │ │ └── icon2.png
│ │ ├── layout
│ │ │ ├── activity_customize.xml
│ │ │ ├── activity_itemnews.xml
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_newstop_content.xml
│ │ │ ├── activity_show_description.xml
│ │ │ ├── app_bar_main.xml
│ │ │ ├── checkbox.xml
│ │ │ ├── content.xml
│ │ │ ├── content_main.xml
│ │ │ └── nav_header_main.xml
│ │ ├── menu
│ │ │ ├── activity_main_drawer.xml
│ │ │ ├── item.xml
│ │ │ └── main.xml
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── values-v21
│ │ │ └── styles.xml
│ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ └── web_hi_res_512.png
│ └── test
│ └── java
│ └── com
│ └── java
│ └── wengjiayi
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── pic
├── Screenshot_20180915-120413.jpg
├── Screenshot_20180915-120423.jpg
├── Screenshot_20180915-120439.jpg
└── Screenshot_20180915-120513.jpg
├── release
├── app-release.apk
└── output.json
├── settings.gradle
└── 大作业报告.pdf
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # IntelliJ
36 | *.iml
37 | .idea/workspace.xml
38 | .idea/tasks.xml
39 | .idea/gradle.xml
40 | .idea/assetWizardSettings.xml
41 | .idea/dictionaries
42 | .idea/libraries
43 | .idea/caches
44 |
45 | # Keystore files
46 | # Uncomment the following line if you do not want to check your keystore files in.
47 | #*.jks
48 |
49 | # External native build folder generated in Android Studio 2.2 and later
50 | .externalNativeBuild
51 |
52 | # Google Services (e.g. APIs or Firebase)
53 | google-services.json
54 |
55 | # Freeline
56 | freeline.py
57 | freeline/
58 | freeline_project_description.json
59 |
60 | # fastlane
61 | fastlane/report.xml
62 | fastlane/Preview.html
63 | fastlane/screenshots
64 | fastlane/test_output
65 | fastlane/readme.md
66 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Trinkle
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Simple News
2 |
3 | Implement with SDK24, Android version>=7.0
4 |
5 | ## Features
6 |
7 | - RSS
8 | - Choose different topic of news
9 | - Favor news
10 | - Search news
11 | - Share news to other app
12 | - Recommend news by your recent browse
13 |
14 | ## Screenshots
15 |
16 |
22 |
23 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.java.wengjiayi"
7 | minSdkVersion 24
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | implementation 'com.android.support:appcompat-v7:28.0.0-rc02'
24 | implementation 'com.android.support:support-v4:28.0.0-rc02'
25 | implementation 'com.android.support:design:28.0.0-rc02'
26 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
27 | testImplementation 'junit:junit:4.12'
28 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
29 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
30 | compile 'com.jwenfeng.pulltorefresh:library:1.0.3'
31 | // compile project(':library')
32 | }
33 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/release/output.json:
--------------------------------------------------------------------------------
1 | [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/java/wengjiayi/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.java.wengjiayi;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.java.wengjiayi", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/java/wengjiayi/ItemNewsActivity.java:
--------------------------------------------------------------------------------
1 | package com.java.wengjiayi;
2 |
3 | import android.content.Context;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.os.Bundle;
6 | import android.content.Intent;
7 | import android.util.Log;
8 | import android.view.KeyEvent;
9 | import android.view.Menu;
10 | import android.view.MenuItem;
11 | import android.view.View;
12 | import android.webkit.WebChromeClient;
13 | import android.webkit.WebSettings;
14 | import android.webkit.WebView;
15 | import android.webkit.WebViewClient;
16 | import android.widget.ProgressBar;
17 | import android.widget.TextView;
18 | import android.widget.Toast;
19 |
20 | import java.io.BufferedReader;
21 | import java.io.ByteArrayOutputStream;
22 | import java.io.File;
23 | import java.io.FileInputStream;
24 | import java.io.FileNotFoundException;
25 | import java.io.FileOutputStream;
26 | import java.io.FileWriter;
27 | import java.io.IOException;
28 | import java.io.InputStreamReader;
29 | import java.net.MalformedURLException;
30 | import java.net.URL;
31 |
32 | public class ItemNewsActivity extends AppCompatActivity {
33 |
34 | public String webUrl;
35 | public String favorInfo;
36 | public String fileName;
37 | public String favorFile;
38 | public String prefix;
39 | private WebView webView;
40 | public String html;
41 | public int networkState;
42 | @Override
43 | protected void onCreate(Bundle savedInstanceState) {
44 | super.onCreate(savedInstanceState);
45 | setContentView(R.layout.activity_newstop_content);
46 | Intent intent = getIntent();
47 | prefix = getFilesDir().getPath();
48 | webUrl = intent.getStringExtra("link");
49 | favorInfo = intent.getStringExtra("info");
50 | fileName = intent.getStringExtra("file");
51 | favorFile = prefix + "/favor/" + fileName;
52 | setupViews();
53 | showContent();
54 | }
55 |
56 | private void setupViews() {
57 | // scrollView = (ScrollView) findViewById(R.id.news_scrollView);
58 | // newsContentView = (NewsContentView) findViewById(R.id.newscontentView);
59 | webView = (WebView) findViewById(R.id.news_webView);
60 | webView.getSettings().setDomStorageEnabled(true);
61 | webView.getSettings().setJavaScriptEnabled(true);
62 | webView.getSettings().setAppCacheEnabled(true);
63 | webView.getSettings().setSupportZoom(true);
64 | webView.getSettings().setBuiltInZoomControls(true);
65 | webView.setWebChromeClient(new WebChromeClient());
66 | webView.setWebViewClient(new MyWebViewClient("http://world.people.com.cn"));
67 | webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
68 | webView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
69 | webView.getSettings().setLoadWithOverviewMode(true);
70 | }
71 | @Override
72 | public boolean onCreateOptionsMenu(Menu menu) {
73 | // Inflate the menu; this adds items to the action bar if it is present.
74 | getMenuInflater().inflate(R.menu.item, menu);
75 | MenuItem tv = (MenuItem) menu.findItem(R.id.action_favor);
76 | if (new File(favorFile).exists())
77 | tv.setTitle("取消收藏");
78 | else
79 | tv.setTitle("收藏新闻");
80 | return true;
81 | }
82 | @Override
83 | public boolean onOptionsItemSelected(MenuItem item) {
84 | // Handle action bar item clicks here. The action bar will
85 | // automatically handle clicks on the Home/Up button, so long
86 | // as you specify a parent activity in AndroidManifest.xml.
87 | int id = item.getItemId();
88 |
89 | //noinspection SimplifiableIfStatement
90 | if (id == R.id.action_share) {
91 | Intent share = new Intent();
92 | share.setAction(Intent.ACTION_SEND);
93 | share.setType("text/plain");
94 | // share.putExtra(Intent.EXTRA_SUBJECT, "233");
95 | share.putExtra(Intent.EXTRA_TEXT, WebContent.getTitle(html).replace(" ","")+" "+webUrl);
96 | share = Intent.createChooser(share, "分享该新闻至:");
97 | startActivity(share);
98 | return true;
99 | }
100 | if (id == R.id.action_img) {
101 | Toast.makeText(this, "Sorry,暂时不能分享图片", Toast.LENGTH_SHORT).show();
102 | return true;
103 | }
104 | if (id == R.id.action_clear) {
105 | Toast.makeText(this, "缓存清空成功!", Toast.LENGTH_SHORT).show();
106 | return true;
107 | }
108 | if (id == R.id.action_favor) {
109 | File favor = new File(favorFile);
110 | Log.i("favor", " " + favor.exists() + " " + favorInfo);
111 | if (favor.exists()) {
112 | favor.delete();
113 | Toast.makeText(this, "取消收藏成功!", Toast.LENGTH_SHORT).show();
114 | item.setTitle("收藏新闻");
115 | } else {
116 | try {
117 | FileWriter fw = new FileWriter(favor);
118 | fw.write(favorInfo);
119 | fw.close();
120 | // FileOutputStream out = openFileOutput(favorFile, MODE_PRIVATE);
121 | // out.write(favorInfo.getBytes());
122 | // out.close();
123 | } catch (FileNotFoundException e) {
124 | e.printStackTrace();
125 | } catch (IOException e) {
126 | e.printStackTrace();
127 | }
128 | Toast.makeText(this, "收藏成功!", Toast.LENGTH_SHORT).show();
129 | item.setTitle("取消收藏");
130 | }
131 | }
132 | return super.onOptionsItemSelected(item);
133 | }
134 |
135 | private void showContent() {
136 | if (webUrl.contains("qq.com"))
137 | webUrl = webUrl.replace("http", "https");
138 | Log.i("loadurl", webUrl);
139 | File file = new File(prefix+"/"+fileName);
140 | Log.i("loadurl", prefix+"/"+fileName);
141 | networkState = 1;
142 | Thread t = new Thread(new Runnable() {
143 | @Override
144 | public void run() {
145 | try {
146 | html = WebContent.getHtml(webUrl);
147 | } catch (ExceptionInInitializerError e) {
148 | networkState = 0;
149 | }
150 | }
151 | });
152 | t.start();
153 | try{
154 | t.join();
155 | }
156 | catch (InterruptedException e) {
157 | e.printStackTrace();
158 | }
159 | if (!file.exists() || networkState == 1) {
160 | Log.i("state", "no exist " + networkState);
161 | if (networkState == 1) {
162 | String charSet = "";
163 | try {
164 | charSet = WebContent.getCharset(html);
165 | } catch (ExceptionInInitializerError e) {
166 | ShowDescriptionActivity.show("网络断掉了qwq", this, false);
167 | }
168 | webView.loadDataWithBaseURL(null, html, "text/html", charSet, null);
169 | webView.loadUrl(webUrl);
170 | }
171 | else
172 | html = "网络断掉了qwq网络断掉了qwq
请检查网络连接。
";
173 | // ShowDescriptionActivity.show("网络断掉了qwq", this, false);
174 | if (networkState == 1) {
175 | Log.i("html", html);
176 | try {
177 | FileOutputStream out = openFileOutput(fileName, MODE_PRIVATE);
178 | out.write(html.getBytes());
179 | out.close();
180 | } catch (FileNotFoundException e) {
181 | e.printStackTrace();
182 | } catch (IOException e) {
183 | e.printStackTrace();
184 | }
185 | }
186 | else {
187 | webView.loadDataWithBaseURL(null, html, "text/html", WebContent.getCharset(html), null);
188 | }
189 | }
190 | else {
191 | Log.i("state", "exist");
192 | try {
193 | FileInputStream inStream=this.openFileInput(fileName);
194 | ByteArrayOutputStream stream=new ByteArrayOutputStream();
195 | byte[] buffer=new byte[1024];
196 | int length=-1;
197 | while((length=inStream.read(buffer))!=-1) {
198 | stream.write(buffer,0,length);
199 | }
200 | stream.close();
201 | inStream.close();
202 | html = stream.toString();
203 | } catch (FileNotFoundException e) {
204 | e.printStackTrace();
205 | }
206 | catch (IOException e){
207 | e.printStackTrace();
208 | }
209 | String charSet = "";
210 | try {
211 | charSet = WebContent.getCharset(html);
212 | } catch (ExceptionInInitializerError e) {
213 | ShowDescriptionActivity.show("网络断掉了qwq", this, false);
214 | }
215 | webView.loadDataWithBaseURL(null, html, "text/html", charSet, null);
216 | }
217 | }
218 |
219 | //点击backspace可返回上个页面,而不是退出(若webview只加载了一个页面)
220 | @Override
221 | public boolean onKeyDown(int keyCode, KeyEvent event) {
222 | if (keyCode == KeyEvent.KEYCODE_BACK) {
223 | if (webView.getVisibility() == View.VISIBLE) {
224 | // 按返回时,看网页是否能返回
225 | if (webView.canGoBack()) {
226 | webView.goBack();
227 | //返回true webview自己处理
228 | return true;
229 | }
230 | }
231 | }
232 | return super.onKeyDown(keyCode, event);
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/app/src/main/java/com/java/wengjiayi/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.java.wengjiayi;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.ByteArrayOutputStream;
5 | import java.io.File;
6 | import java.io.FileInputStream;
7 | import java.io.FileNotFoundException;
8 | import java.io.FileOutputStream;
9 | import java.io.FileReader;
10 | import java.io.FileWriter;
11 | import java.io.IOException;
12 | import java.net.UnknownHostException;
13 | import java.text.SimpleDateFormat;
14 | import java.util.ArrayList;
15 | import java.util.Date;
16 | import java.util.Scanner;
17 |
18 | import javax.xml.parsers.ParserConfigurationException;
19 |
20 | import org.xml.sax.SAXException;
21 |
22 | //import com.handmark.pulltorefresh.library.PullToRefreshBase;
23 | //import com.handmark.pulltorefresh.library.PullToRefreshListView;
24 | import com.java.wengjiayi.rss.*;
25 | import com.jwenfeng.library.pulltorefresh.BaseRefreshListener;
26 | import com.jwenfeng.library.pulltorefresh.PullToRefreshLayout;
27 |
28 | import android.app.AlertDialog;
29 | import android.content.Context;
30 | import android.content.DialogInterface;
31 | import android.graphics.Color;
32 | import android.os.AsyncTask;
33 | import android.os.Bundle;
34 | import android.app.Activity;
35 | import android.content.Intent;
36 | import android.os.Handler;
37 | import android.support.annotation.Nullable;
38 | import android.support.v4.view.GravityCompat;
39 | import android.support.v4.widget.DrawerLayout;
40 | import android.support.v7.app.ActionBarDrawerToggle;
41 | import android.support.v7.app.AppCompatActivity;
42 | import android.support.design.widget.FloatingActionButton;
43 | import android.support.design.widget.Snackbar;
44 | import android.support.design.widget.NavigationView;
45 | import android.support.v7.widget.Toolbar;
46 | import android.view.View;
47 | import android.util.AttributeSet;
48 | import android.util.Log;
49 | import android.view.LayoutInflater;
50 | import android.view.Menu;
51 | import android.view.MenuItem;
52 | import android.view.View;
53 | import android.widget.AdapterView;
54 | import android.widget.AdapterView.OnItemClickListener;
55 | import android.widget.ArrayAdapter;
56 | import android.widget.EditText;
57 | import android.widget.LinearLayout;
58 | import android.widget.ListView;
59 | import android.widget.SimpleAdapter;
60 | import android.support.v4.app.NavUtils;
61 | import android.widget.TextView;
62 | import android.widget.Toast;
63 |
64 | public class MainActivity extends AppCompatActivity implements OnItemClickListener{//, NavigationView.OnNavigationItemSelectedListener {
65 |
66 | // 从网络获取RSS地址
67 | public String RSS_URL = "http://news.qq.com/newssh/rss_newssh.xml";
68 | public String RSS_URL2 = "http://news.qq.com/newssh/rss_newssh.xml";
69 | public String prefix = "";
70 | public final String tag = "RSSReader";
71 | public String[] urls = {"http://www.people.com.cn/rss/politics.xml",
72 | "http://www.people.com.cn/rss/world.xml",
73 | "http://www.people.com.cn/rss/finance.xml",
74 | "http://www.people.com.cn/rss/sports.xml",
75 | "http://www.people.com.cn/rss/haixia.xml",
76 | "http://www.people.com.cn/rss/edu.xml",
77 | "http://www.people.com.cn/rss/culture.xml",
78 | "http://www.people.com.cn/rss/game.xml"};
79 | private RssFeed feed = null, feed2 = null;
80 | private ListView newsList;
81 | private SimpleAdapter simpleAdapter;
82 | boolean[] choiceSets = {true,true,true,true,true,true,true,true};
83 | int[] count = {0,0,0,0,0,0,0,0};
84 | Context context;
85 | public void getBest() {
86 | int id = 0, id2 = 0, max = 0, max2 = 0;
87 | for (int i = 0; i < count.length; ++i) {
88 | if (max < count[i]) {
89 | id2 = id;
90 | max2 = max;
91 | id = i;
92 | max = count[i];
93 | } else if (count[i] <= max && max2 < count[i]) {
94 | id2 = i;
95 | max2 = count[i];
96 | }
97 | }
98 | RSS_URL = urls[id];
99 | RSS_URL2 = urls[id2];
100 | Log.i("best", RSS_URL+" "+RSS_URL2);
101 | Log.i("best", id + " " + id2);
102 | Log.i("best", " "+count[0]+" "+count[1]+" "+count[2]+" "+count[3]+" "+count[4]+" "+count[5]+" "+count[6]+" "+count[7]);
103 | }
104 | @Override
105 | public void onCreate(Bundle savedInstanceState) {
106 | super.onCreate(savedInstanceState);
107 | setContentView(R.layout.activity_main);
108 | context = this;
109 | // 获取控件
110 | RSS_URL = "http://www.people.com.cn/rss/world.xml";
111 | // TextView tv = (TextView)findViewById(R.id.title);
112 | // tv.setText("国际新闻");
113 | prefix = getFilesDir().getPath();
114 | Log.i("favorDir", prefix+"/favor");
115 | File favor = new File(prefix+"/favor");
116 | if (!favor.exists()) favor.mkdirs();
117 | File file = new File(prefix+"/9bf6d3581229");
118 | String state = "11111111";
119 | if (file.exists()) {
120 | try {
121 | FileInputStream inStream=this.openFileInput("9bf6d3581229");
122 | ByteArrayOutputStream stream=new ByteArrayOutputStream();
123 | byte[] buffer=new byte[8];
124 | int length=-1;
125 | if((length=inStream.read(buffer))!=-1)
126 | stream.write(buffer,0,length);
127 | stream.close();
128 | inStream.close();
129 | state = stream.toString();
130 | } catch (FileNotFoundException e) {
131 | e.printStackTrace();
132 | }
133 | catch (IOException e){
134 | e.printStackTrace();
135 | }
136 | }
137 | File rec = new File(prefix+"/rec.txt");
138 | if (rec.exists()) {
139 | try {
140 | Scanner scan = new Scanner(rec);
141 | for (int i = 0; i < 8; ++i)
142 | count[i] = scan.nextInt();
143 | } catch (FileNotFoundException e) {
144 | e.printStackTrace();
145 | }
146 | }
147 | getBest();
148 | Log.i("init", state);
149 | setUpColumn(R.id.col_, "recommend");
150 | setUpColumn(R.id.col0, "favor");
151 | setUpColumn(R.id.col1, urls[0]);
152 | setUpColumn(R.id.col2, urls[1]);
153 | setUpColumn(R.id.col3, urls[2]);
154 | setUpColumn(R.id.col4, urls[3]);
155 | setUpColumn(R.id.col5, urls[4]);
156 | setUpColumn(R.id.col6, urls[5]);
157 | setUpColumn(R.id.col7, urls[6]);
158 | setUpColumn(R.id.col8, urls[7]);
159 | setState(state);
160 | setPullToRefesh();
161 | refreshMain();
162 | }
163 | private int findUrl(String s) {
164 | for (int i = 0; i < urls.length; ++i)
165 | if (urls[i].equals(s))
166 | return i;
167 | return -1;
168 | }
169 | private void setState(String state) {
170 | TextView tv;
171 | Log.i("state", state);
172 | for (int i = 0; i < urls.length; ++i) {
173 | int id;
174 | if (i == 0) id = R.id.col1;
175 | else if (i == 1) id = R.id.col2;
176 | else if (i == 2) id = R.id.col3;
177 | else if (i == 3) id = R.id.col4;
178 | else if (i == 4) id = R.id.col5;
179 | else if (i == 5) id = R.id.col6;
180 | else if (i == 6) id = R.id.col7;
181 | else id = R.id.col8;
182 | tv = (TextView) findViewById(id);
183 | if (state.charAt(i) == '1') {
184 | tv.setVisibility(View.VISIBLE);
185 | Log.i("state", "true");
186 | choiceSets[i] = true;
187 | }
188 | else {
189 | tv.setVisibility(View.GONE);
190 | Log.i("state", "false");
191 | choiceSets[i] = false;
192 | }
193 | }
194 | }
195 | private void refreshMain() {
196 | Log.i("rssurl:", RSS_URL);
197 | if (!NetworkAvail.check(this)) {
198 | ShowDescriptionActivity.show("没有网络qwq请检查", this, true);
199 | Log.i("network", "no");
200 | return;
201 | }
202 | if (RSS_URL.startsWith("http")) {
203 | Thread t = new Thread(new Runnable() {
204 | @Override
205 | public void run() {
206 | try {
207 | feed = new RssFeed_SAXParser().getFeed(RSS_URL);
208 | } catch (ParserConfigurationException e) {
209 | e.printStackTrace();
210 | } catch (SAXException e) {
211 | e.printStackTrace();
212 | } catch (IOException e) {
213 | e.printStackTrace();
214 | }
215 | }
216 | });
217 | t.start();
218 | try {
219 | t.join();
220 | } catch (InterruptedException e) {
221 | e.printStackTrace();
222 | }
223 | Log.i("find", " "+findUrl(RSS_URL));
224 | Log.i("find", " "+RSS_URL);
225 | feed.modify(findUrl(RSS_URL));
226 | }
227 | else if (RSS_URL.equals("favor")){ // 收藏
228 | feed = new RssFeed();
229 | File[] favors = new File(prefix+"/favor").listFiles();
230 | for (int i = 0; i < favors.length; ++i) {
231 | System.out.println(favors[i].getPath());
232 | feed.addItem(new RssItem(readFile(favors[i].getPath())));
233 | }
234 | }
235 | else { // 推荐
236 | getBest();
237 | Thread t = new Thread(new Runnable() {
238 | @Override
239 | public void run() {
240 | try {
241 | feed = new RssFeed_SAXParser().getFeed(RSS_URL);
242 | } catch (ParserConfigurationException e) {
243 | e.printStackTrace();
244 | } catch (SAXException e) {
245 | e.printStackTrace();
246 | } catch (IOException e) {
247 | e.printStackTrace();
248 | }
249 | }
250 | });
251 | t.start();
252 | try {
253 | t.join();
254 | } catch (InterruptedException e) {
255 | e.printStackTrace();
256 | }
257 | feed.modify(findUrl(RSS_URL));
258 | //-----
259 | t = new Thread(new Runnable() {
260 | @Override
261 | public void run() {
262 | try {
263 | feed2 = new RssFeed_SAXParser().getFeed(RSS_URL2);
264 | } catch (ParserConfigurationException e) {
265 | e.printStackTrace();
266 | } catch (SAXException e) {
267 | e.printStackTrace();
268 | } catch (IOException e) {
269 | e.printStackTrace();
270 | }
271 | }
272 | });
273 | t.start();
274 | try {
275 | t.join();
276 | } catch (InterruptedException e) {
277 | e.printStackTrace();
278 | }
279 | feed2.modify(findUrl(RSS_URL2));
280 | feed = merge(feed, feed2);
281 | RSS_URL = "recommend";
282 | }
283 | showListView();
284 | }
285 | private RssFeed merge(RssFeed f1, RssFeed f2) {
286 | feed = new RssFeed();
287 | f1.refresh();
288 | f2.refresh();
289 | f1.getAllItems(getFilesDir().toString());
290 | f2.getAllItems(getFilesDir().toString());
291 | // 7:3
292 | for (int i = 0; i < f1.getSize() && feed.getSize() < 7; ++i)
293 | if (!f1.getItem(i).getTitle().contains("[已读] "))
294 | feed.addItem(f1.getItem(i));
295 | for (int i = 0; i < f2.getSize() && feed.getSize() < 10; ++i)
296 | if (!f2.getItem(i).getTitle().contains("[已读] "))
297 | feed.addItem(f2.getItem(i));
298 | return feed;
299 | }
300 | private String readFile(String path) {
301 | StringBuilder sb = new StringBuilder();
302 | String s = "";
303 | try {
304 | BufferedReader br = new BufferedReader(new FileReader(path));
305 | while( (s = br.readLine()) != null) {
306 | sb.append(s + "\n");
307 | }
308 | br.close();
309 | } catch (IOException e) {
310 | e.printStackTrace();
311 | }
312 | return sb.toString();
313 | }
314 | public void showMultiChoiceDialog(final Context context) {
315 | final ArrayList yourChoices = new ArrayList<>();
316 | final String[] items = { "国内新闻","国际新闻","经济新闻","体育新闻","台湾新闻","教育新闻","文化新闻","游戏新闻" };
317 | // 设置默认选中的选项,全为false默认均未选中
318 | // yourChoices.clear();
319 | // for (int i = 0; i < items.length; ++i)
320 | // if (choiceSets[i])
321 | // yourChoices.add(i);
322 | // System.out.println(yourChoices);
323 | android.support.v7.app.AlertDialog.Builder multiChoiceDialog =
324 | new android.support.v7.app.AlertDialog.Builder(context);
325 | multiChoiceDialog.setTitle("请选择要订阅的新闻类型");
326 | multiChoiceDialog.setMultiChoiceItems(items, choiceSets,
327 | new DialogInterface.OnMultiChoiceClickListener() {
328 | @Override
329 | public void onClick(DialogInterface dialog, int which,
330 | boolean isChecked) {
331 | Log.i("choice", " "+which);
332 | if (isChecked) {
333 | choiceSets[which] = true;
334 | // yourChoices.add(which);
335 | } else {
336 | choiceSets[which] = false;
337 | }
338 | }
339 | });
340 | multiChoiceDialog.setPositiveButton("确定",
341 | new DialogInterface.OnClickListener() {
342 | @Override
343 | public void onClick(DialogInterface dialog, int which) {
344 | // int size = yourChoices.size();
345 | // boolean[] choice = {false,false,false,false,false,false,false,false};
346 | String str = "";
347 | String file = "";
348 | for (int i = 0; i < choiceSets.length; ++i) {
349 | if (choiceSets[i]) {
350 | str += items[i] + " ";
351 | file += "1";
352 | }
353 | else
354 | file += "0";
355 | }
356 | Toast.makeText(context,
357 | "你选中了:" + str,
358 | Toast.LENGTH_SHORT).show();
359 | try {
360 | FileOutputStream out = context.openFileOutput("9bf6d3581229", MODE_PRIVATE);
361 | out.write(file.getBytes());
362 | out.close();
363 | } catch (FileNotFoundException e) {
364 | e.printStackTrace();
365 | } catch (IOException e) {
366 | e.printStackTrace();
367 | }
368 | setState(file);
369 | }
370 | });
371 | multiChoiceDialog.show();
372 | }
373 | private void setPullToRefesh(){
374 | final PullToRefreshLayout pullToRefreshLayout = (PullToRefreshLayout)findViewById(R.id.id_lv_up);
375 | pullToRefreshLayout.setRefreshListener(new BaseRefreshListener() {
376 | @Override
377 | public void refresh() {
378 | new Handler().postDelayed(new Runnable() {
379 | @Override
380 | public void run() {
381 | String s = "没有更多新闻了哟~";
382 | if (RSS_URL.equals("recommend"))
383 | s = "又推荐了好多呢~";
384 | refreshMain();
385 | Toast.makeText(context, s, Toast.LENGTH_SHORT).show();
386 | // feed.refresh();
387 | // 结束刷新
388 | pullToRefreshLayout.finishRefresh();
389 | }
390 | }, 1000);
391 |
392 | }
393 |
394 | @Override
395 | public void loadMore() {
396 | new Handler().postDelayed(new Runnable() {
397 |
398 | @Override
399 | public void run() {
400 | if (RSS_URL.equals("recommend")) {
401 | refreshMain();
402 | Toast.makeText(context, "又推荐了好多呢~", Toast.LENGTH_SHORT).show();
403 | pullToRefreshLayout.finishLoadMore();
404 | }
405 | else {
406 | feed.refresh();
407 | simpleAdapter = new SimpleAdapter(context, feed.getAllItems(getFilesDir().toString()), android.R.layout.simple_list_item_2, new String[]{RssItem.TITLE, RssItem.PUBDATE,}, new int[]{android.R.id.text1, android.R.id.text2});
408 | newsList.setAdapter(simpleAdapter);
409 | newsList.setSelection(newsList.getHeaderViewsCount());
410 | // noMoreFlag = true;
411 | // 结束刷新
412 | pullToRefreshLayout.finishLoadMore();
413 | Toast.makeText(context, "又加载了好多呢~", Toast.LENGTH_SHORT).show();
414 | }
415 | }
416 | }, 1000);
417 | }
418 | });
419 | }
420 | @Override
421 | public boolean onCreateOptionsMenu(Menu menu) {
422 | // Inflate the menu; this adds items to the action bar if it is present.
423 | getMenuInflater().inflate(R.menu.main, menu);
424 | return true;
425 | }
426 | @Override
427 | public boolean onOptionsItemSelected(MenuItem item) {
428 | // Handle action bar item clicks here. The action bar will
429 | // automatically handle clicks on the Home/Up button, so long
430 | // as you specify a parent activity in AndroidManifest.xml.
431 | int id = item.getItemId();
432 |
433 | //noinspection SimplifiableIfStatement
434 | if (id == R.id.action_share) {
435 | showMultiChoiceDialog(context);
436 | // Toast.makeText(context, "要分享哪个新闻呢?", Toast.LENGTH_SHORT).show();
437 | return true;
438 | }
439 | if (id == R.id.action_search) {
440 | searchContent();
441 | return true;
442 | }
443 | if (id == R.id.action_nofavor) {
444 | File[] favors = new File(prefix+"/favor").listFiles();
445 | for (int i = 0; i < favors.length; ++i) {
446 | new File(favors[i].getPath()).delete();
447 | }
448 | Toast.makeText(context, "成功清空收藏!", Toast.LENGTH_SHORT).show();
449 | return true;
450 | }
451 | if (id == R.id.action_clear) {
452 | Toast.makeText(context, "缓存清空成功!", Toast.LENGTH_SHORT).show();
453 | return true;
454 | }
455 |
456 | return super.onOptionsItemSelected(item);
457 | }
458 | private void searchContent() {
459 | final EditText editText = new EditText(MainActivity.this);
460 | AlertDialog.Builder inputDialog =
461 | new AlertDialog.Builder(MainActivity.this);
462 | inputDialog.setTitle("在该页面搜索:").setView(editText);
463 | inputDialog.setPositiveButton("确定",
464 | new DialogInterface.OnClickListener() {
465 | @Override
466 | public void onClick(DialogInterface dialog, int which) {
467 | String s = editText.getText().toString();
468 | feed.setSearch(s);
469 | showListView();
470 | Toast.makeText(context,"已为您搜索关键字\""+s+"\"\n上拉即可恢复", Toast.LENGTH_SHORT);
471 | // feed.setSearch("");
472 | }
473 | }).show();
474 | }
475 | /*
476 | * 把RSS内容绑定到ui界面进行显示
477 | */
478 | private void setUpColumn(int id, final String url) {
479 | TextView tv = (TextView) findViewById(id);
480 | final String s = tv.getText().toString();
481 | tv.setOnClickListener(new View.OnClickListener() {
482 | @Override
483 | public void onClick(View v) {
484 | Log.i("click!", url);
485 | // Toast.makeText(context,"获取"+s+"中", Toast.LENGTH_SHORT).show();
486 | RSS_URL = url;
487 | refreshMain();
488 | Toast.makeText(context, s + "切换成功!\n向右滑动返回", Toast.LENGTH_SHORT).show();
489 | // Toast.makeText(context, s + "切换成功!\n向右滑动返回", Toast.LENGTH_SHORT).show();
490 | }
491 | });
492 | }
493 | private void showListView() {
494 | Log.i(tag,"success");
495 | newsList = (ListView) this.findViewById(R.id.list);
496 | if (!NetworkAvail.check(this)) {
497 | ShowDescriptionActivity.show("没有网络qwq请检查", this, true);
498 | Log.i("network", "no");
499 | return;
500 | }
501 | else
502 | Log.i("network", "yes ");
503 | if (feed == null) {
504 | ShowDescriptionActivity.show("访问的RSS无效", this, true);
505 | return;
506 | }
507 |
508 | simpleAdapter = new SimpleAdapter(this,
509 | feed.getAllItems(getFilesDir().toString()), android.R.layout.simple_list_item_2,
510 | new String[] { RssItem.TITLE, RssItem.PUBDATE, }, new int[] {
511 | android.R.id.text1, android.R.id.text2 });
512 | newsList.setAdapter(simpleAdapter);
513 | newsList.setOnItemClickListener(this);
514 | newsList.setSelection(0);
515 | }
516 |
517 | @Override
518 | public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
519 | position = feed.getmap(position);
520 | String s = feed.getItem(position).getTitle();
521 |
522 | Intent intent = new Intent();
523 | intent.setClass(this, ItemNewsActivity.class);
524 | intent.putExtra("link", feed.getItem(position).getLink());
525 | intent.putExtra("file", feed.getItem(position).toFileName());
526 | intent.putExtra("info", feed.getItem(position).toFavor());
527 | Bundle bundle = new Bundle();
528 | bundle.putString("title", feed.getItem(position).getTitle());
529 | bundle.putString("description",feed.getItem(position).getDescription());
530 | bundle.putString("link", feed.getItem(position).getLink());
531 | bundle.putString("pubdate", feed.getItem(position).getPubdate());
532 | // 用android.intent.extra.INTENT的名字来传递参数
533 | intent.putExtra("android.intent.extra.rssItem", bundle);
534 | startActivity(intent);
535 | int first = newsList.getFirstVisiblePosition();
536 | if (!s.startsWith("[已读] ")) {
537 | feed.getItem(position).setTitle("[已读] " + s);
538 | ++count[Integer.parseInt(feed.getItem(position).getCategory())];
539 | logCount();
540 | simpleAdapter = new SimpleAdapter(this,
541 | feed.getAllItems(getFilesDir().toString()), android.R.layout.simple_list_item_2,
542 | new String[] { RssItem.TITLE, RssItem.PUBDATE, }, new int[] {
543 | android.R.id.text1, android.R.id.text2 });
544 | // newsList.getChildAt(position-first).setBackgroundColor(getResources().getColor(R.color.colorAccent));
545 | newsList.setAdapter(simpleAdapter);
546 | newsList.setSelection(first);
547 | }
548 | }
549 | private void logCount() {
550 | String s = "";
551 | for (int i = 0; i < count.length; ++i)
552 | s += count[i] + " ";
553 | try {
554 | FileWriter fw = new FileWriter(prefix+"/rec.txt");
555 | fw.write(s);
556 | fw.close();
557 | } catch (FileNotFoundException e) {
558 | e.printStackTrace();
559 | } catch (IOException e) {
560 | e.printStackTrace();
561 | }
562 | }
563 | }
564 |
--------------------------------------------------------------------------------
/app/src/main/java/com/java/wengjiayi/MyWebViewClient.java:
--------------------------------------------------------------------------------
1 | package com.java.wengjiayi;
2 |
3 | import android.util.Log;
4 | import android.webkit.WebView;
5 | import android.webkit.WebViewClient;
6 |
7 | public class MyWebViewClient extends WebViewClient {
8 | String baseUrl;
9 | MyWebViewClient(String url) { baseUrl = url; }
10 | @Override
11 | public boolean shouldOverrideUrlLoading(WebView view, String url) { //网页加载时的连接的网址
12 | if (!url.startsWith("http:") ) {
13 | view.loadUrl(baseUrl+url);
14 | Log.i("imgurl", baseUrl+url);
15 | return false;
16 | }
17 | return true;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/java/wengjiayi/NetworkAvail.java:
--------------------------------------------------------------------------------
1 | package com.java.wengjiayi;
2 |
3 | import android.content.Context;
4 | import android.net.ConnectivityManager;
5 | import android.net.NetworkInfo;
6 | public class NetworkAvail {
7 | /**
8 | * 检查网络是否可用
9 | *
10 | * @param context
11 | * @return
12 | */
13 | public static boolean check(Context context) {
14 |
15 | ConnectivityManager manager = (ConnectivityManager) context
16 | .getApplicationContext().getSystemService(
17 | Context.CONNECTIVITY_SERVICE);
18 |
19 | if (manager == null) {
20 | return false;
21 | }
22 |
23 | NetworkInfo networkinfo = manager.getActiveNetworkInfo();
24 |
25 | if (networkinfo == null || !networkinfo.isAvailable()) {
26 | return false;
27 | }
28 |
29 | return true;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/java/wengjiayi/ShowDescriptionActivity.java:
--------------------------------------------------------------------------------
1 | package com.java.wengjiayi;
2 |
3 | import android.content.Context;
4 | import android.content.DialogInterface;
5 | import android.os.Bundle;
6 | import android.app.Activity;
7 | import android.content.Intent;
8 | import android.support.v7.app.AlertDialog;
9 | import android.view.Menu;
10 | import android.view.MenuItem;
11 | import android.view.View;
12 | import android.view.View.OnClickListener;
13 | import android.widget.Button;
14 | import android.widget.TextView;
15 | import android.support.v4.app.NavUtils;
16 |
17 | public class ShowDescriptionActivity {
18 | public static void show(String e, Context c, final boolean exit) {
19 | AlertDialog.Builder d = new AlertDialog.Builder(c);
20 | d.setTitle(e);
21 | d.setPositiveButton("确定",
22 | new DialogInterface.OnClickListener() {
23 | @Override
24 | public void onClick(DialogInterface dialog, int which) {
25 | if (exit)
26 | System.exit(0);
27 | }
28 | });
29 | d.show();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/java/wengjiayi/WebContent.java:
--------------------------------------------------------------------------------
1 | package com.java.wengjiayi;
2 | import java.io.BufferedWriter;
3 | import java.io.FileWriter;
4 | import java.io.IOException;
5 | import java.io.InputStreamReader;
6 | import java.io.PrintWriter;
7 | import java.net.MalformedURLException;
8 | import java.net.URL;
9 | import java.util.Scanner;
10 | import java.util.regex.Matcher;
11 | import java.util.regex.Pattern;
12 |
13 | public class WebContent {
14 |
15 | /**
16 | * 返回当前URL的网页HTML代码
17 | *
18 | * @param htmlURL
19 | * - 网页地址
20 | * @return 当前网页的HTML代码
21 | */
22 | static public String getHtml(String htmlURL) {
23 | URL url;
24 | Scanner in = null;
25 | StringBuilder sb = new StringBuilder();
26 | String charset;
27 | System.out.println(htmlURL);
28 | try {
29 | url = new URL(htmlURL);
30 | charset = getCharset(htmlURL);
31 | System.out.println(charset);
32 | in = new Scanner(new InputStreamReader(url.openStream(), charset));
33 | while (in.hasNextLine()) {
34 | sb.append(in.nextLine());
35 | }
36 | return sb.toString();
37 | } catch (MalformedURLException e) {
38 | System.out.println("URL错误!!"+sb.toString());
39 | throw new ExceptionInInitializerError(e);
40 | } catch (IOException e) {
41 | System.out.println("字符串处理出错!!"+sb.toString());
42 | throw new ExceptionInInitializerError(e);
43 | } finally {
44 | if (in != null) {
45 | in.close();
46 | }
47 | }
48 |
49 | }
50 |
51 | /**
52 | * 通过URL取得一个网页的编码集
53 | *
54 | * @param htmlURL
55 | * - 网页URL
56 | * @return 编码集名称
57 | * 如果未找到则返回 空字符串
58 | */
59 | static public String getCharset(final String htmlURL) {
60 | Scanner in = null;
61 | try {
62 | URL url = new URL(htmlURL);
63 | in = new Scanner(url.openStream());
64 | StringBuilder sb = new StringBuilder();
65 | String regex = "charset=[^>\\s]*\\b";
66 | Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
67 | Matcher m;
68 | while (in.hasNextLine()) {
69 | sb.append(in.nextLine());
70 | m = p.matcher(sb.toString());
71 | if (m.find()) {
72 | return m.group().toLowerCase()
73 | .replaceAll("charset=\"?", "").trim();
74 | }
75 | }
76 | return "";
77 | } catch (MalformedURLException e) {
78 | return "gb2312";
79 | // System.out.println("URL错误!!");
80 | // throw new ExceptionInInitializerError(e);
81 | } catch (IOException e) {
82 | return "gb2312";
83 | // System.out.println("字符串处理出错!!");
84 | // throw new ExceptionInInitializerError(e);
85 | } finally {
86 | if (in != null) {
87 | in.close();
88 | }
89 | }
90 | }
91 |
92 | /**
93 | * 从网页HTML代码中去的文章标题
94 | *
95 | * @param html
96 | * - 网页HTML代码
97 | * @return 文章标题
98 | * 如果未找到则返回 空字符串
99 | */
100 | static public String getTitle(final String html) {
101 | String regex = "[\\s\\S]*?";
102 | Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
103 | Matcher m = p.matcher(html);
104 | if (m.find()) {
105 | return m.group().toLowerCase().replaceAll("?title>", "").trim();
106 | } else {
107 | return "";
108 | }
109 | }
110 |
111 | public static void write(String htmlURL) throws IOException {
112 | // String htmlURL = "http://www.baidu.com";
113 | // String htmlURL = "http://www.tangshanqiche.com/nNews/ShowNews.aspx?PageNumber=2&NewsClassTypeId=105";
114 | String html = getHtml(htmlURL);
115 | // String title = getTitle(html);
116 |
117 | // PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(
118 | // getFilesDir().toString() + "/" + htmlURL.replace("/", "_"))));
119 | // out.write(html);
120 | // out.close();
121 | }
122 |
123 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/java/wengjiayi/rss/RssFeed.java:
--------------------------------------------------------------------------------
1 | package com.java.wengjiayi.rss;
2 |
3 | import android.util.Log;
4 |
5 | import com.java.wengjiayi.MainActivity;
6 |
7 | import java.io.File;
8 | import java.util.ArrayList;
9 | import java.util.Collections;
10 | import java.util.HashMap;
11 | import java.util.List;
12 |
13 | public class RssFeed {
14 |
15 | private String title; // 标题
16 | private String pubdate; // 发布日期
17 | private int itemCount; // 用于计算列表的数目
18 | private List rssItems; // 用于描述列表item
19 | private String search;
20 | public List> data;
21 | public ArrayList mapping;
22 | public RssFeed() {
23 | itemCount = 0;
24 | search = "";
25 | mapping = new ArrayList();
26 | rssItems = new ArrayList();
27 | data = new ArrayList>();
28 | }
29 | public int getSize() { return rssItems.size(); }
30 | // 添加RssItem条目,返回列表长度
31 | public int addItem(RssItem rssItem) {
32 | rssItems.add(rssItem);
33 | itemCount++;
34 | return itemCount;
35 | }
36 | public void modify(int id) {
37 | Log.i("modify",id+" ");
38 | Log.i("modify",rssItems.size()+" ");
39 | for (int i = 0; i < rssItems.size(); ++i)
40 | rssItems.get(i).setCategory(Integer.toString(id));
41 | }
42 | // 根据下标获取RssItem
43 | public RssItem getItem(int position) {
44 | return rssItems.get(position);
45 | }
46 | public void clearSearch() { search = ""; }
47 | public void setSearch(String s) { search = s; }
48 | public void refresh() {
49 | Collections.shuffle(rssItems);
50 | }
51 | public int getmap(int pos) { return mapping.get(pos); }
52 | public List> getAllItems(String path) {
53 | data.clear();
54 | mapping.clear();
55 | for (int i = 0; i < rssItems.size(); i++) {
56 | File file = new File(path+"/"+rssItems.get(i).getLink().replace("/", "_").replace(":", ""));
57 | if (file.exists() && !rssItems.get(i).getTitle().startsWith("[已读] "))
58 | rssItems.get(i).setTitle("[已读] " + rssItems.get(i).getAllTitle());
59 | String desc = rssItems.get(i).getDescription();
60 | String title = rssItems.get(i).getTitle();
61 | if (search.length() == 0 || rssItems.get(i).getAllTitle().contains(search)) {
62 | mapping.add(i);
63 | HashMap item = new HashMap();
64 | item.put(RssItem.TITLE, title);
65 | item.put(RssItem.PUBDATE, desc);
66 | data.add(item);
67 | }
68 | }
69 | return data;
70 | }
71 |
72 | public String getTitle() {
73 | return title;
74 | }
75 |
76 | public void setTitle(String title) {
77 | this.title = title;
78 | }
79 |
80 | public String getPubdate() {
81 | return pubdate;
82 | }
83 |
84 | public void setPubdate(String pubdate) {
85 | this.pubdate = pubdate;
86 | }
87 |
88 | public int getItemCount() {
89 | return itemCount;
90 | }
91 |
92 | public void setItemCount(int itemCount) {
93 | this.itemCount = itemCount;
94 | }
95 |
96 | public ArrayList getLinks() {
97 | ArrayList links = new ArrayList();
98 | for (int i = 0; i < rssItems.size(); ++i)
99 | links.add(rssItems.get(i).getLink());
100 | return links;
101 | }
102 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/java/wengjiayi/rss/RssFeed_SAXParser.java:
--------------------------------------------------------------------------------
1 | package com.java.wengjiayi.rss;
2 | import java.io.*;
3 | import java.net.URL;
4 |
5 | import javax.xml.parsers.ParserConfigurationException;
6 | import javax.xml.parsers.SAXParser;
7 | import javax.xml.parsers.SAXParserFactory;
8 | import android.util.Log;
9 |
10 | import org.xml.sax.InputSource;
11 | import org.xml.sax.SAXException;
12 | import org.xml.sax.XMLReader;
13 |
14 | public class RssFeed_SAXParser {
15 |
16 | public RssFeed getFeed(String urlStr) throws ParserConfigurationException, SAXException, IOException{
17 | URL url = new URL(urlStr);
18 | url.openStream();
19 | SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); //构建SAX解析工厂
20 | SAXParser saxParser = saxParserFactory.newSAXParser(); //解析工厂生产解析器
21 | XMLReader xmlReader = saxParser.getXMLReader(); //通过saxParser构建xmlReader阅读器
22 | RssHandler rssHandler=new RssHandler();
23 | xmlReader.setContentHandler(rssHandler);
24 | //使用url打开流,并将流作为 xmlReader解析的输入源并解析
25 | InputSource inputSource = new InputSource(url.openStream());
26 | xmlReader.parse(inputSource);
27 | return rssHandler.getRssFeed();
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/java/wengjiayi/rss/RssHandler.java:
--------------------------------------------------------------------------------
1 | package com.java.wengjiayi.rss;
2 |
3 | import org.xml.sax.Attributes;
4 | import org.xml.sax.SAXException;
5 | import org.xml.sax.helpers.DefaultHandler;
6 |
7 | //import android.util.Log;
8 |
9 | public class RssHandler extends DefaultHandler {
10 |
11 | RssFeed rssFeed;
12 | RssItem rssItem;
13 |
14 | String lastElementName = "";// 标记变量,用于标记在解析过程中我们关心的几个标签,若不是我们关心的标签记做0
15 |
16 | final int RSS_TITLE = 1;// 若是 title 标签,记做1,注意有两个title,但我们都保存在item的成员变量中
17 | final int RSS_LINK = 2;// 若是 link 标签,记做2
18 | final int RSS_DESCRIPTION = 3;// 若是 description 标签,记做3
19 | final int RSS_CATEGORY = 4;// 若是category标签,记做 4
20 | final int RSS_PUBDATE = 5; // 若是pubdate标签,记做5,注意有两个pubdate,但我们都保存在item的pubdate成员变量中
21 |
22 | int currentFlag = 0;
23 |
24 | public RssHandler() {
25 |
26 | }
27 |
28 | @Override
29 | public void startDocument() throws SAXException {
30 | super.startDocument();
31 | rssFeed = new RssFeed();
32 | rssItem = new RssItem();
33 |
34 | }
35 |
36 | @Override
37 | public void characters(char[] ch, int start, int length)
38 | throws SAXException {
39 | super.characters(ch, start, length);
40 | // 获取字符串
41 | String text = new String(ch, start, length);
42 | // Log.i("i", "要获取的内容:" + text);
43 |
44 | switch (currentFlag) {
45 | case RSS_TITLE:
46 | rssItem.setTitle(text);
47 | currentFlag = 0;// 设置完后,重置为开始状态
48 | break;
49 | case RSS_PUBDATE:
50 | rssItem.setPubdate(text);
51 | currentFlag = 0;// 设置完后,重置为开始状态
52 | break;
53 | case RSS_CATEGORY:
54 | rssItem.setCategory(text);
55 | currentFlag = 0;// 设置完后,重置为开始状态
56 | break;
57 | case RSS_LINK:
58 | rssItem.setLink(text);
59 | currentFlag = 0;// 设置完后,重置为开始状态
60 | break;
61 | case RSS_DESCRIPTION:
62 | rssItem.setDescription(text);
63 | currentFlag = 0;// 设置完后,重置为开始状态
64 | break;
65 | default:
66 | break;
67 | }
68 | }
69 |
70 | @Override
71 | public void startElement(String uri, String localName, String qName,
72 | Attributes attributes) throws SAXException {
73 | super.startElement(uri, localName, qName, attributes);
74 | if ("chanel".equals(localName)) {
75 | // 这个标签内没有我们关心的内容,所以不作处理,currentFlag=0
76 | currentFlag = 0;
77 | return;
78 | }
79 | if ("item".equals(localName)) {
80 | rssItem = new RssItem();
81 | return;
82 | }
83 | if ("title".equals(localName)) {
84 | currentFlag = RSS_TITLE;
85 | return;
86 | }
87 | if ("description".equals(localName)) {
88 | currentFlag = RSS_DESCRIPTION;
89 | return;
90 | }
91 | if ("link".equals(localName)) {
92 | currentFlag = RSS_LINK;
93 | return;
94 | }
95 | if ("pubDate".equals(localName)) {
96 | currentFlag = RSS_PUBDATE;
97 | return;
98 | }
99 | if ("category".equals(localName)) {
100 | currentFlag = RSS_CATEGORY;
101 | return;
102 | }
103 |
104 | }
105 |
106 | @Override
107 | public void endElement(String uri, String localName, String qName)
108 | throws SAXException {
109 | super.endElement(uri, localName, qName);
110 | // 如果解析一个item节点结束,就将rssItem添加到rssFeed中。
111 | if ("item".equals(localName)) {
112 |
113 | rssFeed.addItem(rssItem);
114 | return;
115 | }
116 | }
117 |
118 | @Override
119 | public void endDocument() throws SAXException {
120 | super.endDocument();
121 | }
122 |
123 | public RssFeed getRssFeed() {
124 | return rssFeed;
125 | }
126 |
127 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/java/wengjiayi/rss/RssItem.java:
--------------------------------------------------------------------------------
1 | package com.java.wengjiayi.rss;
2 |
3 |
4 | import android.util.Log;
5 |
6 | import com.java.wengjiayi.MainActivity;
7 |
8 | import java.io.File;
9 | import java.util.regex.Matcher;
10 | import java.util.regex.Pattern;
11 |
12 | public class RssItem {
13 |
14 | private String title;
15 | private String description;
16 | private String link;
17 | private String category;
18 | private String pubdate;
19 |
20 | public static final String TITLE = "title";
21 | public static final String PUBDATE = "pubdate";
22 | public static final String DESC = "description";
23 |
24 | public RssItem() {
25 |
26 | }
27 | public RssItem(String q) {
28 | String[] items = q.split("\n");
29 | title = items[0];
30 | description = items[1];
31 | link = items[2];
32 | category = items[3];
33 | pubdate = items[4];
34 | }
35 | public String getAllTitle() { return title; }
36 | public String getTitle() {
37 | // System.out.println(title);
38 | if (title.length() > 20) {
39 | return title.substring(0, 19) + "...";
40 | }
41 | return title;
42 | }
43 |
44 | public void setTitle(String title) { this.title = title; }
45 |
46 | public String getDescription() {
47 | if (description == null)
48 | return "[ 点击阅读更多 ]";
49 | // Log.i("length"," "+description.length());
50 | if (description.length() > 23) {
51 | return description.substring(0, 22) + "...";
52 | }
53 | return description;
54 | }
55 |
56 | public void setDescription(String description) {
57 | description += " > [ 点击阅读更多 ]";
58 | description = description.replaceAll("<(.*?)>", " ").replaceAll(">", ">").replaceAll("<", "<").replaceAll(" ", "").replaceAll(" {2,}", " ").replaceAll(" {2,}", " ") ;
59 | this.description = description;
60 | // Log.i("desc", description);
61 | }
62 |
63 | public String getLink() {
64 | return link;
65 | }
66 |
67 | public void setLink(String link) {
68 | this.link = link;
69 | }
70 |
71 | public String getCategory() {
72 | return category;
73 | }
74 |
75 | public void setCategory(String category) {
76 | this.category = category;
77 | }
78 |
79 | public String getPubdate() {
80 | return pubdate;
81 | }
82 |
83 | public void setPubdate(String pubdate) {
84 | this.pubdate = pubdate;
85 | }
86 |
87 | @Override
88 | public String toString() {
89 | return "RssItem [title=" + title + ", description=" + description
90 | + ", link=" + link + ", category=" + category + ", pubdate="
91 | + pubdate + "]";
92 | }
93 | public String toFavor() {
94 | // if (tmp.length() > 50) {
95 | // tmp = tmp.substring(0, 49);
96 | // }
97 | String tmp = title + "\n" + description + "\n" + link + "\n" + category + "\n" + pubdate;
98 | // tmp = tmp.replaceAll(" ", "").replaceAll(" ", "");
99 | Log.i("toFavor", " "+tmp.length());
100 | return tmp;
101 | }
102 | public String toFileName() {
103 | return link.replace("/", "_").replace(":", "");
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_menu_camera.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_menu_gallery.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_menu_manage.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_menu_send.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_menu_share.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_menu_slideshow.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/side_nav_bar.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/drawable/icon.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/drawable/icon2.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_customize.xml:
--------------------------------------------------------------------------------
1 |
5 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_itemnews.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
13 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_newstop_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
20 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_show_description.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
10 |
11 |
17 |
18 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/app_bar_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
22 |
23 |
24 |
25 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/checkbox.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
13 |
20 |
27 |
34 |
41 |
48 |
55 |
62 |
68 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/nav_header_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
19 |
20 |
28 |
36 |
44 |
52 |
60 |
68 |
76 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
100 |
108 |
109 |
116 |
117 |
124 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/activity_main_drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/item.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
9 |
14 |
19 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
9 |
14 |
19 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 8dp
6 | 176dp
7 | 17dp
8 | 16dp
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SimpleNews
3 | Open navigation drawer
4 | Close navigation drawer
5 | Android Studio
6 | wengjy16@mails.tsinghua.edu.cn
7 | Navigation header
8 | Settings
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/web_hi_res_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/web_hi_res_512.png
--------------------------------------------------------------------------------
/app/src/test/java/com/java/wengjiayi/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.java.wengjiayi;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.1.4'
11 |
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Sep 09 11:24:47 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/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 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
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 Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/pic/Screenshot_20180915-120413.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/pic/Screenshot_20180915-120413.jpg
--------------------------------------------------------------------------------
/pic/Screenshot_20180915-120423.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/pic/Screenshot_20180915-120423.jpg
--------------------------------------------------------------------------------
/pic/Screenshot_20180915-120439.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/pic/Screenshot_20180915-120439.jpg
--------------------------------------------------------------------------------
/pic/Screenshot_20180915-120513.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/pic/Screenshot_20180915-120513.jpg
--------------------------------------------------------------------------------
/release/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/release/app-release.apk
--------------------------------------------------------------------------------
/release/output.json:
--------------------------------------------------------------------------------
1 | [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | //include ':library'
--------------------------------------------------------------------------------
/大作业报告.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/大作业报告.pdf
--------------------------------------------------------------------------------