From b9fd53e0685544f63d381e63ac332723254553a2 Mon Sep 17 00:00:00 2001 From: hai <1411648363@qq.com> Date: Wed, 31 May 2023 23:01:08 +0800 Subject: [PATCH] first commit --- .gitignore | 9 + LICENSE | 201 ++++++ _config.yml | 1 + app/.gitignore | 1 + app/build.gradle | 48 ++ app/proguard-rules.pro | 25 + app/src/main/AndroidManifest.xml | 73 ++ .../kdp/wanandroidclient/api/ApiServer.java | 194 ++++++ .../application/AppConfig.java | 23 + .../application/AppContext.java | 38 + .../wanandroidclient/application/WApp.java | 29 + .../kdp/wanandroidclient/bean/Article.java | 305 ++++++++ .../com/kdp/wanandroidclient/bean/Banner.java | 95 +++ .../kdp/wanandroidclient/bean/BaseBean.java | 36 + .../kdp/wanandroidclient/bean/Chapter.java | 91 +++ .../com/kdp/wanandroidclient/bean/Friend.java | 74 ++ .../kdp/wanandroidclient/bean/HomeData.java | 33 + .../kdp/wanandroidclient/bean/Hotword.java | 65 ++ .../wanandroidclient/bean/PageListData.java | 81 +++ .../wanandroidclient/bean/ProjectCate.java | 91 +++ .../com/kdp/wanandroidclient/bean/Tree.java | 164 +++++ .../com/kdp/wanandroidclient/bean/User.java | 106 +++ .../kdp/wanandroidclient/common/Const.java | 62 ++ .../common/ListDataHolder.java | 46 ++ .../common/UrlConstainer.java | 99 +++ .../com/kdp/wanandroidclient/event/Event.java | 24 + .../kdp/wanandroidclient/event/RxEvent.java | 86 +++ .../inter/OnArticleListItemClickListener.java | 10 + .../inter/OnItemClickListener.java | 6 + .../inter/OnProjectListItemClickListener.java | 8 + .../inter/OnTreeItemClickListener.java | 11 + .../inter/VerifyAccountCallback.java | 11 + .../manager/ImageLoaderManager.java | 45 ++ .../manager/UserInfoManager.java | 108 +++ .../kdp/wanandroidclient/net/NetConfig.java | 50 ++ .../net/NetExceptionHandle.java | 85 +++ .../kdp/wanandroidclient/net/RxRetrofit.java | 73 ++ .../wanandroidclient/net/RxSchedulers.java | 33 + .../net/callback/RxBaseObserver.java | 52 ++ .../net/callback/RxConsumer.java | 28 + .../net/callback/RxFunction.java | 27 + .../net/callback/RxObserver.java | 32 + .../net/callback/RxPageListObserver.java | 55 ++ .../net/callback/RxZipObserver.java | 45 ++ .../net/interceptor/CacheInterceptor.java | 60 ++ .../interceptor/LoadCookieInterceptor.java | 29 + .../net/interceptor/RequestInterceptor.java | 41 ++ .../interceptor/SaveCookieInterceptor.java | 40 ++ .../wanandroidclient/ui/LauncherActivity.java | 135 ++++ .../ui/adapter/ArticleListAdapter.java | 156 +++++ .../ui/adapter/BannerAdapter.java | 125 ++++ .../ui/adapter/BaseListAdapter.java | 62 ++ .../ui/adapter/ChaptersFragPagerAdapter.java | 39 ++ .../ui/adapter/ProjectFragPagerAdapter.java | 41 ++ .../ui/adapter/ProjectListAdapter.java | 58 ++ .../ui/adapter/TreeAdapter.java | 49 ++ .../ui/adapter/TreeFragPagerAdapter.java | 40 ++ .../ui/base/BaseAbListActivity.java | 285 ++++++++ .../ui/base/BaseAbListFragment.java | 289 ++++++++ .../ui/base/BaseActivity.java | 160 +++++ .../ui/base/BaseFragment.java | 83 +++ .../ui/base/BasePresenterActivity.java | 104 +++ .../ui/base/BasePresenterFragment.java | 79 +++ .../ui/base/BaseTabActivity.java | 54 ++ .../ui/base/BaseTabFragment.java | 26 + .../ui/chapter/ChapterContract.java | 20 + .../ui/chapter/ChapterListContract.java | 23 + .../ui/chapter/ChapterListFragment.java | 168 +++++ .../ui/chapter/ChapterListPresenter.java | 88 +++ .../ui/chapter/ChaptersFragment.java | 65 ++ .../ui/chapter/ChaptersPresenter.java | 44 ++ .../ui/core/model/IChapterListModel.java | 20 + .../ui/core/model/IChapterModel.java | 20 + .../ui/core/model/ICommonModel.java | 30 + .../ui/core/model/IHomeModel.java | 41 ++ .../ui/core/model/ILogonModel.java | 44 ++ .../ui/core/model/IModel.java | 14 + .../ui/core/model/IProjectCateModel.java | 20 + .../ui/core/model/IProjectModel.java | 15 + .../ui/core/model/ISearchModel.java | 41 ++ .../ui/core/model/ITreeListModel.java | 24 + .../ui/core/model/ITreeModel.java | 21 + .../ui/core/model/IUserModel.java | 30 + .../ui/core/model/impl/BaseModel.java | 14 + .../ui/core/model/impl/ChapterListModel.java | 28 + .../ui/core/model/impl/ChapterModel.java | 26 + .../ui/core/model/impl/CommonModel.java | 41 ++ .../ui/core/model/impl/HomeModel.java | 66 ++ .../ui/core/model/impl/LogonModel.java | 80 +++ .../ui/core/model/impl/ProjectCateModel.java | 28 + .../ui/core/model/impl/ProjectModel.java | 23 + .../ui/core/model/impl/SearchModel.java | 58 ++ .../ui/core/model/impl/TreeListModel.java | 30 + .../ui/core/model/impl/TreeModel.java | 30 + .../ui/core/model/impl/UserModel.java | 42 ++ .../ui/core/presenter/BasePresenter.java | 66 ++ .../ui/core/presenter/IPresenter.java | 31 + .../ui/core/view/IListDataView.java | 17 + .../ui/core/view/IPageLoadDataView.java | 17 + .../wanandroidclient/ui/core/view/IView.java | 41 ++ .../ui/home/HomeContract.java | 30 + .../ui/home/HomeFragment.java | 224 ++++++ .../ui/home/HomePresenter.java | 141 ++++ .../ui/logon/LogonActivity.java | 82 +++ .../ui/logon/LogonContract.java | 41 ++ .../ui/logon/LogonPresenter.java | 97 +++ .../ui/main/MainActivity.java | 333 +++++++++ .../ui/main/SearchActivity.java | 332 +++++++++ .../ui/main/SearchContract.java | 40 ++ .../ui/main/SearchPresenter.java | 142 ++++ .../ui/project/ProjectCateContract.java | 17 + .../ui/project/ProjectCatePresenter.java | 46 ++ .../ui/project/ProjectContract.java | 18 + .../ui/project/ProjectFragment.java | 58 ++ .../ui/project/ProjectListFragment.java | 158 +++++ .../ui/project/ProjectPresenter.java | 85 +++ .../ui/tree/TreeActivity.java | 81 +++ .../ui/tree/TreeContract.java | 24 + .../ui/tree/TreeFragment.java | 86 +++ .../ui/tree/TreeListContract.java | 28 + .../ui/tree/TreeListFragment.java | 173 +++++ .../ui/tree/TreeListPresenter.java | 96 +++ .../ui/tree/TreePresenter.java | 54 ++ .../ui/user/AboutUsActivity.java | 75 ++ .../ui/user/CollectArticleActivity.java | 110 +++ .../ui/user/UserContract.java | 23 + .../ui/user/UserPresenter.java | 73 ++ .../ui/web/WebViewActivity.java | 202 ++++++ .../ui/web/WebViewContract.java | 20 + .../ui/web/WebViewPresenter.java | 44 ++ .../utils/AesEncryptionUtils.java | 140 ++++ .../kdp/wanandroidclient/utils/AppUtils.java | 32 + .../kdp/wanandroidclient/utils/DateUtils.java | 34 + .../wanandroidclient/utils/IntentUtils.java | 19 + .../utils/LightStatusbarUtils.java | 112 +++ .../kdp/wanandroidclient/utils/LogUtils.java | 97 +++ .../wanandroidclient/utils/NetworkUtils.java | 23 + .../kdp/wanandroidclient/utils/PreUtils.java | 96 +++ .../kdp/wanandroidclient/utils/RomUtils.java | 110 +++ .../wanandroidclient/utils/ToastUtils.java | 42 ++ .../utils/ViewAnimatorHelper.java | 64 ++ .../widget/BannerViewPager.java | 195 ++++++ .../widget/LMRecyclerView.java | 264 +++++++ .../widget/NoAlphaItemAnimator.java | 656 ++++++++++++++++++ .../wanandroidclient/widget/StatusLayout.java | 83 +++ app/src/main/res/color/tab_text_color.xml | 7 + .../main/res/drawable-v21/ripple_close.xml | 6 + .../res/drawable-xhdpi/ic_img_default.png | Bin 0 -> 1124 bytes app/src/main/res/drawable-xhdpi/ic_logo.png | Bin 0 -> 13452 bytes .../main/res/drawable/article_tag_shape.xml | 19 + .../res/drawable/chapter_tab_selsctor.xml | 5 + .../main/res/drawable/home_tab_selsctor.xml | 5 + .../res/drawable/ic_about_us_gray_24dp.xml | 11 + .../drawable/ic_arrow_upward_white_24dp.xml | 5 + .../res/drawable/ic_chapter_gray_24dp.xml | 12 + .../res/drawable/ic_chapter_light_24dp.xml | 12 + .../drawable/ic_chevron_right_black_24dp.xml | 9 + .../main/res/drawable/ic_close_white_24dp.xml | 9 + .../res/drawable/ic_exit_to_app_gray_24dp.xml | 9 + .../res/drawable/ic_favorite_gray_24dp.xml | 9 + .../res/drawable/ic_favorite_light_24dp.xml | 9 + .../res/drawable/ic_favorite_white_24dp.xml | 9 + .../main/res/drawable/ic_home_gray_24dp.xml | 9 + .../main/res/drawable/ic_home_light_24dp.xml | 9 + .../main/res/drawable/ic_menu_white_24dp.xml | 9 + .../ic_open_in_browser_green_24dp.xml | 10 + .../res/drawable/ic_project_gray_24dp.xml | 21 + .../res/drawable/ic_project_light_24dp.xml | 21 + .../res/drawable/ic_search_white_24dp.xml | 9 + .../main/res/drawable/ic_share_green_24dp.xml | 9 + .../main/res/drawable/ic_system_gray_24dp.xml | 18 + .../res/drawable/ic_system_light_24dp.xml | 18 + .../res/drawable/project_tab_selsctor.xml | 5 + .../main/res/drawable/system_tab_selsctor.xml | 5 + app/src/main/res/drawable/tag_shape.xml | 7 + app/src/main/res/layout/activity_about_us.xml | 75 ++ app/src/main/res/layout/activity_base.xml | 26 + app/src/main/res/layout/activity_login.xml | 77 ++ app/src/main/res/layout/activity_main.xml | 114 +++ app/src/main/res/layout/activity_splash.xml | 13 + .../main/res/layout/activity_tree_child.xml | 27 + app/src/main/res/layout/activity_webview.xml | 6 + app/src/main/res/layout/base_tab_layout.xml | 48 ++ app/src/main/res/layout/empty_layout.xml | 12 + app/src/main/res/layout/error_layout.xml | 12 + app/src/main/res/layout/header_layout.xml | 23 + .../main/res/layout/include_recycler_list.xml | 38 + app/src/main/res/layout/item_banner.xml | 25 + .../res/layout/item_footer_load_error.xml | 15 + .../res/layout/item_footer_loading_more.xml | 27 + .../main/res/layout/item_footer_nomore.xml | 14 + .../res/layout/item_home_article_list.xml | 91 +++ app/src/main/res/layout/item_project_list.xml | 102 +++ app/src/main/res/layout/item_root_footer.xml | 6 + app/src/main/res/layout/item_search_tag.xml | 14 + app/src/main/res/layout/item_tree.xml | 52 ++ .../res/layout/item_tree_article_list.xml | 58 ++ app/src/main/res/layout/loading_layout.xml | 14 + .../main/res/layout/main_header_banner.xml | 15 + app/src/main/res/layout/menu_item_share.xml | 10 + app/src/main/res/layout/search_header.xml | 38 + .../main/res/menu/content_menu_setting.xml | 24 + app/src/main/res/menu/main_menu_setting.xml | 20 + app/src/main/res/menu/navigation_menu.xml | 22 + app/src/main/res/menu/search_menu_setting.xml | 13 + app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2050 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 3622 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1359 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2186 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 2941 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 5220 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 4853 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 8489 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 6995 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 12413 bytes app/src/main/res/values-v19/styles.xml | 17 + app/src/main/res/values-v21/styles.xml | 34 + app/src/main/res/values/ToolBarStyle.xml | 31 + app/src/main/res/values/colors.xml | 26 + app/src/main/res/values/strings.xml | 60 ++ app/src/main/res/values/styles.xml | 38 + .../main/res/xml/network_security_config.xml | 8 + build.gradle | 29 + gradle.properties | 16 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54413 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 +++++ gradlew.bat | 84 +++ release/app-release.apk | Bin 0 -> 3192440 bytes release/output.json | 1 + screenshot/Article.png | Bin 0 -> 183655 bytes screenshot/Chapters.png | Bin 0 -> 197714 bytes screenshot/Collect.png | Bin 0 -> 162837 bytes screenshot/Home.png | Bin 0 -> 293605 bytes screenshot/Project.png | Bin 0 -> 320657 bytes screenshot/Search.png | Bin 0 -> 231571 bytes screenshot/SlideMenu.png | Bin 0 -> 63113 bytes screenshot/System.png | Bin 0 -> 167600 bytes settings.gradle | 1 + 239 files changed, 12840 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 _config.yml create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/com/kdp/wanandroidclient/api/ApiServer.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/application/AppConfig.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/application/AppContext.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/application/WApp.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/bean/Article.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/bean/Banner.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/bean/BaseBean.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/bean/Chapter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/bean/Friend.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/bean/HomeData.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/bean/Hotword.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/bean/PageListData.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/bean/ProjectCate.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/bean/Tree.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/bean/User.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/common/Const.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/common/ListDataHolder.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/common/UrlConstainer.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/event/Event.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/event/RxEvent.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/inter/OnArticleListItemClickListener.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/inter/OnItemClickListener.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/inter/OnProjectListItemClickListener.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/inter/OnTreeItemClickListener.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/inter/VerifyAccountCallback.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/manager/ImageLoaderManager.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/manager/UserInfoManager.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/NetConfig.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/NetExceptionHandle.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/RxRetrofit.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/RxSchedulers.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/callback/RxBaseObserver.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/callback/RxConsumer.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/callback/RxFunction.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/callback/RxObserver.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/callback/RxPageListObserver.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/callback/RxZipObserver.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/interceptor/CacheInterceptor.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/interceptor/LoadCookieInterceptor.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/interceptor/RequestInterceptor.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/net/interceptor/SaveCookieInterceptor.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/LauncherActivity.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ArticleListAdapter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/adapter/BannerAdapter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/adapter/BaseListAdapter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ChaptersFragPagerAdapter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ProjectFragPagerAdapter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/adapter/ProjectListAdapter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/adapter/TreeAdapter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/adapter/TreeFragPagerAdapter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseAbListActivity.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseAbListFragment.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseActivity.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseFragment.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/base/BasePresenterActivity.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/base/BasePresenterFragment.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseTabActivity.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/base/BaseTabFragment.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterContract.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterListContract.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterListFragment.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChapterListPresenter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChaptersFragment.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/chapter/ChaptersPresenter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IChapterListModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IChapterModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ICommonModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IHomeModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ILogonModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IProjectCateModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IProjectModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ISearchModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ITreeListModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/ITreeModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/IUserModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/BaseModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ChapterListModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ChapterModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/CommonModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/HomeModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/LogonModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ProjectCateModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/ProjectModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/SearchModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/TreeListModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/TreeModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/model/impl/UserModel.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/presenter/BasePresenter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/presenter/IPresenter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/view/IListDataView.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/view/IPageLoadDataView.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/core/view/IView.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/home/HomeContract.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/home/HomeFragment.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/home/HomePresenter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/logon/LogonActivity.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/logon/LogonContract.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/logon/LogonPresenter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/main/MainActivity.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/main/SearchActivity.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/main/SearchContract.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/main/SearchPresenter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectCateContract.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectCatePresenter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectContract.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectFragment.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectListFragment.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/project/ProjectPresenter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeActivity.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeContract.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeFragment.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeListContract.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeListFragment.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreeListPresenter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/tree/TreePresenter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/user/AboutUsActivity.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/user/CollectArticleActivity.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/user/UserContract.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/user/UserPresenter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/web/WebViewActivity.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/web/WebViewContract.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/ui/web/WebViewPresenter.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/utils/AesEncryptionUtils.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/utils/AppUtils.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/utils/DateUtils.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/utils/IntentUtils.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/utils/LightStatusbarUtils.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/utils/LogUtils.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/utils/NetworkUtils.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/utils/PreUtils.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/utils/RomUtils.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/utils/ToastUtils.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/utils/ViewAnimatorHelper.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/widget/BannerViewPager.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/widget/LMRecyclerView.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/widget/NoAlphaItemAnimator.java create mode 100644 app/src/main/java/com/kdp/wanandroidclient/widget/StatusLayout.java create mode 100644 app/src/main/res/color/tab_text_color.xml create mode 100644 app/src/main/res/drawable-v21/ripple_close.xml create mode 100644 app/src/main/res/drawable-xhdpi/ic_img_default.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_logo.png create mode 100644 app/src/main/res/drawable/article_tag_shape.xml create mode 100644 app/src/main/res/drawable/chapter_tab_selsctor.xml create mode 100644 app/src/main/res/drawable/home_tab_selsctor.xml create mode 100644 app/src/main/res/drawable/ic_about_us_gray_24dp.xml create mode 100644 app/src/main/res/drawable/ic_arrow_upward_white_24dp.xml create mode 100644 app/src/main/res/drawable/ic_chapter_gray_24dp.xml create mode 100644 app/src/main/res/drawable/ic_chapter_light_24dp.xml create mode 100644 app/src/main/res/drawable/ic_chevron_right_black_24dp.xml create mode 100644 app/src/main/res/drawable/ic_close_white_24dp.xml create mode 100644 app/src/main/res/drawable/ic_exit_to_app_gray_24dp.xml create mode 100644 app/src/main/res/drawable/ic_favorite_gray_24dp.xml create mode 100644 app/src/main/res/drawable/ic_favorite_light_24dp.xml create mode 100644 app/src/main/res/drawable/ic_favorite_white_24dp.xml create mode 100644 app/src/main/res/drawable/ic_home_gray_24dp.xml create mode 100644 app/src/main/res/drawable/ic_home_light_24dp.xml create mode 100644 app/src/main/res/drawable/ic_menu_white_24dp.xml create mode 100644 app/src/main/res/drawable/ic_open_in_browser_green_24dp.xml create mode 100644 app/src/main/res/drawable/ic_project_gray_24dp.xml create mode 100644 app/src/main/res/drawable/ic_project_light_24dp.xml create mode 100644 app/src/main/res/drawable/ic_search_white_24dp.xml create mode 100644 app/src/main/res/drawable/ic_share_green_24dp.xml create mode 100644 app/src/main/res/drawable/ic_system_gray_24dp.xml create mode 100644 app/src/main/res/drawable/ic_system_light_24dp.xml create mode 100644 app/src/main/res/drawable/project_tab_selsctor.xml create mode 100644 app/src/main/res/drawable/system_tab_selsctor.xml create mode 100644 app/src/main/res/drawable/tag_shape.xml create mode 100644 app/src/main/res/layout/activity_about_us.xml create mode 100644 app/src/main/res/layout/activity_base.xml create mode 100644 app/src/main/res/layout/activity_login.xml create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/activity_splash.xml create mode 100644 app/src/main/res/layout/activity_tree_child.xml create mode 100644 app/src/main/res/layout/activity_webview.xml create mode 100644 app/src/main/res/layout/base_tab_layout.xml create mode 100644 app/src/main/res/layout/empty_layout.xml create mode 100644 app/src/main/res/layout/error_layout.xml create mode 100644 app/src/main/res/layout/header_layout.xml create mode 100644 app/src/main/res/layout/include_recycler_list.xml create mode 100644 app/src/main/res/layout/item_banner.xml create mode 100644 app/src/main/res/layout/item_footer_load_error.xml create mode 100644 app/src/main/res/layout/item_footer_loading_more.xml create mode 100644 app/src/main/res/layout/item_footer_nomore.xml create mode 100644 app/src/main/res/layout/item_home_article_list.xml create mode 100644 app/src/main/res/layout/item_project_list.xml create mode 100644 app/src/main/res/layout/item_root_footer.xml create mode 100644 app/src/main/res/layout/item_search_tag.xml create mode 100644 app/src/main/res/layout/item_tree.xml create mode 100644 app/src/main/res/layout/item_tree_article_list.xml create mode 100644 app/src/main/res/layout/loading_layout.xml create mode 100644 app/src/main/res/layout/main_header_banner.xml create mode 100644 app/src/main/res/layout/menu_item_share.xml create mode 100644 app/src/main/res/layout/search_header.xml create mode 100644 app/src/main/res/menu/content_menu_setting.xml create mode 100644 app/src/main/res/menu/main_menu_setting.xml create mode 100644 app/src/main/res/menu/navigation_menu.xml create mode 100644 app/src/main/res/menu/search_menu_setting.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/values-v19/styles.xml create mode 100644 app/src/main/res/values-v21/styles.xml create mode 100644 app/src/main/res/values/ToolBarStyle.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 app/src/main/res/xml/network_security_config.xml create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 release/app-release.apk create mode 100644 release/output.json create mode 100644 screenshot/Article.png create mode 100644 screenshot/Chapters.png create mode 100644 screenshot/Collect.png create mode 100644 screenshot/Home.png create mode 100644 screenshot/Project.png create mode 100644 screenshot/Search.png create mode 100644 screenshot/SlideMenu.png create mode 100644 screenshot/System.png create mode 100644 settings.gradle 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 0000000000000000000000000000000000000000..53199e33330993a12a7f8034cc7ae8e5c7c0fcab GIT binary patch literal 1124 zcmV-q1e^PbP)>f|KB<1 zp8GMx8gE?V`q2mIbp-nn=tm%?2l^=#(}A&vuNZ;p>FJ3|rSi6j+(9xE+ggVch{#_6 zE_$AKKq+;;+3;lnf*{x$MbT#@%K$zBaJfVKNp!zOM0S$g0N^F9^`XW|n*z+u&0Sk6 zm7HMreTy-s7visIot>S%X?}kGJjn^q^ET!4`5)^cngWDjcvlof-;ms+lzKl2SH_vc z_kBgu0+`fVA8iR>t=$LUXeyQ3n9JpUj$=HjvA*xS^>ES{^L#r1F9JB2N~JQnT<+JT zM8uiLT3Z5ePHX*CAHaE_P$*nisZ_?&>GY+|n>YU*$Gh!}O*8^Q5bPC^!vNNcNCm*F z#+bL-5z=j+!~m?d_X7ArM85Jo?|2kNkCNO@@^Piq>23wK8kZ!%F#r!Jr8ba^oMCJ2 zcK|MEt#`E&((U&o0j2;vSP$U){`UZi#+V)53T!nlNq}ttKChnB;3(>o->tCnBjZ4EID)NNXwK5t- z(fH8N(4}lP>vqXyoA3K+0AG^aA|k)L>P7N;0LPV5Pm`2ZAc+ICGFbcGAbu*7$xN0? zB?s_S8 z0C7?&^~7?KNEDz@DBQNVxcCss`E)vcc5H0S<@JB9D(oD9t*!FBNhF%CiNpZhPAnoH zk{ou5MdTNk2F93T_5YN$_6*7UT8VEK2@!dx@yJ}QeDP1CVHj?&R4N|<_}240cUQhq zL|y@K1Igz^Z-q2bfsE=yw2v}>kiO8p}91af;@62Yi3(i0g z1mjT@S(4Y(47OWY8W*eEE3CDrNIong`;9T~z<8y_0w@-XnR2;&Swv<>Mn>)&9Uc9r z-gu!<*tE2?^jI~8elW(o*KV0@0^tQga00+?7fGd*+x6oCh=>=4;r_wF!FMMnCjRJF zT)X3(J!|b`^$Pi0Y=CwRuV%kkDYTlx+MT;ofUEIU;!l#NwbpyeO?>X!4d(J-lJkNg4dp#X>auQ||002P#TH~bw0DuF;{=Xz5z}nCc ztYp{=v8RTq4*)<)``-@-kYC6I0FY=qtE%ehIeGYc_&9lZa=cbmb?0W`=FMo(7-b|i&Et%XaupeCB zaAW|`Tr``cIOzs}A0kFS=m0;M05HQwI}$(^0N@rBs=@`RAqVW9zE;EspsF+fFal8J zkE)3PDL8;UK6M^k1&E}{=m3RUJ3d4Q9KZ2*yZ zDni2mj_0@lRW{lYJPs|q(x<_k_SK-G3Zd#2^JgrgeBrso2^xi#JO5faA`@7+=>2`2BP=4on=)tAPd9-}j znT-1YK&3NwFmJuSK7;3p#DV$sxK^73=79jG#aOlJVfnq@d?F%524DdOze@ zACZuf?(y1|bv<28QoNV=>w!)RKgT}3TL?Y`c;zeS)gdaPZ@s{8{p$@Jacm{f>2nSI@cx8bt>Thlslk0L>`f<=VxfGMzVL%XJn<&6Q(Y`8>Z?B96t180 zw=e9>iG!s+6TXs^mXSH-t`p2nYzmPr24x%?hzD+^Bty@Ed8=Vaq7=Cyi4L2I35m(78t`4?`YSF||Gv&C{yHpZu8j z!|3U(I7^;mE@iHVF-YKCAe27i7frToHZVIpd$Y#K*vD9DI;e)!sMeUW)(RL_`O3Ia zZ?&3uilKJ8n$@_YTDpGJAiy9^Z%p61qTBlR-4%rVYh3KB(m0I@BS`Du!xT=2L2>&d zkjjiV5N}7wHCNl#{OjeemOuVAi@#`ymZ7RojRVvn!=Y%yYXpa45RN{SnJd&BA&`s<)HLZT$KuBRCIuPJhFvKF4_Nal?U58i!Xfo{%K&Xn|~zB zHQ%9qqG{51c=(6VA1Dg?b2n9Hy3n;4+&$O1aFY`IS*>tDGf6rLnKPdgI)tC2or8hq z0u_Nlz%$@1^!1Fzj?j+#jES2bi%9`lfo#E0%a^)>y6^r4?#py5bZPv*`OggB&Wr8d z?2_!}?-K0EoP*DK&)v_*Scf0cu+GT&%7uFS`VITLd3Nr6`YYLA{k^(>%fykzvCOCF zM2fNUP5KbcNB@tEhVE5~Rc}jzhqBk1_uGFrPo7m2bQJuln{%I9999?ztG(71DPZ|& z*{^W$;`9&$TJaiQEvhQ0>N`o_6t(iVGPSzyV$*Fbd|W*9)cwc8cXM>tP>7AJqJsU^ z=oX&hpdxNba7lc3WjEKdLN|B#On6>6;Z5|JNtB?_so3eXndLS5&SuP715iqTs<9r zti?7%of%&{yH03{R6all(pD)UVsa`q7>AvzClpxkM5DdzA_~9G(i8k=%%o22L z&TCFsCaW;3u)=`cIj2c*_Gv3$tFD8fU?%xm(1!ajmdyBHm^JQ8pOXc8w^*%-iY9}4 zJqiXb(Jz95=1k^+Z5mw~W>XT&4tLv^+@$XqLc7vodTp>qR9m3w5t^$>BJnoRZj^!w2oyFQ+bIG?X*ySvBq-o^W2%Z#JYR`PQ1-futCS7qJTohqB1iY=Q zZS9_WJU`R37!Yzo5zR_3w{(!T+u1A_XyV$X7Iyab_x`)ZG-g|t_MVOK#WwW--GSn? zkk286=V}K#F1{Xae;Rsvz7o3|NT;xA$a=09M0(5@?BxeEdzuMQ^u1W`eX?8`8nUYQ zuWYpcPyfhrUPS8|(T+xt;BVO;Kd5kpd#OTM*TKK!iXv7kR`u@I`|ty?Rfr!_D$S(0 zw1`jP=yL5{=4zE)RdJDylEPHppCF9ZO`fZzzGauA>R7Q7qwl^}t2O55$EqZM^Mg7yjpai~%J3T7N8q(*hfAy611V39l%(Bbx5u_6T?Mg$=@HtlyOK zfPy%__%7>yGXC#x^zK0 z83Wy*4oNSQ#(L_5s!zNRYO5P$k=#i3&e0%+qsp7Q(S@*swuAO%WEXoL-;MH}-pRxj z1X(q%i&5MRy}J>`#GG97{w|TgqD>BnhJiK!5X=hzL`Gwg=sotj1pxRx1pu~f007xs z0D#fsgZ02G0D%A7>z6N#0_S&&OtUPevnzWNhK(v|G7N}PIFvu-3KL{9J+)z3eXxMC zVQi6aaSrhNXta1cF4~ndRm*;5dj+;=;Wp zSIIa_e4P%24vr|U5>7YX4RfI72AzzYHeTVw#QgJv*cd4g1E3X`op5QL&9yNpD&Eql z2S1efB6CIHo@YV8l4Nq*bL-vZ z2#SlYVZnSmPEUS@cV?8({*~+g!iTFRxW#zI4~oyb{ao7-ogL}bs7$zjiOGn})^D$7pyB@*6*oB}) zESD9UWDL`wqd|PzBM*yL?Gcvy>mvBwyMJ*x!R`+o@d7%K?WG9JH>!(Ix?|y~K(B#3 zAwos`n{3_N#;<_3UNwK}!yvb)H+IVAW-Jvk!W5m0ayAox66{$DMJJPTiI(th{B+8h z?WYE=qCv?(4G^g#6H`CX(!7@oy$B+6&lnbksr@d9ib#q5l!&&)#Cc>(YkT1yj;t41H7{LSKHn%m6TLjACCN zZ#`Uka3hGONYp+`GCVe&=jBsX?wcRTirj(U%sl(~NTFBU?eB51^oY?b3yU!WL&>yv zlP5lp7OWK(H?Elm4PaKdR`_>cD!0rSY-A?c5ezyDpUJsFe9+{?uS1ZLJTv1`2+p4& zA{dVOuvsOxnTVgRUsypicwDVpSDOnwQs4~b^W$5%0pelwZ)VIU*npRappPL*dzHWP zjK9XjNIkPc{DO8d{J6Cs4#jOV)1EX=2C$#ESh7WyvSHo~MDjpmVeGZ1?3(;NG%e0( zRX+S$b5LZ_%OhvE6S^q zmPw99q*gogK-F}Tit+t}K^$}<1b5^uQOZX%qw5xwIDZ&^NSF@j(1sJ3chb|$sC3P+ z<0x?yLWF*yMhkVp1Fg+J#`8EMz1$oZH8?Q7S#uI_Is%QTV^9Hv@-Q-lDS+k`^Dn9v z2}jYKZ%?Zow!ds15V!?Q+v6wd@U~aetnNck1Q_#F_{hMe91Xp{r5Ann z98W?Y|3j7%$AWQOffe@-NA51T=RY6crUI6cD|MX41?_!Rd9AcMH##7Tv;LTDGrvb~ zIB(2_0*p04(KbqlQfN8Fd%8(Xkani-@ z>~fDTqP^L%aG3^Zou3_`Lb@$=3!3t zk^g)EhrNOI?Gy9?93NDsr##+C5sKILXFw>1(0N=Hw*gE_pH_A=1B3$t2^mZjl+r_L zY-L6lB1@`8e?a!=ioJBvB}7$`14Jz}VWKVK!e;%!Xzn|ythVivNNkcmjl!l6xpxm4 zr^g7SN6g+E>$l?0Mc9eIO|zkAK57g!}x{;}0-@<_2(WUBsK`mvy|D!leW2 z>GJ^vLq=S7WGaZ5x49-!7^+Bgqcg$--m~+%WylG=4d%c|CV#$$A`SXq-WkDSYeHas z&&v)8q1^)wQ!i;|bR5$HHsXn@9xQwVeQQsemX9T~fS_!RH3${D0ijFR(a&VR4ri_!F~MUKFoqY?))h6iHKcIjzZ9+A~o!Ymgg;r&O&t?d@b%?1cKC6 zzdbL#MGhI*l~s>`5%I=#SG+Y10^mIrm)CP8NQ0vNPpHnphtT60MYoQXq`OvlH9spEsR;^+ssLS0XCtgX9P~yI!uM(@AuM#^gq>?_ zH7&vnW9+c+*VY|BQUPzGP9mj1fBd{iU-}cswQl>M`l?j`G&99$d#K)!8i@B2%1fXN zwkHj^oH`&DiZeTi5-4k2NkW^tb^MF8cD!t+dE?$em(*jGJOD`&5WX~bgZvV~Sj5IO zI|e>7H)^A6lkVSn6wt6XBvKP$cNaS6pXl;_4(0uGz<#`|4d;G7?K94+h}LbqfhOxG zw>#B8FcR)}p-*UHpfRy_Y)5lHKcg~)JTU%aav|ta%K2sG%Nm*)wzY)#2ct-Ta#iW5rtAoff%k$c?9twy$lC1p)#mo9O*i6i67A4=4Th|`adEb|zsz*O+6~|f6 zcVvWaw>WntqXIe@3y?=LRgopO1%UG{>P*##hE`FTwcmDDJ=d}{WHBOKeq|yKk({(N24lLQ z1(oG2`~)&pZ8w}rVbZaKQgw+3>~kjvTGeOVAo~@k7vh8sV!l3y1I%F$dl{SsP%9(i zB?O`;5u#pRtlqkQi2D{%Z?&r5`ud4+csd=PGco6Wn3XDvPOAZN9;>scf9a$L;Q5k% zxfFZK(8>O1q8*6hg5xtQZ{0Jb;ty<$j-5OJ_MCaUm0VgpLZ(h$EF*fFyndPYwG0-o zTYn4zRruF=Qd{oMYP{6C=6@U?@R&v* zcJ_)(RxiS^(y(!*f^--OV@y2UJ$}=mXZR0U%TF}vUGscg_t$YXZ>bh4pQw<9=4-3J zY|bOJdr4Y5=G4JoV%g&qn#4!)NUh@k6fEv=bubQ{{8=rRTwmI)W>`2k6;jx4ijNg> zIy^`P6XGaF9vkyQSI8eBG|B4mrPD(D9iQ%H@HWX7hgKN@OT7SkL?mkgH73D8-Z6n5X)IA&W z934rz*NB(|S#7gh+EE6}(ec~#5Z;X{+*7s}?HtL&@P{7nng6qu-JYFRTvQqx*c57S9Jc#O*vN^Y5$&~IyoRZjIl}9DGeO6XM56@pTa6+m2@`*9MKy87A;zK*xgskk!PwT1igv$R4tT`hNT3hAy3 z#v%lsCUm@`!fvy)QM_A$D*v0Q_=8mhVJ^QOhK^r)eB?g%BroNs?sf*TZ&N*xKA(6h zshvnLKa}{;2OT1#`27}iHED)^g?11M4~`=JMpF93=jqD83_-_}#SVo$pIjTV3lG#o z`_Bnq?r1KQ_UqVynHnIq6X>}oLhw#`a{@F3o2oB>?`6-nIkxC+yv?QE?^R5R^2a|PAK!1L4yPx6Z48*I0B{Z?4a3wM7 z6n7Fu5VH&}kP1&UW?Cqi+_im#TH%a}hlOrNWPQ;Dsa=~U)7yj-ws80hw+L!Z-iFGo zful-n`Mo-Rb~{GYCGC(fPFYV}Ju=7A7wWiNOVjYQkm_3EO;q|@I|9-L`6yO2hEyn% z!#%2ycPUO4WPvfx1N8~at17h+ok?T#JdWcH-_4sv)!9?;V$}f39Ki*$evnpY$($ia zOpI|;t{Q0fvD}#l z8Qyde4tZIKWcmul!z|6M!RJpkCh)%yy&)uj2l`yy5vgmlpg)#lDw{uma0i9OeMYpx z-M>nRr^o{*@^KDFKtgBH2zy@#VI%z#B}lVz587&%I`1W0Eng}Q<8)8oN_DN>hx)b9 z-O~hK(Ed>xPdyD#pNtztQA3FnP+@%k){#1AU2NC=UezrLik;;`P@J${7<282j8Nuj zR$Fn>z*JA%XFKg7$)admDEKR``KPM@l!(Ay>9%(9V5;*a2~g$@Hnuc@kj)@-XTxuVv5~2n z{lUXE$R;#)MvBS-xkqQi5`)^v-c(&t`QK&98)tmSaTD8-KoR3VvH$Q?|E9hxE6NWf ze?|)mHBJZ?8jyaUbvqZPQCu5i+?kPNX{7X6pPt;HSNxR&j253woz#p5cyw>Nv4niG z=t^I%541HeptM2SDhXqk1%6>PYms-uOJlo#ssWHH+DB^H@OLWjH|rdk6YaZ!{vWD696JW$IKada1Iw8qDtLLb15 z%20?n-l3#1iN$Tt0}B-gi?*ORUg`@CFnnD~J1*yiT57Q~m~=6=nQc%B+Z1711In4Z zN$`F7KSHK!_P?htP?gho1cGFni4tDuY~HA1bj3= z9kJkDXboizBuScLH{e@aeYy+}bkSz=3`C4*E%i)tV*iN84tU!uU4`A9klv{QYpvyN zHBO?psPe5n1ipBDOtay`2X6-wA<%4@mjEH8?QKBWBNt-V`1xZJ#%VF_u9wQte%rd) zYqTg2lvQ=0YuX%U`INJQRimI-BZcehj(f4i zKmnIg8Nx5zAn?{IXeF$NQZtDtq{@(Z!QfT|&5tgb7{noVMUa8vwXM`T-8S@r_;Qq6 zRR{>8B+A5+t`h!q8~}87rEhV$k%nsimd%==T2G+V$OL`9`v`;RS1#^Y@1L6!LVM45 z<1gIEA^ef8fUzKUYPDor**n=rMo-~Gc6e|uXTpT@F4-p!+-0wWH9fotlR;pL$6#V$ zCkt;*iNDxwY)D`affWG-9x@cLgYj=@Lq^ms91`^X@d{wRpgvTJwERcp9t6b^UtvR} zL?vvK9r7Fk4#`XIo}ILg;VmDh_lmh~f*KNcsBQZ8+9ur%JR)TubP~z0Mw}!p>{DQD zhoTftb{1W4QzAq-!Q_c=xC}R`D(`gxCHURR1Heho;n=@1*6fuS&2iQT-J~}PSlVY} zU9p6Ivp=m!C{G$nVxA$2e|ic022)(Y>vLB4MKhxZ+MVp$iHybhGsYej0qD+uVN}6& z`vsFm;(z?2VNHn*x0~`tl(ATnR9-^S{6fAZ^5OvgXj4yqRuL`k7Jy#;7c=`upUqX) zMG*STHi>?3gYWR^p(@nK!b+YvlqG5IT;=KAgF``#(S7W0?FQkWaBEV%PJw+UC~_1+ zOEbeEyIv;(-BawT*)|+yZ^L9bl8or1KccvhOKeWFRZ*-6T?@OD{MVra?t#xs$7G2t zR&N7S*qn)7iI%p_ga!m+EQOFT-x_rvx@f-ZKP-2`IaX73kIrALZLXEb-zO)XNkq-T z-U)YGy#QU#Vu7n2Jnhtk`jyIsAc%SV1Yakuq!*Y@8Q@%?gV*Duf2;Z;dU=)2;W-oP zBqA?qM^j;*X2w6H_8HZyvl?fiqX^yJ^476_a>90$c^GaIKfCBZ1uJi_FfrIzH`hwD ziGNBp0REbw@CrmGGQuL09O~57nVGMe!9WmZ?@9D z7>LHEieEB_Y}EMx)iI5(txQgX;coYV;eMUYMA-F^<4t4N8lHxJq5t3T301tZpgI$9 z5{Oq}gJuS}uuUU0Czn;1Sd}YGq|#gNnKdVixA>i3DBw()RQ_kbNWD64V=tG`-nPqp z$vPK!PwZ9aiRFvuyyrC{zIBIOjyGNH!X%OvBBQzV=jf1f|ETSA;q76k&c+6IG`pvf9#WWj}1u>xw`f9#?qY~7A zrUHkf|QGJYNI2QX3tJtL?D)4xK| zEsy$+A9Wl>)lPp>=)mu5A1Cb9YfY~oT!C&@s0k6flFH|wSb|IlP~onjSHMqCx*jn3 z;T%X`g&{y{NAQ!xB$F>s$dhgU*8BY%e}Qe@nd~QG#aT?+{HrVZge3S@5sDt3M%6dI zef0bzq^$TeKBhuSQA3n$iXkjKO>IG?PvA=%3`#L&l?3}8JFBO4| z)2gS{R*{1j#`?EPqG-LbQR+)!X-qRm9M@#48qV^jE#piF+^6<u+m2tK8>zQ?>_~As(D9J*k3krhg}Q=~=nRX0I_}s3WcrXT_~B90tQ~ z$_pZ}(UA*%Eld?aiC5A~F+cRx+2%(V`nuCyb|AxaHw?`MO9Tg)bN)T!mrcbE- zo&X+O8SZ9`xdu&FzG~GI>0gNq=x7bLqHMV~L$9KwXB*mXe~`nY=l>zq{Xs z-*+3*?u`xpg4CC`P4e?V6$GHo)E${!`_H}+wy+DD28^rXh27n6Ui3?p-yISNpls26 z{u{Fp(D0Fc>#8&3ln)Fxv5vw;*~b_|pA03t$5QrHO|kMogS4tlE)&9ljG@IZgslA8 z9CPjjD~8iwxdp@~?W8CwcO?pY7MXJGdtYJ znsipRL}8m(#wiw`MOX`6r09TeK)leP)R`f9GLAHz@B9-i3kva;7JfC#nbFXWJv80 zaid*jOmqLME&6bN0^#7W^A;nWxELu)mradoZes9QKaJ6rW?z1Wnh2ECo`e&G?lU`$ z%EnejO0I+kZJ~+$#A1TbH+oK5efb~4F(Af@{;3#%`7rB|BX&)JwH0L30{N7;cvW&= z{mV;-IoNWD%L;OuC76OOxxm@5N_(P(?kZLjm0_3fFeOHBX&z`b=pU;SQ(YuUloFv- zyeers&5RLQ`xlqLnhB;?mPWi#nwhCS*#w|cY{Ku&@^)nj8DdxF(PPg@mmCtWN}VZr zAaQL>vvZsUXdRBdfix=^c`d;M)s{eid}Yt+z<=ojS8n{T4*@S!WonuP+gggARGzGK zl>2*LS!8(6ny&_^;8l@&f*sg0!QbjN$k0H}%g~>CMJs;wkOoV}H(tMSTTFAlK~B7_2W$7YeXh1rvQjcU`&6 zkCE2ks;2tGbc6w#tBFJ)xWE~$Z_`SOr32E#JclHQ6&lZNgb&5gI*jI=!5K<8%lO3~ zu&ZIj`%lAdM#(KhAWk4b;Io!G4?Bv5k*5Xq^owqKY6X-p9gnd*8%5kH;xnvUZD#z- zA=)T%C#nEm7gh@^><%w^T$Dg`(nRXS?pRrsb%UvZ@0o?Y>qhm|PBpa9-v&t?cZ3lu zC3X9!%#UfpaY`T2-WwF%C-tyr^nMbR4jlHgUjJc}bt)mr)SNAFD0HZX1u=G5yvoUT zC+>#%DS)xKhPl1t9^OEVzKr?w=vz6L{(++uIR8D7u{E^uBR8&P3en`dn3Cz#0?x<# zc+}K_NGpl(GqwF%QE%&sa`g0%vGVd|_UkU^`*4oq{K<^#gS7YVds1((PW@)2-PnGF zM?f5n&?@RJXTI$6?gP-XC;ajwh`F%IKV_aqjeQ`PG{D}x?}Fk@5TlJ5e;?p3_c7Et z@ZiwQly4=yN02pMHXLm!`N@XJ8!7jx0uMZf#NgV&hvh5!HQdruN0LLtJ&cUizvcUA zFfBvGeG?x&55mM66l8O=Y*m&%+8lJ}ZZ0r7H@JK%BCu5wR0{_t&Utd2TZO@+J{dVu z2_U-gp{1`5FK(IK5t3CWhOd8sqXI$`h+S)|1J#?M_YDMid5aBtl^Lr?LABo*NP^pNSdS=UL{NuhkBW49_^dG>^-4l>1^mfHNK%~k zTfz;LIizS3K0fX%q(wssDatDS1;L7)=UV3klJ?Dzby z(E!C#Ve?JQ{~8)>kAF|Wb=h58E3Q7h4Lx-v*=(?i-Rd_cyZK`fR>Bs)Ei`E~AiTh-XI^eEgHh~4^9D@Oa;dS*n>skjGz zkK~*@%Ohv&;B>hzJ{_m!3a%yJ*fKN1W&bwFP+uzQb|9;}kVb3QmNN3sQYgYxC}16t z!VecDei8&+xK7slL!ac{imfI;^Qur&>|9m6a>r?L{8!4t)gWW$8NaY$t4FuTU_95@ z{UiC+E)8Rr<-_Yki)7v=ZEYrhy3_80+ZxGBt%LK;%RA+wVT%R0yt-<0(mw^aBF8Le zBO<=QE3=M+5&St$y5Nao(?=;@2mORMuDt6zT9p@2bIW1rHF=c%pr?QPtHSCW_MbIeKyLgNSnv1kLx7t{;R4#S zybFu|ruz|ZzEeA|-4?u8mlmj5u+(_6LuTjm{-3(M#l$cN{K=|BMu=R_b`!(`RMwTm z^BQNvkN?#!$xn66{vm;|0TD+D$N(z??VP)vdZ>3%rd@Iv{nWSE?1)e7aa0ghs+(*Q z179hwt1KoUpKnATJN3CjT@75nD$>R_LCV`kjcTV+&EQJ2&)TyasIJ}7Ex+&Cc*R3p$HPZ?{sJD<87??~vrr$lBqfB_vJFuu`*-^0MecwFX ztd6GZ7jcD7PA+hUX)jO*XOo=&UR0M-bc`dcLqf`K%-1cT!R<}a{j#Q5P%&mUXzb{* z^^gg1wZD%C1Xa!7XiTS0gXD5(W#>xdOtwO2Y0)o-{vsRV4e+_$89|)i=pLS!+io z_rG}gex<}Xa}r8r3r5Ipwa`lbrMBl&!^z%0D#_<}kKCS4$34`2GY@BWMtZXzX-a%% zAN$vvOix9C(yxk6VYEACCFY(3%CgK!EXRARllJpFPc{h=Hu+Aa7D!w{Oi-c`*l6O^S;H z?33z(qU`U<-*9TjmBSO7J46iaN6snu1S+<5x*sBpaKpkVM^T=}wdLGiz)m z_0a*C>#yRgjyl#I;vLW+p{HslYlJG^pst*I-IFOb?Vb8nZB>CB6z^y8fY?O& zmM1eht!wpt_>dJY@&`X9=U!nUh_XuH6q0v9{F)Pu1i&57LXpvtw-LfMBw(4?wO|pI!JsV8W_{s?nn8m~{$mQsjuVSKu)U6alV;|XqLT$(E031?2Fv?nCe8xlmd!1DDe+&A zwZW=m16c}kvq`LPV91=uR@Tu*6njjJGu1Mc|6Wt}4x)@8>hILWN(eKjA*?A~gQ-EX z`Uc)1^S)`X?%pzb{DF(J9nt~M-L$`arWO@R`LeHdc-#ox*5zP+i~O)qD?gUyA%dN| zO^?=RoKUYL9UmRNipiOOAzEwy>^~Rv#RZS&@0!Z11;#diC^*te6@GZ&N;2Uqm$Uh> zXplJ+P$4JLGPa7dzE$^_IDorBf6C>2?a^ekHgU9O!jsis#dQ%JjSl>7I#kod`3>0% zTYkdtoRLjzuJVpvA<)TZ+szJ(?)1yvGRsZP#Z+s?8g}9XbpEO~tZG<~+dfZ>v=Zid~<}-mSP!m_MbPC!|~6$sR@>ni%khQ zPj7MjRlEm3c}d})d!W#+Ku2RFm&3v&Gq{;2^0)|6eA-A(?4p$K^G4Tz*m8)_^ zAEir?rLW1};h@K8=$}UVy?@J7>?yPcH0Q`I35Xc8hJNa}Q;`Nf|H@<1x&h?=bY(A`!UVF zz4+sq*Rc))DAV<9)Ty5#DRY%{>>`yb%(5vw*67@AB-sXnH;Iw|?q}WynEVG9cs&-r zR4!|lKD*T7FEp?K?_KqAMNYj+i(H&UPry59tz034+*$0&x3__Dkc?LQ=Pp5xgR6n+ zw5j+@s|O{so0$rY3lcl?lQafj$`G3Q(h1Cb{vmer{@mS&m)cX@%9@$l^Ok2u zJpbu$N!i%fX5X0$kSER#^Rn3!+;jSfrZ)W#6i%*Z*f4esY! z@K}{EL#DrWLmH@Hqc)dfc=K-GY2`&hKKEeBUL=#}ILW*`sd392Jog(HMvsO15R zZu;%_$;)|;t(f>Df_rJ_s{#{i7U8FZe~$fyrm^WZnn1^_$;G#+C?6T<{2O}AcJ!Np z=BMj)5bfRqFLy}GIk?b8fB5oF7BiWEl%0s=*bqzbsFHA@mRw9tA#v@Te0F{4UZUz- z5LEXGVslLOZjape9nUez?xvSR2(#VT%5HndsN<4~>+dB042xEX&#&{L?XxGWpJ}hk zjj#L*x%E0YIW-D+bGsz|HnX`MOSL%MTstt#Q8aNdEhVJ<7TW(=d^z7Zab3eBH2Sh3 z<)Pvm=7r>$pm22-&?lMn!&YZTDI|@Rt0N0TiMI#RrKt8{nv|=Soq8!aG8kjxD|LfS zkHKu9su&zB1gSvv4YF;E(zv^*%8UK<@vs{@Fk(%+@1<)@{KO1z;(PQPJM|+*wLZ7^ z`wvvDO27g>91I4a`IpJ|@?}vo>lf)pH+L&4KFCKAw9l;g%}mxb*075r>+x(JDQBLjhxdxvn0*+5FPJGj94O0Ec(^# zLn0Vbi3xR8@0*g(_qz*wlGVMC_4gt4{pk7Qg*%FTx$q8px~0?;d;G|Om6j`(c*I5m zIC14g-(1(EmOa;8QQ3F$Kq-$r&vcR`vDW14d!P(wxfM4Zfw zGut=+Vfk|6U*b_zD*uP=|AX~Z|C#?|1OJ~A{Kp3VI~@5xl)#tn-UHyG!R#%jf02Sc Q@CkUWrt`91#pd1r1BWsgh5!Hn literal 0 HcmV?d00001 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 @@ + + + + + + + + + + + + + + + + + + + + + + +