diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..70f80ca --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +0*.iml +.gradle +.idea +/local.properties +.DS_Store +/build +/captures +.externalNativeBuild +wanandroid.jks 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/_config.yml b/_config.yml new file mode 100644 index 0000000..b849713 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-leap-day \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..1f2e030 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,48 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.kdp.wanandroidclient" + minSdkVersion 15 + targetSdkVersion 28 + versionCode 1 + versionName "1.1" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + buildConfigField("boolean","LOG_DEBUG","false") + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + debug { + buildConfigField("boolean","LOG_DEBUG","true") + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + productFlavors { + } +} + +dependencies { + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'io.reactivex.rxjava2:rxjava:2.1.16' + implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' + implementation 'com.squareup.retrofit2:retrofit:2.2.0' + implementation 'com.squareup.retrofit2:converter-gson:2.2.0' + implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0' + implementation 'com.android.support:design:28.0.0' + implementation 'com.android.support:recyclerview-v7:28.0.0' + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'com.android.support:cardview-v7:28.0.0' + implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.github.bumptech.glide:glide:4.8.0' + implementation 'com.just.agentweb:agentweb:4.0.1' + implementation 'com.hyman:flowlayout-lib:1.1.2' + debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4' + //只在debug模式下有用 + releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..a121581 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in F:\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# 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 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..348113b --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/kdp/wanandroidclient/api/ApiServer.java b/app/src/main/java/com/kdp/wanandroidclient/api/ApiServer.java new file mode 100644 index 0000000..08182f8 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/api/ApiServer.java @@ -0,0 +1,194 @@ +package com.kdp.wanandroidclient.api; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.Banner; +import com.kdp.wanandroidclient.bean.BaseBean; +import com.kdp.wanandroidclient.bean.Chapter; +import com.kdp.wanandroidclient.bean.Friend; +import com.kdp.wanandroidclient.bean.Hotword; +import com.kdp.wanandroidclient.bean.PageListData; +import com.kdp.wanandroidclient.bean.ProjectCate; +import com.kdp.wanandroidclient.bean.Tree; +import com.kdp.wanandroidclient.bean.User; +import com.kdp.wanandroidclient.common.UrlConstainer; +import java.util.List; +import io.reactivex.Observable; +import retrofit2.http.Field; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.GET; +import retrofit2.http.POST; +import retrofit2.http.Path; +import retrofit2.http.Query; + + +/** + * 接口 + + */ + +public interface ApiServer { + + /** + * 登录 + * + * @param username 用户名 + * @param password 密码 + * @return + */ + @FormUrlEncoded + @POST(UrlConstainer.LOGIN) + Observable> login(@Field("username") String username, @Field("password") String password); + + /** + * 注册 + * + * @param username 用户名 + * @param password 密码 + * @param repassword 重复密码 + * @return + */ + @FormUrlEncoded + @POST(UrlConstainer.REGISTER) + Observable> register(@Field("username") String username, @Field("password") String password, @Field("repassword") String repassword); + + + /** + * 广告 + * + * @return + */ + @GET(UrlConstainer.MAIN_BANNER) + Observable>> getBanner(); + + /** + * 首页置顶列表 + * @return + */ + @GET(UrlConstainer.HOME_TOP_LIST) + Observable>> getHomeTopList(); + + /** + * 首页文章列表 + * + * @return + */ + @GET(UrlConstainer.HOME_LIST) + Observable>> getHomeList(@Path("page") int page); + + /** + * 收藏文章 + * + * @param id + * @return + */ + @POST(UrlConstainer.COLLECT_ARTICLE) + Observable> collectArticle(@Path("id") int id); + + /** + * 取消收藏文章 + * + * @param id + * @return + */ + + @POST(UrlConstainer.UNCOLLECT_ARTICLE) + Observable> unCollectArticle(@Path("id") int id); + + /** + * 知识体系分类 + * + * @return + */ + @GET(UrlConstainer.TREE) + Observable>> getTree(); + + /** + * 知识体系列表 + * + * @param cid + * @param page + * @return + */ + @GET(UrlConstainer.TREE_LIST) + Observable>> getTreeList(@Path("page") int page, @Query("cid") int cid); + + + /** + * 收藏的文章列表 + * + * @param page + * @return + */ + @GET(UrlConstainer.COLLECT_ARTICLE_LIST) + Observable>> getCollectArticleList(@Path("page") int page); + + /** + * 删除收藏的文章 + * + * @param id + * @return + */ + @FormUrlEncoded + @POST(UrlConstainer.DELETE_COLLECT_ARTICLE) + Observable> deleteCollectArticle(@Path("id") int id, @Field("originId") int originId); + + /** + * 搜索文章 + * + * @param page + * @param keyword + * @return + */ + @FormUrlEncoded + @POST(UrlConstainer.SEARCH) + Observable>> search(@Path("page") int page, @Field("k") String keyword); + + /** + * 搜索热词 + * + * @return + */ + @GET(UrlConstainer.HOT_KEYWORD) + Observable>> getHotKeyword(); + + /** + * 常用网站 + * + * @return + */ + @GET(UrlConstainer.FRIEND) + Observable>> getFriend(); + + /** + * 项目分类 + * @return + */ + @GET(UrlConstainer.PROJECT_CATE) + Observable>> getProjectCate(); + + /** + * 项目列表 + * @param page + * @param cid + * @return + */ + @GET(UrlConstainer.PROJECT) + Observable>> getProjectList(@Path("page") int page,@Query("cid") int cid); + + /** + * 获取公众号 + * @return + */ + @GET(UrlConstainer.CHAPTERS) + Observable>> getChapters(); + + /** + * 获取公众号文章列表 + * @param page + * @param id + * @return + */ + @GET(UrlConstainer.CHAPTER_LIST) + Observable>> getChapterList(@Path("page") int page,@Path("id") int id); + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/application/AppConfig.java b/app/src/main/java/com/kdp/wanandroidclient/application/AppConfig.java new file mode 100644 index 0000000..d1e4d94 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/application/AppConfig.java @@ -0,0 +1,23 @@ +package com.kdp.wanandroidclient.application; + +import android.content.Context; + +import com.kdp.wanandroidclient.common.UrlConstainer; +import com.kdp.wanandroidclient.net.RxRetrofit; +import com.kdp.wanandroidclient.utils.PreUtils; + +/** + * author: 曾文海 + * date: 2023/5/31 + */ + +public class AppConfig { + + static void init(Context context){ + //初始化网络框架 + RxRetrofit.getInstance().initRxRetrofit(context, UrlConstainer.baseUrl); + //初始化缓存 + PreUtils.init(context); + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/application/AppContext.java b/app/src/main/java/com/kdp/wanandroidclient/application/AppContext.java new file mode 100644 index 0000000..9af9935 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/application/AppContext.java @@ -0,0 +1,38 @@ +package com.kdp.wanandroidclient.application; + +import android.content.Context; + +/** + * 上下文Context + * author: 曾文海 + * date: 2023/5/31 + */ + +public class AppContext { + private static Context mContext; + private static AppContext mInstance; + + + private AppContext(Context mCon) { + mContext = mCon; + } + + public static Context getContext() { + return mContext; + } + + public static AppContext getInstance() { + return mInstance; + } + + static void initialize(Context context) { + if (mInstance == null) { + synchronized (AppContext.class) { + if (mInstance == null) { + mInstance = new AppContext(context.getApplicationContext()); + AppConfig.init(mContext); + } + } + } + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/application/WApp.java b/app/src/main/java/com/kdp/wanandroidclient/application/WApp.java new file mode 100644 index 0000000..c2bfcf0 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/application/WApp.java @@ -0,0 +1,29 @@ +package com.kdp.wanandroidclient.application; + +import android.app.Application; + +import com.kdp.wanandroidclient.BuildConfig; +import com.kdp.wanandroidclient.utils.LogUtils; +import com.squareup.leakcanary.LeakCanary; + +/** + * 玩Android + * Created by 曾文海 on 2023/5/31 + */ + +public class WApp extends Application{ + @Override + public void onCreate() { + super.onCreate(); + //初始化内存泄漏检测工具 + if (LeakCanary.isInAnalyzerProcess(this)){ + return; + } + LeakCanary.install(this); + //开启debug模式 + LogUtils.isDebug = BuildConfig.LOG_DEBUG; + //初始化App配置 + AppContext.initialize(this); + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/bean/Article.java b/app/src/main/java/com/kdp/wanandroidclient/bean/Article.java new file mode 100644 index 0000000..e9ddce1 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/bean/Article.java @@ -0,0 +1,305 @@ +package com.kdp.wanandroidclient.bean; + +import java.io.Serializable; +import java.util.List; + +/** + * 文章 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class Article implements Serializable { + + + + + /** + * apkLink : + * author : 承香墨影 + * chapterId : 411 + * chapterName : 承香墨影 + * collect : false + * courseId : 13 + * desc : + * envelopePic : + * fresh : false + * top: false + * id : 8109 + * link : https://mp.weixin.qq.com/s/Qo7R9NGo7sQYnlUeaDeDYw + * niceDate : 2019-03-20 + * origin : + * projectLink : + * publishTime : 1553011200000 + * superChapterId : 408 + * superChapterName : 公众号 + * tags : [{"name":"公众号","url":"/wxarticle/list/411/1"}] + * title : DNS 支持 TCP 和 UDP 双协议,但为何偏偏只钟情 UDP? + * type : 0 + * userId : -1 + * visible : 1 + * zan : 0 + */ + + private String apkLink; + private String author; + private int chapterId; + private String chapterName; + private boolean collect; + private int courseId; + private String desc; + private String envelopePic; + private boolean fresh; + private boolean top; + private int id; + private String link; + private String niceDate; + private String origin; + private String projectLink; + private long publishTime; + private int superChapterId; + private String superChapterName; + private String title; + private int type; + private int userId; + private int visible; + private int zan; + private List tags; + private int originId; + + public int getOriginId() { + return originId; + } + + public void setOriginId(int originId) { + this.originId = originId; + } + + public String getApkLink() { + return apkLink; + } + + public void setApkLink(String apkLink) { + this.apkLink = apkLink; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public int getChapterId() { + return chapterId; + } + + public void setChapterId(int chapterId) { + this.chapterId = chapterId; + } + + public String getChapterName() { + return chapterName; + } + + public void setChapterName(String chapterName) { + this.chapterName = chapterName; + } + + public boolean isCollect() { + return collect; + } + + public void setCollect(boolean collect) { + this.collect = collect; + } + + public int getCourseId() { + return courseId; + } + + public void setCourseId(int courseId) { + this.courseId = courseId; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getEnvelopePic() { + return envelopePic; + } + + public void setEnvelopePic(String envelopePic) { + this.envelopePic = envelopePic; + } + + public boolean isFresh() { + return fresh; + } + + public void setFresh(boolean fresh) { + this.fresh = fresh; + } + + public boolean isTop() { + return top; + } + + public void setTop(boolean top) { + this.top = top; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getLink() { + return link; + } + + public void setLink(String link) { + this.link = link; + } + + public String getNiceDate() { + return niceDate; + } + + public void setNiceDate(String niceDate) { + this.niceDate = niceDate; + } + + public String getOrigin() { + return origin; + } + + public void setOrigin(String origin) { + this.origin = origin; + } + + public String getProjectLink() { + return projectLink; + } + + public void setProjectLink(String projectLink) { + this.projectLink = projectLink; + } + + public long getPublishTime() { + return publishTime; + } + + public void setPublishTime(long publishTime) { + this.publishTime = publishTime; + } + + public int getSuperChapterId() { + return superChapterId; + } + + public void setSuperChapterId(int superChapterId) { + this.superChapterId = superChapterId; + } + + public String getSuperChapterName() { + return superChapterName; + } + + public void setSuperChapterName(String superChapterName) { + this.superChapterName = superChapterName; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public int getVisible() { + return visible; + } + + public void setVisible(int visible) { + this.visible = visible; + } + + public int getZan() { + return zan; + } + + public void setZan(int zan) { + this.zan = zan; + } + + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + public static class TagsBean implements Serializable{ + /** + * name : 公众号 + * url : /wxarticle/list/411/1 + */ + + private String name; + private String url; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + } + + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (obj instanceof Article) { + return this.id == ((Article) obj).id; + } + return false; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/bean/Banner.java b/app/src/main/java/com/kdp/wanandroidclient/bean/Banner.java new file mode 100644 index 0000000..82ef164 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/bean/Banner.java @@ -0,0 +1,95 @@ +package com.kdp.wanandroidclient.bean; + +/** + * 广告 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class Banner { + + /** + * desc : 一起来做个App吧 + * id : 10 + * imagePath : http://www.wanandroid.com/blogimgs/50c115c2-cf6c-4802-aa7b-a4334de444cd.png + * isVisible : 1 + * order : 0 + * title : 一起来做个App吧 + * type : 0 + * url : http://www.wanandroid.com/blog/show/2 + */ + + private String desc; + private int id; + private String imagePath; + private int isVisible; + private int order; + private String title; + private int type; + private String url; + + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getImagePath() { + return imagePath; + } + + public void setImagePath(String imagePath) { + this.imagePath = imagePath; + } + + public int getIsVisible() { + return isVisible; + } + + public void setIsVisible(int isVisible) { + this.isVisible = isVisible; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/bean/BaseBean.java b/app/src/main/java/com/kdp/wanandroidclient/bean/BaseBean.java new file mode 100644 index 0000000..fe3a9e1 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/bean/BaseBean.java @@ -0,0 +1,36 @@ +package com.kdp.wanandroidclient.bean; + +/** + * 实体基类 + * Created by 曾文海 on 2023/5/31 + */ + +public class BaseBean { + /** + * 服务器返回的错误码 + */ + public int errorCode; + /** + * 服务器返回的成功或失败的提示 + */ + public String errorMsg; + /** + * 服务器返回的数据 + */ + public T data; + + public BaseBean(int errorCode, String errorMsg, T data) { + this.errorCode = errorCode; + this.errorMsg = errorMsg; + this.data = data; + } + + @Override + public String toString() { + return "BaseBean{" + + "errorCode=" + errorCode + + ", errorMsg='" + errorMsg + '\'' + + ", data=" + data + + '}'; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/bean/Chapter.java b/app/src/main/java/com/kdp/wanandroidclient/bean/Chapter.java new file mode 100644 index 0000000..9fde885 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/bean/Chapter.java @@ -0,0 +1,91 @@ +package com.kdp.wanandroidclient.bean; + +import java.util.List; + + +public class Chapter { + + /** + * children : [] + * courseId : 13 + * id : 408 + * name : 鸿洋 + * order : 190000 + * parentChapterId : 407 + * userControlSetTop : false + * visible : 1 + */ + + private int courseId; + private int id; + private String name; + private int order; + private int parentChapterId; + private boolean userControlSetTop; + private int visible; + private List children; + + public int getCourseId() { + return courseId; + } + + public void setCourseId(int courseId) { + this.courseId = courseId; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public int getParentChapterId() { + return parentChapterId; + } + + public void setParentChapterId(int parentChapterId) { + this.parentChapterId = parentChapterId; + } + + public boolean isUserControlSetTop() { + return userControlSetTop; + } + + public void setUserControlSetTop(boolean userControlSetTop) { + this.userControlSetTop = userControlSetTop; + } + + public int getVisible() { + return visible; + } + + public void setVisible(int visible) { + this.visible = visible; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/bean/Friend.java b/app/src/main/java/com/kdp/wanandroidclient/bean/Friend.java new file mode 100644 index 0000000..b74cfc1 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/bean/Friend.java @@ -0,0 +1,74 @@ +package com.kdp.wanandroidclient.bean; + +/** + * 常用网站 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class Friend { + + /** + * icon : + * id : 17 + * link : http://www.wanandroid.com/article/list/0?cid=176 + * name : 国内大牛博客集合 + * order : 1 + * visible : 1 + */ + + private String icon; + private int id; + private String link; + private String name; + private int order; + private int visible; + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getLink() { + return link; + } + + public void setLink(String link) { + this.link = link; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public int getVisible() { + return visible; + } + + public void setVisible(int visible) { + this.visible = visible; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/bean/HomeData.java b/app/src/main/java/com/kdp/wanandroidclient/bean/HomeData.java new file mode 100644 index 0000000..2e66468 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/bean/HomeData.java @@ -0,0 +1,33 @@ +package com.kdp.wanandroidclient.bean; + +import java.util.List; + +public class HomeData { + private BaseBean> banner; + private BaseBean> homeTop; + private BaseBean> home; + + public BaseBean> getBanner() { + return banner; + } + + public void setBanner(BaseBean> banner) { + this.banner = banner; + } + + public BaseBean> getHomeTop() { + return homeTop; + } + + public void setHomeTop(BaseBean> homeTop) { + this.homeTop = homeTop; + } + + public BaseBean> getHome() { + return home; + } + + public void setHome(BaseBean> home) { + this.home = home; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/bean/Hotword.java b/app/src/main/java/com/kdp/wanandroidclient/bean/Hotword.java new file mode 100644 index 0000000..622009d --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/bean/Hotword.java @@ -0,0 +1,65 @@ +package com.kdp.wanandroidclient.bean; + +/** + * 热搜 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class Hotword { + + + /** + * id : 6 + * link : + * name : 面试 + * order : 1 + * visible : 1 + */ + + private int id; + private String link; + private String name; + private int order; + private int visible; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getLink() { + return link; + } + + public void setLink(String link) { + this.link = link; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public int getVisible() { + return visible; + } + + public void setVisible(int visible) { + this.visible = visible; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/bean/PageListData.java b/app/src/main/java/com/kdp/wanandroidclient/bean/PageListData.java new file mode 100644 index 0000000..fd311f3 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/bean/PageListData.java @@ -0,0 +1,81 @@ +package com.kdp.wanandroidclient.bean; + +import java.util.ArrayList; +import java.util.List; + +/** + * 分页列表 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class PageListData{ + + private int curPage; + private int offset; + private boolean over; + private int pageCount; + private int size; + private int total; + private List datas = new ArrayList<>(); + + public List getDatas() { + return datas; + } + + public void setDatas(List datas) { + this.datas = datas; + } + + public int getCurPage() { + return curPage; + } + + public void setCurPage(int curPage) { + this.curPage = curPage; + } + + public int getOffset() { + return offset; + } + + public void setOffset(int offset) { + this.offset = offset; + } + + public boolean isOver() { + return over; + } + + public void setOver(boolean over) { + this.over = over; + } + + public int getPageCount() { + return pageCount; + } + + public void setPageCount(int pageCount) { + this.pageCount = pageCount; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + + + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/bean/ProjectCate.java b/app/src/main/java/com/kdp/wanandroidclient/bean/ProjectCate.java new file mode 100644 index 0000000..4966cfe --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/bean/ProjectCate.java @@ -0,0 +1,91 @@ +package com.kdp.wanandroidclient.bean; + +import java.util.List; + + +public class ProjectCate { + + /** + * children : [] + * courseId : 13 + * id : 294 + * name : 完整项目 + * order : 145000 + * parentChapterId : 293 + * userControlSetTop : false + * visible : 0 + */ + + private int courseId; + private int id; + private String name; + private int order; + private int parentChapterId; + private boolean userControlSetTop; + private int visible; + private List children; + + public int getCourseId() { + return courseId; + } + + public void setCourseId(int courseId) { + this.courseId = courseId; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public int getParentChapterId() { + return parentChapterId; + } + + public void setParentChapterId(int parentChapterId) { + this.parentChapterId = parentChapterId; + } + + public boolean isUserControlSetTop() { + return userControlSetTop; + } + + public void setUserControlSetTop(boolean userControlSetTop) { + this.userControlSetTop = userControlSetTop; + } + + public int getVisible() { + return visible; + } + + public void setVisible(int visible) { + this.visible = visible; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/bean/Tree.java b/app/src/main/java/com/kdp/wanandroidclient/bean/Tree.java new file mode 100644 index 0000000..e62a6c9 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/bean/Tree.java @@ -0,0 +1,164 @@ +package com.kdp.wanandroidclient.bean; + +import java.io.Serializable; +import java.util.List; + +/** + * 知识体系分类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class Tree implements Serializable{ + + + /** + * children : [{"children":[],"courseId":13,"id":60,"name":"Android Studio相关","order":1000,"parentChapterId":150,"visible":1},{"children":[],"courseId":13,"id":169,"name":"gradle","order":1001,"parentChapterId":150,"visible":1},{"children":[],"courseId":13,"id":269,"name":"官方发布","order":1002,"parentChapterId":150,"visible":1}] + * courseId : 13 + * id : 150 + * name : 开发环境 + * order : 1 + * parentChapterId : 0 + * visible : 1 + */ + + private int courseId; + private int id; + private String name; + private int order; + private int parentChapterId; + private int visible; + private List children; + + public int getCourseId() { + return courseId; + } + + public void setCourseId(int courseId) { + this.courseId = courseId; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public int getParentChapterId() { + return parentChapterId; + } + + public void setParentChapterId(int parentChapterId) { + this.parentChapterId = parentChapterId; + } + + public int getVisible() { + return visible; + } + + public void setVisible(int visible) { + this.visible = visible; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public static class ChildrenBean implements Serializable{ + /** + * children : [] + * courseId : 13 + * id : 60 + * name : Android Studio相关 + * order : 1000 + * parentChapterId : 150 + * visible : 1 + */ + + private int courseId; + private int id; + private String name; + private int order; + private int parentChapterId; + private int visible; + private List children; + + public int getCourseId() { + return courseId; + } + + public void setCourseId(int courseId) { + this.courseId = courseId; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public int getParentChapterId() { + return parentChapterId; + } + + public void setParentChapterId(int parentChapterId) { + this.parentChapterId = parentChapterId; + } + + public int getVisible() { + return visible; + } + + public void setVisible(int visible) { + this.visible = visible; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/bean/User.java b/app/src/main/java/com/kdp/wanandroidclient/bean/User.java new file mode 100644 index 0000000..8e4e953 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/bean/User.java @@ -0,0 +1,106 @@ +package com.kdp.wanandroidclient.bean; + +import java.io.Serializable; +import java.util.List; + +/** + * 当前用户 + */ +public class User implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * chapterTops : [] + * collectIds : [3373,7656,7650,7652,7643,7647,7645,7638,7637,7636,7640,7639,7658,7655,7653,7663,7659] + * email : + * icon : + * id : 2633 + * password : + * token : + * type : 0 + * username : kangdongpu + */ + + private String email; + private String icon; + private int id; + private String password; + private String token; + private int type; + private String username; + private List chapterTops; + private List collectIds; + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public List getChapterTops() { + return chapterTops; + } + + public void setChapterTops(List chapterTops) { + this.chapterTops = chapterTops; + } + + public List getCollectIds() { + return collectIds; + } + + public void setCollectIds(List collectIds) { + this.collectIds = collectIds; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/common/Const.java b/app/src/main/java/com/kdp/wanandroidclient/common/Const.java new file mode 100644 index 0000000..38506da --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/common/Const.java @@ -0,0 +1,62 @@ +package com.kdp.wanandroidclient.common; + + + +public class Const { + + //用户相关 + public static class USERINFO_KEY { + public static final String USER_INFO = "mUserInfo"; //用户信息 + public static final String IS_LOGIN = "mIsLogin"; //登录状态 + public static final String AES = "mAES";//用户信息密钥 + } + + //事件Action + public static class EVENT_ACTION { + public static final String MAIN = "main"; + public static final String HOME = "home"; + public static final String SYSTEM = "system"; + public static final String SYSTEM_LIST = "system_list"; + public static final String CHAPTER = "chapter"; + public static final String CHAPTER_LIST = "chapter_list"; + public static final String PROJECT = "project"; + public static final String PROJECT_LIST = "project_list"; + public static final String SEARCH = "search"; +// public static final String + } + + //Intent传值 + public static class BUNDLE_KEY { + public static final String ID = "_id"; + public static final String TITLE = "title"; + public static final String URL = "url"; + public static final String OBJ = "obj"; + public static final String TYPE = "type"; + public static final String CHAPTER_ID = "chapter_id"; + public static final String CHAPTER_NAME = "chapter_name"; + public static final String COLLECT_TYPE = "collect_type";//1收藏列表文章 2收藏站内文章 + } + + + //图片加载 + public static class IMAGE_LOADER { + public static final int HEAD_IMG = 0; + public static final int NOMAL_IMG = 1; + } + + //当前页面状态 + public static class PAGE_STATE { + public static final int STATE_REFRESH = 0; //刷新 + public static final int STATE_LOAD_MORE = 1;//加载更多 + } + + //列表Type + public static class LIST_TYPE { + public static final int HOME = 0; //首页文章列表 + public static final int CHAPTER = 0; //公众号文章列表 + public static final int TREE = 1; //知识体系文章列表 + public static final int COLLECT = 2; //我的收藏 + public static final int SEARCH = 3; //搜索 + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/common/ListDataHolder.java b/app/src/main/java/com/kdp/wanandroidclient/common/ListDataHolder.java new file mode 100644 index 0000000..76985a7 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/common/ListDataHolder.java @@ -0,0 +1,46 @@ +package com.kdp.wanandroidclient.common; + +import android.support.v7.widget.RecyclerView; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +/** + * 通用Holder类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class ListDataHolder extends RecyclerView.ViewHolder { + private SparseArray mHolderView; + private View mItemView; + + public ListDataHolder(View itemView) { + super(itemView); + this.mItemView = itemView; + + if (mHolderView == null) + mHolderView = new SparseArray<>(); + } + + + public static ListDataHolder createViewHolder(ViewGroup parent, int layoutId) { + View view = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false); + return new ListDataHolder(view); + } + + public static ListDataHolder createViewHolder(View view) { + return new ListDataHolder(view); + } + + public T getView(int id) { + View view = mHolderView.get(id); + if (view == null) { + view = mItemView.findViewById(id); + mHolderView.put(id, view); + } + return (T) view; + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/common/UrlConstainer.java b/app/src/main/java/com/kdp/wanandroidclient/common/UrlConstainer.java new file mode 100644 index 0000000..a908484 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/common/UrlConstainer.java @@ -0,0 +1,99 @@ +package com.kdp.wanandroidclient.common; + +/** + * Api接口地址 + */ +public class UrlConstainer { + public static final String baseUrl = "https://www.wanandroid.com/"; + /** + * 登录 + */ + public static final String LOGIN = "user/login"; + + /** + * 注册 + */ + public static final String REGISTER = "user/register"; + + /** + * 首页置顶列表 + */ + public static final String HOME_TOP_LIST = "article/top/json"; + + /** + * 首页文章列表 + */ + public static final String HOME_LIST = "article/list/{page}/json"; + + /** + * 首页广告 + */ + public static final String MAIN_BANNER = "banner/json"; + + /** + * 收藏文章 + */ + public static final String COLLECT_ARTICLE = "lg/collect/{id}/json"; + + /** + * 取消收藏的文章 + */ + public static final String UNCOLLECT_ARTICLE = "lg/uncollect_originId/{id}/json"; + + /** + * 删除收藏的文章 + */ + public static final String DELETE_COLLECT_ARTICLE = "lg/uncollect/{id}/json"; + + + /** + * 知识体系 + */ + public static final String TREE = "tree/json"; + + /** + * 知识体系文章列表 + */ + public static final String TREE_LIST = "article/list/{page}/json"; + /** + * 收藏的文章列表 + */ + public static final String COLLECT_ARTICLE_LIST = "lg/collect/list/{page}/json"; + + /** + * 搜索 + */ + public static final String SEARCH = "article/query/{page}/json"; + + /** + * 搜索热词 + */ + public static final String HOT_KEYWORD = "/hotkey/json"; + + /** + * 常用网站 + */ + public static final String FRIEND = "friend/json"; + + + /** + * 公众号 + */ + public static final String CHAPTERS = "wxarticle/chapters/json"; + + /** + * 公众号文章列表 + */ + public static final String CHAPTER_LIST = "wxarticle/list/{id}/{page}/json"; + + /** + * 项目分类 + */ + public static final String PROJECT_CATE = "project/tree/json"; + + /** + * 项目 + */ + public static final String PROJECT = "project/list/{page}/json"; + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/event/Event.java b/app/src/main/java/com/kdp/wanandroidclient/event/Event.java new file mode 100644 index 0000000..df7e03b --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/event/Event.java @@ -0,0 +1,24 @@ +package com.kdp.wanandroidclient.event; + +/** + * 事件类型 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class Event { + public enum Type { + REFRESH_ITEM, REFRESH_LIST, SCROLL_TOP,SCALE + } + + public Type type; + public Object object; + public Event(Type type) { + this(type,null); + } + + public Event(Type type, Object object) { + this.type = type; + this.object = object; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/event/RxEvent.java b/app/src/main/java/com/kdp/wanandroidclient/event/RxEvent.java new file mode 100644 index 0000000..ce3eb25 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/event/RxEvent.java @@ -0,0 +1,86 @@ +package com.kdp.wanandroidclient.event; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.reactivex.observers.DisposableObserver; +import io.reactivex.subjects.PublishSubject; + +/** + * 事件线 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class RxEvent { + + private static RxEvent mInstance; + private Map> mSubjectMaps = new HashMap<>(); + + public static RxEvent getInstance() { + if (mInstance == null) { + synchronized (RxEvent.class) { + if (mInstance == null) + mInstance = new RxEvent(); + } + } + return mInstance; + } + + + /** + * 注册事件 + * + * @param mAction + * @return + */ + public PublishSubject registerEvent(String mAction) { + List mSubjectList = mSubjectMaps.get(mAction); + if (mSubjectList == null) { + mSubjectList = new ArrayList<>(); + } + mSubjectMaps.put(mAction, mSubjectList); + PublishSubject mSubject = PublishSubject.create(); + mSubjectList.add(mSubject); + return mSubject; + } + + /** + * 发送事件 + * + * @param mAction + * @param object + */ + public void postEvent(String mAction, Object object) { + List mSubjectList = mSubjectMaps.get(mAction); + if (mSubjectList != null && !mSubjectList.isEmpty()) { + for (PublishSubject mSubject : mSubjectList) { + mSubject.onNext(object); + } + } + } + + /** + * 注销事件 + * + * @param mAction + * @param mSubject + */ + public void unRegisterEvent(String mAction, PublishSubject mSubject, DisposableObserver mDisposable) { + + List mSubjectList = mSubjectMaps.get(mAction); + //中断事件 + if (mDisposable != null && !mDisposable.isDisposed()) + mDisposable.dispose(); + if (mSubjectList != null){ + mSubjectList.remove(mSubject); + if (mSubjectList.isEmpty()){ + mSubjectMaps.remove(mAction); + + } + } + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/inter/OnArticleListItemClickListener.java b/app/src/main/java/com/kdp/wanandroidclient/inter/OnArticleListItemClickListener.java new file mode 100644 index 0000000..888f817 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/inter/OnArticleListItemClickListener.java @@ -0,0 +1,10 @@ +package com.kdp.wanandroidclient.inter; + +import com.kdp.wanandroidclient.bean.Article; + + + +public interface OnArticleListItemClickListener extends OnItemClickListener
{ + void onDeleteCollectClick(int position,int id,int originId); + void onCollectClick(int position,int id); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/inter/OnItemClickListener.java b/app/src/main/java/com/kdp/wanandroidclient/inter/OnItemClickListener.java new file mode 100644 index 0000000..b6265cb --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/inter/OnItemClickListener.java @@ -0,0 +1,6 @@ +package com.kdp.wanandroidclient.inter; + + +public interface OnItemClickListener{ + void onItemClick(int position,T bean); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/inter/OnProjectListItemClickListener.java b/app/src/main/java/com/kdp/wanandroidclient/inter/OnProjectListItemClickListener.java new file mode 100644 index 0000000..fac8979 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/inter/OnProjectListItemClickListener.java @@ -0,0 +1,8 @@ +package com.kdp.wanandroidclient.inter; + +import com.kdp.wanandroidclient.bean.Article; + + +public interface OnProjectListItemClickListener extends OnItemClickListener
{ + void onCollectClick(int position,int id); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/inter/OnTreeItemClickListener.java b/app/src/main/java/com/kdp/wanandroidclient/inter/OnTreeItemClickListener.java new file mode 100644 index 0000000..9240cfb --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/inter/OnTreeItemClickListener.java @@ -0,0 +1,11 @@ +package com.kdp.wanandroidclient.inter; + +import com.kdp.wanandroidclient.bean.Tree; + + + +public interface OnTreeItemClickListener { + + void onItemClick(Tree mTree); + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/inter/VerifyAccountCallback.java b/app/src/main/java/com/kdp/wanandroidclient/inter/VerifyAccountCallback.java new file mode 100644 index 0000000..d4dff47 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/inter/VerifyAccountCallback.java @@ -0,0 +1,11 @@ +package com.kdp.wanandroidclient.inter; + +/** + * 帐号验证接口 + * author: 曾文海 + * date: 2023/5/31 + */ + +public interface VerifyAccountCallback { + void onVerifyResult(String msg); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/manager/ImageLoaderManager.java b/app/src/main/java/com/kdp/wanandroidclient/manager/ImageLoaderManager.java new file mode 100644 index 0000000..1caeaab --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/manager/ImageLoaderManager.java @@ -0,0 +1,45 @@ +package com.kdp.wanandroidclient.manager; + +import android.widget.ImageView; +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.common.Const; + +/** + * Glide图片加载管理类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class ImageLoaderManager { + private static RequestOptions nomal_image_options = RequestOptions + .placeholderOf(R.drawable.ic_img_default) + .error(R.drawable.ic_img_default) + .centerCrop(); + private static RequestOptions head_options = RequestOptions + .placeholderOf(R.mipmap.ic_launcher_round) + .centerCrop(); + + public static void displayImage(Object resource,ImageView imageView,int type) { + + switch (type) { + case Const.IMAGE_LOADER.HEAD_IMG: + loadImg(resource,head_options,imageView); + break; + case Const.IMAGE_LOADER.NOMAL_IMG: + loadImg(resource,nomal_image_options,imageView); + break; + default: + break; + } + + } + + + private static void loadImg(Object resource, RequestOptions options, ImageView imageView){ + Glide.with(AppContext.getContext()).load(resource).apply(options).into(imageView); + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/manager/UserInfoManager.java b/app/src/main/java/com/kdp/wanandroidclient/manager/UserInfoManager.java new file mode 100644 index 0000000..831610f --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/manager/UserInfoManager.java @@ -0,0 +1,108 @@ +package com.kdp.wanandroidclient.manager; + +import android.text.TextUtils; +import android.util.Base64; + +import com.kdp.wanandroidclient.bean.User; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.utils.AesEncryptionUtils; +import com.kdp.wanandroidclient.utils.PreUtils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import javax.crypto.spec.SecretKeySpec; + +/** + * 用户信息管理类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class UserInfoManager { + + /** + * 获取用户信息 + * @return + */ + public static User getUserInfo() { + SecretKeySpec keySpec = getAesKey(); + String userInfo = AesEncryptionUtils.decrypt(keySpec, (String) PreUtils.get(Const.USERINFO_KEY.USER_INFO, "")); + if (TextUtils.isEmpty(userInfo)) return null; + try { + return translateStringTOUserInfo(userInfo); + } catch (IOException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 保存用户信息 + * @param user + */ + public static void saveUserInfo(User user){ + try { + String userInfo = translateUserInfoTOString(user); + SecretKeySpec key = AesEncryptionUtils.createKey(); + String aesContent = AesEncryptionUtils.encrypt(key, userInfo); + //保存用户信息 + PreUtils.put(Const.USERINFO_KEY.USER_INFO, aesContent); + //保存密钥 + saveAesKey(key); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + private static void saveAesKey(SecretKeySpec keySpec){ + PreUtils.put(Const.USERINFO_KEY.AES, Base64.encodeToString(keySpec.getEncoded(),Base64.DEFAULT)); + } + + private static SecretKeySpec getAesKey(){ + String keyStr = (String) PreUtils.get(Const.USERINFO_KEY.AES, ""); + return AesEncryptionUtils.getSecretKey(Base64.decode(keyStr, Base64.DEFAULT)); + } + + public static boolean isLogin() { + return (boolean) PreUtils.get(Const.USERINFO_KEY.IS_LOGIN, false); + } + + public static void saveIsLogin(boolean isLogin){ + PreUtils.put(Const.USERINFO_KEY.IS_LOGIN,isLogin); + } + + /** + * User 转 String + * @param user + * @return + * @throws IOException + */ + private static String translateUserInfoTOString(User user) throws IOException{ + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(user); + return Base64.encodeToString(bos.toByteArray(), Base64.DEFAULT); + } + + /** + * String 转 User + * @param userStr + * @return + * @throws IOException + * @throws ClassNotFoundException + */ + private static User translateStringTOUserInfo(String userStr) throws IOException, ClassNotFoundException { + if (userStr == null) return null; + byte[] base64Bytes = Base64.decode(userStr,Base64.DEFAULT); + ByteArrayInputStream bis = new ByteArrayInputStream(base64Bytes); + ObjectInputStream ois = new ObjectInputStream(bis); + return (User) ois.readObject(); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/NetConfig.java b/app/src/main/java/com/kdp/wanandroidclient/net/NetConfig.java new file mode 100644 index 0000000..c325bff --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/NetConfig.java @@ -0,0 +1,50 @@ +package com.kdp.wanandroidclient.net; + +/** + * 异常信息 + */ + +public interface NetConfig { + + //请求成功 + int REQUEST_SUCCESS = 0; + //请求失败 + int REQUEST_ERROR = -1; + /** + * 连接错误,网络异常 + */ + int CONNECT_ERROR = 1001; + + /** + * 无法连接到主机 + */ + int UNKNOWN_HOST = 1002; + + /** + * 连接超时 + */ + int CONNECT_TIMEOUT = 1003; + + /** + * 请求超时 + */ + int REQUEST_TIMEOUT = 1004; + + /** + * 解析错误 + */ + int PARSE_ERROR = 1005; + /** + * 未知错误 + */ + int UNKNOWN_ERROR = 1006; + + /** + * 非法参数 + */ + int ILLEGAL_PARAMS = 1007; + + + + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/NetExceptionHandle.java b/app/src/main/java/com/kdp/wanandroidclient/net/NetExceptionHandle.java new file mode 100644 index 0000000..f00a5c2 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/NetExceptionHandle.java @@ -0,0 +1,85 @@ +package com.kdp.wanandroidclient.net; + +import android.content.Context; +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.utils.NetworkUtils; +import com.kdp.wanandroidclient.utils.ToastUtils; + +import org.json.JSONException; + +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.text.ParseException; + + +public class NetExceptionHandle { + + /** + * 处理异常错误 + * @param context + * @param t + */ + public static void dealException(Context context, Throwable t) { + if (!NetworkUtils.isAvailable(context)){ + onException(NetConfig.CONNECT_ERROR, context); + return; + } + if (t instanceof ConnectException) { + //连接错误,网络异常 + onException(NetConfig.CONNECT_ERROR, context); + }else if (t instanceof UnknownHostException){ + //无法连接到主机 + onException(NetConfig.UNKNOWN_HOST,context); + } + else if (t instanceof InterruptedException) { + //连接超时 + onException(NetConfig.CONNECT_TIMEOUT, context); + } else if (t instanceof JSONException || t instanceof ParseException) { + //解析错误 + onException(NetConfig.PARSE_ERROR, context); + } else if (t instanceof SocketTimeoutException) { + //请求超时 + onException(NetConfig.REQUEST_TIMEOUT, context); + } else if (t instanceof UnknownError) { + //未知错误 + onException(NetConfig.UNKNOWN_ERROR, context); + } else if (t instanceof IllegalArgumentException){ + //未知错误 + onException(NetConfig.ILLEGAL_PARAMS, context); + } + } + + + /** + * 异常信息提示 + * @param errorCode + * @param context + */ + private static void onException(int errorCode, Context context) { + switch (errorCode) { + case NetConfig.CONNECT_ERROR: + ToastUtils.showToast(context, R.string.connect_error); + break; + case NetConfig.UNKNOWN_HOST: + ToastUtils.showToast(context,R.string.unknown_host); + break; + case NetConfig.CONNECT_TIMEOUT: + ToastUtils.showToast(context, R.string.connect_timeout); + break; + case NetConfig.PARSE_ERROR: + ToastUtils.showToast(context, R.string.parse_error); + break; + case NetConfig.REQUEST_TIMEOUT: + ToastUtils.showToast(context, R.string.request_timeout); + break; + case NetConfig.UNKNOWN_ERROR: + ToastUtils.showToast(context, R.string.unknown_error); + break; + case NetConfig.ILLEGAL_PARAMS: + ToastUtils.showToast(context,R.string.illegal_params); + break; + } + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/RxRetrofit.java b/app/src/main/java/com/kdp/wanandroidclient/net/RxRetrofit.java new file mode 100644 index 0000000..bd55902 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/RxRetrofit.java @@ -0,0 +1,73 @@ +package com.kdp.wanandroidclient.net; + +import android.content.Context; + +import com.kdp.wanandroidclient.api.ApiServer; +import com.kdp.wanandroidclient.net.interceptor.CacheInterceptor; +import com.kdp.wanandroidclient.net.interceptor.LoadCookieInterceptor; +import com.kdp.wanandroidclient.net.interceptor.RequestInterceptor; +import com.kdp.wanandroidclient.net.interceptor.SaveCookieInterceptor; + +import java.util.concurrent.TimeUnit; + +import okhttp3.Cache; +import okhttp3.OkHttpClient; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; +import retrofit2.converter.gson.GsonConverterFactory; + + +/** + * 网络请求工具类 + */ +public class RxRetrofit { + private Retrofit retrofit; + private static ApiServer apiServer; + + private static final class Holder { + private static final RxRetrofit INSTANCE = new RxRetrofit(); + } + + public void initRxRetrofit(final Context context, String baseUrl) { + + OkHttpClient okHttpClient = new OkHttpClient.Builder() + //链接超时 + .connectTimeout(10, TimeUnit.SECONDS) + //读取超时 + .readTimeout(10, TimeUnit.SECONDS) + //配置缓存,okhttp会自动缓存数据到本地 + .cache(new Cache(context.getExternalFilesDir("http_cache"), 10 << 20)) + //添加Cookie拦截器 + .addInterceptor(new SaveCookieInterceptor()) + .addInterceptor(new LoadCookieInterceptor()) + //添加缓存拦截器 + .addInterceptor(new RequestInterceptor()) + .addNetworkInterceptor(new CacheInterceptor()) + .build(); + + retrofit = new Retrofit.Builder() + .baseUrl(baseUrl) + .client(okHttpClient) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .addConverterFactory(GsonConverterFactory.create()) + .build(); + apiServer = retrofit.create(ApiServer.class); + } + + public static RxRetrofit getInstance() { + return Holder.INSTANCE; + } + + /** + * 获取ApiServer对象 + * + * @return apiServer + */ + public static ApiServer Api() { + if (apiServer == null) + throw new IllegalStateException("You must invoke init method first in Application"); + return apiServer; + } + + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/RxSchedulers.java b/app/src/main/java/com/kdp/wanandroidclient/net/RxSchedulers.java new file mode 100644 index 0000000..59048e0 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/RxSchedulers.java @@ -0,0 +1,33 @@ +package com.kdp.wanandroidclient.net; + +import com.kdp.wanandroidclient.bean.BaseBean; + +import io.reactivex.Observable; +import io.reactivex.ObservableSource; +import io.reactivex.ObservableTransformer; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; + +/** + * author: 曾文海 + * date: 2023/5/31 + */ + +public class RxSchedulers{ + + /** + * 指定被观察者为io线程 + * 观察者为主线程 + */ + + public static ObservableTransformer,BaseBean> io_main() { + return new ObservableTransformer, BaseBean>() { + @Override + public ObservableSource> apply(Observable> upstream) { + return upstream.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); + } + }; + } + + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxBaseObserver.java b/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxBaseObserver.java new file mode 100644 index 0000000..6cc4d4d --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxBaseObserver.java @@ -0,0 +1,52 @@ +package com.kdp.wanandroidclient.net.callback; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.bean.BaseBean; +import com.kdp.wanandroidclient.net.NetExceptionHandle; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; +import com.kdp.wanandroidclient.ui.core.view.IView; + +import io.reactivex.observers.DisposableObserver; + +/** + * RxRetrofit请求回调基类 + */ + +public abstract class RxBaseObserver extends DisposableObserver> { + + protected IView view; + + RxBaseObserver(BasePresenter mPresenter) { + this.view = mPresenter.getView(); + } + + + @Override + protected void onStart() { + super.onStart(); + //显示loading + showLoading(); + } + + public void showLoading() { + view.showLoading(""); + } + + @Override + public void onError(Throwable e) { + //隐藏loading + hideLoading(); + //处理异常 + NetExceptionHandle.dealException(AppContext.getContext(),e); + } + + @Override + public void onComplete() { + hideLoading(); + } + + private void hideLoading() { + if (null != view) + this.view.hideLoading(); + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxConsumer.java b/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxConsumer.java new file mode 100644 index 0000000..f202a10 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxConsumer.java @@ -0,0 +1,28 @@ +package com.kdp.wanandroidclient.net.callback; + +import com.kdp.wanandroidclient.bean.BaseBean; +import com.kdp.wanandroidclient.net.NetConfig; + +import io.reactivex.annotations.NonNull; +import io.reactivex.functions.Consumer; + +/** + * author: 曾文海 + * date: 2023/5/31 + */ + +public abstract class RxConsumer implements Consumer> { + + @Override + public void accept(@NonNull BaseBean tBaseBean) throws Exception { + if (tBaseBean.errorCode == NetConfig.REQUEST_SUCCESS){ + onSuccess(tBaseBean.data); + }else { + onFail(tBaseBean.errorMsg); + } + } + + protected abstract void onFail(String errorMsg); + + protected abstract void onSuccess(T data); +} \ No newline at end of file diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxFunction.java b/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxFunction.java new file mode 100644 index 0000000..091e52a --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxFunction.java @@ -0,0 +1,27 @@ +package com.kdp.wanandroidclient.net.callback; + +import com.kdp.wanandroidclient.bean.BaseBean; +import com.kdp.wanandroidclient.net.NetConfig; + +import io.reactivex.Observable; +import io.reactivex.functions.Function; + +/** + * 用来处理嵌套请求 + * author: 曾文海 + * date: 2023/5/31 + */ + +public abstract class RxFunction implements Function, Observable>> { + + @Override + public Observable> apply(BaseBean tBaseBean) throws Exception { + if (tBaseBean.errorCode == NetConfig.REQUEST_SUCCESS){ + return doOnNextRequest(); + } + return null; + } + + protected abstract Observable> doOnNextRequest(); + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxObserver.java b/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxObserver.java new file mode 100644 index 0000000..6a1f114 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxObserver.java @@ -0,0 +1,32 @@ +package com.kdp.wanandroidclient.net.callback; + +import com.kdp.wanandroidclient.bean.BaseBean; +import com.kdp.wanandroidclient.net.NetConfig; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; + +/** + * RxRetrofit通用接口回调类 + */ + +public abstract class RxObserver extends RxBaseObserver { + public RxObserver(BasePresenter mPresenter) { + super(mPresenter); + } + + @Override + public void onNext(BaseBean mBaseBean) { + + //请求成功 + if (mBaseBean.errorCode == NetConfig.REQUEST_SUCCESS) { + onSuccess(mBaseBean.data); + } else { + //失败 + onFail(mBaseBean.errorCode, mBaseBean.errorMsg); + } + } + + protected abstract void onSuccess(T data); + + protected abstract void onFail(int errorCode, String errorMsg); + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxPageListObserver.java b/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxPageListObserver.java new file mode 100644 index 0000000..c3253af --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxPageListObserver.java @@ -0,0 +1,55 @@ +package com.kdp.wanandroidclient.net.callback; + +import com.kdp.wanandroidclient.bean.BaseBean; +import com.kdp.wanandroidclient.bean.PageListData; +import com.kdp.wanandroidclient.net.NetConfig; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; +import com.kdp.wanandroidclient.ui.core.view.IPageLoadDataView; + +import java.util.List; + +/** + * 分页加载功能的接口回调类 + * 分页加载逻辑在这里统一处理 + * author: 曾文海 + * date: 2023/5/31 + */ + +public abstract class RxPageListObserver extends RxBaseObserver> { + + private IPageLoadDataView mListDataView; + + public RxPageListObserver(BasePresenter mPresenter) { + super(mPresenter); + this.mListDataView = (IPageLoadDataView) mPresenter.getView(); + } + @Override + public void onNext(BaseBean> baseBean) { + if (baseBean.errorCode == NetConfig.REQUEST_SUCCESS) { + + PageListData mListData = baseBean.data; + if (mListDataView.getPage() == mListDataView.getFirstPage()) { + mListDataView.clearListData(); + } + if (mListData.isOver()) { + mListDataView.showNoMore(); + } else { + mListDataView.autoLoadMore(); + } + onSuccess(mListData.getDatas()); + } else { + onFail(baseBean.errorCode, baseBean.errorMsg); + } + } + + + @Override + public void onError(Throwable e) { + super.onError(e); + mListDataView.showError(); + } + + public abstract void onSuccess(List mData); + + public abstract void onFail(int errorCode, String errorMsg); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxZipObserver.java b/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxZipObserver.java new file mode 100644 index 0000000..43873af --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/callback/RxZipObserver.java @@ -0,0 +1,45 @@ +package com.kdp.wanandroidclient.net.callback; + +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.net.NetExceptionHandle; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; +import com.kdp.wanandroidclient.ui.core.view.IView; + +import io.reactivex.observers.DisposableObserver; + +public abstract class RxZipObserver extends DisposableObserver { + protected IView view; + + protected RxZipObserver(BasePresenter mPresenter) { + this.view = mPresenter.getView(); + } + + @Override + protected void onStart() { + super.onStart(); + //显示loading + showLoading(); + } + + + @Override + public void onError(Throwable e) { + //隐藏loading + hideLoading(); + //处理异常 + NetExceptionHandle.dealException(AppContext.getContext(),e); + } + + @Override + public void onComplete() { + hideLoading(); + } + + public void showLoading() { + view.showLoading(""); + } + private void hideLoading() { + if (null != view) + this.view.hideLoading(); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/interceptor/CacheInterceptor.java b/app/src/main/java/com/kdp/wanandroidclient/net/interceptor/CacheInterceptor.java new file mode 100644 index 0000000..402110b --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/interceptor/CacheInterceptor.java @@ -0,0 +1,60 @@ +package com.kdp.wanandroidclient.net.interceptor; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import okhttp3.CacheControl; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +/** + * 有网时的缓存策略,且缓存60秒,过期后返回服务器的数据并刷新缓存 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class CacheInterceptor implements Interceptor { + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + + //FORCE_NETWORK 里设置了 no-cache + //no-cache,强制客户端直接向服务器发送请求,也就是说每次请求都必须向服务器发送。 + //如果缓存被删除,则返回请求的数据并重新缓存数据,否则如下: + // 服务器接收到请求,然后判断资源是否变更,是则返回新内容,刷新缓存,否则返回304(Not Modified)。 + // 若资源未变更,且缓存时间未过期则返回缓存的数据,若缓存时间已过期,则会请求服务器获取数据。 + // 当缓存过期后,如果资源一直未变更(返回304),okhttp客户端会请求网络返回数据,且刷新本地缓存。 + + //若不设置no-cache,那么只要缓存时间过期,就会请求网络获取数据并刷新缓存, + //若缓存时间未过期,不管资源有没有变更,则一直从缓存中取数据。 + + //注意:拦截器只有配置到addInterceptor中时,onlyIfCached、maxStale才会生效,且如果no-cache和maxStale同时存在,maxStale也会失效。 + //若no-cache、onlyIfCached同时存在,no-cache会失效 +// CacheControl requestCacheControl = new CacheControl.Builder() +// .noCache() +// .onlyIfCached() +// .maxStale(60*60,TimeUnit.SECONDS) +// .build(); + + request = request.newBuilder() + .cacheControl(CacheControl.FORCE_NETWORK) +// .cacheControl(requestCacheControl) + .build(); + Response response = chain.proceed(request); + //max-age:如果仅在请求头中设置它,不起作用,必须要在响应头中设置,如果同时设置了它,且请求头中的max-age小于响应头中的max-age + //则请求头的max-age生效。因此可以这样设计:用响应头的max-age控制缓存的时间,用请求头的max-age控制缓存刷新的时间 + //max-age会存储到缓存文件中,告知缓存多长时间,在没有超过缓存时间的情况下,请求会返回缓存内的数据, + //在超出max-age的情况下向服务端发起新的请求,请求失败的情况下返回缓存数据,否则向服务端重新发起请求。 + + //注意:拦截器只有配置到addNetInterceptor中,response中的max-age才会生效 + CacheControl responseCacheControl = new CacheControl.Builder() + .maxAge(60, TimeUnit.SECONDS) + .build(); + response = response.newBuilder() + .removeHeader("Pragma")//若服务器不支持缓存{pragma : [no-cache]},则需移除Pragma + .header("Cache-Control", responseCacheControl.toString()) + .build(); + + return response; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/interceptor/LoadCookieInterceptor.java b/app/src/main/java/com/kdp/wanandroidclient/net/interceptor/LoadCookieInterceptor.java new file mode 100644 index 0000000..8e138e8 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/interceptor/LoadCookieInterceptor.java @@ -0,0 +1,29 @@ +package com.kdp.wanandroidclient.net.interceptor; + +import android.text.TextUtils; + +import com.kdp.wanandroidclient.utils.PreUtils; + +import java.io.IOException; + +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +/** + * 添加Cookie + * author: 曾文海 + * date: 2023/5/31 + */ + +public class LoadCookieInterceptor implements Interceptor { + @Override + public Response intercept(Chain chain) throws IOException { + Request.Builder builder = chain.request().newBuilder(); + String mCookieStr = (String) PreUtils.get(chain.request().url().host(), ""); + if (!TextUtils.isEmpty(mCookieStr)) { + builder.addHeader("Cookie", mCookieStr.substring(0, mCookieStr.length() - 1));//长度减1为了去除最后的逗号 + } + return chain.proceed(builder.build()); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/interceptor/RequestInterceptor.java b/app/src/main/java/com/kdp/wanandroidclient/net/interceptor/RequestInterceptor.java new file mode 100644 index 0000000..6bf87dd --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/interceptor/RequestInterceptor.java @@ -0,0 +1,41 @@ +package com.kdp.wanandroidclient.net.interceptor; + +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.utils.NetworkUtils; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import okhttp3.CacheControl; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +/** + * 无网时仅从缓存中获取(缓存时间为Integer.MAX_VALUE秒) + * author: 曾文海 + * date: 2023/5/31 + */ + +public class RequestInterceptor implements Interceptor { + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + //无网络时从缓存中获取 + if (!NetworkUtils.isAvailable(AppContext.getContext())) { + //max-stale:指示客户机可以接收超出max-age时间的响应消息,若respond中没有设置max-age,相当于max-age=0 + //请求的缓存过期时间: max-stale+max-age(response中) + //在请求设置中有效,在响应设置中无效 + +// CacheControl cacheControl = new CacheControl.Builder() +// .onlyIfCached() +// .maxStale(60*60,TimeUnit.SECONDS) +// .build(); + request = request.newBuilder() + .cacheControl(CacheControl.FORCE_CACHE) +// .cacheControl(cacheControl) + .build(); + } + return chain.proceed(request); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/net/interceptor/SaveCookieInterceptor.java b/app/src/main/java/com/kdp/wanandroidclient/net/interceptor/SaveCookieInterceptor.java new file mode 100644 index 0000000..b832a14 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/net/interceptor/SaveCookieInterceptor.java @@ -0,0 +1,40 @@ +package com.kdp.wanandroidclient.net.interceptor; + +import android.util.Log; + +import com.kdp.wanandroidclient.common.UrlConstainer; +import com.kdp.wanandroidclient.utils.PreUtils; + +import java.io.IOException; +import java.util.List; + +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +/** + * 保存Cookie + * author: 曾文海 + * date: 2023/5/31 + */ + +public class SaveCookieInterceptor implements Interceptor { + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + System.out.println("ddddddddddd"+request); + Response response = chain.proceed(request); + List mCookieList = response.headers("Set-Cookie"); + //保存Cookie + if (!mCookieList.isEmpty() && request.url().toString().endsWith(UrlConstainer.LOGIN)) { + StringBuilder sb = new StringBuilder(); + for (String cookie : mCookieList) { + //注意Cookie请求头字段中的每个Cookie之间用逗号或分号分隔 + sb.append(cookie).append(","); + } + PreUtils.put(response.request().url().host(), sb.toString()); + Log.e(SaveCookieInterceptor.class.getSimpleName(), "intercept: url : " + request.url()); + } + return response; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/LauncherActivity.java b/app/src/main/java/com/kdp/wanandroidclient/ui/LauncherActivity.java new file mode 100644 index 0000000..9ef7636 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/LauncherActivity.java @@ -0,0 +1,135 @@ +package com.kdp.wanandroidclient.ui; + +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.view.KeyEvent; +import android.view.View; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.bean.User; +import com.kdp.wanandroidclient.manager.UserInfoManager; +import com.kdp.wanandroidclient.ui.base.BasePresenterActivity; +import com.kdp.wanandroidclient.ui.logon.LogonContract; +import com.kdp.wanandroidclient.ui.logon.LogonPresenter; +import com.kdp.wanandroidclient.ui.main.MainActivity; + +import java.lang.ref.WeakReference; + + +/** + * 启动页、程序入口 + + */ + +public class LauncherActivity extends BasePresenterActivity implements LogonContract.ILoginRegisterView { + private User user; + private Handler mHandler; + private DelayRunnable mRunnable; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + hideBottomUIMenu(); + //倒计时 + startCountdown(); + } + + private void startCountdown() { + mHandler = new Handler(); + mRunnable = new DelayRunnable(this); + mHandler.postDelayed(mRunnable, 2000); + } + + /** + * 隐藏虚拟按键,并且全屏 + */ + protected void hideBottomUIMenu() { + //隐藏虚拟按键,并且全屏 + if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api + View v = this.getWindow().getDecorView(); + v.setSystemUiVisibility(View.GONE); + } else if (Build.VERSION.SDK_INT >= 19) { + //for new api versions. + View decorView = getWindow().getDecorView(); + int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN; + decorView.setSystemUiVisibility(uiOptions); + } + } + + //自动登录 + private void autoLogin() { + if (UserInfoManager.isLogin()) { + //自动登录 + user = UserInfoManager.getUserInfo(); + if (user != null) + mPresenter.login(); + } + startToActivity(); + } + + + private static class DelayRunnable implements Runnable { + private WeakReference mWeakReference; + + DelayRunnable(LauncherActivity instance) { + mWeakReference = new WeakReference<>(instance); + } + + @Override + public void run() { + LauncherActivity instance = mWeakReference.get(); + if (instance == null) return; + instance.autoLogin(); + } + } + + //进入首页 + private void startToActivity() { + Intent intent = new Intent(this, MainActivity.class); + startActivity(intent); + finish(); + } + + @Override + protected int getLayoutId() { + return R.layout.activity_splash; + } + + @Override + protected LogonPresenter createPresenter() { + return new LogonPresenter(); + } + + @Override + public String getUserName() { + return user.getUsername(); + } + + @Override + public String getPassWord() { + return user.getPassword(); + } + + + @Override + public void showResult(String msg) { + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + + if (keyCode == KeyEvent.KEYCODE_BACK) { + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + protected void onDestroy() { + mHandler.removeCallbacks(mRunnable); + super.onDestroy(); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ArticleListAdapter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ArticleListAdapter.java new file mode 100644 index 0000000..96b8b09 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ArticleListAdapter.java @@ -0,0 +1,156 @@ +package com.kdp.wanandroidclient.ui.adapter; + +import android.graphics.Color; +import android.os.Build; +import android.support.v4.content.ContextCompat; +import android.text.Html; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.common.ListDataHolder; +import com.kdp.wanandroidclient.inter.OnArticleListItemClickListener; +import com.kdp.wanandroidclient.utils.DateUtils; + +/** + * 文章列表 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class ArticleListAdapter extends BaseListAdapter
{ + + private int Type; + private OnArticleListItemClickListener listener; + + public ArticleListAdapter(OnArticleListItemClickListener listener, int type) { + this.listener = listener; + this.Type = type; + } + + @Override + protected int getLayoutId(int viewType) { + return R.layout.item_home_article_list; + } + + @Override + public void bindDatas(ListDataHolder holder, final Article bean, int itemType, final int position) { + TextView tv_tag = holder.getView(R.id.tv_tag); + TextView tv_author = holder.getView(R.id.tv_author); + TextView tv_title = holder.getView(R.id.tv_title); + TextView tv_time = holder.getView(R.id.tv_time); + TextView tv_type = holder.getView(R.id.tv_type); + ImageView img_collect = holder.getView(R.id.img_collect); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + tv_title.setText(Html.fromHtml(bean.getTitle(), Html.FROM_HTML_MODE_LEGACY)); + } else { + tv_title.setText(Html.fromHtml(bean.getTitle())); + } + + tv_author.setText(bean.getAuthor()); + tv_time.setText(DateUtils.parseTime(bean.getPublishTime())); + + + if (Type == Const.LIST_TYPE.HOME || Type == Const.LIST_TYPE.SEARCH){ + coverToArticleList(tv_type,tv_tag,img_collect,position,bean); + }else if (Type == Const.LIST_TYPE.TREE){ + coverToTreeList(tv_type,tv_tag,img_collect,position,bean); + }else if (Type == Const.LIST_TYPE.COLLECT){ + coverToCollectList(tv_type,tv_tag,img_collect,position,bean); + } + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null) { + listener.onItemClick(position,bean); + } + } + }); + } + + + /** + * 首页、搜索的文章列表 + * @param tv_type + * @param tv_tag + * @param img_collect + * @param position + * @param bean + */ + private void coverToArticleList(TextView tv_type,TextView tv_tag, ImageView img_collect, final int position, final Article bean) { + tv_type.setText(String.format("%1$s / %2$s",bean.getSuperChapterName(), bean.getChapterName())); + tv_tag.setVisibility(View.VISIBLE); + + if (bean.isTop()){ + tv_tag.setActivated(true); + tv_tag.setText("置顶"); + tv_tag.setTextColor(Color.RED); + }else if (bean.isFresh()){ + tv_tag.setText("新"); + tv_tag.setTextColor(Color.RED); + tv_tag.setActivated(true); + }else if (bean.getTags().size() > 0){ + tv_tag.setActivated(false); + tv_tag.setText(bean.getTags().get(0).getName()); + tv_tag.setTextColor(ContextCompat.getColor(AppContext.getContext(),R.color._009a61)); + }else { + tv_tag.setVisibility(View.GONE); + } + + img_collect.setImageResource(bean.isCollect() ? R.drawable.ic_favorite_light_24dp : R.drawable.ic_favorite_gray_24dp); + img_collect.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null) { + listener.onCollectClick(position, bean.getId()); + } + } + }); + } + + /** + * 体系文章列表 + * @param tv_tag + */ + private void coverToTreeList(TextView tv_type, TextView tv_tag, ImageView img_collect, final int position, final Article bean) { + tv_type.setText(String.format("%1$s / %2$s",bean.getSuperChapterName(), bean.getChapterName())); + tv_tag.setVisibility(View.GONE); + img_collect.setImageResource(bean.isCollect() ? R.drawable.ic_favorite_light_24dp : R.drawable.ic_favorite_gray_24dp); + img_collect.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null) { + listener.onCollectClick(position, bean.getId()); + } + } + }); + } + + /** + * 收藏的文章列表 + * @param tv_type + * @param img_collect + * @param position + * @param bean + */ + private void coverToCollectList(TextView tv_type,TextView tv_tag,ImageView img_collect, final int position, final Article bean) { + tv_type.setText(bean.getChapterName()); + tv_tag.setVisibility(View.GONE); + img_collect.setImageResource(R.drawable.ic_favorite_light_24dp); + img_collect.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null) { + listener.onDeleteCollectClick(position, bean.getId(), bean.getOriginId()); + } + } + }); + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/BannerAdapter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/BannerAdapter.java new file mode 100644 index 0000000..c37c797 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/BannerAdapter.java @@ -0,0 +1,125 @@ +package com.kdp.wanandroidclient.ui.adapter; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.view.PagerAdapter; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.Banner; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.manager.ImageLoaderManager; +import com.kdp.wanandroidclient.ui.web.WebViewActivity; + +import java.util.List; + +/** + + * author: 曾文海 + * date: 2023/5/31 + */ + +public class BannerAdapter extends PagerAdapter { + + private SparseArray mViews; + private List mBannerDatas; + + + public BannerAdapter(List mBannerDatas) { + this.mBannerDatas = mBannerDatas; + mViews = new SparseArray<>(); + } + + public void notifyDatas(List mBannerDatas) { + this.mBannerDatas = mBannerDatas; + notifyDataSetChanged(); + } + + @Override + public int getCount() { + if (mBannerDatas == null) return 0; + return mBannerDatas.size() <= 1 ? mBannerDatas.size() : Integer.MAX_VALUE; + } + + @NonNull + @Override + public Object instantiateItem(@NonNull final ViewGroup container, int position) { + + View view = mViews.get(position); + if (view == null) { + position %= mBannerDatas.size(); + final Banner bean = mBannerDatas.get(position); + view = LayoutInflater.from(AppContext.getContext()).inflate(R.layout.item_banner, container, false); + ImageView imageView = view.findViewById(R.id.img); + TextView titleView = view.findViewById(R.id.title); + ImageLoaderManager.displayImage(bean.getImagePath(), imageView, Const.IMAGE_LOADER.NOMAL_IMG); + titleView.setText(bean.getTitle()); + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(container.getContext(), WebViewActivity.class); + Article mArticle = new Article(); + mArticle.setTitle(bean.getTitle()); + mArticle.setLink(bean.getUrl()); + Bundle bundle = new Bundle(); + bundle.putSerializable(Const.BUNDLE_KEY.OBJ, mArticle); + intent.putExtras(bundle); + container.getContext().startActivity(intent); + } + }); + mViews.put(position, view); + } + container.addView(view); + return view; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + container.removeView((View) object); + } + + + @Override + public boolean isViewFromObject(View view, Object object) { + return view == object; + } + + + @Override + public int getItemPosition(Object object) { + mViews.clear(); + return POSITION_NONE; + } + +// @Override +// public void startUpdate(ViewGroup container) { +// super.startUpdate(container); + + //ViewPager显示的页面数据有所改变的回调(还未处理) +// } + +// @Override +// public void finishUpdate(ViewGroup container) { +// super.finishUpdate(container); + + //页面数据改变的处理结束后的回调 + //此处的处理其实就是 回调instantiateItem和destroyItem方法 + //当数据发生变化 +// int position = viewPager.getCurrentItem(); +// if (position == 0){ +// viewPager.setCurrentItem(8,false); +// }else if (position == 9){ +// viewPager.setCurrentItem(1,false); +// } +// } + + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/BaseListAdapter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/BaseListAdapter.java new file mode 100644 index 0000000..bb022bd --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/BaseListAdapter.java @@ -0,0 +1,62 @@ +package com.kdp.wanandroidclient.ui.adapter; + +import android.support.v7.widget.RecyclerView; +import android.view.ViewGroup; + +import com.kdp.wanandroidclient.common.ListDataHolder; +import com.kdp.wanandroidclient.widget.LMRecyclerView; + +import java.util.List; + +/** + * 适配器基类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public abstract class BaseListAdapter extends RecyclerView.Adapter { + + private List mList; + + //刷新所有数据 + public void notifyAllDatas(List mList, LMRecyclerView recyclerView) { + this.mList = mList; + recyclerView.notifyDataSetChanged(); + } + + //刷新单条数据 + public void notifyItemDataChanged(int position, LMRecyclerView recyclerView) { + recyclerView.notifyItemChanged(position); + } + + //移除单条数据 + public void notifyItemDataRemove(int position, LMRecyclerView recyclerView) { + recyclerView.notifyItemRemoved(position); + } + + + @Override + public ListDataHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return ListDataHolder.createViewHolder(parent, getLayoutId(viewType)); + } + + protected abstract int getLayoutId(int viewType); + + + @Override + public void onBindViewHolder(ListDataHolder holder, int position) { + //初始化View + T bean = mList.get(position); + //绑定数据 + bindDatas(holder, bean, holder.getItemViewType(), position); + } + + + @Override + public int getItemCount() { + return mList != null ? mList.size() : 0; + } + + public abstract void bindDatas(ListDataHolder holder, T bean, int itemType, int position); + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ChaptersFragPagerAdapter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ChaptersFragPagerAdapter.java new file mode 100644 index 0000000..e2e2c50 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ChaptersFragPagerAdapter.java @@ -0,0 +1,39 @@ +package com.kdp.wanandroidclient.ui.adapter; + +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; + +import com.kdp.wanandroidclient.bean.Chapter; +import com.kdp.wanandroidclient.ui.chapter.ChapterListFragment; + +import java.util.List; + +/*** + * @author kdp + * @date 2023 + * @description + */ +public class ChaptersFragPagerAdapter extends FragmentPagerAdapter { + private List list; + public ChaptersFragPagerAdapter(FragmentManager fm,List list) { + super(fm); + this.list = list; + } + @Override + public Fragment getItem(int positions) { + return ChapterListFragment.instantiate(list.get(positions).getId()); + } + + @Nullable + @Override + public CharSequence getPageTitle(int position) { + return list.get(position).getName(); + } + + @Override + public int getCount() { + return list!=null?list.size():0; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ProjectFragPagerAdapter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ProjectFragPagerAdapter.java new file mode 100644 index 0000000..83f00f7 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ProjectFragPagerAdapter.java @@ -0,0 +1,41 @@ +package com.kdp.wanandroidclient.ui.adapter; + +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; + +import com.kdp.wanandroidclient.bean.ProjectCate; +import com.kdp.wanandroidclient.ui.project.ProjectListFragment; + +import java.util.List; + +/*** + * @author kdp + * @date 2023 + * @description + */ +public class ProjectFragPagerAdapter extends FragmentPagerAdapter { + + private List projectCateList; + public ProjectFragPagerAdapter(FragmentManager fm,List projectCateList) { + super(fm); + this.projectCateList = projectCateList; + } + + @Override + public Fragment getItem(int position) { + return ProjectListFragment.instantiate(projectCateList.get(position).getId()); + } + + @Nullable + @Override + public CharSequence getPageTitle(int position) { + return projectCateList.get(position).getName(); + } + + @Override + public int getCount() { + return projectCateList == null ? 0:projectCateList.size(); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ProjectListAdapter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ProjectListAdapter.java new file mode 100644 index 0000000..dc6f379 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ProjectListAdapter.java @@ -0,0 +1,58 @@ +package com.kdp.wanandroidclient.ui.adapter; + +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.common.ListDataHolder; +import com.kdp.wanandroidclient.inter.OnProjectListItemClickListener; +import com.kdp.wanandroidclient.manager.ImageLoaderManager; +import com.kdp.wanandroidclient.utils.DateUtils; + +public class ProjectListAdapter extends BaseListAdapter
{ + private OnProjectListItemClickListener listener; + + public ProjectListAdapter(OnProjectListItemClickListener listener) { + this.listener = listener; + } + + @Override + protected int getLayoutId(int viewType) { + return R.layout.item_project_list; + } + @Override + public void bindDatas(ListDataHolder holder, final Article bean, int itemType, final int position) { + ImageView iv_img = holder.getView(R.id.iv_img); + ImageView img_collect = holder.getView(R.id.img_collect); + TextView tv_title = holder.getView(R.id.tv_title); + TextView tv_desc = holder.getView(R.id.tv_desc); + TextView tv_time = holder.getView(R.id.tv_time); + TextView tv_name = holder.getView(R.id.tv_name); + + ImageLoaderManager.displayImage(bean.getEnvelopePic(),iv_img, Const.IMAGE_LOADER.NOMAL_IMG); + tv_title.setText(bean.getTitle()); + tv_desc.setText(bean.getDesc()); + tv_time.setText(DateUtils.parseTime(bean.getPublishTime())); + tv_name.setText(bean.getAuthor()); + img_collect.setImageResource(bean.isCollect() ? R.drawable.ic_favorite_light_24dp : R.drawable.ic_favorite_gray_24dp); + + img_collect.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null) + listener.onCollectClick(position,bean.getId()); + } + }); + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener!=null) + listener.onItemClick(position,bean); + } + }); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/TreeAdapter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/TreeAdapter.java new file mode 100644 index 0000000..ed6a689 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/TreeAdapter.java @@ -0,0 +1,49 @@ +package com.kdp.wanandroidclient.ui.adapter; + +import android.view.View; +import android.widget.TextView; + +import com.kdp.wanandroidclient.common.ListDataHolder; +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.bean.Tree; +import com.kdp.wanandroidclient.inter.OnItemClickListener; + +/** + * 知识体系 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class TreeAdapter extends BaseListAdapter { + private OnItemClickListener listener; + + public TreeAdapter(OnItemClickListener listener) { + this.listener = listener; + } + + @Override + protected int getLayoutId(int viewType) { + return R.layout.item_tree; + } + + @Override + public void bindDatas(ListDataHolder holder, final Tree bean, int itemType, final int position) { + + TextView tv_title = holder.getView(R.id.tv_title); + TextView tv_content = holder.getView(R.id.tv_content); + tv_title.setText(bean.getName()); + tv_content.setText(""); + for (Tree.ChildrenBean child : bean.getChildren()) { + tv_content.append(child.getName() + " "); + } + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener!=null) + listener.onItemClick(position,bean); + } + }); + + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/TreeFragPagerAdapter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/TreeFragPagerAdapter.java new file mode 100644 index 0000000..f987366 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/adapter/TreeFragPagerAdapter.java @@ -0,0 +1,40 @@ +package com.kdp.wanandroidclient.ui.adapter; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; + +import com.kdp.wanandroidclient.bean.Tree; +import com.kdp.wanandroidclient.ui.tree.TreeListFragment; + +import java.util.List; + +/** + * fragmetn适配器 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class TreeFragPagerAdapter extends FragmentPagerAdapter { + private List mTreeDatas; + + public TreeFragPagerAdapter(FragmentManager fm, List mTreeDatas) { + super(fm); + this.mTreeDatas = mTreeDatas; + } + + @Override + public Fragment getItem(int position) { + return TreeListFragment.instantiate(mTreeDatas.get(position).getId()); + } + + @Override + public int getCount() { + return mTreeDatas != null ? mTreeDatas.size() : 0; + } + + @Override + public CharSequence getPageTitle(int position) { + return mTreeDatas.get(position).getName(); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseAbListActivity.java b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseAbListActivity.java new file mode 100644 index 0000000..0d2a864 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseAbListActivity.java @@ -0,0 +1,285 @@ +package com.kdp.wanandroidclient.ui.base; + +import android.annotation.SuppressLint; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; +import com.kdp.wanandroidclient.ui.adapter.BaseListAdapter; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; +import com.kdp.wanandroidclient.ui.core.view.IPageLoadDataView; +import com.kdp.wanandroidclient.utils.ViewAnimatorHelper; +import com.kdp.wanandroidclient.widget.StatusLayout; +import com.kdp.wanandroidclient.widget.LMRecyclerView; +import com.kdp.wanandroidclient.widget.NoAlphaItemAnimator; + +import java.util.ArrayList; +import java.util.List; + +/** + * Activity 列表基类 + + */ + +public abstract class BaseAbListActivity

extends BasePresenterActivity

implements LMRecyclerView.OnFooterAutoLoadMoreListener, IPageLoadDataView { + + + protected StatusLayout mStatusLayout; + protected SwipeRefreshLayout mRefreshLayout; + protected LMRecyclerView mRecyclerView; + private FloatingActionButton btn_scroll_top; + protected BaseListAdapter mListAdapter; + private ViewAnimatorHelper viewAnimatorHelper; + protected int page; + protected int state = -1; + protected boolean isAutoLoadMore = true;//是否显示自动加载 + protected List mListData = new ArrayList<>(); + + @SuppressLint("RestrictedApi") + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + btn_scroll_top.setVisibility(View.VISIBLE); + btn_scroll_top.setOnClickListener(onScrollTopListener); + viewAnimatorHelper = new ViewAnimatorHelper(); + viewAnimatorHelper.bindView(btn_scroll_top); + mRefreshLayout.setOnRefreshListener(mOnRefreshListener); + mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + mRecyclerView.setItemAnimator(new NoAlphaItemAnimator()); + mRecyclerView.addOnScrollListener(onScrollListener); + setCanLoadMore(isCanLoadMore()); + mRecyclerView.addFooterAutoLoadMoreListener(this); + mListAdapter = getListAdapter(); + mStatusLayout.showLoding(); + if (mListAdapter != null) { + mRecyclerView.addHeaderView(initHeaderView()); + mRecyclerView.setAdapter(mListAdapter); + loadDatas(); + } + } + + /** + * 初始化控件 + */ + @Override + protected void initViews() { + mRefreshLayout = findViewById(R.id.refreshLayout); + mStatusLayout = findViewById(R.id.containerLayout); + mRecyclerView = findViewById(R.id.recyclerView); + btn_scroll_top = findViewById(R.id.btn_scroll_top); + } + + private RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + if (dy > 0 && btn_scroll_top.getVisibility() != View.INVISIBLE && !viewAnimatorHelper.isAnimating()){ + viewAnimatorHelper.hideFloatActionButton(); + }else if (dy < 0 && btn_scroll_top.getVisibility() != View.VISIBLE){ + viewAnimatorHelper.showFloatActionButton(); + } + } + }; + + private View.OnClickListener onScrollTopListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + mRecyclerView.smoothScrollToPosition(0); + } + }; + /** + * 加载Layout布局 + * @return + */ + @Override + protected int getLayoutId() { + return R.layout.include_recycler_list; + } + + @Override + public List getData() { + return mListData; + } + + + + /** + * 请求数据成功展示内容 + */ + @Override + public void showContent() { + mStatusLayout.showContent(); + mListAdapter.notifyAllDatas(mListData, mRecyclerView); + } + + /** + * 下拉刷新监听 + */ + private SwipeRefreshLayout.OnRefreshListener mOnRefreshListener = new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + refreshData(); + } + }; + + + /** + * 刷新数据,回到第一页 + */ + public void refreshData() { + state = Const.PAGE_STATE.STATE_REFRESH; + isAutoLoadMore = true; + page = getFirstPage(); + loadDatas(); + } + + /** + * 滑到底部开始加载更多数据 + */ + @Override + public void loadMore() { + if (!isAutoLoadMore) return; + state = Const.PAGE_STATE.STATE_LOAD_MORE; + loadDatas(); + } + + /** + * 底部加载更多失败,点击重新加载 + */ + @Override + public void reLoadMore() { + isAutoLoadMore = true; + loadMore(); + } + + + /** + * 清空当前列表数据 + */ + @Override + public void clearListData() { + mListData.clear(); + } + + /** + * 开始自动加载更多 + */ + @Override + public void autoLoadMore() { + mRecyclerView.showLoadMore(); + page++; + isAutoLoadMore = true; + } + + /** + * 显示没有更多数据了 + */ + @Override + public void showNoMore() { + mRecyclerView.showNoMoreData(); + isAutoLoadMore = false; + } + + + /** + * 数据加载异常处理 + */ + @Override + public void showError() { + isAutoLoadMore = false; + //如果是加载更多出现异常,那么底部就显示点击重新加载; + // 否则,就清空数据,显示没有数据 + if (state == Const.PAGE_STATE.STATE_LOAD_MORE) { + mRecyclerView.showLoadMoreError(); + mListAdapter.notifyAllDatas(mListData, mRecyclerView); + } else { + mStatusLayout.showError(); + } + + } + + /** + * 当前请求页 + * @return + */ + public int getPage() { + return page; + } + + /** + * 显示Loading + * @param msg + */ + @Override + public void showLoading(String msg) { + if (state == Const.PAGE_STATE.STATE_REFRESH) + setRefreshing(true); + } + + @Override + public void hideLoading() { + setRefreshing(false); + } + + /** + * 是否能加载更多 + * @param isCanLoadMore + */ + public void setCanLoadMore(boolean isCanLoadMore) { + mRecyclerView.setCanLoadMore(isCanLoadMore); + } + + /** + * 是否禁用刷新 + * @param isEnableRefresh + */ + public void setRefreshEnable(boolean isEnableRefresh) { + mRefreshLayout.setEnabled(isEnableRefresh); + } + + /** + * 没有数据时显示 + */ + @Override + public void showEmpty() { + mStatusLayout.showEmpty(); + } + + + protected void setRefreshing(final boolean isRefrshing) { + mRefreshLayout.postDelayed(new Runnable() { + @Override + public void run() { + mRefreshLayout.setRefreshing(isRefrshing); + } + }, 100); + } + + @Override + public int getFirstPage() { + return 0; + } + + /** + * 是否允许自动加载更多 + * @return + */ + protected boolean isCanLoadMore(){ + return false; + } + + protected View initHeaderView(){ + return null; + } + + protected abstract void loadDatas(); + + protected abstract BaseListAdaptergetListAdapter(); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseAbListFragment.java b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseAbListFragment.java new file mode 100644 index 0000000..8bc7c2a --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseAbListFragment.java @@ -0,0 +1,289 @@ +package com.kdp.wanandroidclient.ui.base; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.View; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; +import com.kdp.wanandroidclient.ui.adapter.BaseListAdapter; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; +import com.kdp.wanandroidclient.ui.core.view.IPageLoadDataView; +import com.kdp.wanandroidclient.utils.ToastUtils; +import com.kdp.wanandroidclient.widget.StatusLayout; +import com.kdp.wanandroidclient.widget.LMRecyclerView; + +import java.util.ArrayList; +import java.util.List; + +/** + * fragment列表基类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public abstract class BaseAbListFragment

extends BasePresenterFragment

implements LMRecyclerView.OnFooterAutoLoadMoreListener, IPageLoadDataView { + + protected StatusLayout mStatusLayout; + protected SwipeRefreshLayout mRefreshLayout; + protected LMRecyclerView mRecyclerView; + protected BaseListAdapter mListAdapter; + protected int page = getFirstPage(); + protected int state = -1; + protected boolean isAutoLoadMore = true;//是否开启自动加载 + private boolean isPreload; //是否已经预加载完成 + private boolean isVisible; //是否可见 + private boolean isFirst = true;//是否第一次加载数据 + private boolean isEnableLazy = false; //是否开启懒加载 + protected List mListData = new ArrayList<>(); + + + @Override + protected void initViews(View view) { + mRefreshLayout = view.findViewById(R.id.refreshLayout); + mStatusLayout = view.findViewById(R.id.containerLayout); + mRecyclerView = view.findViewById(R.id.recyclerView); + } + + + @Override + public List getData() { + return mListData; + } + + /** + * 请求数据成功展示内容 + */ + @Override + public void showContent() { + mStatusLayout.showContent(); + mListAdapter.notifyAllDatas(mListData, mRecyclerView); + } + + + @Override + protected int getLayoutId() { + return R.layout.include_recycler_list; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mRefreshLayout.setOnRefreshListener(mOnRefreshListener); + mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); + mRecyclerView.setCanLoadMore(isCanLoadMore()); + mRecyclerView.addFooterAutoLoadMoreListener(this); + mListAdapter = getListAdapter(); + if (mListAdapter != null) { + mRecyclerView.addHeaderView(initHeaderView()); + mRecyclerView.setAdapter(mListAdapter); + if (isEnableLazy) { + isPreload = true; + isFirst = true; + lazyLoad(); + } else { + mStatusLayout.showLoding(); + loadDatas(); + } + } + } + + + private void lazyLoad() { + if (!isPreload || !isVisible || !isFirst) { + return; + } + mStatusLayout.showLoding(); + loadDatas(); + isFirst = false; + } + + /** + * 懒加载处理 + * + * @param isVisibleToUser + */ + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + isEnableLazy = isEnableLazy();//默认不开启懒加载 + if (!isEnableLazy) return; + if (isVisibleToUser) { + isVisible = true; + lazyLoad(); + } else { + isVisible = false; + } + } + + /** + * 下拉刷新监听 + */ + private SwipeRefreshLayout.OnRefreshListener mOnRefreshListener = new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + refreshData(); + } + }; + + + /** + * 刷新数据,回到第一页 + */ + public void refreshData() { + state = Const.PAGE_STATE.STATE_REFRESH; + isAutoLoadMore = true; + page = getFirstPage(); + loadDatas(); + } + + /** + * 滑到底部开始加载更多数据 + */ + @Override + public void loadMore() { + if (!isAutoLoadMore) return; + state = Const.PAGE_STATE.STATE_LOAD_MORE; + loadDatas(); + } + + /** + * 底部加载更多失败,点击重新加载 + */ + @Override + public void reLoadMore() { + isAutoLoadMore = true; + loadMore(); + } + + + /** + * 清空当前列表数据 + */ + @Override + public void clearListData() { + mListData.clear(); + } + + /** + * 自动加载更多 + */ + @Override + public void autoLoadMore() { + mRecyclerView.showLoadMore(); + page++; + isAutoLoadMore = true; + } + + + /** + * 底部加载没有更多数据时显示 + */ + @Override + public void showNoMore() { + mRecyclerView.showNoMoreData(); + isAutoLoadMore = false; + } + + + /** + * 数据加载异常时显示 + */ + @Override + public void showError() { + isAutoLoadMore = false; + //如果是加载更多出现异常,那么底部就显示点击重新加载; + // 否则,就清空数据,显示没有数据 + if (state == Const.PAGE_STATE.STATE_LOAD_MORE) { + mRecyclerView.showLoadMoreError(); + mListAdapter.notifyAllDatas(mListData, mRecyclerView); + } else { + mStatusLayout.showError(); + } + + } + + /** + * 没有数据时显示 + */ + @Override + public void showEmpty() { + mStatusLayout.showEmpty(); + } + + + /** + * 当前请求页 + * + * @return + */ + public int getPage() { + return page; + } + + @Override + public void showLoading(String msg) { + if (state == Const.PAGE_STATE.STATE_REFRESH) + setRefreshing(true); + } + + @Override + public void hideLoading() { + setRefreshing(false); + } + + + @Override + public void showFail(String msg) { + ToastUtils.showToast(getActivity(), msg); + } + + protected void setRefreshing(final boolean isRefrshing) { + mRefreshLayout.postDelayed(new Runnable() { + @Override + public void run() { + mRefreshLayout.setRefreshing(isRefrshing); + } + }, 100); + + } + + @Override + public int getFirstPage() { + return 0; + } + + /** + * 是否开启懒加载 + * @return + */ + protected boolean isEnableLazy() { + return false; + } + /** + * 是否允许自动加载更多 + * @return + */ + protected boolean isCanLoadMore(){ + return false; + } + @Override + protected void getBundle(Bundle bundle) { + } + + protected View initHeaderView(){ + return null; + } + + protected abstract void loadDatas(); + + protected abstract BaseListAdapter getListAdapter(); + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseActivity.java b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseActivity.java new file mode 100644 index 0000000..081527f --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseActivity.java @@ -0,0 +1,160 @@ +package com.kdp.wanandroidclient.ui.base; +import android.app.ProgressDialog; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.event.RxEvent; +import io.reactivex.observers.DisposableObserver; +import io.reactivex.subjects.PublishSubject; + +/** + * Activity 基类 + */ +public abstract class BaseActivity extends AppCompatActivity { + protected Toolbar mToolbar; + protected FrameLayout mContainerLayout; + private ProgressDialog loadingDialog = null; + private PublishSubject mSubject; + private DisposableObserver mDisposableObserver; + + @Override + protected void onCreate(Bundle bundle) { + if (bundle != null) { + //如果系统回收Activity,但是系统却保留了Fragment,当Activity被重新初始化,此时,系统保存的Fragment 的getActivity为空, + //所以要移除旧的Fragment,重新初始化新的Fragment + String FRAGMENTS_TAG = "android:support:fragments"; + bundle.remove(FRAGMENTS_TAG); + } + super.onCreate(bundle); + + setContentView(R.layout.activity_base); + Intent intent = getIntent(); + if (intent != null) + getIntent(intent); + mToolbar = findViewById(R.id.toolbar); + mContainerLayout = findViewById(R.id.frameLayout); + + //初始化ToolBar + boolean isToolbar = initToolbar(); + if (isToolbar) { + setSupportActionBar(mToolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + mToolbar.setNavigationOnClickListener(new View.OnClickListener() { + //必须要在setSupportActionBar之后,不然不起作用 + @Override + public void onClick(View v) { + onNavigationClick(); + } + }); + } else { + mToolbar.setVisibility(View.GONE); + } + //初始化Content + initContent(getLayoutId()); + //注册事件线 + mSubject = RxEvent.getInstance().registerEvent(registerEvent()); + mDisposableObserver = new ReceiveEvent(); + mSubject.subscribe(mDisposableObserver); + } + + + + + + private class ReceiveEvent extends DisposableObserver{ + @Override + public void onNext(Object o) { + receiveEvent(o); + } + + @Override + public void onError(Throwable e) { + } + + @Override + public void onComplete() { + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + //注销事件 + RxEvent.getInstance().unRegisterEvent(registerEvent(), mSubject, mDisposableObserver); + } + + private void initContent(int layoutId) { + if (layoutId != 0) { + View contentView = LayoutInflater.from(this).inflate(layoutId, mContainerLayout, false); + mContainerLayout.addView(contentView); + initViews(); + } + } + + /** + * 显示带消息的进度框 + * + * @param title 提示 + */ + protected void showLoadingDialog(String title) { + createLoadingDialog(); + loadingDialog.setMessage(title); + if (!loadingDialog.isShowing()) + loadingDialog.show(); + } + + /** + * 显示进度框 + */ + protected void showLoadingDialog() { + createLoadingDialog(); + if (!loadingDialog.isShowing()) + loadingDialog.show(); + } + + /** + * 创建LodingDialog + */ + private void createLoadingDialog() { + if (loadingDialog == null) { + loadingDialog = new ProgressDialog(this); + loadingDialog.setCancelable(true); + loadingDialog.setCanceledOnTouchOutside(false); + } + } + + /** + * 隐藏进度框 + */ + protected void hideLoadingDialog() { + if (loadingDialog != null && loadingDialog.isShowing()) { + loadingDialog.dismiss(); + } + } + + protected void receiveEvent(Object object){ } + + protected String registerEvent(){ + return null; + } + + protected void onNavigationClick() { + finish(); + } + + protected abstract int getLayoutId(); + + protected boolean initToolbar(){ + return false; + } + + protected void getIntent(Intent intent){ } + + protected abstract void initViews(); + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseFragment.java b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseFragment.java new file mode 100644 index 0000000..1e8cb0b --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseFragment.java @@ -0,0 +1,83 @@ +package com.kdp.wanandroidclient.ui.base; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.kdp.wanandroidclient.event.RxEvent; + +import io.reactivex.observers.DisposableObserver; +import io.reactivex.subjects.PublishSubject; + +/** + * author: 曾文海 + * date: 2023/5/31 + */ + +public abstract class BaseFragment extends Fragment { + private PublishSubject mSubject; + private DisposableObserver mDisposableObserver; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Bundle bundle = getArguments(); + if (bundle != null) + getBundle(bundle); + mDisposableObserver = new ReceiveEvent(); + //注册事件 + mSubject = RxEvent.getInstance().registerEvent(registerEvent()); + mSubject.subscribe(mDisposableObserver); + } + + private class ReceiveEvent extends DisposableObserver { + @Override + public void onNext(Object o) { + receiveEvent(o); + } + + @Override + public void onError(Throwable e) { + } + + @Override + public void onComplete() { + } + } + + + @Override + public void onDestroy() { + super.onDestroy(); + //注销事件 + RxEvent.getInstance().unRegisterEvent(registerEvent(), mSubject, mDisposableObserver); + } + + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = null; + int layoutId = getLayoutId(); + if (layoutId != 0) { + view = inflater.inflate(getLayoutId(), container, false); + initViews(view); + } + return view; + } + + protected abstract void initViews(View view); + + protected abstract int getLayoutId(); + + protected void receiveEvent(Object object){} + + protected String registerEvent(){ return null; } + + protected void getBundle(Bundle bundle){} + + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/base/BasePresenterActivity.java b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BasePresenterActivity.java new file mode 100644 index 0000000..b49f002 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BasePresenterActivity.java @@ -0,0 +1,104 @@ +package com.kdp.wanandroidclient.ui.base; + +import android.content.Intent; +import android.os.Bundle; + +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; +import com.kdp.wanandroidclient.ui.core.view.IView; +import com.kdp.wanandroidclient.utils.ToastUtils; + +/** + * 管理Presenter的Activity基类 + + */ + +public abstract class BasePresenterActivity

extends BaseActivity implements IView { + + protected P mPresenter; + + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + mPresenter = createPresenter(); + attachView(); + + } + + @Override + protected void onNavigationClick() { + finish(); + } + + @Override + protected void getIntent(Intent intent) { + } + + protected abstract P createPresenter(); + + @Override + protected void onDestroy() { + //接触presenter与View关联 + detachView(); + //移除所有请求 + removeAllDisposable(); + super.onDestroy(); + } + + //关联View + private void attachView() { + if (mPresenter != null) { + mPresenter.attachView(this); + } + } + + private void detachView() { + if (mPresenter != null) { + mPresenter.detachView(); + } + } + + protected void removeAllDisposable() { + if (mPresenter != null) { + mPresenter.removeAllDisposable(); + } + } + + @Override + protected boolean initToolbar() { + return false; + } + + @Override + protected void initViews() { + } + + @Override + public void showLoading(String msg) { + } + + @Override + public void hideLoading() { + hideLoadingDialog(); + } + + @Override + public void showFail(String msg) { + ToastUtils.showToast(this, msg); + } + @Override + public void showError() { + } + @Override + public void showEmpty() { + } + + + @Override + protected void receiveEvent(Object object) { + } + + @Override + protected String registerEvent() { + return null; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/base/BasePresenterFragment.java b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BasePresenterFragment.java new file mode 100644 index 0000000..53ac23c --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BasePresenterFragment.java @@ -0,0 +1,79 @@ +package com.kdp.wanandroidclient.ui.base; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.View; + +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; +import com.kdp.wanandroidclient.ui.core.view.IView; + +/** + * author: 曾文海 + * date: 2023/5/31 + */ + +public abstract class BasePresenterFragment

extends BaseFragment implements IView{ + + protected P mPresenter; + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mPresenter = createPresenter(); + //关联View + attachView(); + } + + + + @Override + public void onDestroy() { + super.onDestroy(); + //解除关联 + detachView(); + } + + @Override + protected void initViews(View view) { + } + + @Override + protected int getLayoutId() { + return 0; + } + + private void detachView() { + if (mPresenter != null) { + mPresenter.detachView(); + mPresenter.removeAllDisposable(); + mPresenter = null; + } + } + + private void attachView() { + if (mPresenter != null) { + mPresenter.attachView(this); + } + } + + protected abstract P createPresenter(); + + @Override + public void showLoading(String msg) { + } + + @Override + public void hideLoading() { + } + + @Override + public void showFail(String msg) { + } + + @Override + public void showError() { + } + + @Override + public void showEmpty() { + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseTabActivity.java b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseTabActivity.java new file mode 100644 index 0000000..a1d6ba8 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseTabActivity.java @@ -0,0 +1,54 @@ +package com.kdp.wanandroidclient.ui.base; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.TabLayout; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.view.View; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; + +/*** + * @author kdp + * @date 2023 + * @description + */ +public abstract class BaseTabActivity extends BaseActivity{ + + private TabLayout tabLayout; + protected ViewPager viewPager; + protected FloatingActionButton btn_scroll_top; + + + @Override + protected int getLayoutId() { + return R.layout.base_tab_layout; + } + + @Override + protected void initViews() { + tabLayout = findViewById(R.id.tabLayout); + viewPager = findViewById(R.id.viewPager); + btn_scroll_top = findViewById(R.id.btn_scroll_top); + } + + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + FragmentPagerAdapter fragPagerAdapter = createFragPagerAdapter(); + if (fragPagerAdapter != null){ + viewPager.setAdapter(fragPagerAdapter); + tabLayout.setupWithViewPager(viewPager); + } + } + + + protected abstract FragmentPagerAdapter createFragPagerAdapter(); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseTabFragment.java b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseTabFragment.java new file mode 100644 index 0000000..deb6787 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseTabFragment.java @@ -0,0 +1,26 @@ +package com.kdp.wanandroidclient.ui.base; +import android.support.design.widget.TabLayout; +import android.support.v4.view.ViewPager; +import android.view.View; +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; + +/*** + * @author kdp + * @date 2023 + * @description + */ +public abstract class BaseTabFragment

extends BasePresenterFragment

{ + protected TabLayout tabLayout; + protected ViewPager viewPager; + @Override + protected int getLayoutId() { + return R.layout.base_tab_layout; + } + + @Override + protected void initViews(View view) { + tabLayout = view.findViewById(R.id.tabLayout); + viewPager = view.findViewById(R.id.viewPager); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterContract.java b/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterContract.java new file mode 100644 index 0000000..2aa09e4 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterContract.java @@ -0,0 +1,20 @@ +package com.kdp.wanandroidclient.ui.chapter; + +import com.kdp.wanandroidclient.bean.Chapter; +import com.kdp.wanandroidclient.ui.core.view.IListDataView; + +/*** + * @author kdp + * @date 2023 + * @description + */ +public interface ChapterContract { + + interface IChaptersPresenter { + void getChapters(); + } + + interface IChaptersView extends IListDataView{ + + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterListContract.java b/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterListContract.java new file mode 100644 index 0000000..977c6e0 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterListContract.java @@ -0,0 +1,23 @@ +package com.kdp.wanandroidclient.ui.chapter; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.ui.core.view.IPageLoadDataView; + +/*** + * @author kdp + * @date 2023 + * @description + */ +public interface ChapterListContract { + + interface IChapterListPresenter{ + void getChapterList(); + void collectArticle(); + void unCollectArticle(); + } + + interface IChapterListView extends IPageLoadDataView

{ + int getCid(); + int getArticleId(); + void collect(boolean isCollect,String result); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterListFragment.java b/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterListFragment.java new file mode 100644 index 0000000..4748c4e --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterListFragment.java @@ -0,0 +1,168 @@ +package com.kdp.wanandroidclient.ui.chapter; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.widget.RecyclerView; +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; +import com.kdp.wanandroidclient.inter.OnArticleListItemClickListener; +import com.kdp.wanandroidclient.manager.UserInfoManager; +import com.kdp.wanandroidclient.ui.adapter.ArticleListAdapter; +import com.kdp.wanandroidclient.ui.adapter.BaseListAdapter; +import com.kdp.wanandroidclient.ui.base.BaseAbListFragment; +import com.kdp.wanandroidclient.ui.web.WebViewActivity; +import com.kdp.wanandroidclient.utils.IntentUtils; +import com.kdp.wanandroidclient.utils.ToastUtils; + +import java.util.List; + +/*** + * @author kdp + * @date 2023 + * @description + */ +public class ChapterListFragment extends BaseAbListFragment implements ChapterListContract.IChapterListView,OnArticleListItemClickListener { + + private int cid;//公众号id + private int id;//文章id + private int position; + + public static ChapterListFragment instantiate(int cid){ + ChapterListFragment instance = new ChapterListFragment(); + Bundle bundle = new Bundle(); + bundle.putInt(Const.BUNDLE_KEY.ID,cid); + instance.setArguments(bundle); + return instance; + } + + @Override + protected boolean isCanLoadMore() { + return true; + } + + @Override + protected boolean isEnableLazy() { + return true; + } + + @Override + protected void getBundle(Bundle bundle) { + if (bundle !=null){ + cid = bundle.getInt(Const.BUNDLE_KEY.ID,0); + } + } + + @Override + protected void loadDatas() { + mPresenter.getChapterList(); + } + + @Override + protected BaseListAdapter
getListAdapter() { + return new ArticleListAdapter(this,Const.LIST_TYPE.CHAPTER); + } + + @Override + protected ChapterListPresenter createPresenter() { + return new ChapterListPresenter(); + } + + @Override + public void setData(List
data) { + mListData.addAll(data); + } + + @Override + public int getFirstPage() { + return 1; + } + + @Override + public int getCid() { + return cid; + } + + @Override + public int getArticleId() { + return id; + } + + @Override + public void collect(boolean isCollect, String result) { + notifyItemData(isCollect,result); + } + + private void notifyItemData(boolean isCollect, String result) { + mListData.get(position).setCollect(isCollect); + mListAdapter.notifyItemDataChanged(position, mRecyclerView); + ToastUtils.showToast(getActivity(), result); + } + + + @Override + public void onDeleteCollectClick(int position, int id, int originId) { + } + + @Override + public void onCollectClick(int position, int id) { + if (!UserInfoManager.isLogin()) + IntentUtils.goLogin(getActivity()); + this.id = id; + this.position = position; + if (mListData.get(this.position).isCollect()) + mPresenter.unCollectArticle(); + else + mPresenter.collectArticle(); + } + + @Override + public void onItemClick(int position, Article bean) { + Intent intent = new Intent(getActivity(), WebViewActivity.class); + Bundle bundle = new Bundle(); + bundle.putSerializable(Const.BUNDLE_KEY.OBJ, bean); + bundle.putString(Const.BUNDLE_KEY.TYPE, Const.EVENT_ACTION.CHAPTER_LIST); + intent.putExtras(bundle); + startActivity(intent); + } + + @Override + protected void receiveEvent(Object object) { + Event mEvent = (Event) object; + if (mEvent.type == Event.Type.REFRESH_ITEM) { + Article bean = (Article) mEvent.object; + for (int i = 0; i < mListData.size(); i++) { + if (bean.equals(mListData.get(i))) { + position = i; + notifyItemData(bean.isCollect(), getString(R.string.collect_success)); + } + } + }else if (mEvent.type == Event.Type.SCROLL_TOP && (int)mEvent.object == cid){ + mRecyclerView.smoothScrollToPosition(0); + } + else if (mEvent.type == Event.Type.REFRESH_LIST){ + refreshData(); + } + } + + @Override + protected String registerEvent() { + return Const.EVENT_ACTION.CHAPTER_LIST; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mRecyclerView.addOnScrollListener(onScrollListener); + } + + private RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + RxEvent.getInstance().postEvent(Const.EVENT_ACTION.MAIN,new Event(Event.Type.SCALE,dy)); + } + }; +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterListPresenter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterListPresenter.java new file mode 100644 index 0000000..aec8a57 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterListPresenter.java @@ -0,0 +1,88 @@ +package com.kdp.wanandroidclient.ui.chapter; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; +import com.kdp.wanandroidclient.ui.core.model.impl.ChapterListModel; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; + +import java.util.List; + +/*** + * @author kdp + * @date 2023 + * @description + */ +public class ChapterListPresenter extends BasePresenter implements ChapterListContract.IChapterListPresenter{ + private ChapterListModel chapterArticleModel; + private ChapterListContract.IChapterListView chapterArticleView; + + public ChapterListPresenter() { + this.chapterArticleModel = new ChapterListModel(); + } + + @Override + public void getChapterList() { + chapterArticleView = getView(); + RxPageListObserver
rxPageListObserver = new RxPageListObserver
(this) { + @Override + public void onSuccess(List
mData) { + chapterArticleView.setData(mData); + if (chapterArticleView.getData().size() == 0){ + chapterArticleView.showEmpty(); + }else { + chapterArticleView.showContent(); + } + } + @Override + public void onFail(int errorCode, String errorMsg) { + chapterArticleView.showFail(errorMsg); + } + }; + chapterArticleModel.getChapterArticleList(chapterArticleView.getPage(),chapterArticleView.getCid(),rxPageListObserver); + addDisposable(rxPageListObserver); + } + + @Override + public void collectArticle() { + RxObserver mCollectRxObserver = new RxObserver(this) { + @Override + protected void onStart() { + } + @Override + protected void onSuccess(String data) { + chapterArticleView.collect(true, AppContext.getContext().getString(R.string.collect_success)); + } + @Override + protected void onFail(int errorCode, String errorMsg) { + view.showFail(errorMsg); + } + + }; + chapterArticleModel.collectArticle(chapterArticleView.getArticleId(), mCollectRxObserver); + addDisposable(mCollectRxObserver); + } + + @Override + public void unCollectArticle() { + RxObserver unCollectRxObserver = new RxObserver(this) { + + @Override + protected void onStart() { + } + @Override + protected void onSuccess(String data) { + chapterArticleView.collect(false, AppContext.getContext().getString(R.string.uncollect_success)); + } + + @Override + protected void onFail(int errorCode, String errorMsg) { + view.showFail(errorMsg); + } + }; + chapterArticleModel.unCollectArticle(chapterArticleView.getArticleId(), unCollectRxObserver); + addDisposable(unCollectRxObserver); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChaptersFragment.java b/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChaptersFragment.java new file mode 100644 index 0000000..5545758 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChaptersFragment.java @@ -0,0 +1,65 @@ +package com.kdp.wanandroidclient.ui.chapter; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.kdp.wanandroidclient.bean.Chapter; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; +import com.kdp.wanandroidclient.ui.adapter.ChaptersFragPagerAdapter; +import com.kdp.wanandroidclient.ui.base.BaseTabFragment; +import com.kdp.wanandroidclient.ui.main.MainActivity; + +import java.util.ArrayList; +import java.util.List; + +/*** + * @author kdp + * @date 2023 + * @description + */ +public class ChaptersFragment extends BaseTabFragment implements ChapterContract.IChaptersView{ + private List chapterList = new ArrayList<>(); + @Override + protected ChaptersPresenter createPresenter() { + return new ChaptersPresenter(); + } + + @Override + public void setData(List data) { + chapterList.clear(); + chapterList.addAll(data); + } + + @Override + public List getData() { + return chapterList; + } + + @Override + public void showContent() { + ChaptersFragPagerAdapter adapter = new ChaptersFragPagerAdapter(getChildFragmentManager(),chapterList); + viewPager.setAdapter(adapter); + viewPager.setOffscreenPageLimit(chapterList.size()); + tabLayout.setupWithViewPager(viewPager); + } + + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mPresenter.getChapters(); + } + + public void scrollToTop(){ + int id = chapterList.get(viewPager.getCurrentItem()).getId(); + RxEvent.getInstance().postEvent(Const.EVENT_ACTION.CHAPTER_LIST,new Event(Event.Type.SCROLL_TOP,id)); + } + + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChaptersPresenter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChaptersPresenter.java new file mode 100644 index 0000000..e0c991a --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChaptersPresenter.java @@ -0,0 +1,44 @@ +package com.kdp.wanandroidclient.ui.chapter; + +import com.kdp.wanandroidclient.bean.Chapter; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.ui.core.model.impl.ChapterModel; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; + +import java.util.List; + +/*** + * @author kdp + * @date 2023 + * @description + */ +public class ChaptersPresenter extends BasePresenter implements ChapterContract.IChaptersPresenter{ + private ChapterModel chapterModel; + private ChapterContract.IChaptersView chaptersView; + + ChaptersPresenter() { + this.chapterModel = new ChapterModel(); + } + + @Override + public void getChapters() { + chaptersView = getView(); + RxObserver> rxObserver = new RxObserver>(this) { + @Override + protected void onSuccess(List data) { + chaptersView.setData(data); + if (chaptersView.getData().size() == 0) { + chaptersView.showEmpty(); + }else { + chaptersView.showContent(); + } + } + @Override + protected void onFail(int errorCode, String errorMsg) { + chaptersView.showFail(errorMsg); + } + }; + chapterModel.getChapters(rxObserver); + addDisposable(rxObserver); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IChapterListModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IChapterListModel.java new file mode 100644 index 0000000..b164d6d --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IChapterListModel.java @@ -0,0 +1,20 @@ +package com.kdp.wanandroidclient.ui.core.model; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; + +/*** + * @author kdp + * @date 2023 + * @description + */ +public interface IChapterListModel { + + /** + * 获取公众号文章列表 + * @param page 页码 + * @param cid 公众号cid + * @param rxPageListObserver + */ + void getChapterArticleList(int page, int cid, RxPageListObserver
rxPageListObserver); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IChapterModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IChapterModel.java new file mode 100644 index 0000000..5057a90 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IChapterModel.java @@ -0,0 +1,20 @@ +package com.kdp.wanandroidclient.ui.core.model; + +import com.kdp.wanandroidclient.bean.Chapter; +import com.kdp.wanandroidclient.net.callback.RxObserver; + +import java.util.List; + +/*** + * @author kdp + * @date 2019/3/25 16:34 + * @description + */ +public interface IChapterModel { + + /** + * 获取公众号 + * @param rxObserver + */ + void getChapters(RxObserver> rxObserver); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ICommonModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ICommonModel.java new file mode 100644 index 0000000..560bfaf --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ICommonModel.java @@ -0,0 +1,30 @@ +package com.kdp.wanandroidclient.ui.core.model; + +import com.kdp.wanandroidclient.net.callback.RxObserver; + +/** + * 通用业务接口 + * author: 曾文海 + * date: 2023/5/31 + */ + +public interface ICommonModel { + + /** + * 收藏文章 + * + * @param id 文章id + * @param callback + */ + void collectArticle(int id, RxObserver callback); + + + /** + * 取消收藏文章 + * + * @param id 文章id + * @param callback + */ + void unCollectArticle(int id, RxObserver callback); + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IHomeModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IHomeModel.java new file mode 100644 index 0000000..badad54 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IHomeModel.java @@ -0,0 +1,41 @@ +package com.kdp.wanandroidclient.ui.core.model; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.Banner; +import com.kdp.wanandroidclient.bean.BaseBean; +import com.kdp.wanandroidclient.bean.HomeData; +import com.kdp.wanandroidclient.bean.PageListData; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; +import java.util.List; +import io.reactivex.Observable; +import io.reactivex.functions.Function3; +import io.reactivex.observers.DisposableObserver; + +/** + * 首页业务接口 + * author: 曾文海 + * date: 2023/5/31 + */ + +public interface IHomeModel { + /** + * 获取首页banner、置顶文章、列表文章 + * @param page 页码 + * @param function3 + * @param rxObserver + */ + void getHomeData(int page, Function3>, BaseBean>, BaseBean>, HomeData> function3, DisposableObserver rxObserver); + + /** + * 获取更多文章 + * @param page 页码 + * @param rxPageListObserver + */ + void getMoreArticleList(int page,RxPageListObserver
rxPageListObserver); + + + Observable>> getBannerObservable(); + Observable>> getHomeTopObservable(); + Observable>> getHomeListObservable(int page); + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ILogonModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ILogonModel.java new file mode 100644 index 0000000..c9c1676 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ILogonModel.java @@ -0,0 +1,44 @@ +package com.kdp.wanandroidclient.ui.core.model; + +import com.kdp.wanandroidclient.bean.User; +import com.kdp.wanandroidclient.inter.VerifyAccountCallback; +import com.kdp.wanandroidclient.net.callback.RxObserver; + +/** + * 登录注册业务接口 + + */ + +public interface ILogonModel { + /** + * 登录 + * + * @param username 用户名 + * @param password 密码 + */ + void login(String username, String password,RxObserver callback); + + + /** + * 注册 + * + * @param username 用户名 + * @param password 密码 + */ + void register(String username, String password,RxObserver callback); + + + /** + * 保存用户信息 + * @param user 用户 + */ + void saveUserInfo(User user); + + /** + * 账号密码判空 + * @param username 用户名 + * @param password 密码 + * @return + */ + boolean verifyAccount(String username, String password, VerifyAccountCallback callback); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IModel.java new file mode 100644 index 0000000..9b74017 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IModel.java @@ -0,0 +1,14 @@ +package com.kdp.wanandroidclient.ui.core.model; + +import com.kdp.wanandroidclient.api.ApiServer; + + +public interface IModel { + /** + * 使用RxRetrofit请求数据 + * + * @return + */ + ApiServer doRxRequest(); + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IProjectCateModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IProjectCateModel.java new file mode 100644 index 0000000..af8db94 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IProjectCateModel.java @@ -0,0 +1,20 @@ +package com.kdp.wanandroidclient.ui.core.model; + +import com.kdp.wanandroidclient.bean.ProjectCate; +import com.kdp.wanandroidclient.net.callback.RxObserver; + +import java.util.List; + +/*** + * @author kdp + * @date 2019/3/20 16:52 + * @description + */ +public interface IProjectCateModel { + + /** + * 获取项目分类 + * @param rxObserver + */ + void getProjectCate(RxObserver> rxObserver); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IProjectModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IProjectModel.java new file mode 100644 index 0000000..d431c2c --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IProjectModel.java @@ -0,0 +1,15 @@ +package com.kdp.wanandroidclient.ui.core.model; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; + +public interface IProjectModel { + + /** + * 获取项目列表 + * @param page 页码 + * @param cid 项目分类id + * @param rxPageListObserver + */ + void getProjectList(int page, int cid, RxPageListObserver
rxPageListObserver); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ISearchModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ISearchModel.java new file mode 100644 index 0000000..24ce72a --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ISearchModel.java @@ -0,0 +1,41 @@ +package com.kdp.wanandroidclient.ui.core.model; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.Friend; +import com.kdp.wanandroidclient.bean.Hotword; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; + +import java.util.List; + +/** + * 和搜索相关的业务接口 + * author: 曾文海 + * date: 2023/5/31 + */ + +public interface ISearchModel { + + /** + * 搜索文章 + * + * @param page 页码 + * @param keyword 关键词 + * @param rxPageListObserver + */ + void searchArticle(int page, String keyword, RxPageListObserver
rxPageListObserver); + + /** + * 搜索热词 + * + * @param rxObserver + */ + void getHotWord(RxObserver> rxObserver); + + /** + * 常用网站 + * + * @param rxObserver + */ + void getFriend(RxObserver> rxObserver); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ITreeListModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ITreeListModel.java new file mode 100644 index 0000000..9703ece --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ITreeListModel.java @@ -0,0 +1,24 @@ +package com.kdp.wanandroidclient.ui.core.model; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; + +/** + * 知识体系列表业务接口 + * author: 曾文海 + * date: 2023/5/31 + */ + +public interface ITreeListModel { + + + /** + * 获取知识体系文章列表 + * + * @param page 页码 + * @param cid 知识体系分类id + * @param rxObserver + */ + void getTreeList(int page, int cid, RxPageListObserver
rxObserver); + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ITreeModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ITreeModel.java new file mode 100644 index 0000000..c0040eb --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ITreeModel.java @@ -0,0 +1,21 @@ +package com.kdp.wanandroidclient.ui.core.model; + +import com.kdp.wanandroidclient.bean.Tree; +import com.kdp.wanandroidclient.net.callback.RxObserver; + +import java.util.List; + +/** + * 知识体系业务接口 + * author: 曾文海 + * date: 2023/5/31 + */ + +public interface ITreeModel { + /** + * 获取知识体系分类 + * + * @param callback + */ + void getTree(RxObserver> callback); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IUserModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IUserModel.java new file mode 100644 index 0000000..739f181 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IUserModel.java @@ -0,0 +1,30 @@ +package com.kdp.wanandroidclient.ui.core.model; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; + +/** + * 和用户相关的业务接口 + * author: 曾文海 + * date: 2023/5/31 + */ + +public interface IUserModel { + /** + * 收藏的文章列表 + * @param page 页码 + * @param rxObserver + */ + void getCollectArticleList(int page, RxPageListObserver
rxObserver); + + + /** + * 删除收藏 + * + * @param id 收藏列表的文章id + * @param originId 首页列表的文章id + * @param callback + */ + void deleteCollectArticle(int id, int originId, RxObserver callback); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/BaseModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/BaseModel.java new file mode 100644 index 0000000..5aaecb6 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/BaseModel.java @@ -0,0 +1,14 @@ +package com.kdp.wanandroidclient.ui.core.model.impl; + +import com.kdp.wanandroidclient.api.ApiServer; +import com.kdp.wanandroidclient.net.RxRetrofit; +import com.kdp.wanandroidclient.ui.core.model.IModel; + + +public class BaseModel implements IModel { + + @Override + public ApiServer doRxRequest() { + return RxRetrofit.Api(); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ChapterListModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ChapterListModel.java new file mode 100644 index 0000000..066fddf --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ChapterListModel.java @@ -0,0 +1,28 @@ +package com.kdp.wanandroidclient.ui.core.model.impl; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.PageListData; +import com.kdp.wanandroidclient.net.RxSchedulers; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; +import com.kdp.wanandroidclient.ui.core.model.IChapterListModel; + +/*** + * @author kdp + * @date 2019/3/27 9:28 + * @description + */ +public class ChapterListModel extends CommonModel implements IChapterListModel { + /** + * 获取公众号文章列表 + * @param page 页码 + * @param cid 公众号cid + * @param rxPageListObserver + */ + @Override + public void getChapterArticleList(int page, int cid, RxPageListObserver
rxPageListObserver) { + doRxRequest() + .getChapterList(page,cid) + .compose(RxSchedulers.>io_main()) + .subscribe(rxPageListObserver); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ChapterModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ChapterModel.java new file mode 100644 index 0000000..3feb653 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ChapterModel.java @@ -0,0 +1,26 @@ +package com.kdp.wanandroidclient.ui.core.model.impl; +import com.kdp.wanandroidclient.bean.Chapter; +import com.kdp.wanandroidclient.net.RxSchedulers; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.ui.core.model.IChapterModel; + +import java.util.List; + +/*** + * @author kdp + * @date 2019/3/25 16:35 + * @description + */ +public class ChapterModel extends BaseModel implements IChapterModel{ + /** + * 获取公众号 + * @param rxObserver + */ + @Override + public void getChapters(RxObserver> rxObserver) { + doRxRequest() + .getChapters() + .compose(RxSchedulers.>io_main()) + .subscribe(rxObserver); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/CommonModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/CommonModel.java new file mode 100644 index 0000000..60cd20c --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/CommonModel.java @@ -0,0 +1,41 @@ +package com.kdp.wanandroidclient.ui.core.model.impl; + +import com.kdp.wanandroidclient.ui.core.model.ICommonModel; +import com.kdp.wanandroidclient.net.RxSchedulers; +import com.kdp.wanandroidclient.net.callback.RxObserver; + +/** + * author: 康栋普 + * date: 2018/2/26 + */ + +public class CommonModel extends BaseModel implements ICommonModel { + /** + * 收藏 + * @param id 文章id + * @param callback + */ + @Override + public void collectArticle(int id, RxObserver callback) { + doRxRequest(). + collectArticle(id) + .compose(RxSchedulers.io_main()) + .subscribe(callback); + + } + + /** + * 取消收藏 + * @param id 文章id + * @param callback + */ + @Override + public void unCollectArticle(int id, RxObserver callback) { + doRxRequest() + .unCollectArticle(id) + .compose(RxSchedulers.io_main()) + .subscribe(callback); + } + + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/HomeModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/HomeModel.java new file mode 100644 index 0000000..e751afb --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/HomeModel.java @@ -0,0 +1,66 @@ +package com.kdp.wanandroidclient.ui.core.model.impl; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.Banner; +import com.kdp.wanandroidclient.bean.BaseBean; +import com.kdp.wanandroidclient.bean.HomeData; +import com.kdp.wanandroidclient.bean.PageListData; +import com.kdp.wanandroidclient.net.RxSchedulers; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; +import com.kdp.wanandroidclient.ui.core.model.IHomeModel; +import java.util.List; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.functions.Function3; +import io.reactivex.observers.DisposableObserver; +import io.reactivex.schedulers.Schedulers; + +/** + * author: 康栋普 + * date: 2018/2/22 + */ + +public class HomeModel extends CommonModel implements IHomeModel { + + /** + * 获取首页banner、置顶文章、列表文章 + * @param page + * @param function3 + * @param rxObserver + */ + @Override + public void getHomeData(int page, Function3>, BaseBean>, BaseBean>, HomeData> function3, DisposableObserver rxObserver) { + Observable>> bannerObservable = getBannerObservable(); + Observable>> homeTopObservable = getHomeTopObservable(); + Observable>> homeObservable = getHomeListObservable(page); + Observable.zip(bannerObservable, homeTopObservable, homeObservable, function3) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(rxObserver); + } + /** + * 获取列表文章 + * @param page + * @param rxPageListObserver + */ + @Override + public void getMoreArticleList(int page, RxPageListObserver
rxPageListObserver) { + doRxRequest().getHomeList(page) + .compose(RxSchedulers.>io_main()) + .subscribe(rxPageListObserver); + } + + @Override + public Observable>> getBannerObservable() { + return doRxRequest().getBanner().subscribeOn(Schedulers.newThread()); + } + + @Override + public Observable>> getHomeTopObservable() { + return doRxRequest().getHomeTopList().subscribeOn(Schedulers.newThread()); + } + + @Override + public Observable>> getHomeListObservable(int page) { + return doRxRequest().getHomeList(page); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/LogonModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/LogonModel.java new file mode 100644 index 0000000..d7268aa --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/LogonModel.java @@ -0,0 +1,80 @@ +package com.kdp.wanandroidclient.ui.core.model.impl; + +import android.text.TextUtils; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.bean.User; +import com.kdp.wanandroidclient.inter.VerifyAccountCallback; +import com.kdp.wanandroidclient.manager.UserInfoManager; +import com.kdp.wanandroidclient.net.RxSchedulers; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.ui.core.model.ILogonModel; + +/** + * Created by 康栋普 on 2018/2/1. + */ + +public class LogonModel extends BaseModel implements ILogonModel { + + /** + * 登录 + * @param username 用户名 + * @param password 密码 + * @param callback + */ + @Override + public void login(String username, String password, RxObserver callback) { + doRxRequest() + .login(username, password) + .compose(RxSchedulers.io_main()) + .subscribe(callback); + + + } + + /** + * 注册 + * @param username 用户名 + * @param password 密码 + * @param callback + */ + @Override + public void register(final String username, final String password, RxObserver callback) { + doRxRequest() + .register(username, password, password) + .compose(RxSchedulers.io_main()) + .subscribe(callback); + } + + /** + * 保存用户信息 + * @param user + */ + @Override + public void saveUserInfo(User user) { + //加密保存用户信息和密钥 + UserInfoManager.saveUserInfo(user); + UserInfoManager.saveIsLogin(true); + } + + /** + * 账号密码判空 + * @param username + * @param password + * @param callback + * @return + */ + @Override + public boolean verifyAccount(String username, String password, VerifyAccountCallback callback) { + if (TextUtils.isEmpty(username)) { + callback.onVerifyResult(AppContext.getContext().getString(R.string.username_not_empty)); + return false; + } + if (TextUtils.isEmpty(password)) { + callback.onVerifyResult(AppContext.getContext().getString(R.string.password_not_empty)); + return false; + } + return true; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ProjectCateModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ProjectCateModel.java new file mode 100644 index 0000000..9954c8a --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ProjectCateModel.java @@ -0,0 +1,28 @@ +package com.kdp.wanandroidclient.ui.core.model.impl; + +import com.kdp.wanandroidclient.bean.ProjectCate; +import com.kdp.wanandroidclient.net.RxSchedulers; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.ui.core.model.IProjectCateModel; + +import java.util.List; + +/*** + * @author kdp + * @date 2019/3/20 16:55 + * @description + */ +public class ProjectCateModel extends BaseModel implements IProjectCateModel { + + /** + * 获取项目分类 + * @param rxObserver + */ + @Override + public void getProjectCate(RxObserver> rxObserver) { + doRxRequest() + .getProjectCate() + .compose(RxSchedulers.>io_main()) + .subscribe(rxObserver); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ProjectModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ProjectModel.java new file mode 100644 index 0000000..ba8e1f8 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ProjectModel.java @@ -0,0 +1,23 @@ +package com.kdp.wanandroidclient.ui.core.model.impl; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.PageListData; +import com.kdp.wanandroidclient.net.RxSchedulers; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; +import com.kdp.wanandroidclient.ui.core.model.IProjectModel; + +public class ProjectModel extends CommonModel implements IProjectModel { + /** + * 获取项目列表 + * @param page 页码 + * @param cid 分类id + * @param rxPageListObserver + */ + @Override + public void getProjectList(int page, int cid, RxPageListObserver
rxPageListObserver) { + doRxRequest() + .getProjectList(page,cid) + .compose(RxSchedulers.>io_main()) + .subscribe(rxPageListObserver); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/SearchModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/SearchModel.java new file mode 100644 index 0000000..0367829 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/SearchModel.java @@ -0,0 +1,58 @@ +package com.kdp.wanandroidclient.ui.core.model.impl; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.Friend; +import com.kdp.wanandroidclient.bean.Hotword; +import com.kdp.wanandroidclient.bean.PageListData; +import com.kdp.wanandroidclient.net.RxSchedulers; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; +import com.kdp.wanandroidclient.ui.core.model.ISearchModel; +import com.kdp.wanandroidclient.utils.LogUtils; + +import java.util.List; + +/** + * author: 康栋普 + * date: 2018/4/5 + */ + +public class SearchModel extends CommonModel implements ISearchModel { + /** + * 搜索文章 + * @param page 页码 + * @param keyword 关键词 + * @param rxObserver + */ + @Override + public void searchArticle(int page, String keyword, RxPageListObserver
rxObserver) { + doRxRequest() + .search(page, keyword) + .compose(RxSchedulers.>io_main()) + .subscribe(rxObserver); + } + + /** + * 搜索热词 + * @param observable + */ + @Override + public void getHotWord(RxObserver> observable) { + doRxRequest() + .getHotKeyword() + .compose(RxSchedulers.>io_main()) + .subscribe(observable); + } + + /** + * 常用网站 + * @param rxObserver + */ + @Override + public void getFriend(RxObserver> rxObserver) { + doRxRequest() + .getFriend() + .compose(RxSchedulers.>io_main()) + .subscribe(rxObserver); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/TreeListModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/TreeListModel.java new file mode 100644 index 0000000..b35dd88 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/TreeListModel.java @@ -0,0 +1,30 @@ +package com.kdp.wanandroidclient.ui.core.model.impl; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.PageListData; +import com.kdp.wanandroidclient.net.RxSchedulers; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; +import com.kdp.wanandroidclient.ui.core.model.ITreeListModel; + +/** + * author: 康栋普 + * date: 2018/3/20 + */ + +public class TreeListModel extends CommonModel implements ITreeListModel { + + /** + * 获取知识体系文章列表 + * @param page 页码 + * @param cid 知识体系分类id + * @param rxObserver + */ + @Override + public void getTreeList(int page,int cid,RxPageListObserver
rxObserver) { + doRxRequest() + .getTreeList(page, cid) + .compose(RxSchedulers.>io_main()) + .subscribe(rxObserver); + + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/TreeModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/TreeModel.java new file mode 100644 index 0000000..1c0b727 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/TreeModel.java @@ -0,0 +1,30 @@ +package com.kdp.wanandroidclient.ui.core.model.impl; + +import com.kdp.wanandroidclient.bean.Tree; +import com.kdp.wanandroidclient.net.RxSchedulers; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.ui.core.model.ITreeModel; + +import java.util.List; + +/** + * author: 康栋普 + * date: 2018/2/24 + */ + +public class TreeModel extends BaseModel implements ITreeModel { + + /** + * 获取知识体系分类 + * @param callback + */ + @Override + public void getTree(RxObserver> callback) { + doRxRequest() + .getTree() + .compose(RxSchedulers.>io_main()) + .subscribe(callback); + } + + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/UserModel.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/UserModel.java new file mode 100644 index 0000000..818d598 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/UserModel.java @@ -0,0 +1,42 @@ +package com.kdp.wanandroidclient.ui.core.model.impl; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.PageListData; +import com.kdp.wanandroidclient.net.RxSchedulers; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; +import com.kdp.wanandroidclient.ui.core.model.IUserModel; + +/** + * author: 康栋普 + * date: 2018/3/21 + */ + +public class UserModel extends CommonModel implements IUserModel { + /** + * 收藏的文章列表 + * @param page 页码 + * @param rxObserver + */ + @Override + public void getCollectArticleList(int page, RxPageListObserver
rxObserver) { + doRxRequest() + .getCollectArticleList(page) + .compose(RxSchedulers.>io_main()) + .subscribe(rxObserver); + } + + /** + * 删除收藏 + * @param id 收藏列表的文章id + * @param originId 首页列表的文章id + * @param callback + */ + @Override + public void deleteCollectArticle(int id, int originId, RxObserver callback) { + doRxRequest() + .deleteCollectArticle(id, originId) + .compose(RxSchedulers.io_main()) + .subscribe(callback); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/presenter/BasePresenter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/presenter/BasePresenter.java new file mode 100644 index 0000000..27c14f3 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/presenter/BasePresenter.java @@ -0,0 +1,66 @@ +package com.kdp.wanandroidclient.ui.core.presenter; + +import com.kdp.wanandroidclient.ui.core.view.IView; + +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.disposables.Disposable; + +/** + * 基类Presenter,用来处理view和请求 + + */ + +public class BasePresenter implements IPresenter { + private V view; + //用来存放Disposable的容器 + private CompositeDisposable mCompositeDisposable; + + //绑定View + @Override + public void attachView(V view) { + this.view = view; + } + + //解除View绑定 + @Override + public void detachView() { + this.view = null; + } + + //获取绑定的View + @Override + public V getView() { + checkAttachView(); + return view; + } + + //检查View是否存在 + @Override + public void checkAttachView() { + if (view == null) + throw new RuntimeException("You have no binding this view"); + } + + //添加指定的请求 + @Override + public void addDisposable(Disposable disposable) { + if (mCompositeDisposable == null) + mCompositeDisposable = new CompositeDisposable(); + mCompositeDisposable.add(disposable); + } + + //移除指定的请求 + @Override + public void removeDisposable(Disposable disposable) { + if (mCompositeDisposable != null) + mCompositeDisposable.remove(disposable); + } + + //取消所有的请求Tag + @Override + public void removeAllDisposable() { + if (mCompositeDisposable != null) + mCompositeDisposable.clear(); + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/presenter/IPresenter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/presenter/IPresenter.java new file mode 100644 index 0000000..bd8efea --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/presenter/IPresenter.java @@ -0,0 +1,31 @@ +package com.kdp.wanandroidclient.ui.core.presenter; + +import com.kdp.wanandroidclient.ui.core.view.IView; + +import io.reactivex.disposables.Disposable; + +/** + * Presenter接口类 + + */ + +public interface IPresenter { + + //绑定View + void attachView(V view); + + //解除View绑定 + void detachView(); + + //检查View是否存在 + void checkAttachView(); + + V getView(); + + //添加指定的请求 + void addDisposable(Disposable disposable); + //移除指定的请求 + void removeDisposable(Disposable disposable); + //取消所有请求 + void removeAllDisposable(); +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/view/IListDataView.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/view/IListDataView.java new file mode 100644 index 0000000..4453088 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/view/IListDataView.java @@ -0,0 +1,17 @@ +package com.kdp.wanandroidclient.ui.core.view; + +import java.util.List; + +/*** + * @author kdp + * @date 2019/3/20 13:03 + * @description + */ +public interface IListDataView extends IView{ + + void setData(List data); + + List getData(); + + void showContent(); //显示内容 +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/view/IPageLoadDataView.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/view/IPageLoadDataView.java new file mode 100644 index 0000000..35342fe --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/view/IPageLoadDataView.java @@ -0,0 +1,17 @@ +package com.kdp.wanandroidclient.ui.core.view; + + +/** + * 列表View + * author: 曾文海 + * date: 2023/5/31 + */ + +public interface IPageLoadDataView extends IListDataView{ + + int getFirstPage(); + int getPage(); + void autoLoadMore();//自动加载 + void clearListData();//清空所有数据 + void showNoMore();//没有更多数据 +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/core/view/IView.java b/app/src/main/java/com/kdp/wanandroidclient/ui/core/view/IView.java new file mode 100644 index 0000000..70ba500 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/core/view/IView.java @@ -0,0 +1,41 @@ +package com.kdp.wanandroidclient.ui.core.view; + +/** + * view基类 + + */ + +public interface IView{ + + + /** + * 显示进度条 + * + */ + void showLoading(String msg); + + /** + * 隐藏进度条 + */ + void hideLoading(); + + /** + * 失败 + * + * @param msg + */ + void showFail(String msg); + + /** + * 错误 + */ + void showError(); + + /** + * 没有数据 + */ + void showEmpty();//没有数据 + + + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/home/HomeContract.java b/app/src/main/java/com/kdp/wanandroidclient/ui/home/HomeContract.java new file mode 100644 index 0000000..2a34a8b --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/home/HomeContract.java @@ -0,0 +1,30 @@ +package com.kdp.wanandroidclient.ui.home; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.Banner; +import com.kdp.wanandroidclient.ui.core.view.IPageLoadDataView; + +import java.util.List; + +/** + * Home协约类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public interface HomeContract { + interface IHomePresenter { + void getHomeData(); + void getMoreArticleList(); + + void collectArticle(); + + void unCollectArticle(); + } + + interface IHomeView extends IPageLoadDataView
{ + int getArticleId(); + void setBannerData(List banner); + void collect(boolean isCollect,String result); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/home/HomeFragment.java b/app/src/main/java/com/kdp/wanandroidclient/ui/home/HomeFragment.java new file mode 100644 index 0000000..7635c24 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/home/HomeFragment.java @@ -0,0 +1,224 @@ +package com.kdp.wanandroidclient.ui.home; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.Banner; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; +import com.kdp.wanandroidclient.inter.OnArticleListItemClickListener; +import com.kdp.wanandroidclient.manager.UserInfoManager; +import com.kdp.wanandroidclient.ui.adapter.ArticleListAdapter; +import com.kdp.wanandroidclient.ui.adapter.BannerAdapter; +import com.kdp.wanandroidclient.ui.adapter.BaseListAdapter; +import com.kdp.wanandroidclient.ui.base.BaseAbListFragment; +import com.kdp.wanandroidclient.ui.main.MainActivity; +import com.kdp.wanandroidclient.ui.tree.TreeActivity; +import com.kdp.wanandroidclient.ui.web.WebViewActivity; +import com.kdp.wanandroidclient.utils.IntentUtils; +import com.kdp.wanandroidclient.utils.ToastUtils; +import com.kdp.wanandroidclient.widget.BannerViewPager; +import java.util.ArrayList; +import java.util.List; + + +/** + * 首页文章 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class HomeFragment extends BaseAbListFragment implements HomeContract.IHomeView, OnArticleListItemClickListener { + private int id;//文章id + private int position; + private List mBannerList = new ArrayList<>(); + private BannerViewPager mViewPager; + private BannerAdapter mBannerAdapter; + + @Override + protected HomePresenter createPresenter() { + return new HomePresenter(); + } + + @Override + protected boolean isCanLoadMore() { + return true; + } + + //初始化HeaderView + @Override + protected View initHeaderView() { + View headerView = LayoutInflater.from(getActivity()).inflate(R.layout.main_header_banner, mRecyclerView, false); + mViewPager = headerView.findViewById(R.id.viewPager); + return headerView; + } + + + //设置Banner选中item + private void setCurrentItem(final int position) { + mViewPager.setCurrentItem(position, false); + } + + //加载列表数据 + @Override + protected void loadDatas() { + if (page == getFirstPage()){ + //刷新 + mPresenter.getHomeData(); + }else { + //加载更多 + mPresenter.getMoreArticleList(); + } + } + + @Override + protected BaseListAdapter
getListAdapter() { + return new ArticleListAdapter(this, Const.LIST_TYPE.HOME); + } + + //Banner数据 + @Override + public void setBannerData(List banner) { + mBannerList.clear(); + mBannerList.addAll(banner); + } + + //列表数据 + @Override + public void setData(List
data) { + mListData.addAll(data); + } + + //显示内容 + @Override + public void showContent() { + notifyDatas(); + super.showContent(); + } + + //刷新所有数据 + public void notifyDatas() { + if (mBannerAdapter == null) { + mBannerAdapter = new BannerAdapter(mBannerList); + mViewPager.setAdapter(mBannerAdapter); + mViewPager.setOffscreenPageLimit(mBannerList.size()); + setCurrentItem(1000 * mBannerList.size()); + } + mBannerAdapter.notifyDatas(mBannerList); + } + + + //收藏结果 + @Override + public void collect(boolean isCollect, String result) { + notifyItemData(isCollect, result); + } + + //刷新单条Item + private void notifyItemData(boolean isCollect, String result) { + mListData.get(position).setCollect(isCollect); + position++; + mListAdapter.notifyItemDataChanged(position, mRecyclerView); + ToastUtils.showToast(getActivity(), result); + } + + //文章id + @Override + public int getArticleId() { + return id; + } + + //进入详情 + @Override + public void onItemClick(int position,Article bean) { + Intent intent = new Intent(getActivity(), WebViewActivity.class); + Bundle bundle = new Bundle(); + bundle.putSerializable(Const.BUNDLE_KEY.OBJ, bean); + bundle.putString(Const.BUNDLE_KEY.TYPE, Const.EVENT_ACTION.HOME); + intent.putExtras(bundle); + startActivity(intent); + } + + @Override + public void onDeleteCollectClick(int position, int id, int originId) { + } + + //收藏click + @Override + public void onCollectClick(int position, int id) { + if (!UserInfoManager.isLogin()) + IntentUtils.goLogin(getActivity()); + this.position = position; + this.id = id; + if (mListData.get(this.position).isCollect()) + mPresenter.unCollectArticle(); + else + mPresenter.collectArticle(); + } + + @Override + public void onResume() { + super.onResume(); + mViewPager.start(); + } + + @Override + public void onPause() { + super.onPause(); + mViewPager.stop(); + } + + @Override + public void onHiddenChanged(boolean hidden) { + if (hidden) { + mViewPager.stop(); + } else { + mViewPager.start(); + } + } + + @Override + protected void receiveEvent(Object object) { + Event mEvent = (Event) object; + if (mEvent.type == Event.Type.REFRESH_ITEM) { + Article bean = (Article) mEvent.object; + for (int i = 0; i < mListData.size(); i++) { + if (bean.equals(mListData.get(i))) { + position = i; + notifyItemData(bean.isCollect(), getString(R.string.collect_success)); + } + } + }else if (mEvent.type == Event.Type.SCROLL_TOP){ + mRecyclerView.smoothScrollToPosition(0); + }else if (mEvent.type == Event.Type.REFRESH_LIST){ + refreshData(); + } + } + + @Override + protected String registerEvent() { + return Const.EVENT_ACTION.HOME; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mRecyclerView.addOnScrollListener(onScrollListener); + } + + private RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + RxEvent.getInstance().postEvent(Const.EVENT_ACTION.MAIN,new Event(Event.Type.SCALE,dy)); + } + }; +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/home/HomePresenter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/home/HomePresenter.java new file mode 100644 index 0000000..6445eaf --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/home/HomePresenter.java @@ -0,0 +1,141 @@ +package com.kdp.wanandroidclient.ui.home; +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.Banner; +import com.kdp.wanandroidclient.bean.BaseBean; +import com.kdp.wanandroidclient.bean.HomeData; +import com.kdp.wanandroidclient.bean.PageListData; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; +import com.kdp.wanandroidclient.net.callback.RxZipObserver; +import com.kdp.wanandroidclient.ui.core.model.impl.HomeModel; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; +import java.util.List; +import io.reactivex.functions.Function3; + +/** + * Home Presenter + * author: 曾文海 + * date: 2023/5/31 + */ + +public class HomePresenter extends BasePresenter implements HomeContract.IHomePresenter { + private HomeModel homeModel; + private HomeContract.IHomeView homeView; + + HomePresenter() { + this.homeModel = new HomeModel(); + } + + /** + * 获取首页Banner、置顶文章、列表文章 + */ + @Override + public void getHomeData() { + homeView = getView(); + Function3>, BaseBean>, BaseBean>, HomeData> function3 = new Function3>, BaseBean>, BaseBean>, HomeData>() { + @Override + public HomeData apply(BaseBean> banner, BaseBean> homeTop, BaseBean> home) throws Exception { + HomeData homeData = new HomeData(); + homeData.setBanner(banner); + for (Article bean : homeTop.data){ + //置顶 + bean.setTop(true); + } + homeData.setHomeTop(homeTop); + homeData.setHome(home); + return homeData; + } + }; + + + RxZipObserver rxZipObserver = new RxZipObserver(this) { + @Override + public void onNext(HomeData homeData) { + homeView.setBannerData(homeData.getBanner().data); + List
list = homeData.getHome().data.getDatas(); + list.addAll(0,homeData.getHomeTop().data); + homeView.clearListData(); + homeView.autoLoadMore(); + homeView.setData(list); + if (homeView.getData().size() == 0) { + homeView.showEmpty(); + }else { + homeView.showContent(); + } + } + }; + homeModel.getHomeData(homeView.getPage(),function3, rxZipObserver); + addDisposable(rxZipObserver); + } + + /** + * 加载更多,获取更多文章 + */ + @Override + public void getMoreArticleList() { + homeView = getView(); + RxPageListObserver
rxPageListObserver = new RxPageListObserver
(this) { + @Override + public void onSuccess(List
homeList) { + homeView.getData().addAll(homeList); + homeView.showContent(); + } + @Override + public void onFail(int errorCode, String errorMsg) { + homeView.showFail(errorMsg); + } + }; + homeModel.getMoreArticleList(homeView.getPage(),rxPageListObserver); + addDisposable(rxPageListObserver); + } + + /** + * 收藏 + */ + @Override + public void collectArticle() { + RxObserver mCollectRxObserver = new RxObserver(this) { + @Override + protected void onStart() { + } + @Override + protected void onSuccess(String data) { + homeView.collect(true, AppContext.getContext().getString(R.string.collect_success)); + } + @Override + protected void onFail(int errorCode, String errorMsg) { + view.showFail(errorMsg); + } + + }; + homeModel.collectArticle(homeView.getArticleId(), mCollectRxObserver); + addDisposable(mCollectRxObserver); + } + + /** + * 取消收藏 + */ + @Override + public void unCollectArticle() { + RxObserver unCollectRxObserver = new RxObserver(this) { + + @Override + protected void onStart() { + } + @Override + protected void onSuccess(String data) { + homeView.collect(false, AppContext.getContext().getString(R.string.uncollect_success)); + } + + @Override + protected void onFail(int errorCode, String errorMsg) { + view.showFail(errorMsg); + } + }; + homeModel.unCollectArticle(homeView.getArticleId(), unCollectRxObserver); + addDisposable(unCollectRxObserver); + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/logon/LogonActivity.java b/app/src/main/java/com/kdp/wanandroidclient/ui/logon/LogonActivity.java new file mode 100644 index 0000000..1294444 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/logon/LogonActivity.java @@ -0,0 +1,82 @@ +package com.kdp.wanandroidclient.ui.logon; + +import android.os.Bundle; +import android.view.View; +import android.widget.EditText; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; +import com.kdp.wanandroidclient.ui.base.BasePresenterActivity; +import com.kdp.wanandroidclient.utils.LightStatusbarUtils; +import com.kdp.wanandroidclient.utils.ToastUtils; + +/** + * 登录、注册 + + */ + +public class LogonActivity extends BasePresenterActivity implements LogonContract.ILoginRegisterView { + private EditText et_username, et_password; + + @Override + public String getUserName() { + return et_username.getText().toString().trim(); + } + + @Override + public String getPassWord() { + return et_password.getText().toString().trim(); + } + + @Override + public void showResult(String msg) { + ToastUtils.showToast(AppContext.getContext(), msg); + RxEvent.getInstance().postEvent(Const.EVENT_ACTION.HOME, new Event(Event.Type.REFRESH_LIST)); + finish(); + } + + @Override + public void showLoading(String msg) { + showLoadingDialog(msg); + } + + @Override + protected LogonPresenter createPresenter() { + return new LogonPresenter(); + } + + @Override + protected int getLayoutId() { + return R.layout.activity_login; + } + + + @Override + protected void initViews() { + et_username = findViewById(R.id.et_username); + et_password = findViewById(R.id.et_password); + findViewById(R.id.bt_login).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mPresenter.login(); + } + }); + findViewById(R.id.bt_register).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mPresenter.register(); + } + }); + } + + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + LightStatusbarUtils.setLightStatusBar(this, true); + } + + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/logon/LogonContract.java b/app/src/main/java/com/kdp/wanandroidclient/ui/logon/LogonContract.java new file mode 100644 index 0000000..0ab528c --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/logon/LogonContract.java @@ -0,0 +1,41 @@ +package com.kdp.wanandroidclient.ui.logon; + +import com.kdp.wanandroidclient.ui.core.view.IView; + +/** + * 登录、注册协约类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public interface LogonContract { + + interface IUserPresenter { + void login(); + + void register(); + } + + + interface ILoginRegisterView extends IView { + + /** + * 获取用户名 + * + * @return + */ + String getUserName(); + + /** + * 获取密码 + * + * @return + */ + String getPassWord(); + + /** + * 登录或注册Result + */ + void showResult(String msg); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/logon/LogonPresenter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/logon/LogonPresenter.java new file mode 100644 index 0000000..6f1afb7 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/logon/LogonPresenter.java @@ -0,0 +1,97 @@ +package com.kdp.wanandroidclient.ui.logon; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.bean.User; +import com.kdp.wanandroidclient.inter.VerifyAccountCallback; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.ui.core.model.impl.LogonModel; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; + +/** + * 登录、注册Presenter + + */ + +public class LogonPresenter extends BasePresenter implements LogonContract.IUserPresenter { + + private String username, password; + private LogonModel logonModel; + private LogonContract.ILoginRegisterView mLogonView; + + public LogonPresenter() { + this.logonModel = new LogonModel(); + } + + /** + * 登录 + */ + @Override + public void login() { + if(!verifyAccount()) return; + RxObserver mLoginRxObserver = new RxObserver(this) { + @Override + protected void onStart() { + mLogonView.showLoading(AppContext.getContext().getString(R.string.isLoging)); + } + + @Override + protected void onSuccess(User userBean) { + userBean.setPassword(password); + logonModel.saveUserInfo(userBean); + mLogonView.showResult(AppContext.getContext().getString(R.string.login_success)); + } + + @Override + protected void onFail(int errorCode, String errorMsg) { + mLogonView.showFail(errorMsg); + } + }; + logonModel.login(username, password, mLoginRxObserver); + addDisposable(mLoginRxObserver); + } + + /** + * 注册 + */ + @Override + public void register() { + if (!verifyAccount()) return; + RxObserver mRegisterRxObserver = new RxObserver(this) { + @Override + protected void onStart() { + mLogonView.showLoading(AppContext.getContext().getString(R.string.isRegistering)); + } + + @Override + protected void onSuccess(String data) { + mLogonView.showResult(AppContext.getContext().getString(R.string.register_success)); + } + + @Override + protected void onFail(int errorCode, String errorMsg) { + mLogonView.showFail(errorMsg); + } + }; + logonModel.register(username, password, mRegisterRxObserver); + addDisposable(mRegisterRxObserver); + } + + private VerifyAccountCallback mVerifyAccountCallback = new VerifyAccountCallback() { + @Override + public void onVerifyResult(String msg) { + mLogonView.showFail(msg); + } + }; + + /** + * 帐号验证 + */ + private boolean verifyAccount() { + mLogonView = getView(); + username = mLogonView.getUserName(); + password = mLogonView.getPassWord(); + return logonModel.verifyAccount(username, password, mVerifyAccountCallback); + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/main/MainActivity.java b/app/src/main/java/com/kdp/wanandroidclient/ui/main/MainActivity.java new file mode 100644 index 0000000..59c8bce --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/main/MainActivity.java @@ -0,0 +1,333 @@ +package com.kdp.wanandroidclient.ui.main; + +import android.animation.Animator; +import android.annotation.SuppressLint; +import android.content.Intent; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.NavigationView; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.animation.LinearOutSlowInInterpolator; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBarDrawerToggle; +import android.util.Log; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewPropertyAnimator; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.bean.User; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; +import com.kdp.wanandroidclient.manager.ImageLoaderManager; +import com.kdp.wanandroidclient.manager.UserInfoManager; +import com.kdp.wanandroidclient.ui.base.BaseActivity; +import com.kdp.wanandroidclient.ui.chapter.ChaptersFragment; +import com.kdp.wanandroidclient.ui.home.HomeFragment; +import com.kdp.wanandroidclient.ui.project.ProjectFragment; +import com.kdp.wanandroidclient.ui.tree.TreeFragment; +import com.kdp.wanandroidclient.ui.user.AboutUsActivity; +import com.kdp.wanandroidclient.ui.user.CollectArticleActivity; +import com.kdp.wanandroidclient.utils.IntentUtils; +import com.kdp.wanandroidclient.utils.PreUtils; +import com.kdp.wanandroidclient.utils.ToastUtils; +import com.kdp.wanandroidclient.utils.ViewAnimatorHelper; + +/** + * 管理首页Tab的Activity + */ +public class MainActivity extends BaseActivity implements View.OnClickListener { + private DrawerLayout mDrawerLayout; + private NavigationView mNavigationView; + private FloatingActionButton btn_scroll_top; + private TextView mNameView; + private ImageView mAvatarView; + private Button[] btns; + private Fragment[] fragments; + private int currentPosition; + private int index; + private ViewAnimatorHelper viewAnimatorHelper; + + + @Override + protected int getLayoutId() { + return R.layout.activity_main; + } + + @Override + protected boolean initToolbar() { + mToolbar.setTitle(R.string.app_name); + mToolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp); + return true; + } + + + public void setCurrentTitle() { + if (currentPosition == 0) + mToolbar.setTitle(R.string.app_name); + else if (currentPosition == 1) + mToolbar.setTitle(R.string.system); + else if(currentPosition == 2) + mToolbar.setTitle(R.string.chapter); + else if (currentPosition == 3) + mToolbar.setTitle(R.string.project); + + } + + @Override + protected void initViews() { + mDrawerLayout = findViewById(R.id.drawerLayout); + mNavigationView = findViewById(R.id.navigation_view); + btn_scroll_top = findViewById(R.id.btn_scroll_top); + btns = new Button[4]; + btns[0] = findViewById(R.id.btn_main); + btns[1] = findViewById(R.id.btn_system); + btns[2] = findViewById(R.id.btn_chapter); + btns[3] = findViewById(R.id.btn_project); + btns[0].setSelected(true); + + for (int i = 0; i < btns.length; i++) { + btns[i].setOnClickListener(this); + if (i != currentPosition) { + btns[i].setScaleX(0.9f); + btns[i].setScaleY(0.9f); + } + } + + btn_scroll_top.setOnClickListener(onScrollTopListener); + } + + private View.OnClickListener onScrollTopListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + String action=""; + switch (currentPosition) { + case 0: + action = Const.EVENT_ACTION.HOME; + break; + case 1: + action = Const.EVENT_ACTION.SYSTEM; + break; + case 2: + ((ChaptersFragment)fragments[2]).scrollToTop(); + return; + case 3: + ((ProjectFragment)fragments[3]).scrollToTop(); + return; + + } + RxEvent.getInstance().postEvent(action,new Event(Event.Type.SCROLL_TOP)); + } + }; + + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + //设置Home旋转开关按钮 + ActionBarDrawerToggle mToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, + R.string.navigation_drawer_open, + R.string.navigation_drawer_close); + mToggle.syncState(); + mDrawerLayout.addDrawerListener(mToggle); + mNavigationView.setItemIconTintList(null); + mNavigationView.setNavigationItemSelectedListener(onNavigationItemSelectedListener); + //侧滑菜单 + initNavigationHeaderView(); + initFragments(); + viewAnimatorHelper = new ViewAnimatorHelper(); + viewAnimatorHelper.bindView(btn_scroll_top); + } + + + + private void initNavigationHeaderView() { + View mHeaderView = mNavigationView.getHeaderView(0); + mAvatarView = mHeaderView.findViewById(R.id.img_avatar); + mNameView = mHeaderView.findViewById(R.id.tv_name); + } + + private void setUserData() { + if (UserInfoManager.isLogin()) { + User user = UserInfoManager.getUserInfo(); + if (user != null) { + mNameView.setText(user.getUsername()); + ImageLoaderManager.displayImage(user.getIcon(), mAvatarView, Const.IMAGE_LOADER.HEAD_IMG); + } + } else { + mNameView.setText("未登录"); + } + } + + + private void initFragments() { + fragments = new Fragment[]{new HomeFragment(), new TreeFragment(),new ChaptersFragment(),new ProjectFragment()}; + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.add(R.id.container, fragments[0]).show(fragments[0]).commitAllowingStateLoss(); + } + + + //设置侧滑item click + private NavigationView.OnNavigationItemSelectedListener onNavigationItemSelectedListener = new NavigationView.OnNavigationItemSelectedListener() { + @Override + public boolean onNavigationItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_favorite_article: { + if (!UserInfoManager.isLogin()) { + IntentUtils.goLogin(MainActivity.this); + } else { + startActivity(new Intent(MainActivity.this, CollectArticleActivity.class)); + } + } + break; + case R.id.menu_about: + startActivity(new Intent(MainActivity.this, AboutUsActivity.class)); + break; + case R.id.menu_exit: + exitToLogin(); + break; + } + return true; + } + }; + + + //创建Menu + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main_menu_setting, menu); + return super.onCreateOptionsMenu(menu); + } + + //Menu点击事件 + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_search: + startActivity(new Intent(this, SearchActivity.class)); + break; + default: + break; + } + return super.onOptionsItemSelected(item); + } + + //退出登录 + private void exitToLogin() { + IntentUtils.goLogin(this); + PreUtils.clearAll(); + //刷新首页数据 + RxEvent.getInstance().postEvent(Const.EVENT_ACTION.HOME, new Event(Event.Type.REFRESH_LIST)); + } + + @Override + protected void onResume() { + super.onResume(); + setUserData(); + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.btn_main: + index = 0; + break; + case R.id.btn_system: + index = 1; + break; + case R.id.btn_chapter: + index = 2; + break; + case R.id.btn_project: + index = 3; + break; + default: + } + + showCurrentFragment(index); + } + + /** + * 切换显示当前Fragment + * + * @param index + */ + private void showCurrentFragment(int index) { + if (currentPosition != index) { + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.hide(fragments[currentPosition]); + if (!fragments[index].isAdded()) { + ft.add(R.id.container, fragments[index]); + } + ft.show(fragments[index]).commit(); + btns[currentPosition].setSelected(false); + btns[index].setSelected(true); + scaleView(); + currentPosition = index; + setCurrentTitle(); + } + } + + /** + * view放大缩小 + */ + private void scaleView() { + btns[currentPosition].animate().scaleX(0.9f).scaleY(0.9f) + .setDuration(150).start(); + btns[index].animate().scaleX(1.0f).scaleY(1.0f) + .setDuration(150).start(); + } + + private long mExitTime; + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + + if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { + + if (mDrawerLayout.isDrawerOpen(Gravity.START)) { + mDrawerLayout.closeDrawer(Gravity.START); + return true; + } + + if (System.currentTimeMillis() - mExitTime < 2000) { + finish(); + } else { + mExitTime = System.currentTimeMillis(); + ToastUtils.showToast(AppContext.getContext(), "请再按一次退出程序"); + } + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + protected void receiveEvent(Object object) { + Event event = (Event) object; + if (event.type == Event.Type.SCALE){ + scroll((int) event.object); + } + } + + public void scroll(int offsetY){ + if (offsetY > 0 && btn_scroll_top.getVisibility() != View.INVISIBLE && !viewAnimatorHelper.isAnimating()){ + viewAnimatorHelper.hideFloatActionButton(); + }else if (offsetY < 0 && btn_scroll_top.getVisibility() != View.VISIBLE){ + viewAnimatorHelper.showFloatActionButton(); + } + } + + @Override + protected String registerEvent() { + return Const.EVENT_ACTION.MAIN; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/main/SearchActivity.java b/app/src/main/java/com/kdp/wanandroidclient/ui/main/SearchActivity.java new file mode 100644 index 0000000..e3037c2 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/main/SearchActivity.java @@ -0,0 +1,332 @@ +package com.kdp.wanandroidclient.ui.main; + +import android.content.Intent; +import android.graphics.Color; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.SearchView; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.Friend; +import com.kdp.wanandroidclient.bean.Hotword; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.inter.OnArticleListItemClickListener; +import com.kdp.wanandroidclient.manager.UserInfoManager; +import com.kdp.wanandroidclient.ui.adapter.ArticleListAdapter; +import com.kdp.wanandroidclient.ui.adapter.BaseListAdapter; +import com.kdp.wanandroidclient.ui.base.BaseAbListActivity; +import com.kdp.wanandroidclient.ui.web.WebViewActivity; +import com.kdp.wanandroidclient.utils.IntentUtils; +import com.kdp.wanandroidclient.utils.ToastUtils; +import com.zhy.view.flowlayout.FlowLayout; +import com.zhy.view.flowlayout.TagAdapter; +import com.zhy.view.flowlayout.TagFlowLayout; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * 搜索页面 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class SearchActivity extends BaseAbListActivity implements SearchContract.ISearchView, OnArticleListItemClickListener { + private SearchView mSearchView; + private SearchView.SearchAutoComplete searchAutoComplete; + private View mHeaderView; + private TagFlowLayout mKeywordTagLayout, mFriendTagLayout; + private int position; + private int id; + private String keyword = ""; + private List mHotwordDatas = new ArrayList<>(); + private List mFriendDatas = new ArrayList<>(); + + @Override + protected boolean initToolbar() { + mToolbar.setTitle(R.string.search); + return true; + } + + @Override + protected View initHeaderView() { + mHeaderView = LayoutInflater.from(this).inflate(R.layout.search_header, mRecyclerView, false); + mKeywordTagLayout = mHeaderView.findViewById(R.id.keywordTaglayout); + mFriendTagLayout = mHeaderView.findViewById(R.id.friendTaglayout); + return mHeaderView; + } + + //设置搜索的数据 + @Override + public void setData(List
data) { + mRecyclerView.removeHeaderView(); + setRefreshEnable(true); + setCanLoadMore(true); + if (state != Const.PAGE_STATE.STATE_LOAD_MORE) + mRecyclerView.scrollToPosition(0); + mListData.addAll(data); + } + + @Override + public void showError() { + super.showError(); + setRefreshEnable(true); + } + + + @Override + public String getKeyword() { + return keyword; + } + + + @Override + public int getArticleId() { + return id; + } + + //热搜关键词 + @Override + public void setHotwordData(final List mHotListDatas) { + mHotwordDatas.clear(); + mHotwordDatas.addAll(mHotListDatas); + mKeywordTagLayout.setAdapter(new TagAdapter(mHotListDatas) { + @Override + public View getView(FlowLayout parent, int position, Hotword hotword) { + TextView tagView = (TextView) LayoutInflater.from(SearchActivity.this).inflate(R.layout.item_search_tag, mKeywordTagLayout, false); + tagView.setText(hotword.getName()); + setTagTextColor(tagView); + return tagView; + } + }); + mKeywordTagLayout.setOnTagClickListener(new TagFlowLayout.OnTagClickListener() { + @Override + public boolean onTagClick(View view, int position, FlowLayout parent) { + keyword = mHotListDatas.get(position).getName(); + searchAutoComplete.setText(keyword); + searchAutoComplete.setSelection(keyword.length()); + mSearchView.setQuery(keyword, true); + return false; + } + }); + } + + //历史网站 + @Override + public void setFriendData(final List mFriendListDatas) { + mFriendDatas.clear(); + mFriendDatas.addAll(mFriendListDatas); + mFriendTagLayout.setAdapter(new TagAdapter(mFriendDatas) { + @Override + public View getView(FlowLayout parent, int position, Friend friend) { + TextView tagView = (TextView) LayoutInflater.from(SearchActivity.this).inflate(R.layout.item_search_tag, mFriendTagLayout, false); + tagView.setText(friend.getName()); + setTagTextColor(tagView); + return tagView; + } + }); + mFriendTagLayout.setOnTagClickListener( + + new TagFlowLayout.OnTagClickListener() { + @Override + public boolean onTagClick(View view, int position, FlowLayout parent) { + Friend mFriend = mFriendListDatas.get(position); + Article bean = new Article(); + bean.setTitle(mFriend.getName()); + bean.setLink(mFriend.getLink()); + Intent intent = new Intent(SearchActivity.this, WebViewActivity.class); + Bundle bundle = new Bundle(); + bundle.putSerializable(Const.BUNDLE_KEY.OBJ, bean); + intent.putExtras(bundle); + startActivity(intent); + return false; + } + }); + } + + //关键词颜色 + private void setTagTextColor(TextView tagView) { + int red, green, blue; + Random mRandow = new Random(); + red = mRandow.nextInt(255); + green = mRandow.nextInt(255); + blue = mRandow.nextInt(255); + int color = Color.rgb(red, green, blue); + tagView.setTextColor(color); + } + + //SearchView相关设置 + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.search_menu_setting, menu); + MenuItem menuItem = menu.findItem(R.id.menu_search); + //获取搜索框 + mSearchView = (SearchView) menuItem.getActionView(); + //设置搜索hint + mSearchView.setQueryHint(getString(R.string.search_keyword)); + mSearchView.onActionViewExpanded(); + //去除搜索框背景 + deleteSearchPlate(); + searchAutoComplete = mSearchView.findViewById(R.id.search_src_text); + searchAutoComplete.setHintTextColor(ContextCompat.getColor(this, R.color._60ffffff)); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + ImageView mCloseView = mSearchView.findViewById(R.id.search_close_btn); + mCloseView.setBackground(ContextCompat.getDrawable(this, R.drawable.ripple_close)); + } + + mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + keyword = query; + refreshData(); + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + if (TextUtils.isEmpty(newText)) { + keyword = newText; + if (mHotwordDatas.size() == 0) + loadTagDatas(); + } + + return false; + } + }); + return super.onCreateOptionsMenu(menu); + } + + //去除SearchView的输入框默认背景 + private void deleteSearchPlate() { + try { + Class cla = mSearchView.getClass(); + Field mSearchPlateField = cla.getDeclaredField("mSearchPlate"); + mSearchPlateField.setAccessible(true); + View searchPlate = (View) mSearchPlateField.get(mSearchView); + searchPlate.setBackground(null); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @Override + protected SearchPresenter createPresenter() { + return new SearchPresenter(); + } + + @Override + protected boolean isCanLoadMore() { + return true; + } + + @Override + protected void loadDatas() { + if (mSearchView == null) { + loadTagDatas(); + return; + } + + loadArticleListDatas(); + } + + private void loadTagDatas() { + setRefreshEnable(false); + setCanLoadMore(false); + mListData.clear(); + mRecyclerView.addHeaderView(mHeaderView); + mListAdapter.notifyAllDatas(mListData, mRecyclerView); + showContent(); + mPresenter.getHotWord(); + mPresenter.getFriend(); + } + + private void loadArticleListDatas() { + mHotwordDatas.clear(); + mFriendDatas.clear(); + mPresenter.search(); + } + + @Override + protected BaseListAdapter
getListAdapter() { + return new ArticleListAdapter(this, Const.LIST_TYPE.SEARCH); + } + + @Override + public void onItemClick(int position,Article bean) { + Intent intent = new Intent(this, WebViewActivity.class); + Bundle bundle = new Bundle(); + bundle.putSerializable(Const.BUNDLE_KEY.OBJ, bean); + bundle.putString(Const.BUNDLE_KEY.TYPE, Const.EVENT_ACTION.SEARCH); + intent.putExtras(bundle); + startActivity(intent); + } + + @Override + public void onDeleteCollectClick(int position, int id, int originId) { + } + + @Override + public void collect(boolean isCollect, String result) { + notifyItemData(isCollect, result); + } + + private void notifyItemData(boolean isCollect, String result) { + mListData.get(position).setCollect(isCollect); + mListAdapter.notifyItemDataChanged(position, mRecyclerView); + ToastUtils.showToast(AppContext.getContext(), result); + } + + /** + * 收藏 + * @param position + * @param id + */ + @Override + public void onCollectClick(int position, int id) { + if (!UserInfoManager.isLogin()) + IntentUtils.goLogin(this); + this.position = position; + this.id = id; + if (mListData.get(this.position).isCollect()) + mPresenter.unCollectArticle(); + else + mPresenter.collectArticle(); + } + + @Override + protected String registerEvent() { + return Const.EVENT_ACTION.SEARCH; + } + + @Override + protected void receiveEvent(Object object) { + Event mEvent = (Event) object; + if (mEvent.type == Event.Type.REFRESH_ITEM) { + Article bean = (Article) mEvent.object; + for (int i = 0; i < mListData.size(); i++) { + if (bean.equals(mListData.get(i))) { + position = i; + notifyItemData(bean.isCollect(), getString(R.string.collect_success)); + } + } + } else if (mEvent.type == Event.Type.REFRESH_LIST){ + refreshData(); + } + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/main/SearchContract.java b/app/src/main/java/com/kdp/wanandroidclient/ui/main/SearchContract.java new file mode 100644 index 0000000..ccc435c --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/main/SearchContract.java @@ -0,0 +1,40 @@ +package com.kdp.wanandroidclient.ui.main; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.Friend; +import com.kdp.wanandroidclient.bean.Hotword; +import com.kdp.wanandroidclient.ui.core.view.IPageLoadDataView; + +import java.util.List; + +/** + * 搜索协约类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class SearchContract { + interface ISearchPresenter { + void search(); + + void getHotWord(); + + void getFriend(); + + void collectArticle(); + + void unCollectArticle(); + } + + interface ISearchView extends IPageLoadDataView
{ + int getArticleId();//文章id + + String getKeyword(); + + void setHotwordData(List mHotwordDatas); + + void setFriendData(List mFriendListDatas); + + void collect(boolean isCollect,String result); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/main/SearchPresenter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/main/SearchPresenter.java new file mode 100644 index 0000000..3606a75 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/main/SearchPresenter.java @@ -0,0 +1,142 @@ +package com.kdp.wanandroidclient.ui.main; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.bean.Friend; +import com.kdp.wanandroidclient.bean.Hotword; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; +import com.kdp.wanandroidclient.ui.core.model.impl.SearchModel; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; + +import java.util.List; + +/** + * 搜索Presenter + * author: 曾文海 + * date: 2023/5/31 + */ + +public class SearchPresenter extends BasePresenter implements SearchContract.ISearchPresenter { + private SearchModel searchModel; + private SearchContract.ISearchView searchView; + + SearchPresenter() { + this.searchModel = new SearchModel(); + } + /** + * 搜索文章 + */ + @Override + public void search() { + searchView = getView(); + RxPageListObserver
searchRxPageListObserver = new RxPageListObserver
(this) { + @Override + public void onSuccess(List
mData) { + searchView.setData(mData); + if (searchView.getData().size() == 0) + searchView.showEmpty(); + else + searchView.showContent(); + } + + @Override + public void onFail(int errorCode, String errorMsg) { + searchView.showFail(errorMsg); + } + }; + searchModel.searchArticle(searchView.getPage(), searchView.getKeyword(), searchRxPageListObserver); + addDisposable(searchRxPageListObserver); + } + + /** + * 获取热搜 + */ + @Override + public void getHotWord() { + searchView = getView(); + RxObserver> hotWordRxObserver = new RxObserver>(this) { + @Override + protected void onSuccess(List data) { + searchView.setHotwordData(data); + } + + @Override + protected void onFail(int errorCode, String errorMsg) { + searchView.showFail(errorMsg); + } + + @Override + public void showLoading() { + } + }; + searchModel.getHotWord(hotWordRxObserver); + addDisposable(hotWordRxObserver); + } + + /** + * 获取常用网站 + */ + @Override + public void getFriend() { + RxObserver> friendRxObserver = new RxObserver>(this) { + @Override + protected void onSuccess(List data) { + searchView.setFriendData(data); + } + + @Override + protected void onFail(int errorCode, String errorMsg) { + searchView.showFail(errorMsg); + } + + @Override + public void showLoading() { + } + }; + searchModel.getFriend(friendRxObserver); + addDisposable(friendRxObserver); + } + + @Override + public void collectArticle() { + RxObserver collectRxObserver = new RxObserver(this) { + @Override + protected void onStart() { + } + @Override + protected void onSuccess(String data) { + searchView.collect(true, AppContext.getContext().getString(R.string.collect_success)); + } + @Override + protected void onFail(int errorCode, String errorMsg) { + view.showFail(errorMsg); + } + + }; + searchModel.collectArticle(searchView.getArticleId(), collectRxObserver); + addDisposable(collectRxObserver); + } + + @Override + public void unCollectArticle() { + RxObserver unCollectRxObserver = new RxObserver(this) { + + @Override + protected void onStart() { + } + @Override + protected void onSuccess(String data) { + searchView.collect(false, AppContext.getContext().getString(R.string.uncollect_success)); + } + + @Override + protected void onFail(int errorCode, String errorMsg) { + view.showFail(errorMsg); + } + }; + searchModel.unCollectArticle(searchView.getArticleId(), unCollectRxObserver); + addDisposable(unCollectRxObserver); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectCateContract.java b/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectCateContract.java new file mode 100644 index 0000000..88db31e --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectCateContract.java @@ -0,0 +1,17 @@ +package com.kdp.wanandroidclient.ui.project; +import com.kdp.wanandroidclient.bean.ProjectCate; +import com.kdp.wanandroidclient.ui.core.view.IListDataView; + +/*** + * @author kdp + * @date 2019/3/20 16:58 + * @description + */ +public interface ProjectCateContract { + + interface IProjectCatePresenter { + void getProjectCate(); + } + interface IProjectCateView extends IListDataView{ + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectCatePresenter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectCatePresenter.java new file mode 100644 index 0000000..d9b09ea --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectCatePresenter.java @@ -0,0 +1,46 @@ +package com.kdp.wanandroidclient.ui.project; +import com.kdp.wanandroidclient.bean.ProjectCate; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.ui.core.model.impl.ProjectCateModel; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; + +import java.util.List; + +/*** + * @author kdp + * @date 2019/3/20 17:00 + * @description + */ +public class ProjectCatePresenter extends BasePresenter implements ProjectCateContract.IProjectCatePresenter{ + private ProjectCateModel projectCateModel; + private ProjectCateContract.IProjectCateView projectCateView; + + ProjectCatePresenter() { + this.projectCateModel = new ProjectCateModel(); + } + + @Override + public void getProjectCate() { + projectCateView = getView(); + RxObserver> rxObserver = new RxObserver>(this) { + @Override + protected void onSuccess(List data) { + projectCateView.setData(data); + if (projectCateView.getData().size() == 0){ + projectCateView.showEmpty(); + }else { + projectCateView.showContent(); + } + } + + @Override + protected void onFail(int errorCode, String errorMsg) { + projectCateView.showFail(errorMsg); + } + }; + + projectCateModel.getProjectCate(rxObserver); + addDisposable(rxObserver); + + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectContract.java b/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectContract.java new file mode 100644 index 0000000..9922b80 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectContract.java @@ -0,0 +1,18 @@ +package com.kdp.wanandroidclient.ui.project; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.ui.core.view.IPageLoadDataView; + +public interface ProjectContract { + interface IProjectPresenter { + void getProjectList(); + void collectArticle(); + void unCollectArticle(); + } + + interface IProjectView extends IPageLoadDataView
{ + int getCid(); + int getArticleId(); + void collect(boolean isCollect,String result); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectFragment.java b/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectFragment.java new file mode 100644 index 0000000..9d530ee --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectFragment.java @@ -0,0 +1,58 @@ +package com.kdp.wanandroidclient.ui.project; +import android.annotation.SuppressLint; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.View; + +import com.kdp.wanandroidclient.bean.ProjectCate; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; +import com.kdp.wanandroidclient.ui.adapter.ProjectFragPagerAdapter; +import com.kdp.wanandroidclient.ui.base.BaseTabFragment; + + +import java.util.ArrayList; +import java.util.List; + +public class ProjectFragment extends BaseTabFragment implements ProjectCateContract.IProjectCateView{ + + private List cateList = new ArrayList<>(); + + @Override + protected ProjectCatePresenter createPresenter() { + return new ProjectCatePresenter(); + } + + @Override + public void setData(List data) { + cateList.clear(); + cateList.addAll(data); + } + + + @Override + public List getData() { + return cateList; + } + @Override + public void showContent() { + ProjectFragPagerAdapter adapter = new ProjectFragPagerAdapter(getChildFragmentManager(),cateList); + viewPager.setAdapter(adapter); + viewPager.setOffscreenPageLimit(cateList.size()); + tabLayout.setupWithViewPager(viewPager); + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mPresenter.getProjectCate(); + } + + + public void scrollToTop(){ + int id = cateList.get(viewPager.getCurrentItem()).getId(); + RxEvent.getInstance().postEvent(Const.EVENT_ACTION.PROJECT_LIST,new Event(Event.Type.SCROLL_TOP,id)); + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectListFragment.java b/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectListFragment.java new file mode 100644 index 0000000..4226331 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectListFragment.java @@ -0,0 +1,158 @@ +package com.kdp.wanandroidclient.ui.project; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.widget.RecyclerView; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; +import com.kdp.wanandroidclient.inter.OnProjectListItemClickListener; +import com.kdp.wanandroidclient.manager.UserInfoManager; +import com.kdp.wanandroidclient.ui.adapter.BaseListAdapter; +import com.kdp.wanandroidclient.ui.adapter.ProjectListAdapter; +import com.kdp.wanandroidclient.ui.base.BaseAbListFragment; +import com.kdp.wanandroidclient.ui.web.WebViewActivity; +import com.kdp.wanandroidclient.utils.IntentUtils; +import com.kdp.wanandroidclient.utils.ToastUtils; + +import java.util.List; + +public class ProjectListFragment extends BaseAbListFragment implements ProjectContract.IProjectView,OnProjectListItemClickListener { + private int id;//文章id + private int position; + private int cid;//分类id + public static ProjectListFragment instantiate(int cid){ + ProjectListFragment instance = new ProjectListFragment(); + Bundle bundle = new Bundle(); + bundle.putInt(Const.BUNDLE_KEY.ID,cid); + instance.setArguments(bundle); + return instance; + } + + @Override + protected boolean isCanLoadMore() { + return true; + } + + @Override + protected boolean isEnableLazy() { + return true; + } + + + @Override + protected void getBundle(Bundle bundle) { + cid = bundle.getInt(Const.BUNDLE_KEY.ID); + } + + @Override + protected void loadDatas() { + mPresenter.getProjectList(); + } + + @Override + protected BaseListAdapter
getListAdapter() { + return new ProjectListAdapter(this); + } + + @Override + protected ProjectPresenter createPresenter() { + return new ProjectPresenter(); + } + + @Override + public int getCid() { + return cid; + } + + @Override + public int getArticleId() { + return id; + } + + @Override + public void collect(boolean isCollect, String result) { + notifyItemData(isCollect, result); + } + + private void notifyItemData(boolean isCollect, String result) { + mListData.get(position).setCollect(isCollect); + mListAdapter.notifyItemDataChanged(position, mRecyclerView); + ToastUtils.showToast(getActivity(), result); + } + + @Override + public void setData(List
data) { + mListData.addAll(data); + } + + @Override + public int getFirstPage() { + return 1; + } + + @Override + public void onCollectClick(int position, int id) { + if (!UserInfoManager.isLogin()) + IntentUtils.goLogin(getActivity()); + this.id = id; + this.position = position; + if (mListData.get(this.position).isCollect()) + mPresenter.unCollectArticle(); + else + mPresenter.collectArticle(); + + } + + @Override + public void onItemClick(int position, Article bean) { + Intent intent = new Intent(getActivity(), WebViewActivity.class); + Bundle bundle = new Bundle(); + bundle.putSerializable(Const.BUNDLE_KEY.OBJ, bean); + bundle.putString(Const.BUNDLE_KEY.TYPE, Const.EVENT_ACTION.PROJECT_LIST); + intent.putExtras(bundle); + startActivity(intent); + } + + + @Override + protected void receiveEvent(Object object) { + Event mEvent = (Event) object; + if (mEvent.type == Event.Type.REFRESH_ITEM) { + Article bean = (Article) mEvent.object; + for (int i = 0; i < mListData.size(); i++) { + if (bean.equals(mListData.get(i))) { + position = i; + notifyItemData(bean.isCollect(), getString(R.string.collect_success)); + } + } + }else if (mEvent.type == Event.Type.SCROLL_TOP && (int)mEvent.object == cid){ + mRecyclerView.smoothScrollToPosition(0); + }else if (mEvent.type == Event.Type.REFRESH_LIST){ + refreshData(); + } + } + + + @Override + protected String registerEvent() { + return Const.EVENT_ACTION.PROJECT_LIST; + } + + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mRecyclerView.addOnScrollListener(onScrollListener); + } + + private RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + RxEvent.getInstance().postEvent(Const.EVENT_ACTION.MAIN,new Event(Event.Type.SCALE,dy)); + } + }; +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectPresenter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectPresenter.java new file mode 100644 index 0000000..8e7bea4 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectPresenter.java @@ -0,0 +1,85 @@ +package com.kdp.wanandroidclient.ui.project; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; +import com.kdp.wanandroidclient.ui.core.model.impl.ProjectModel; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; + +import java.util.List; + +public class ProjectPresenter extends BasePresenter implements ProjectContract.IProjectPresenter { + private ProjectModel projectModel; + private ProjectContract.IProjectView projectView; + + ProjectPresenter() { + this.projectModel = new ProjectModel(); + } + + @Override + public void getProjectList() { + projectView = getView(); + RxPageListObserver
rxPageListObserver = new RxPageListObserver
(this) { + @Override + public void onSuccess(List
mData) { + projectView.setData(mData); + if (projectView.getData().size() == 0){ + projectView.showEmpty(); + }else { + projectView.showContent(); + } + } + + @Override + public void onFail(int errorCode, String errorMsg) { + projectView.showFail(errorMsg); + } + }; + projectModel.getProjectList(projectView.getPage(),projectView.getCid(),rxPageListObserver); + addDisposable(rxPageListObserver); + } + + @Override + public void collectArticle() { + RxObserver mCollectRxObserver = new RxObserver(this) { + @Override + protected void onStart() { + } + @Override + protected void onSuccess(String data) { + projectView.collect(true, AppContext.getContext().getString(R.string.collect_success)); + } + @Override + protected void onFail(int errorCode, String errorMsg) { + view.showFail(errorMsg); + } + + }; + projectModel.collectArticle(projectView.getArticleId(), mCollectRxObserver); + addDisposable(mCollectRxObserver); + } + + @Override + public void unCollectArticle() { + RxObserver unCollectRxObserver = new RxObserver(this) { + + @Override + protected void onStart() { + } + + @Override + protected void onSuccess(String data) { + projectView.collect(false, AppContext.getContext().getString(R.string.uncollect_success)); + } + + @Override + protected void onFail(int errorCode, String errorMsg) { + view.showFail(errorMsg); + } + }; + projectModel.unCollectArticle(projectView.getArticleId(), unCollectRxObserver); + addDisposable(unCollectRxObserver); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeActivity.java b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeActivity.java new file mode 100644 index 0000000..4cab06b --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeActivity.java @@ -0,0 +1,81 @@ +package com.kdp.wanandroidclient.ui.tree; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.FragmentPagerAdapter; +import android.view.View; + +import com.kdp.wanandroidclient.bean.Tree; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; +import com.kdp.wanandroidclient.ui.adapter.TreeFragPagerAdapter; +import com.kdp.wanandroidclient.ui.base.BaseTabActivity; +import com.kdp.wanandroidclient.utils.ViewAnimatorHelper; + +import java.util.ArrayList; +import java.util.List; + +/** + * 知识体系二级分类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class TreeActivity extends BaseTabActivity { + public ViewAnimatorHelper viewAnimatorHelper; + private String mTitle; + private List mTreeDatas = new ArrayList<>(); + @Override + protected boolean initToolbar() { + mToolbar.setTitle(mTitle); + return true; + } + + @Override + protected void getIntent(Intent intent) { + Bundle bundle = intent.getExtras(); + Tree mTree = null; + if (bundle != null) { + mTree = (Tree) bundle.getSerializable(Const.BUNDLE_KEY.OBJ); + } + if (mTree != null) { + mTitle = mTree.getName(); + mTreeDatas = mTree.getChildren(); + } + } + + @SuppressLint("RestrictedApi") + @Override + protected FragmentPagerAdapter createFragPagerAdapter() { + btn_scroll_top.setVisibility(View.VISIBLE); + btn_scroll_top.setOnClickListener(onScrollTopListener); + viewPager.setOffscreenPageLimit(mTreeDatas.size()); + return new TreeFragPagerAdapter(getSupportFragmentManager(), mTreeDatas); + } + + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + viewAnimatorHelper = new ViewAnimatorHelper(); + viewAnimatorHelper.bindView(btn_scroll_top); + } + + private View.OnClickListener onScrollTopListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + int id = mTreeDatas.get(viewPager.getCurrentItem()).getId(); + RxEvent.getInstance().postEvent(Const.EVENT_ACTION.SYSTEM_LIST,new Event(Event.Type.SCROLL_TOP,id)); + } + }; + + + public void scroll(int offsetY){ + if (offsetY > 0 && btn_scroll_top.getVisibility() != View.INVISIBLE && !viewAnimatorHelper.isAnimating()){ + viewAnimatorHelper.hideFloatActionButton(); + }else if (offsetY < 0 && btn_scroll_top.getVisibility() != View.VISIBLE){ + viewAnimatorHelper.showFloatActionButton(); + } + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeContract.java b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeContract.java new file mode 100644 index 0000000..dfbf1e2 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeContract.java @@ -0,0 +1,24 @@ +package com.kdp.wanandroidclient.ui.tree; + +import com.kdp.wanandroidclient.bean.Tree; +import com.kdp.wanandroidclient.ui.core.view.IPageLoadDataView; + +import java.util.List; + +/** + * 知识体系协约类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public interface TreeContract { + + interface ITreePresenter { + + void loadTree(); + } + + interface ITreeView extends IPageLoadDataView { + void setData(List tree); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeFragment.java b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeFragment.java new file mode 100644 index 0000000..16db2a2 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeFragment.java @@ -0,0 +1,86 @@ +package com.kdp.wanandroidclient.ui.tree; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; + +import com.kdp.wanandroidclient.bean.Tree; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; +import com.kdp.wanandroidclient.inter.OnItemClickListener; +import com.kdp.wanandroidclient.ui.adapter.BaseListAdapter; +import com.kdp.wanandroidclient.ui.adapter.TreeAdapter; +import com.kdp.wanandroidclient.ui.base.BaseAbListFragment; +import com.kdp.wanandroidclient.ui.main.MainActivity; + +import java.util.List; + +/** + * 知识体系 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class TreeFragment extends BaseAbListFragment implements TreeContract.ITreeView, OnItemClickListener { + + @Override + protected TreePresenter createPresenter() { + return new TreePresenter(); + } + + //加载列表数据 + @Override + protected void loadDatas() { + mPresenter.loadTree(); + } + + @Override + protected BaseListAdapter getListAdapter() { + return new TreeAdapter(this); + } + + @Override + public void setData(List data) { + mListData.clear(); + mListData.addAll(data); + } + + + //进入子分类页面 + @Override + public void onItemClick(int position,Tree mTree) { + Intent intent = new Intent(getActivity(), TreeActivity.class); + Bundle b = new Bundle(); + b.putSerializable(Const.BUNDLE_KEY.OBJ, mTree); + intent.putExtras(b); + startActivity(intent); + } + + @Override + protected void receiveEvent(Object object) { + Event mEvent = (Event) object; + if (mEvent.type == Event.Type.SCROLL_TOP){ + mRecyclerView.smoothScrollToPosition(0); + } + } + + @Override + protected String registerEvent() { + return Const.EVENT_ACTION.SYSTEM; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mRecyclerView.addOnScrollListener(onScrollListener); + } + + private RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + RxEvent.getInstance().postEvent(Const.EVENT_ACTION.MAIN,new Event(Event.Type.SCALE,dy)); + } + }; +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeListContract.java b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeListContract.java new file mode 100644 index 0000000..f82f168 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeListContract.java @@ -0,0 +1,28 @@ +package com.kdp.wanandroidclient.ui.tree; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.ui.core.view.IPageLoadDataView; + +/** + * 知识体系列表协约类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public interface TreeListContract { + + interface ITreePresenter { + + void loadTreeList(); + + void collectArticle(); + + void unCollectArticle(); + } + + interface ITreeListView extends IPageLoadDataView
{ + int getCid(); + int getArticleId();//文章id + void collect(boolean isCollect,String result); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeListFragment.java b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeListFragment.java new file mode 100644 index 0000000..ab541af --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeListFragment.java @@ -0,0 +1,173 @@ +package com.kdp.wanandroidclient.ui.tree; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.widget.RecyclerView; +import android.util.Log; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; +import com.kdp.wanandroidclient.inter.OnArticleListItemClickListener; +import com.kdp.wanandroidclient.manager.UserInfoManager; +import com.kdp.wanandroidclient.ui.adapter.ArticleListAdapter; +import com.kdp.wanandroidclient.ui.adapter.BaseListAdapter; +import com.kdp.wanandroidclient.ui.base.BaseAbListFragment; +import com.kdp.wanandroidclient.ui.web.WebViewActivity; +import com.kdp.wanandroidclient.utils.IntentUtils; +import com.kdp.wanandroidclient.utils.ToastUtils; + +import java.util.List; + +/** + * 知识体系下的文章 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class TreeListFragment extends BaseAbListFragment implements TreeListContract.ITreeListView, OnArticleListItemClickListener { + private int cid;//分类id + private int id;//文章id + private int position; + + //实例化fragment + public static TreeListFragment instantiate(int cid) { + TreeListFragment instance = new TreeListFragment(); + Bundle b = new Bundle(); + b.putInt(Const.BUNDLE_KEY.ID, cid); + instance.setArguments(b); + return instance; + } + + + @Override + protected boolean isEnableLazy() { + return true; + } + + @Override + protected TreeListPresenter createPresenter() { + return new TreeListPresenter(); + } + + @Override + protected void getBundle(Bundle bundle) { + cid = bundle.getInt(Const.BUNDLE_KEY.ID); + } + + @Override + protected boolean isCanLoadMore() { + return true; + } + + @Override + protected BaseListAdapter
getListAdapter() { + return new ArticleListAdapter(this, Const.LIST_TYPE.TREE); + } + + //分类id + @Override + public int getCid() { + return cid; + } + + //列表数据 + @Override + public void setData(List
data) { + mListData.addAll(data); + } + + //加载列表数据 + @Override + protected void loadDatas() { + mPresenter.loadTreeList(); + } + + //文章id + @Override + public int getArticleId() { + return id; + } + + //收藏结果 + @Override + public void collect(boolean isCollect, String result) { + notifyItemData(isCollect, result); + } + + private void notifyItemData(boolean isCollect, String result) { + mListData.get(position).setCollect(isCollect); + mListAdapter.notifyItemDataChanged(position, mRecyclerView); + ToastUtils.showToast(getActivity(), result); + } + + //进入详情 + @Override + public void onItemClick(int position,Article bean) { + Intent intent = new Intent(getActivity(), WebViewActivity.class); + Bundle bundle = new Bundle(); + bundle.putSerializable(Const.BUNDLE_KEY.OBJ, bean); + bundle.putString(Const.BUNDLE_KEY.TYPE, Const.EVENT_ACTION.SYSTEM_LIST); + intent.putExtras(bundle); + startActivity(intent); + } + + @Override + public void onDeleteCollectClick(int position, int id, int originId) { + } + + //收藏click + @Override + public void onCollectClick(int position, int id) { + if (!UserInfoManager.isLogin()) + IntentUtils.goLogin(getActivity()); + this.position = position; + this.id = id; + if (mListData.get(this.position).isCollect()) + mPresenter.unCollectArticle(); + else + mPresenter.collectArticle(); + } + + + @Override + protected void receiveEvent(Object object) { + Event mEvent = (Event) object; + if (mEvent.type == Event.Type.REFRESH_ITEM) { + Article bean = (Article) mEvent.object; + for (int i = 0; i < mListData.size(); i++) { + if (bean.equals(mListData.get(i))) { + position = i; + notifyItemData(bean.isCollect(), getString(R.string.collect_success)); + } + } + }else if (mEvent.type == Event.Type.SCROLL_TOP && (int) mEvent.object == cid){ + mRecyclerView.smoothScrollToPosition(0); + }else if (mEvent.type == Event.Type.REFRESH_LIST){ + refreshData(); + } + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mRecyclerView.addOnScrollListener(onScrollListener); + } + + @Override + protected String registerEvent() { + return Const.EVENT_ACTION.SYSTEM_LIST; + } + + private RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + if (getActivity() == null) return; + ((TreeActivity)getActivity()).scroll(dy); + } + }; + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeListPresenter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeListPresenter.java new file mode 100644 index 0000000..21efc55 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeListPresenter.java @@ -0,0 +1,96 @@ +package com.kdp.wanandroidclient.ui.tree; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; +import com.kdp.wanandroidclient.ui.core.model.impl.TreeListModel; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; + +import java.util.List; + +/** + * 知识体系列表Presenter + * author: 曾文海 + * date: 2023/5/31 + */ + +public class TreeListPresenter extends BasePresenter implements TreeListContract.ITreePresenter { + + private TreeListModel treeListModel; + private TreeListContract.ITreeListView treeListView; + + TreeListPresenter() { + treeListModel = new TreeListModel(); + } + + /** + * 获取知识体系下的文章 + */ + @Override + public void loadTreeList() { + treeListView = getView(); + RxPageListObserver
mTreeListRxPageListObserver = new RxPageListObserver
(this) { + @Override + public void onSuccess(List
mData) { + treeListView.setData(mData); + if (treeListView.getData().size() == 0) + treeListView.showEmpty(); + else + treeListView.showContent(); + } + + @Override + public void onFail(int errorCode, String errorMsg) { + treeListView.showFail(errorMsg); + } + }; + treeListModel.getTreeList(treeListView.getPage(), treeListView.getCid(), mTreeListRxPageListObserver); + addDisposable(mTreeListRxPageListObserver); + + } + + @Override + public void collectArticle() { + treeListView = getView(); + RxObserver mCollectRxObserver = new RxObserver(this) { + @Override + protected void onStart() { + } + @Override + protected void onSuccess(String data) { + treeListView.collect(true, AppContext.getContext().getString(R.string.collect_success)); + } + @Override + protected void onFail(int errorCode, String errorMsg) { + view.showFail(errorMsg); + } + + }; + treeListModel.collectArticle(treeListView.getArticleId(), mCollectRxObserver); + addDisposable(mCollectRxObserver); + } + + @Override + public void unCollectArticle() { + treeListView = getView(); + RxObserver unCollectRxObserver = new RxObserver(this) { + + @Override + protected void onStart() { + } + @Override + protected void onSuccess(String data) { + treeListView.collect(false, AppContext.getContext().getString(R.string.uncollect_success)); + } + + @Override + protected void onFail(int errorCode, String errorMsg) { + view.showFail(errorMsg); + } + }; + treeListModel.unCollectArticle(treeListView.getArticleId(), unCollectRxObserver); + addDisposable(unCollectRxObserver); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreePresenter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreePresenter.java new file mode 100644 index 0000000..d36d46c --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreePresenter.java @@ -0,0 +1,54 @@ +package com.kdp.wanandroidclient.ui.tree; + +import com.kdp.wanandroidclient.bean.Tree; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.ui.core.model.impl.TreeModel; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; +import java.util.List; + +/** + * 知识体系 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class TreePresenter extends BasePresenter implements TreeContract.ITreePresenter { + + private TreeModel mTreeModel; + private TreeContract.ITreeView mSystemView; + + TreePresenter() { + mTreeModel = new TreeModel(); + } + + /** + * 获取知识体系下的分类 + */ + @Override + public void loadTree() { + mSystemView = getView(); + RxObserver> mTreeRxObserver = new RxObserver>(this) { + @Override + protected void onSuccess(List data) { + mSystemView.setData(data); + if (mSystemView.getData().size() == 0) { + mSystemView.showEmpty(); + } else { + mSystemView.showContent(); + } + } + @Override + protected void onFail(int errorCode, String errorMsg) { + mSystemView.showFail(errorMsg); + } + + @Override + public void onError(Throwable e) { + super.onError(e); + mSystemView.showError(); + } + }; + mTreeModel.getTree(mTreeRxObserver); + addDisposable(mTreeRxObserver); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/user/AboutUsActivity.java b/app/src/main/java/com/kdp/wanandroidclient/ui/user/AboutUsActivity.java new file mode 100644 index 0000000..9368d48 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/user/AboutUsActivity.java @@ -0,0 +1,75 @@ +package com.kdp.wanandroidclient.ui.user; + +import android.content.res.ColorStateList; +import android.os.Build; +import android.os.Bundle; +import android.support.design.widget.CollapsingToolbarLayout; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.text.Html; +import android.text.method.LinkMovementMethod; +import android.view.View; +import android.widget.TextView; +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.utils.AppUtils; +import com.kdp.wanandroidclient.utils.LightStatusbarUtils; + + +/** + * 关于我们 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class AboutUsActivity extends AppCompatActivity { + private TextView mVersionView, mIntroduceView; + + + @Override + protected void onCreate(Bundle bundle) { + LightStatusbarUtils.setLightStatusBar(this,false); + super.onCreate(bundle); + setContentView(R.layout.activity_about_us); + CollapsingToolbarLayout mCollapsingToolbarLayout = findViewById(R.id.collapsingbarlayout); + Toolbar mToolbar = findViewById(R.id.toolbar); + mToolbar.setTitle(R.string.about_us); + setSupportActionBar(mToolbar); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + //设置展开后的字体颜色 + mCollapsingToolbarLayout.setExpandedTitleTextColor(ColorStateList.valueOf(ContextCompat.getColor(this,R.color.white))); + //设置收缩后的字体颜色 + mCollapsingToolbarLayout.setCollapsedTitleTextColor(ColorStateList.valueOf(ContextCompat.getColor(this,R.color.white))); + mToolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + mVersionView = findViewById(R.id.version); + mIntroduceView = findViewById(R.id.introduce); + setVersion(); + setIntroduce(); + + } + + private void setIntroduce() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + mIntroduceView.setText(Html.fromHtml(getString(R.string.about_us_introduce), Html.FROM_HTML_MODE_LEGACY)); + } else { + mIntroduceView.setText(Html.fromHtml(getString(R.string.about_us_introduce))); + } + //设置跳转 + mIntroduceView.setMovementMethod(LinkMovementMethod.getInstance()); + + } + //设置版本 + private void setVersion() { + String mVersionFormat = getString(R.string.version_format); + String mVersionName = AppUtils.getVersionName(this); + String mAppName = getString(R.string.app_name); + String mVersionStr = String.format(mVersionFormat, mAppName, mVersionName); + mVersionView.setText(mVersionStr); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/user/CollectArticleActivity.java b/app/src/main/java/com/kdp/wanandroidclient/ui/user/CollectArticleActivity.java new file mode 100644 index 0000000..84bfef5 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/user/CollectArticleActivity.java @@ -0,0 +1,110 @@ +package com.kdp.wanandroidclient.ui.user; + +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.inter.OnArticleListItemClickListener; +import com.kdp.wanandroidclient.ui.adapter.ArticleListAdapter; +import com.kdp.wanandroidclient.ui.adapter.BaseListAdapter; +import com.kdp.wanandroidclient.ui.base.BaseAbListActivity; +import com.kdp.wanandroidclient.ui.web.WebViewActivity; + +import java.util.List; + +/** + * 收藏的文章 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class CollectArticleActivity extends BaseAbListActivity implements UserContract.IUserView, OnArticleListItemClickListener { + + private int id;//文章id + private int originId;//首页列表的文章id + private int position; + + @Override + protected boolean initToolbar() { + mToolbar.setTitle(R.string.favorite_article); + return true; + } + + @Override + protected boolean isCanLoadMore() { + return true; + } + + @Override + protected View initHeaderView() { + return null; + } + + + @Override + protected void loadDatas() { + mPresenter.loadCollectList(); + } + + @Override + protected BaseListAdapter
getListAdapter() { + return new ArticleListAdapter(this, Const.LIST_TYPE.COLLECT); + } + + + @Override + protected UserPresenter createPresenter() { + return new UserPresenter(); + } + + @Override + public void setData(List
data) { + mListData.addAll(data); + } + + + @Override + public void onItemClick(int position,Article bean) { + Intent intent = new Intent(this, WebViewActivity.class); + Bundle bundle = new Bundle(); + bundle.putSerializable(Const.BUNDLE_KEY.OBJ, bean); + intent.putExtras(bundle); + startActivity(intent); + } + + @Override + public int getArticleId() { + return id; + } + + @Override + public void onDeleteCollectClick(int position, int id, int originId) { + this.id = id; + this.originId = originId; + this.position = position; + mPresenter.deleteCollectArticle(); + } + + @Override + public void onCollectClick(int position, int id) { + + } + + @Override + public int getOriginId() { + return originId; + } + + //删除收藏 + @Override + public void deleteCollect() { + if (mListData.size() > 1) { + mListData.remove(position); + mListAdapter.notifyItemDataRemove(position, mRecyclerView); + } else { + loadDatas(); + } + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/user/UserContract.java b/app/src/main/java/com/kdp/wanandroidclient/ui/user/UserContract.java new file mode 100644 index 0000000..529c91a --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/user/UserContract.java @@ -0,0 +1,23 @@ +package com.kdp.wanandroidclient.ui.user; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.ui.core.view.IPageLoadDataView; + +/** + * 用户协约类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public interface UserContract { + interface IUserPresenter { + void loadCollectList(); + void deleteCollectArticle(); + } + + interface IUserView extends IPageLoadDataView
{ + int getOriginId(); + int getArticleId();//文章id + void deleteCollect(); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/user/UserPresenter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/user/UserPresenter.java new file mode 100644 index 0000000..145dbf8 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/user/UserPresenter.java @@ -0,0 +1,73 @@ +package com.kdp.wanandroidclient.ui.user; + +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.net.callback.RxPageListObserver; +import com.kdp.wanandroidclient.ui.core.model.impl.UserModel; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; +import java.util.List; + +/** + * 和用户相关的Presenter + * author: 曾文海 + * date: 2023/5/31 + */ + +public class UserPresenter extends BasePresenter implements UserContract.IUserPresenter { + private UserContract.IUserView mUserView; + private UserModel mUserModel; + + UserPresenter() { + this.mUserModel = new UserModel(); + } + + /** + * 删除收藏 + */ + @Override + public void deleteCollectArticle() { + mUserView = getView(); + RxObserver mDeleteRxObserver = new RxObserver(this) { + @Override + protected void onSuccess(String data) { + mUserView.deleteCollect(); + } + + @Override + protected void onFail(int errorCode, String errorMsg) { + mUserView.showFail(errorMsg); + } + + @Override + public void showLoading() { + } + }; + mUserModel.deleteCollectArticle(mUserView.getArticleId(), mUserView.getOriginId(), mDeleteRxObserver); + addDisposable(mDeleteRxObserver); + } + + /** + * 获取收藏的列表 + */ + @Override + public void loadCollectList() { + mUserView = getView(); + RxPageListObserver
mCollectRxPageListObserver = new RxPageListObserver
(this) { + @Override + public void onSuccess(List
mData) { + mUserView.setData(mData); + if (mUserView.getData().size() == 0) + mUserView.showEmpty(); + else + mUserView.showContent(); + } + + @Override + public void onFail(int errorCode, String errorMsg) { + mUserView.showFail(errorMsg); + } + }; + mUserModel.getCollectArticleList(mUserView.getPage(), mCollectRxPageListObserver); + addDisposable(mCollectRxPageListObserver); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/web/WebViewActivity.java b/app/src/main/java/com/kdp/wanandroidclient/ui/web/WebViewActivity.java new file mode 100644 index 0000000..4c1f58c --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/web/WebViewActivity.java @@ -0,0 +1,202 @@ +package com.kdp.wanandroidclient.ui.web; + +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.text.Html; +import android.text.TextUtils; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.FrameLayout; +import com.just.agentweb.AgentWeb; +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.bean.Article; +import com.kdp.wanandroidclient.common.Const; +import com.kdp.wanandroidclient.event.Event; +import com.kdp.wanandroidclient.event.RxEvent; +import com.kdp.wanandroidclient.manager.UserInfoManager; +import com.kdp.wanandroidclient.ui.base.BasePresenterActivity; +import com.kdp.wanandroidclient.utils.IntentUtils; + +import java.lang.reflect.Method; + +/** + * 文章详情H5 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class WebViewActivity extends BasePresenterActivity implements WebViewContract.IWebView { + private FrameLayout mContainer; + private AgentWeb mAgentWeb; + private Article bean; + private String actionType; + private int id; + private String title = ""; + private String url = ""; + + @Override + protected int getLayoutId() { + return R.layout.activity_webview; + } + + @Override + protected boolean initToolbar() { + mToolbar.setTitle(title); + return true; + } + + @Override + protected void getIntent(Intent intent) { + Bundle bundle = intent.getExtras(); + assert bundle != null; + bean = (Article) bundle.getSerializable(Const.BUNDLE_KEY.OBJ); + actionType = intent.getStringExtra(Const.BUNDLE_KEY.TYPE); + if (bean != null) { + id = bean.getId(); + title = bean.getTitle(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + title = Html.fromHtml(title, Html.FROM_HTML_MODE_LEGACY).toString(); + } else { + title = Html.fromHtml(title).toString(); + } + url = bean.getLink(); + } + } + + @Override + protected WebViewPresenter createPresenter() { + return new WebViewPresenter(); + } + + @Override + protected void initViews() { + mContainer = findViewById(R.id.container); + } + + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + mAgentWeb = AgentWeb.with(this) + .setAgentWebParent(mContainer, new FrameLayout.LayoutParams(-1, -1)) + .useDefaultIndicator(R.color.black) + .createAgentWeb() + .ready() + .go(url); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + + //将事件交给AgentWeb做处理 + if (mAgentWeb.handleKeyEvent(keyCode, event)) { + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.content_menu_setting, menu); + if (TextUtils.isEmpty(actionType)) + menu.findItem(R.id.collect).setVisible(false); + return true; + + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.share: + share(); + break; + case R.id.collect: + collect(); + break; + case R.id.browser: + openInBrowser(); + break; + default: + break; + } + + return super.onOptionsItemSelected(item); + } + + // 让菜单同时显示图标和文字 + @Override + public boolean onMenuOpened(int featureId, Menu menu) { + if (menu != null) { + if (menu.getClass().getSimpleName().equalsIgnoreCase("MenuBuilder")) { + try { + Method method = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE); + method.setAccessible(true); + method.invoke(menu, true); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + return super.onMenuOpened(featureId, menu); + } + + //分享 + private void share() { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_TEXT, "玩Android分享(" + title + "):" + url); + intent.setType("text/plain");//分享文本 + startActivity(Intent.createChooser(intent, "分享")); + } + + //收藏 + private void collect() { + if (!UserInfoManager.isLogin()) + IntentUtils.goLogin(this); + if (bean.isCollect()) return; + mPresenter.collectArticle(); + } + + //打开浏览器 + private void openInBrowser() { + Intent intent = new Intent(Intent.ACTION_VIEW); + Uri uri = Uri.parse(url); + intent.setData(uri); + startActivity(intent); + } + + @Override + protected void onResume() { + mAgentWeb.getWebLifeCycle().onResume(); + super.onResume(); + } + + @Override + protected void onPause() { + mAgentWeb.getWebLifeCycle().onPause(); + super.onPause(); + + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mAgentWeb.destroy(); + } + + @Override + public int getArticleId() { + return id; + } + + @Override + public void collect(boolean isCollect, String result) { + if (!bean.isCollect()) { + bean.setCollect(isCollect); + Event mEvent = new Event(Event.Type.REFRESH_ITEM, bean); + RxEvent.getInstance().postEvent(actionType,mEvent); + } + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/web/WebViewContract.java b/app/src/main/java/com/kdp/wanandroidclient/ui/web/WebViewContract.java new file mode 100644 index 0000000..088b8c6 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/web/WebViewContract.java @@ -0,0 +1,20 @@ +package com.kdp.wanandroidclient.ui.web; + +import com.kdp.wanandroidclient.ui.core.view.IView; + +/** + * 文章详情页协约类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class WebViewContract { + interface IWebViewPresenter{ + void collectArticle(); + } + + interface IWebView extends IView{ + int getArticleId(); + void collect(boolean isCollect,String result); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/ui/web/WebViewPresenter.java b/app/src/main/java/com/kdp/wanandroidclient/ui/web/WebViewPresenter.java new file mode 100644 index 0000000..28cb2ab --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/ui/web/WebViewPresenter.java @@ -0,0 +1,44 @@ +package com.kdp.wanandroidclient.ui.web; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.application.AppContext; +import com.kdp.wanandroidclient.net.callback.RxObserver; +import com.kdp.wanandroidclient.ui.core.model.impl.CommonModel; +import com.kdp.wanandroidclient.ui.core.presenter.BasePresenter; + +/** + * 文章详情页Presenter + * author: 曾文海 + * date: 2023/5/31 + */ + +public class WebViewPresenter extends BasePresenter implements WebViewContract.IWebViewPresenter { + private CommonModel commonModel; + private WebViewContract.IWebView webView; + + WebViewPresenter() { + this.commonModel = new CommonModel(); + } + + @Override + public void collectArticle() { + webView = getView(); + RxObserver mCollectRxObserver = new RxObserver(this) { + @Override + protected void onStart() { + } + @Override + protected void onSuccess(String data) { + webView.collect(true, AppContext.getContext().getString(R.string.collect_success)); + } + @Override + protected void onFail(int errorCode, String errorMsg) { + view.showFail(errorMsg); + } + + }; + commonModel.collectArticle(webView.getArticleId(), mCollectRxObserver); + addDisposable(mCollectRxObserver); + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/utils/AesEncryptionUtils.java b/app/src/main/java/com/kdp/wanandroidclient/utils/AesEncryptionUtils.java new file mode 100644 index 0000000..ec1d355 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/utils/AesEncryptionUtils.java @@ -0,0 +1,140 @@ +package com.kdp.wanandroidclient.utils; + +import android.util.Base64; + +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +/** + * 加密工具 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class AesEncryptionUtils { + + private static final String ENCRYPTION_ALGORITHM = "AES"; + private static final String key = "wanandroid"; //随机源的种子 + + /** + * 生成AES密钥 + */ + public static SecretKeySpec createKey() { + try { + //指定加密算法的名称为AES, + KeyGenerator keyGenerator = KeyGenerator.getInstance(ENCRYPTION_ALGORITHM); + //初始化密钥生成器,指定密钥的长度(单位:bit), + //SecureRandom是生成安全随机数序列 + SecureRandom secureRandom = new SecureRandom(key.getBytes()); + keyGenerator.init(128, secureRandom); + //生成原始对称密钥 + SecretKey secretKey = keyGenerator.generateKey(); + //返回编码格式的密钥 + byte[] enCodeFormat = secretKey.getEncoded(); + //根据字节数组生成AES专用密钥 + SecretKeySpec aesKeySpec = getSecretKey(enCodeFormat); + //返回AES密钥 + return aesKeySpec; + + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 根据字节数组生成AES专用密钥 + * + * @param bytes 字节数组 + * @return SecretKeySpec + */ + public static SecretKeySpec getSecretKey(byte[] bytes) { + return new SecretKeySpec(bytes, ENCRYPTION_ALGORITHM); + } + + + /** + * 明文加密 + * + * @param key AES密钥 + * @param content 加密前的原内容 + * @return + */ + public static String encrypt(SecretKeySpec key, String content){ + try { + + //根据指定算法生成密码器 + Cipher ciper = Cipher.getInstance(ENCRYPTION_ALGORITHM); + //初始化密码器, + // 第一个参数为密码的操作模式:加密(ENCRYPT_MODE),解密(DECRYPT_MODE) + //第二个参数为AES密钥 + ciper.init(Cipher.ENCRYPT_MODE, key); + //获取加密内容的字节数组 + byte[] contentBytes = content.getBytes("utf-8"); + byte[] aesBytes = ciper.doFinal(contentBytes); + //为了避免解密时数据丢失,将加密后的内容进行Base64编码后再返回 + String aesContent = Base64.encodeToString(aesBytes, Base64.DEFAULT); + //将加密后的密文转为字符串返回 + return aesContent; + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + e.printStackTrace(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 密文解密 + * + * @param key AES密钥 + * @param content 加密后的内容 + * @return + */ + public static String decrypt(SecretKeySpec key, String content){ + try { + + //根据指定算法生成密码器 + Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM); + //初始化密码器, + // 第一个参数为密码的操作模式:加密(ENCRYPT_MODE),解密(DECRYPT_MODE) + //第二个参数为AES密钥 + cipher.init(Cipher.DECRYPT_MODE, key); + //先将密文进行Base64解码 + byte[] aesBytes = Base64.decode(content, Base64.DEFAULT); + //将密文进行解密 + byte[] contentBytes = cipher.doFinal(aesBytes); + //将解密后的内容转成字符串并返回 + return new String(contentBytes, "utf-8"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/utils/AppUtils.java b/app/src/main/java/com/kdp/wanandroidclient/utils/AppUtils.java new file mode 100644 index 0000000..c53fb84 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/utils/AppUtils.java @@ -0,0 +1,32 @@ +package com.kdp.wanandroidclient.utils; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; + +/** + * App工具类 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class AppUtils { + + /** + * 获取版本名称 + * + * @param context + * @return + */ + public static String getVersionName(Context context) { + PackageManager pm = context.getPackageManager(); + try { + PackageInfo info = pm.getPackageInfo(context.getPackageName(), 0); + return info.versionName; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return ""; + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/utils/DateUtils.java b/app/src/main/java/com/kdp/wanandroidclient/utils/DateUtils.java new file mode 100644 index 0000000..e1a29ba --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/utils/DateUtils.java @@ -0,0 +1,34 @@ +package com.kdp.wanandroidclient.utils; + +import java.text.SimpleDateFormat; + +/** + * 时间解析工具 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class DateUtils { + + public static String parseTime(long publishTime) { + long time; + time = (System.currentTimeMillis() - publishTime) / 1000; + int day, hour, minute, senond; + day = (int) (time / 3600 / 24); + + if (day == 1) + return "1天前"; + if (day > 1) + return new SimpleDateFormat("yyyy-MM-dd").format(publishTime); + hour = (int) (time / 3600); + if (hour > 0) + return String.valueOf(hour) + "小时前"; + minute = (int) (time / 60); + if (minute > 0) + return String.valueOf(minute) + "分钟前"; + senond = (int) time; + if (senond >= 0) + return "刚刚"; + return ""; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/utils/IntentUtils.java b/app/src/main/java/com/kdp/wanandroidclient/utils/IntentUtils.java new file mode 100644 index 0000000..fb219f5 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/utils/IntentUtils.java @@ -0,0 +1,19 @@ +package com.kdp.wanandroidclient.utils; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; + +import com.kdp.wanandroidclient.ui.logon.LogonActivity; + +/*** + * @author kdp + * @date 2019/3/23 14:46 + * @description + */ +public class IntentUtils { + public static void goLogin(Activity activity){ + if (activity!= null) + activity.startActivity(new Intent(activity, LogonActivity.class)); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/utils/LightStatusbarUtils.java b/app/src/main/java/com/kdp/wanandroidclient/utils/LightStatusbarUtils.java new file mode 100644 index 0000000..9ab2cbf --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/utils/LightStatusbarUtils.java @@ -0,0 +1,112 @@ +package com.kdp.wanandroidclient.utils; + +import android.app.Activity; +import android.os.Build; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * 修改状态栏字体颜色 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class LightStatusbarUtils { + public static void setLightStatusBar(Activity activity, boolean dark) { + switch (RomUtils.getLightStatausBarAvailableRomType()) { + case RomUtils.AvailableRomType.MIUI: + setMIUILightStatusBar(activity, dark); + break; + case RomUtils.AvailableRomType.FLYME: + setFlymeLightStatusBar(activity, dark); + break; + case RomUtils.AvailableRomType.ANDROID_NATIVE: + setAndroidNativeLightStatusBar(activity, dark); + break; + case RomUtils.AvailableRomType.OTHER: + // N/A do nothing + break; + } + } + + /** + * 设置小米状态栏透明 + * + * @param activity + * @param darkmode + * @return + */ + private static boolean setMIUILightStatusBar(Activity activity, boolean darkmode) { + Class clazz = activity.getWindow().getClass(); + try { + int darkModeFlag = 0; + Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams"); + Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE"); + darkModeFlag = field.getInt(layoutParams); + Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class); + extraFlagField.invoke(activity.getWindow(), darkmode ? darkModeFlag : 0, darkModeFlag); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + + /** + * 设置魅族状态栏透明 + * + * @param activity + * @param dark + * @return + */ + private static boolean setFlymeLightStatusBar(Activity activity, boolean dark) { + boolean result = false; + if (activity != null) { + try { + WindowManager.LayoutParams lp = activity.getWindow().getAttributes(); + Field darkFlag = WindowManager.LayoutParams.class + .getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON"); + Field meizuFlags = WindowManager.LayoutParams.class + .getDeclaredField("meizuFlags"); + darkFlag.setAccessible(true); + meizuFlags.setAccessible(true); + int bit = darkFlag.getInt(null); + int value = meizuFlags.getInt(lp); + if (dark) { + value |= bit; + } else { + value &= ~bit; + } + meizuFlags.setInt(lp, value); + activity.getWindow().setAttributes(lp); + result = true; + } catch (Exception e) { + } + } + return result; + } + + /** + * 6.0以上适用 + * 设置状态栏字体色和状态栏颜色 + * + * @param activity + * @param dark 状态栏字体颜色,true黑false白色 + */ + private static void setAndroidNativeLightStatusBar(Activity activity, boolean dark) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Window window = activity.getWindow(); + View decor = window.getDecorView(); + if (dark) { + decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + } else { + decor.setSystemUiVisibility(0); + } + } + } +} + diff --git a/app/src/main/java/com/kdp/wanandroidclient/utils/LogUtils.java b/app/src/main/java/com/kdp/wanandroidclient/utils/LogUtils.java new file mode 100644 index 0000000..8ee4fbc --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/utils/LogUtils.java @@ -0,0 +1,97 @@ +package com.kdp.wanandroidclient.utils; + +import android.util.Log; + +/** + + * Log工具类 + */ + +public class LogUtils { + private static String className; //类名 + private static String methodName; //方法名 + private static int lineNumber; //行数 + public static boolean isDebug; //是否开启debug模式,打印日志 + + /** + * 获取日志信息 + * + * @param traceElements + */ + private static void getLogInfo(StackTraceElement[] traceElements) { + if (!isDebug) return; + className = traceElements[1].getFileName(); + methodName = traceElements[1].getMethodName(); + lineNumber = traceElements[1].getLineNumber(); + } + + /** + * i + * + * @param message + */ + public static void i(String message) { + getLogInfo(new Throwable().getStackTrace()); + Log.i(className, createLog(message)); + } + + /** + * v + * + * @param message + */ + public static void v(String message) { + getLogInfo(new Throwable().getStackTrace()); + Log.v(className, createLog(message)); + } + + /** + * d + * + * @param message + */ + public static void d(String message) { + getLogInfo(new Throwable().getStackTrace()); + Log.d(className, createLog(message)); + } + + /** + * w + * + * @param message + */ + public static void w(String message) { + getLogInfo(new Throwable().getStackTrace()); + Log.w(className, createLog(message)); + } + + /** + * e + * + * @param message + */ + public static void e(String message) { + getLogInfo(new Throwable().getStackTrace()); + Log.e(className, createLog(message)); + } + + + /** + * 创建Log日志 + * + * @param log + * @return + */ + private static String createLog(String log) { + StringBuilder sb = new StringBuilder(); + sb.append(methodName) + .append("(") + .append(className) + .append(":") + .append(lineNumber) + .append(")"); + sb.append(log); + return sb.toString(); + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/utils/NetworkUtils.java b/app/src/main/java/com/kdp/wanandroidclient/utils/NetworkUtils.java new file mode 100644 index 0000000..aa093a0 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/utils/NetworkUtils.java @@ -0,0 +1,23 @@ +package com.kdp.wanandroidclient.utils; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +/** + * 网络工具 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class NetworkUtils { + + //判断是否有网络连接 + public static boolean isAvailable(Context context) { + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); + if (networkInfo != null) + return networkInfo.isAvailable(); + return false; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/utils/PreUtils.java b/app/src/main/java/com/kdp/wanandroidclient/utils/PreUtils.java new file mode 100644 index 0000000..530b664 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/utils/PreUtils.java @@ -0,0 +1,96 @@ +package com.kdp.wanandroidclient.utils; + +import android.content.Context; +import android.content.SharedPreferences; + + +/** + * 缓存 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class PreUtils { + + private static String pre_name = "user_info"; + private static Context mContext; + + public static void init(Context context) { + mContext = context; + } + /** + * 存 + * + * @param key + * @param value + */ + public static void put(String key, Object value) { + SharedPreferences sp = mContext.getSharedPreferences(pre_name, Context.MODE_PRIVATE); + SharedPreferences.Editor et = sp.edit(); + if (value instanceof String) { + et.putString(key, (String) value); + } else if (value instanceof Integer) { + et.putInt(key, (Integer) value); + } else if (value instanceof Boolean) { + et.putBoolean(key, (Boolean) value); + } else if (value instanceof Float) { + et.putFloat(key, (Float) value); + } else if (value instanceof Long) { + et.putLong(key, (Long) value); + } + et.commit(); + } + + public static SharedPreferences getSharedPreferences() { + if (mContext == null) + throw new IllegalStateException("SharedPreferences have not initialized"); + return mContext.getSharedPreferences(pre_name, Context.MODE_PRIVATE); + } + + /** + * 取 + * + * @param key + * @param defauleValue + * @return + */ + public static Object get(String key, Object defauleValue) { + SharedPreferences sp = getSharedPreferences(); + if (defauleValue instanceof String) { + return sp.getString(key, (String) defauleValue); + } else if (defauleValue instanceof Integer) { + return sp.getInt(key, (Integer) defauleValue); + } else if (defauleValue instanceof Boolean) { + return sp.getBoolean(key, (Boolean) defauleValue); + } else if (defauleValue instanceof Float) { + return sp.getFloat(key, (Float) defauleValue); + } else if (defauleValue instanceof Long) { + return sp.getLong(key, (Long) defauleValue); + } + return null; + } + + /** + * 移除指定key + * + * @param remove_key + */ + public static void remove(String remove_key) { + SharedPreferences sp = getSharedPreferences(); + SharedPreferences.Editor et = sp.edit(); + et.remove(remove_key); + et.commit(); + } + + /** + * 清空数据 + */ + public static void clearAll() { + SharedPreferences sp = getSharedPreferences(); + SharedPreferences.Editor et = sp.edit(); + et.clear(); + et.commit(); + } + + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/utils/RomUtils.java b/app/src/main/java/com/kdp/wanandroidclient/utils/RomUtils.java new file mode 100644 index 0000000..eaa28a4 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/utils/RomUtils.java @@ -0,0 +1,110 @@ +package com.kdp.wanandroidclient.utils; + +import android.os.Build; +import android.text.TextUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * Room工具 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class RomUtils { + + class AvailableRomType { + static final int MIUI = 1; //小米 + static final int FLYME = 2; //华为 + static final int ANDROID_NATIVE = 3; //android 原生 + static final int OTHER = 4; + } + + /** + * 获取ROOM + * @return + */ + public static int getLightStatausBarAvailableRomType() { + if (isMIUIV6OrAbove()) { + return AvailableRomType.MIUI; + } + + if (isFlymeV4OrAbove()) { + return AvailableRomType.FLYME; + } + + if (isAndroidMOrAbove()) { + return AvailableRomType.ANDROID_NATIVE; + } + + return AvailableRomType.OTHER; + } + + + //MIUI V6对应的versionCode是4 + //MIUI V7对应的versionCode是5 + private static boolean isMIUIV6OrAbove() { + String miuiVersionCodeStr = getSystemProperty("ro.miui.ui.version.code"); + if (!TextUtils.isEmpty(miuiVersionCodeStr)) { + try { + int miuiVersionCode = Integer.parseInt(miuiVersionCodeStr); + if (miuiVersionCode >= 4) { + return true; + } + } catch (Exception e) { + } + } + return false; + } + + //Flyme V4的displayId格式为 [Flyme OS 4.x.x.xA] + //Flyme V5的displayId格式为 [Flyme 5.x.x.x beta] + private static boolean isFlymeV4OrAbove() { + String displayId = Build.DISPLAY; + if (!TextUtils.isEmpty(displayId) && displayId.contains("Flyme")) { + String[] displayIdArray = displayId.split(" "); + for (String temp : displayIdArray) { + //版本号4以上,形如4.x. + if (temp.matches("^[4-9]\\.(\\d+\\.)+\\S*")) { + return true; + } + } + } + return false; + } + + + //Android Api 23以上 + private static boolean isAndroidMOrAbove() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; + } + + /** + * 获取Rom对应的属性值 + * + * @param name + * @return + */ + public static String getSystemProperty(String name) { + String line = ""; + BufferedReader br = null; + try { + Process p = Runtime.getRuntime().exec("getprop " + name); + br = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024); + line = br.readLine(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return line; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/utils/ToastUtils.java b/app/src/main/java/com/kdp/wanandroidclient/utils/ToastUtils.java new file mode 100644 index 0000000..48a903a --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/utils/ToastUtils.java @@ -0,0 +1,42 @@ +package com.kdp.wanandroidclient.utils; + +import android.content.Context; +import android.widget.Toast; + +/** + * 单例Toast + */ +public class ToastUtils { + + private static String oldMsg; + protected static Toast toast = null; + private static long oneTime = 0; + private static long twoTime = 0; + + public static void showToast(Context context, String s) { + if (toast == null) { + toast = Toast.makeText(context, s, Toast.LENGTH_SHORT); + toast.show(); + oldMsg = s; + oneTime = System.currentTimeMillis(); + } else { + twoTime = System.currentTimeMillis(); + if (s.equals(oldMsg)) { + if (twoTime - oneTime > Toast.LENGTH_SHORT) { + toast.show(); + } + } else { + oldMsg = s; + toast.setText(s); + toast.show(); + } + } + oneTime = twoTime; + } + + + public static void showToast(Context context, int resId) { + showToast(context, context.getString(resId)); + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/utils/ViewAnimatorHelper.java b/app/src/main/java/com/kdp/wanandroidclient/utils/ViewAnimatorHelper.java new file mode 100644 index 0000000..2076467 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/utils/ViewAnimatorHelper.java @@ -0,0 +1,64 @@ +package com.kdp.wanandroidclient.utils; + +import android.animation.Animator; +import android.support.v4.view.animation.LinearOutSlowInInterpolator; +import android.view.View; +import android.view.ViewPropertyAnimator; +import android.view.animation.Animation; + +/*** + * @author kdp + * @date 2019/3/27 16:41 + * @description + */ +public class ViewAnimatorHelper { + private boolean isAnimating; + private ViewPropertyAnimator viewPropertyAnimator; + private View view; + public void bindView(View view){ + if (view == null) + throw new NullPointerException("The view is cannot null"); + this.view = view; + if (viewPropertyAnimator == null){ + viewPropertyAnimator = view.animate(); + viewPropertyAnimator.setDuration(300); + viewPropertyAnimator.setInterpolator(new LinearOutSlowInInterpolator()); + } + } + + public void showFloatActionButton(){ + view.setVisibility(View.VISIBLE); + viewPropertyAnimator.scaleX(1.0f).scaleY(1.0f).alpha(1.0f).setListener(null); + } + + public void hideFloatActionButton(){ + viewPropertyAnimator.scaleX(0.0f).scaleY(0.0f).alpha(0.0f).setListener(animationListener); + } + + private Animator.AnimatorListener animationListener = new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + isAnimating = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + isAnimating = false; + view.setVisibility(View.INVISIBLE); + } + + @Override + public void onAnimationCancel(Animator animation) { + isAnimating = false; + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + }; + + + public boolean isAnimating() { + return isAnimating; + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/widget/BannerViewPager.java b/app/src/main/java/com/kdp/wanandroidclient/widget/BannerViewPager.java new file mode 100644 index 0000000..539100a --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/widget/BannerViewPager.java @@ -0,0 +1,195 @@ +package com.kdp.wanandroidclient.widget; + +import android.content.Context; +import android.os.Handler; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.widget.Scroller; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; + +/** + * Banner广告 + * author: 曾文海 + * date: 2023/5/31 + */ + +public class BannerViewPager extends ViewPager { + + private Handler mHandler; + private TaskRunnable mTaskRunnable; + private BannerViewPager instance; + public static boolean mIsRunning = false; //是否正在执行 + + public BannerViewPager(Context context) { + this(context, null); + } + + public BannerViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + instance = this; + //滚动监听 + //空闲状态 + //手指触摸滑动 + //手指松开,惯性滑动 + OnPageChangeListener mOnPagerChangeListener = new OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + } + + @Override + public void onPageScrollStateChanged(int state) { + switch (state) { + case ViewPager.SCROLL_STATE_IDLE: //空闲状态 + start(); + break; + case ViewPager.SCROLL_STATE_DRAGGING: //手指触摸滑动 + stop(); + break; + case SCROLL_STATE_SETTLING: //手指松开,惯性滑动 + break; + } + } + }; + addOnPageChangeListener(mOnPagerChangeListener); + //修改ViewPager的滚动速度 + setViewPagerDuration(); + } + + /** + * 开启任务 + */ + private void startTimingTask() { + if (mHandler == null && !mIsRunning) { + mHandler = new Handler(); + mTaskRunnable = new TaskRunnable(instance); + mHandler.postDelayed(mTaskRunnable, 6000); + mIsRunning = true; + } + } + + + /** + * 结束任务 + */ + private void stopTimingTask() { + if (mHandler != null && mIsRunning) { + mHandler.removeCallbacks(mTaskRunnable); + mHandler = null; + mIsRunning = false; + } + + } + + private static class TaskRunnable implements Runnable { + + private WeakReference weakReference; + + TaskRunnable(BannerViewPager bannerViewPager) { + this.weakReference = new WeakReference<>(bannerViewPager); + } + + @Override + public void run() { + //执行切换任务 + BannerViewPager instance = weakReference.get(); + if (instance == null) return; + instance.setCurrentItem(); + } + } + + + private void setCurrentItem() { + setCurrentItem(getCurrentItem() + 1, true); + mHandler.postDelayed(mTaskRunnable, 6000); + } + + public void start() { + startTimingTask(); + } + + public void stop() { + stopTimingTask(); + } + + + private class FixedSpeedScroll extends Scroller { + + private int mDuration = 750;//毫秒 + + private FixedSpeedScroll(Context context) { + super(context); + } + + @Override + public void startScroll(int startX, int startY, int dx, int dy, int duration) { + super.startScroll(startX, startY, dx, dy, mDuration); + + } + + @Override + public void startScroll(int startX, int startY, int dx, int dy) { + super.startScroll(startX, startY, dx, dy); + } + } + + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + //防止ViewPager可见时第一次切换无动画效果 + //滚动监听 + setFirstLayout(false); + start(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + stop(); + } + + /** + * + * @param isFirstLayout false + */ + private void setFirstLayout(boolean isFirstLayout) { + try { + Class clazz = Class.forName("android.support.v4.view.ViewPager"); + Field field = clazz.getDeclaredField("mFirstLayout"); + field.setAccessible(true); + field.setBoolean(this, isFirstLayout); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + /** + * 修改ViewPager滑动速度 + */ + private void setViewPagerDuration() { + try { + Class clazz = Class.forName("android.support.v4.view.ViewPager"); + FixedSpeedScroll mScriller = new FixedSpeedScroll(getContext()); + Field field = clazz.getDeclaredField("mScroller"); + field.setAccessible(true); + field.set(this, mScriller); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/widget/LMRecyclerView.java b/app/src/main/java/com/kdp/wanandroidclient/widget/LMRecyclerView.java new file mode 100644 index 0000000..e818029 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/widget/LMRecyclerView.java @@ -0,0 +1,264 @@ +package com.kdp.wanandroidclient.widget; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import com.kdp.wanandroidclient.R; +import com.kdp.wanandroidclient.common.ListDataHolder; + +/** + * author: 曾文海 + * date: 2023/5/31 + * 扩展:滑到底部自动加载 + */ + +public class LMRecyclerView extends RecyclerView { + private OnFooterAutoLoadMoreListener listener; + private boolean isCanLoadMore; //是否允许加载更多 + private boolean isReClickLoadMore;//是否可以点击重新加载 + private View mHeaderView;//头部 + private static final int VIEW_TYPE_NOMAL = 0;//nomal + private static final int VIEW_TYPE_HEADER = 100;//header + private static final int VIEW_TYPE_FOOTER = 200;//footer + + private int footerResId; + + private BaseAdapter mBaseAdapter; + + public LMRecyclerView(Context context) { + this(context, null); + } + + public LMRecyclerView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public LMRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + //添加监听 + addOnScrollListener(mOnScrollListener); + } + + /** + * 是否允许加载更多 + * + * @param isCanLoadMore + */ + public void setCanLoadMore(boolean isCanLoadMore) { + this.isCanLoadMore = isCanLoadMore; + } + + + /** + * 添加headerView + * + * @param header + */ + public void addHeaderView(View header) { + + this.mHeaderView = header; + } + + /** + * 移除headerView + */ + public void removeHeaderView() { + this.mHeaderView = null; + } + + /** + * 显示底部加载状态 + * + * @param footerResId + */ + public void showFooterStatus(int footerResId) { + this.footerResId = footerResId; + } + + + private OnScrollListener mOnScrollListener = new OnScrollListener() { + /** + * 滑动状态监听 + * @param recyclerView + * @param newState + */ + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + } + /** + * 滑动监听 + * @param recyclerView + * @param dx + * @param dy + */ + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + LayoutManager layoutManager = recyclerView.getLayoutManager(); + + if (layoutManager instanceof LinearLayoutManager) { + LinearLayoutManager linearLayout = (LinearLayoutManager) layoutManager; + int mLastChildPosition = linearLayout.findLastVisibleItemPosition(); + int itemTotalCount = linearLayout.getItemCount(); + View lastChildView = linearLayout.getChildAt(linearLayout.getChildCount() - 1); + int lastChildBottom = lastChildView.getBottom(); + int recyclerBottom = getBottom(); + if (mLastChildPosition == itemTotalCount - 1 && lastChildBottom == recyclerBottom) { + if (isCanLoadMore && listener != null) { + //业务代码 + listener.loadMore(); + } + } + } + } + }; + + + @Override + public void setAdapter(Adapter adapter) { + if (adapter != null) { + mBaseAdapter = new BaseAdapter(adapter); + } + super.swapAdapter(mBaseAdapter, true); + } + + private class BaseAdapter extends Adapter { + + private Adapter mAdapter; + + public BaseAdapter(Adapter adapter) { + this.mAdapter = adapter; + } + + @Override + public int getItemViewType(int position) { + if (mHeaderView != null && position == 0) + return VIEW_TYPE_HEADER; + if (isCanLoadMore && position == getItemCount() - 1) + return VIEW_TYPE_FOOTER; + return VIEW_TYPE_NOMAL; + } + + @Override + public ListDataHolder onCreateViewHolder(ViewGroup parent, int viewType) { + + if (viewType == VIEW_TYPE_HEADER) + return ListDataHolder.createViewHolder(mHeaderView); + if (viewType == VIEW_TYPE_FOOTER) + return ListDataHolder.createViewHolder(parent, R.layout.item_root_footer); + return (ListDataHolder) mAdapter.onCreateViewHolder(parent, viewType); + } + + @Override + public void onBindViewHolder(ListDataHolder holder, int position) { + int viewType = getItemViewType(position); + if (viewType == VIEW_TYPE_NOMAL) { + if (mHeaderView != null) position--; + mAdapter.onBindViewHolder(holder, position); + } else if (viewType == VIEW_TYPE_FOOTER) { + showFooterView(holder); + } else if (viewType == VIEW_TYPE_HEADER) { + } + + } + + @Override + public int getItemCount() { + int count = mAdapter.getItemCount(); + if (mHeaderView != null) count++; + if (isCanLoadMore) count++; + return count; + } + } + + /** + * 显示footer + * + * @param holder + */ + private void showFooterView(ListDataHolder holder) { + FrameLayout rootView = holder.getView(R.id.root_footer); + rootView.removeAllViews(); + if (footerResId != 0) { + View footerView = LayoutInflater.from(getContext()).inflate(footerResId, rootView, false); + rootView.addView(footerView); + //只有加载更多失败,才能点击重新加载数据 + + rootView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (!isReClickLoadMore) return; + if (listener != null) { + showLoadMore(); + notifyDataSetChanged(); + listener.reLoadMore(); + } + } + }); + } + } + + //显示底部加载更多 + public void showLoadMore() { + showFooterStatus(R.layout.item_footer_loading_more); + setIsReClickLoadMore(false); + } + + //加载更多,没有更多的数据了 + public void showNoMoreData() { + showFooterStatus(R.layout.item_footer_nomore); + setIsReClickLoadMore(false); + } + + //加载更多失败 + public void showLoadMoreError() { + showFooterStatus(R.layout.item_footer_load_error); + setIsReClickLoadMore(true); + } + + /** + * 底部是否可以重新加载更多 + * + * @param isReClickLoadMore + */ + public void setIsReClickLoadMore(boolean isReClickLoadMore) { + this.isReClickLoadMore = isReClickLoadMore; + } + + + public void notifyDataSetChanged() { + getAdapter().notifyDataSetChanged(); + } + + public void notifyItemChanged(int position) { + getAdapter().notifyItemChanged(position); + + } + + public void notifyItemRemoved(int position) { + getAdapter().notifyItemRemoved(position); + getAdapter().notifyItemRangeChanged(position, getAdapter().getItemCount()); + } + + + public void addFooterAutoLoadMoreListener(OnFooterAutoLoadMoreListener listener) { + this.listener = listener; + } + + public interface OnFooterAutoLoadMoreListener { + //自动加载更多 + void loadMore(); + + //加载出错,点击重新加载 + void reLoadMore(); + } + + +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/widget/NoAlphaItemAnimator.java b/app/src/main/java/com/kdp/wanandroidclient/widget/NoAlphaItemAnimator.java new file mode 100644 index 0000000..062f307 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/widget/NoAlphaItemAnimator.java @@ -0,0 +1,656 @@ +package com.kdp.wanandroidclient.widget; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.support.annotation.NonNull; +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.SimpleItemAnimator; +import android.view.View; +import android.view.ViewPropertyAnimator; + +import java.util.ArrayList; +import java.util.List; + +/** + * author: 曾文海 + * date: 2023/5/31 + */ + +public class NoAlphaItemAnimator extends SimpleItemAnimator { + + + private static final boolean DEBUG = false; + + private static TimeInterpolator sDefaultInterpolator; + + private ArrayList mPendingRemovals = new ArrayList<>(); + private ArrayList mPendingAdditions = new ArrayList<>(); + private ArrayList mPendingMoves = new ArrayList<>(); + private ArrayList mPendingChanges = new ArrayList<>(); + + ArrayList> mAdditionsList = new ArrayList<>(); + ArrayList> mMovesList = new ArrayList<>(); + ArrayList> mChangesList = new ArrayList<>(); + + ArrayList mAddAnimations = new ArrayList<>(); + ArrayList mMoveAnimations = new ArrayList<>(); + ArrayList mRemoveAnimations = new ArrayList<>(); + ArrayList mChangeAnimations = new ArrayList<>(); + + private static class MoveInfo { + public RecyclerView.ViewHolder holder; + public int fromX, fromY, toX, toY; + + MoveInfo(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) { + this.holder = holder; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + } + + private static class ChangeInfo { + public RecyclerView.ViewHolder oldHolder, newHolder; + public int fromX, fromY, toX, toY; + + private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder) { + this.oldHolder = oldHolder; + this.newHolder = newHolder; + } + + ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + this(oldHolder, newHolder); + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + + @Override + public String toString() { + return "ChangeInfo{" + + "oldHolder=" + oldHolder + + ", newHolder=" + newHolder + + ", fromX=" + fromX + + ", fromY=" + fromY + + ", toX=" + toX + + ", toY=" + toY + + '}'; + } + } + + @Override + public void runPendingAnimations() { + boolean removalsPending = !mPendingRemovals.isEmpty(); + boolean movesPending = !mPendingMoves.isEmpty(); + boolean changesPending = !mPendingChanges.isEmpty(); + boolean additionsPending = !mPendingAdditions.isEmpty(); + if (!removalsPending && !movesPending && !additionsPending && !changesPending) { + // nothing to animate + return; + } + // First, remove stuff + for (RecyclerView.ViewHolder holder : mPendingRemovals) { + animateRemoveImpl(holder); + } + mPendingRemovals.clear(); + // Next, move stuff + if (movesPending) { + final ArrayList moves = new ArrayList<>(); + moves.addAll(mPendingMoves); + mMovesList.add(moves); + mPendingMoves.clear(); + Runnable mover = new Runnable() { + @Override + public void run() { + for (NoAlphaItemAnimator.MoveInfo moveInfo : moves) { + animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, + moveInfo.toX, moveInfo.toY); + } + moves.clear(); + mMovesList.remove(moves); + } + }; + if (removalsPending) { + View view = moves.get(0).holder.itemView; + ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration()); + } else { + mover.run(); + } + } + // Next, change stuff, to run in parallel with move animations + if (changesPending) { + final ArrayList changes = new ArrayList<>(); + changes.addAll(mPendingChanges); + mChangesList.add(changes); + mPendingChanges.clear(); + Runnable changer = new Runnable() { + @Override + public void run() { + for (NoAlphaItemAnimator.ChangeInfo change : changes) { + animateChangeImpl(change); + } + changes.clear(); + mChangesList.remove(changes); + } + }; + if (removalsPending) { + RecyclerView.ViewHolder holder = changes.get(0).oldHolder; + ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration()); + } else { + changer.run(); + } + } + // Next, add stuff + if (additionsPending) { + final ArrayList additions = new ArrayList<>(); + additions.addAll(mPendingAdditions); + mAdditionsList.add(additions); + mPendingAdditions.clear(); + Runnable adder = new Runnable() { + @Override + public void run() { + for (RecyclerView.ViewHolder holder : additions) { + animateAddImpl(holder); + } + additions.clear(); + mAdditionsList.remove(additions); + } + }; + if (removalsPending || movesPending || changesPending) { + long removeDuration = removalsPending ? getRemoveDuration() : 0; + long moveDuration = movesPending ? getMoveDuration() : 0; + long changeDuration = changesPending ? getChangeDuration() : 0; + long totalDelay = removeDuration + Math.max(moveDuration, changeDuration); + View view = additions.get(0).itemView; + ViewCompat.postOnAnimationDelayed(view, adder, totalDelay); + } else { + adder.run(); + } + } + } + + @Override + public boolean animateRemove(final RecyclerView.ViewHolder holder) { + resetAnimation(holder); + mPendingRemovals.add(holder); + return true; + } + + private void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimator animation = view.animate(); + mRemoveAnimations.add(holder); + animation.setDuration(getRemoveDuration()).alpha(0).setListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchRemoveStarting(holder); + } + + @Override + public void onAnimationEnd(Animator animator) { + animation.setListener(null); + view.setAlpha(1); + dispatchRemoveFinished(holder); + mRemoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateAdd(final RecyclerView.ViewHolder holder) { + resetAnimation(holder); + holder.itemView.setAlpha(0); + mPendingAdditions.add(holder); + return true; + } + + void animateAddImpl(final RecyclerView.ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimator animation = view.animate(); + mAddAnimations.add(holder); + animation.alpha(1).setDuration(getAddDuration()) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchAddStarting(holder); + } + + @Override + public void onAnimationCancel(Animator animator) { + view.setAlpha(1); + } + + @Override + public void onAnimationEnd(Animator animator) { + animation.setListener(null); + dispatchAddFinished(holder); + mAddAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY, + int toX, int toY) { + final View view = holder.itemView; + fromX += holder.itemView.getTranslationX(); + fromY += holder.itemView.getTranslationY(); + resetAnimation(holder); + int deltaX = toX - fromX; + int deltaY = toY - fromY; + if (deltaX == 0 && deltaY == 0) { + dispatchMoveFinished(holder); + return false; + } + if (deltaX != 0) { + view.setTranslationX(-deltaX); + } + if (deltaY != 0) { + view.setTranslationY(-deltaY); + } + mPendingMoves.add(new NoAlphaItemAnimator.MoveInfo(holder, fromX, fromY, toX, toY)); + return true; + } + + void animateMoveImpl(final RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) { + final View view = holder.itemView; + final int deltaX = toX - fromX; + final int deltaY = toY - fromY; + if (deltaX != 0) { + view.animate().translationX(0); + } + if (deltaY != 0) { + view.animate().translationY(0); + } + // TODO: make EndActions end listeners instead, since end actions aren't called when + // vpas are canceled (and can't end them. why?) + // need listener functionality in VPACompat for this. Ick. + final ViewPropertyAnimator animation = view.animate(); + mMoveAnimations.add(holder); + animation.setDuration(getMoveDuration()).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchMoveStarting(holder); + } + + @Override + public void onAnimationCancel(Animator animator) { + if (deltaX != 0) { + view.setTranslationX(0); + } + if (deltaY != 0) { + view.setTranslationY(0); + } + } + + @Override + public void onAnimationEnd(Animator animator) { + animation.setListener(null); + dispatchMoveFinished(holder); + mMoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + if (oldHolder == newHolder) { + // Don't know how to run change animations when the same view holder is re-used. + // run a move animation to handle position changes. + return animateMove(oldHolder, fromX, fromY, toX, toY); + } + final float prevTranslationX = oldHolder.itemView.getTranslationX(); + final float prevTranslationY = oldHolder.itemView.getTranslationY(); + final float prevAlpha = oldHolder.itemView.getAlpha(); + resetAnimation(oldHolder); + int deltaX = (int) (toX - fromX - prevTranslationX); + int deltaY = (int) (toY - fromY - prevTranslationY); + // recover prev translation state after ending animation + oldHolder.itemView.setTranslationX(prevTranslationX); + oldHolder.itemView.setTranslationY(prevTranslationY); + oldHolder.itemView.setAlpha(prevAlpha); + if (newHolder != null) { + // carry over translation values + resetAnimation(newHolder); + newHolder.itemView.setTranslationX(-deltaX); + newHolder.itemView.setTranslationY(-deltaY); + newHolder.itemView.setAlpha(0); + } + mPendingChanges.add(new NoAlphaItemAnimator.ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); + return true; + } + + void animateChangeImpl(final NoAlphaItemAnimator.ChangeInfo changeInfo) { + final RecyclerView.ViewHolder holder = changeInfo.oldHolder; + final View view = holder == null ? null : holder.itemView; + final RecyclerView.ViewHolder newHolder = changeInfo.newHolder; + final View newView = newHolder != null ? newHolder.itemView : null; + if (view != null) { + final ViewPropertyAnimator oldViewAnim = view.animate().setDuration( + getChangeDuration()); + mChangeAnimations.add(changeInfo.oldHolder); + oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX); + oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY); + oldViewAnim.setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchChangeStarting(changeInfo.oldHolder, true); + } + + @Override + public void onAnimationEnd(Animator animator) { + oldViewAnim.setListener(null); + view.setAlpha(1); + view.setTranslationX(0); + view.setTranslationY(0); + dispatchChangeFinished(changeInfo.oldHolder, true); + mChangeAnimations.remove(changeInfo.oldHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + if (newView != null) { + final ViewPropertyAnimator newViewAnimation = newView.animate(); + mChangeAnimations.add(changeInfo.newHolder); + newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchChangeStarting(changeInfo.newHolder, false); + } + + @Override + public void onAnimationEnd(Animator animator) { + newViewAnimation.setListener(null); + newView.setAlpha(1); + newView.setTranslationX(0); + newView.setTranslationY(0); + dispatchChangeFinished(changeInfo.newHolder, false); + mChangeAnimations.remove(changeInfo.newHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + } + + private void endChangeAnimation(List infoList, RecyclerView.ViewHolder item) { + for (int i = infoList.size() - 1; i >= 0; i--) { + NoAlphaItemAnimator.ChangeInfo changeInfo = infoList.get(i); + if (endChangeAnimationIfNecessary(changeInfo, item)) { + if (changeInfo.oldHolder == null && changeInfo.newHolder == null) { + infoList.remove(changeInfo); + } + } + } + } + + private void endChangeAnimationIfNecessary(NoAlphaItemAnimator.ChangeInfo changeInfo) { + if (changeInfo.oldHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder); + } + if (changeInfo.newHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder); + } + } + + private boolean endChangeAnimationIfNecessary(NoAlphaItemAnimator.ChangeInfo changeInfo, RecyclerView.ViewHolder item) { + boolean oldItem = false; + if (changeInfo.newHolder == item) { + changeInfo.newHolder = null; + } else if (changeInfo.oldHolder == item) { + changeInfo.oldHolder = null; + oldItem = true; + } else { + return false; + } + item.itemView.setAlpha(1); + item.itemView.setTranslationX(0); + item.itemView.setTranslationY(0); + dispatchChangeFinished(item, oldItem); + return true; + } + + @Override + public void endAnimation(RecyclerView.ViewHolder item) { + final View view = item.itemView; + // this will trigger end callback which should set properties to their target values. + view.animate().cancel(); + // TODO if some other animations are chained to end, how do we cancel them as well? + for (int i = mPendingMoves.size() - 1; i >= 0; i--) { + NoAlphaItemAnimator.MoveInfo moveInfo = mPendingMoves.get(i); + if (moveInfo.holder == item) { + view.setTranslationY(0); + view.setTranslationX(0); + dispatchMoveFinished(item); + mPendingMoves.remove(i); + } + } + endChangeAnimation(mPendingChanges, item); + if (mPendingRemovals.remove(item)) { + view.setAlpha(1); + dispatchRemoveFinished(item); + } + if (mPendingAdditions.remove(item)) { + view.setAlpha(1); + dispatchAddFinished(item); + } + + for (int i = mChangesList.size() - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + endChangeAnimation(changes, item); + if (changes.isEmpty()) { + mChangesList.remove(i); + } + } + for (int i = mMovesList.size() - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + for (int j = moves.size() - 1; j >= 0; j--) { + NoAlphaItemAnimator.MoveInfo moveInfo = moves.get(j); + if (moveInfo.holder == item) { + view.setTranslationY(0); + view.setTranslationX(0); + dispatchMoveFinished(item); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(i); + } + break; + } + } + } + for (int i = mAdditionsList.size() - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + if (additions.remove(item)) { + view.setAlpha(1); + dispatchAddFinished(item); + if (additions.isEmpty()) { + mAdditionsList.remove(i); + } + } + } + + // animations should be ended by the cancel above. + //noinspection PointlessBooleanExpression,ConstantConditions + if (mRemoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mRemoveAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mAddAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mAddAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mChangeAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mChangeAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mMoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mMoveAnimations list"); + } + dispatchFinishedWhenDone(); + } + + private void resetAnimation(RecyclerView.ViewHolder holder) { + if (sDefaultInterpolator == null) { + sDefaultInterpolator = new ValueAnimator().getInterpolator(); + } + holder.itemView.animate().setInterpolator(sDefaultInterpolator); + endAnimation(holder); + } + + @Override + public boolean isRunning() { + return (!mPendingAdditions.isEmpty() + || !mPendingChanges.isEmpty() + || !mPendingMoves.isEmpty() + || !mPendingRemovals.isEmpty() + || !mMoveAnimations.isEmpty() + || !mRemoveAnimations.isEmpty() + || !mAddAnimations.isEmpty() + || !mChangeAnimations.isEmpty() + || !mMovesList.isEmpty() + || !mAdditionsList.isEmpty() + || !mChangesList.isEmpty()); + } + + /** + * Check the state of currently pending and running animations. If there are none + * pending/running, call {@link #dispatchAnimationsFinished()} to notify any + * listeners. + */ + void dispatchFinishedWhenDone() { + if (!isRunning()) { + dispatchAnimationsFinished(); + } + } + + @Override + public void endAnimations() { + int count = mPendingMoves.size(); + for (int i = count - 1; i >= 0; i--) { + NoAlphaItemAnimator.MoveInfo item = mPendingMoves.get(i); + View view = item.holder.itemView; + view.setTranslationY(0); + view.setTranslationX(0); + dispatchMoveFinished(item.holder); + mPendingMoves.remove(i); + } + count = mPendingRemovals.size(); + for (int i = count - 1; i >= 0; i--) { + RecyclerView.ViewHolder item = mPendingRemovals.get(i); + dispatchRemoveFinished(item); + mPendingRemovals.remove(i); + } + count = mPendingAdditions.size(); + for (int i = count - 1; i >= 0; i--) { + RecyclerView.ViewHolder item = mPendingAdditions.get(i); + item.itemView.setAlpha(1); + dispatchAddFinished(item); + mPendingAdditions.remove(i); + } + count = mPendingChanges.size(); + for (int i = count - 1; i >= 0; i--) { + endChangeAnimationIfNecessary(mPendingChanges.get(i)); + } + mPendingChanges.clear(); + if (!isRunning()) { + return; + } + + int listCount = mMovesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + count = moves.size(); + for (int j = count - 1; j >= 0; j--) { + NoAlphaItemAnimator.MoveInfo moveInfo = moves.get(j); + RecyclerView.ViewHolder item = moveInfo.holder; + View view = item.itemView; + view.setTranslationY(0); + view.setTranslationX(0); + dispatchMoveFinished(moveInfo.holder); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(moves); + } + } + } + listCount = mAdditionsList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + count = additions.size(); + for (int j = count - 1; j >= 0; j--) { + RecyclerView.ViewHolder item = additions.get(j); + View view = item.itemView; + view.setAlpha(1); + dispatchAddFinished(item); + additions.remove(j); + if (additions.isEmpty()) { + mAdditionsList.remove(additions); + } + } + } + listCount = mChangesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + count = changes.size(); + for (int j = count - 1; j >= 0; j--) { + endChangeAnimationIfNecessary(changes.get(j)); + if (changes.isEmpty()) { + mChangesList.remove(changes); + } + } + } + + cancelAll(mRemoveAnimations); + cancelAll(mMoveAnimations); + cancelAll(mAddAnimations); + cancelAll(mChangeAnimations); + + dispatchAnimationsFinished(); + } + + void cancelAll(List viewHolders) { + for (int i = viewHolders.size() - 1; i >= 0; i--) { + viewHolders.get(i).itemView.animate().cancel(); + } + } + + /** + * {@inheritDoc} + *

+ * If the payload list is not empty, NoAlphaItemAnimator returns true. + * When this is the case: + *

    + *
  • If you override {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)}, both + * ViewHolder arguments will be the same instance. + *
  • + *
  • + * If you are not overriding {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)}, + * then NoAlphaItemAnimator will call {@link #animateMove(RecyclerView.ViewHolder, int, int, int, int)} and + * run a move animation instead. + *
  • + *
+ */ + @Override + public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, + @NonNull List payloads) { + return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads); + } +} diff --git a/app/src/main/java/com/kdp/wanandroidclient/widget/StatusLayout.java b/app/src/main/java/com/kdp/wanandroidclient/widget/StatusLayout.java new file mode 100644 index 0000000..fe30f59 --- /dev/null +++ b/app/src/main/java/com/kdp/wanandroidclient/widget/StatusLayout.java @@ -0,0 +1,83 @@ +package com.kdp.wanandroidclient.widget; +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; + +import com.kdp.wanandroidclient.R; + +/** + * author: 曾文海 + * date: 2023/5/31 + * 加载各种状态的布局(empty、loading、error、content) + */ + +public class StatusLayout extends FrameLayout { + private View mLoadingView; + private View mErrorView; + private View mEmptyView; + private View mContentView; + + public StatusLayout(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + LayoutInflater mInflater = LayoutInflater.from(context); + mLoadingView = mInflater.inflate(R.layout.loading_layout, this, false); + mErrorView = mInflater.inflate(R.layout.error_layout, this, false); + mEmptyView = mInflater.inflate(R.layout.empty_layout, this, false); + addView(mLoadingView); + addView(mErrorView); + addView(mEmptyView); + mLoadingView.setVisibility(GONE); + mErrorView.setVisibility(GONE); + mEmptyView.setVisibility(GONE); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (changed) { + int count = getChildCount(); + mContentView = getChildAt(count - 1); + } + } + + //loading + public void showLoding() { + mLoadingView.setVisibility(VISIBLE); + mErrorView.setVisibility(GONE); + mEmptyView.setVisibility(GONE); + if (mContentView != null) + mContentView.setVisibility(GONE); + } + + //error + public void showError() { + mErrorView.setVisibility(VISIBLE); + mLoadingView.setVisibility(GONE); + mEmptyView.setVisibility(GONE); + if (mContentView != null) + mContentView.setVisibility(GONE); + } + + //empty + public void showEmpty() { + mEmptyView.setVisibility(VISIBLE); + mLoadingView.setVisibility(GONE); + mErrorView.setVisibility(GONE); + if (mContentView != null) + mContentView.setVisibility(GONE); + } + + //content + public void showContent() { + if (mContentView != null) + mContentView.setVisibility(VISIBLE); + mLoadingView.setVisibility(GONE); + mErrorView.setVisibility(GONE); + mEmptyView.setVisibility(GONE); + } + + +} diff --git a/app/src/main/res/color/tab_text_color.xml b/app/src/main/res/color/tab_text_color.xml new file mode 100644 index 0000000..5683a5e --- /dev/null +++ b/app/src/main/res/color/tab_text_color.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/ripple_close.xml b/app/src/main/res/drawable-v21/ripple_close.xml new file mode 100644 index 0000000..9e7eb93 --- /dev/null +++ b/app/src/main/res/drawable-v21/ripple_close.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/ic_img_default.png b/app/src/main/res/drawable-xhdpi/ic_img_default.png new file mode 100644 index 0000000..53199e3 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_img_default.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_logo.png b/app/src/main/res/drawable-xhdpi/ic_logo.png new file mode 100644 index 0000000..b8808a0 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_logo.png differ diff --git a/app/src/main/res/drawable/article_tag_shape.xml b/app/src/main/res/drawable/article_tag_shape.xml new file mode 100644 index 0000000..d87da2c --- /dev/null +++ b/app/src/main/res/drawable/article_tag_shape.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/chapter_tab_selsctor.xml b/app/src/main/res/drawable/chapter_tab_selsctor.xml new file mode 100644 index 0000000..1a10691 --- /dev/null +++ b/app/src/main/res/drawable/chapter_tab_selsctor.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/home_tab_selsctor.xml b/app/src/main/res/drawable/home_tab_selsctor.xml new file mode 100644 index 0000000..f18047e --- /dev/null +++ b/app/src/main/res/drawable/home_tab_selsctor.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_about_us_gray_24dp.xml b/app/src/main/res/drawable/ic_about_us_gray_24dp.xml new file mode 100644 index 0000000..565f671 --- /dev/null +++ b/app/src/main/res/drawable/ic_about_us_gray_24dp.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_arrow_upward_white_24dp.xml b/app/src/main/res/drawable/ic_arrow_upward_white_24dp.xml new file mode 100644 index 0000000..fee89d2 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_upward_white_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_chapter_gray_24dp.xml b/app/src/main/res/drawable/ic_chapter_gray_24dp.xml new file mode 100644 index 0000000..f8003d7 --- /dev/null +++ b/app/src/main/res/drawable/ic_chapter_gray_24dp.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_chapter_light_24dp.xml b/app/src/main/res/drawable/ic_chapter_light_24dp.xml new file mode 100644 index 0000000..037e376 --- /dev/null +++ b/app/src/main/res/drawable/ic_chapter_light_24dp.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_chevron_right_black_24dp.xml b/app/src/main/res/drawable/ic_chevron_right_black_24dp.xml new file mode 100644 index 0000000..e0b2ff7 --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_right_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_close_white_24dp.xml b/app/src/main/res/drawable/ic_close_white_24dp.xml new file mode 100644 index 0000000..0a244b9 --- /dev/null +++ b/app/src/main/res/drawable/ic_close_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_exit_to_app_gray_24dp.xml b/app/src/main/res/drawable/ic_exit_to_app_gray_24dp.xml new file mode 100644 index 0000000..a7cf0e1 --- /dev/null +++ b/app/src/main/res/drawable/ic_exit_to_app_gray_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_favorite_gray_24dp.xml b/app/src/main/res/drawable/ic_favorite_gray_24dp.xml new file mode 100644 index 0000000..433b949 --- /dev/null +++ b/app/src/main/res/drawable/ic_favorite_gray_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_favorite_light_24dp.xml b/app/src/main/res/drawable/ic_favorite_light_24dp.xml new file mode 100644 index 0000000..e467bde --- /dev/null +++ b/app/src/main/res/drawable/ic_favorite_light_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_favorite_white_24dp.xml b/app/src/main/res/drawable/ic_favorite_white_24dp.xml new file mode 100644 index 0000000..1194eff --- /dev/null +++ b/app/src/main/res/drawable/ic_favorite_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_home_gray_24dp.xml b/app/src/main/res/drawable/ic_home_gray_24dp.xml new file mode 100644 index 0000000..5c98360 --- /dev/null +++ b/app/src/main/res/drawable/ic_home_gray_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_home_light_24dp.xml b/app/src/main/res/drawable/ic_home_light_24dp.xml new file mode 100644 index 0000000..6007ad6 --- /dev/null +++ b/app/src/main/res/drawable/ic_home_light_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_white_24dp.xml b/app/src/main/res/drawable/ic_menu_white_24dp.xml new file mode 100644 index 0000000..d317970 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_open_in_browser_green_24dp.xml b/app/src/main/res/drawable/ic_open_in_browser_green_24dp.xml new file mode 100644 index 0000000..bcff494 --- /dev/null +++ b/app/src/main/res/drawable/ic_open_in_browser_green_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_project_gray_24dp.xml b/app/src/main/res/drawable/ic_project_gray_24dp.xml new file mode 100644 index 0000000..092b9d7 --- /dev/null +++ b/app/src/main/res/drawable/ic_project_gray_24dp.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_project_light_24dp.xml b/app/src/main/res/drawable/ic_project_light_24dp.xml new file mode 100644 index 0000000..9f2e79d --- /dev/null +++ b/app/src/main/res/drawable/ic_project_light_24dp.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_search_white_24dp.xml b/app/src/main/res/drawable/ic_search_white_24dp.xml new file mode 100644 index 0000000..196fc51 --- /dev/null +++ b/app/src/main/res/drawable/ic_search_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_share_green_24dp.xml b/app/src/main/res/drawable/ic_share_green_24dp.xml new file mode 100644 index 0000000..6b7fa48 --- /dev/null +++ b/app/src/main/res/drawable/ic_share_green_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_system_gray_24dp.xml b/app/src/main/res/drawable/ic_system_gray_24dp.xml new file mode 100644 index 0000000..b67abf5 --- /dev/null +++ b/app/src/main/res/drawable/ic_system_gray_24dp.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_system_light_24dp.xml b/app/src/main/res/drawable/ic_system_light_24dp.xml new file mode 100644 index 0000000..5775f49 --- /dev/null +++ b/app/src/main/res/drawable/ic_system_light_24dp.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/drawable/project_tab_selsctor.xml b/app/src/main/res/drawable/project_tab_selsctor.xml new file mode 100644 index 0000000..170888d --- /dev/null +++ b/app/src/main/res/drawable/project_tab_selsctor.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/system_tab_selsctor.xml b/app/src/main/res/drawable/system_tab_selsctor.xml new file mode 100644 index 0000000..82d62b0 --- /dev/null +++ b/app/src/main/res/drawable/system_tab_selsctor.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/tag_shape.xml b/app/src/main/res/drawable/tag_shape.xml new file mode 100644 index 0000000..d1b72ae --- /dev/null +++ b/app/src/main/res/drawable/tag_shape.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about_us.xml b/app/src/main/res/layout/activity_about_us.xml new file mode 100644 index 0000000..a091155 --- /dev/null +++ b/app/src/main/res/layout/activity_about_us.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_base.xml b/app/src/main/res/layout/activity_base.xml new file mode 100644 index 0000000..8f9a9a3 --- /dev/null +++ b/app/src/main/res/layout/activity_base.xml @@ -0,0 +1,26 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..16564ce --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + +