From db253e0d8a3754ee9b99a8826e993c8f75e1569b Mon Sep 17 00:00:00 2001 From: Freelander Date: Wed, 19 Oct 2016 00:47:17 +0200 Subject: [PATCH] The first commit --- .gitignore | 56 + .travis.yml | 29 + LICENSE | 201 +++ README.md | 132 ++ README_ZH.md | 127 ++ app/.gitignore | 3 + app/build.gradle | 168 +++ app/proguard-rules.pro | 17 + app/src/main/AndroidManifest.xml | 120 ++ app/src/main/assets/about_app.html | 265 ++++ app/src/main/assets/js/ImageClickEvent.js | 8 + .../main/java/com/jun/elephant/Elephant.java | 79 ++ .../java/com/jun/elephant/api/Networks.java | 152 ++ .../java/com/jun/elephant/api/TokenApi.java | 60 + .../java/com/jun/elephant/api/TopicApi.java | 58 + .../java/com/jun/elephant/api/UserApi.java | 61 + .../com/jun/elephant/common/BaseActivity.java | 255 ++++ .../com/jun/elephant/common/BaseFragment.java | 105 ++ .../com/jun/elephant/common/BaseFuncIml.java | 34 + .../elephant/common/BaseWebViewActivity.java | 187 +++ .../jun/elephant/common/FragmentAdapter.java | 57 + .../com/jun/elephant/common/WebAppClient.java | 156 ++ .../jun/elephant/entity/DrawerMenuEntity.java | 45 + .../com/jun/elephant/entity/TokenEntity.java | 65 + .../elephant/entity/topic/CategoryEntity.java | 95 ++ .../entity/topic/TopicDetailEntity.java | 31 + .../elephant/entity/topic/TopicEntity.java | 546 +++++++ .../entity/topic/TopicListEntity.java | 46 + .../entity/topic/TopicReplyEntity.java | 112 ++ .../elephant/entity/user/MessageEntity.java | 140 ++ .../jun/elephant/entity/user/UserEntity.java | 319 +++++ .../elephant/entity/user/UserInfoEntity.java | 87 ++ .../entity/user/UserMessageEntity.java | 33 + .../com/jun/elephant/global/Constants.java | 101 ++ .../com/jun/elephant/global/UserConstant.java | 82 ++ .../com/jun/elephant/mvpframe/BaseModel.java | 7 + .../jun/elephant/mvpframe/BasePresenter.java | 26 + .../com/jun/elephant/mvpframe/BaseView.java | 12 + .../mvpframe/base/BaseFrameActivity.java | 49 + .../mvpframe/base/BaseFrameFragment.java | 52 + .../base/BaseFrameWebViewActivity.java | 62 + .../com/jun/elephant/mvpframe/rx/RxBus.java | 132 ++ .../jun/elephant/mvpframe/rx/RxManager.java | 49 + .../elephant/mvpframe/rx/RxSchedulers.java | 35 + .../com/jun/elephant/mvpframe/util/TUtil.java | 32 + .../ui/adapter/DrawerMenuAdapter.java | 104 ++ .../elephant/ui/adapter/TopicListAdapter.java | 129 ++ .../ui/adapter/UserMessageAdapter.java | 130 ++ .../jun/elephant/ui/login/LoginActivity.java | 105 ++ .../jun/elephant/ui/login/LoginContract.java | 47 + .../com/jun/elephant/ui/login/LoginModel.java | 58 + .../jun/elephant/ui/login/LoginPresenter.java | 77 + .../elephant/ui/main/AboutAppActivity.java | 96 ++ .../jun/elephant/ui/main/MainActivity.java | 477 +++++++ .../elephant/ui/main/PhotoShowActivity.java | 87 ++ .../jun/elephant/ui/main/SettingActivity.java | 154 ++ .../ui/main/TopicPreviewActivity.java | 91 ++ .../jun/elephant/ui/main/WebViewActivity.java | 101 ++ .../topic/details/TopicDetailsActivity.java | 316 +++++ .../topic/details/TopicDetailsContract.java | 52 + .../ui/topic/details/TopicDetailsModel.java | 60 + .../topic/details/TopicDetailsPresenter.java | 107 ++ .../elephant/ui/topic/list/TopicContract.java | 58 + .../topic/list/TopicListByForumFragment.java | 234 +++ .../ui/topic/list/TopicListByMeFragment.java | 175 +++ .../topic/list/TopicListByUserFragment.java | 173 +++ .../elephant/ui/topic/list/TopicModel.java | 121 ++ .../ui/topic/list/TopicPresenter.java | 125 ++ .../topic/publish/TopicPublishActivity.java | 283 ++++ .../topic/publish/TopicPublishContract.java | 53 + .../ui/topic/publish/TopicPublishModel.java | 51 + .../topic/publish/TopicPublishPresenter.java | 74 + .../topic/reply/TopicCommentListActivity.java | 128 ++ .../ui/topic/reply/TopicReplyContract.java | 41 + .../ui/topic/reply/TopicReplyModel.java | 44 + .../ui/topic/reply/TopicReplyPresenter.java | 60 + .../ui/user/info/UserInfoActivity.java | 351 +++++ .../ui/user/info/UserInfoContract.java | 47 + .../ui/user/info/UserInfoEditActivity.java | 307 ++++ .../elephant/ui/user/info/UserInfoModel.java | 51 + .../ui/user/info/UserInfoPresenter.java | 68 + .../ui/user/message/MessageListContract.java | 28 + .../ui/user/message/MessageModel.java | 49 + .../ui/user/message/MessagePresenter.java | 56 + .../ui/user/message/UserMessageActivity.java | 141 ++ .../ui/user/reply/UserReplyFragment.java | 76 + .../elephant/ui/widget/MultiStateView.java | 350 +++++ .../ui/widget/MySimpleDraweeView.java | 65 + .../ui/widget/MySwipeRefreshLayout.java | 118 ++ .../jun/elephant/ui/widget/ThemeDialog.java | 79 ++ .../jun/elephant/ui/widget/VoteDialog.java | 121 ++ .../java/com/jun/elephant/util/FileUtils.java | 267 ++++ .../main/java/com/jun/elephant/util/JLog.java | 125 ++ .../jun/elephant/util/MarkDownRenderer.java | 47 + .../com/jun/elephant/util/NetworkUtils.java | 98 ++ .../jun/elephant/util/OpenWebViewUtils.java | 96 ++ .../elephant/util/SharePreferencesHelper.java | 93 ++ .../java/com/jun/elephant/util/ShareUtil.java | 61 + .../com/jun/elephant/util/SystemUtil.java | 44 + .../java/com/jun/elephant/util/ThemeUtil.java | 76 + app/src/main/res/drawable-v21/ripple.xml | 9 + app/src/main/res/drawable/btn1_normal.xml | 22 + app/src/main/res/drawable/btn1_selector.xml | 5 + app/src/main/res/drawable/btn2_tran_style.xml | 32 + app/src/main/res/drawable/btn_bg_white.xml | 5 + app/src/main/res/drawable/dialog_bg_style.xml | 5 + app/src/main/res/drawable/edt_line.xml | 7 + app/src/main/res/drawable/edt_line_gray.xml | 8 + app/src/main/res/drawable/edt_line_theme.xml | 8 + .../main/res/drawable/ic_place_black_24dp.xml | 9 + app/src/main/res/drawable/item_bg_line.xml | 18 + app/src/main/res/drawable/shadow.xml | 4 + app/src/main/res/drawable/shadow1.xml | 11 + .../main/res/layout/activity_about_app.xml | 92 ++ .../main/res/layout/activity_comment_list.xml | 90 ++ app/src/main/res/layout/activity_main.xml | 29 + app/src/main/res/layout/activity_setting.xml | 146 ++ .../main/res/layout/activity_show_photo.xml | 28 + .../res/layout/activity_topic_details.xml | 133 ++ .../res/layout/activity_topic_publish.xml | 171 +++ .../main/res/layout/activity_user_info.xml | 288 ++++ .../res/layout/activity_user_info_edit.xml | 269 ++++ .../main/res/layout/activity_user_message.xml | 12 + app/src/main/res/layout/activity_webview.xml | 12 + app/src/main/res/layout/common_avatar.xml | 11 + app/src/main/res/layout/common_toolbar.xml | 69 + app/src/main/res/layout/common_topic_list.xml | 26 + app/src/main/res/layout/common_webview.xml | 24 + app/src/main/res/layout/content_main.xml | 19 + app/src/main/res/layout/dialog_theme.xml | 51 + app/src/main/res/layout/dialog_vote.xml | 50 + app/src/main/res/layout/drawer_header.xml | 76 + app/src/main/res/layout/drawer_layout.xml | 85 ++ .../main/res/layout/fragment_topic_list.xml | 43 + app/src/main/res/layout/item_comment_list.xml | 76 + app/src/main/res/layout/item_drawer_menu.xml | 30 + app/src/main/res/layout/item_message.xml | 66 + app/src/main/res/layout/item_topic_list.xml | 66 + app/src/main/res/layout/shadow.xml | 9 + app/src/main/res/layout/shadow1.xml | 9 + app/src/main/res/layout/state_empty.xml | 11 + app/src/main/res/layout/state_error.xml | 11 + app/src/main/res/layout/state_loading.xml | 12 + app/src/main/res/menu/menu_main.xml | 28 + app/src/main/res/menu/menu_publish.xml | 18 + app/src/main/res/menu/menu_topic.xml | 16 + app/src/main/res/menu/menu_user_info.xml | 33 + app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3418 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2206 bytes app/src/main/res/mipmap-xhdpi/background.png | Bin 0 -> 672455 bytes .../res/mipmap-xhdpi/ic_action_return.png | Bin 0 -> 348 bytes .../mipmap-xhdpi/ic_action_return_white.png | Bin 0 -> 355 bytes .../mipmap-xhdpi/ic_action_sahre_white.png | Bin 0 -> 764 bytes .../main/res/mipmap-xhdpi/ic_action_share.png | Bin 0 -> 688 bytes .../res/mipmap-xhdpi/ic_actionbar_menu.png | Bin 0 -> 235 bytes .../mipmap-xhdpi/ic_actionbar_menu_more.png | Bin 0 -> 326 bytes .../ic_actionbar_menu_more_white.png | Bin 0 -> 389 bytes .../mipmap-xhdpi/ic_actionbar_menu_white.png | Bin 0 -> 260 bytes .../res/mipmap-xhdpi/ic_actionbar_notice.png | Bin 0 -> 578 bytes .../ic_actionbar_notice_white.png | Bin 0 -> 1249 bytes .../res/mipmap-xhdpi/ic_actionbar_search.png | Bin 0 -> 765 bytes .../ic_actionbar_search_white.png | Bin 0 -> 1776 bytes app/src/main/res/mipmap-xhdpi/ic_address.png | Bin 0 -> 760 bytes app/src/main/res/mipmap-xhdpi/ic_edit.png | Bin 0 -> 333 bytes .../main/res/mipmap-xhdpi/ic_edit_bold.png | Bin 0 -> 700 bytes .../main/res/mipmap-xhdpi/ic_edit_code.png | Bin 0 -> 658 bytes app/src/main/res/mipmap-xhdpi/ic_edit_img.png | Bin 0 -> 512 bytes .../main/res/mipmap-xhdpi/ic_edit_italic.png | Bin 0 -> 472 bytes .../main/res/mipmap-xhdpi/ic_edit_line.png | Bin 0 -> 223 bytes .../main/res/mipmap-xhdpi/ic_edit_link.png | Bin 0 -> 788 bytes .../main/res/mipmap-xhdpi/ic_edit_list.png | Bin 0 -> 243 bytes .../main/res/mipmap-xhdpi/ic_edit_quote.png | Bin 0 -> 993 bytes .../main/res/mipmap-xhdpi/ic_edit_title.png | Bin 0 -> 301 bytes app/src/main/res/mipmap-xhdpi/ic_email.png | Bin 0 -> 614 bytes .../main/res/mipmap-xhdpi/ic_eye_white.png | Bin 0 -> 607 bytes .../main/res/mipmap-xhdpi/ic_github_icon.png | Bin 0 -> 1213 bytes app/src/main/res/mipmap-xhdpi/ic_help.png | Bin 0 -> 1204 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4842 bytes app/src/main/res/mipmap-xhdpi/ic_logout.png | Bin 0 -> 550 bytes app/src/main/res/mipmap-xhdpi/ic_main_hot.png | Bin 0 -> 717 bytes .../main/res/mipmap-xhdpi/ic_main_newest.png | Bin 0 -> 1078 bytes .../main/res/mipmap-xhdpi/ic_main_nobody.png | Bin 0 -> 952 bytes .../main/res/mipmap-xhdpi/ic_main_palette.png | Bin 0 -> 1006 bytes .../res/mipmap-xhdpi/ic_main_recommend.png | Bin 0 -> 704 bytes .../main/res/mipmap-xhdpi/ic_main_wiki.png | Bin 0 -> 948 bytes .../main/res/mipmap-xhdpi/ic_main_work.png | Bin 0 -> 1321 bytes .../main/res/mipmap-xhdpi/ic_my_bookmark.png | Bin 0 -> 414 bytes .../main/res/mipmap-xhdpi/ic_my_message.png | Bin 0 -> 637 bytes app/src/main/res/mipmap-xhdpi/ic_my_reply.png | Bin 0 -> 677 bytes app/src/main/res/mipmap-xhdpi/ic_my_topic.png | Bin 0 -> 612 bytes app/src/main/res/mipmap-xhdpi/ic_my_vote.png | Bin 0 -> 542 bytes .../main/res/mipmap-xhdpi/ic_send_default.png | Bin 0 -> 611 bytes .../main/res/mipmap-xhdpi/ic_send_white.png | Bin 0 -> 344 bytes app/src/main/res/mipmap-xhdpi/ic_settings.png | Bin 0 -> 935 bytes app/src/main/res/mipmap-xhdpi/people01.png | Bin 0 -> 14385 bytes app/src/main/res/mipmap-xhdpi/people02.png | Bin 0 -> 12341 bytes app/src/main/res/mipmap-xhdpi/people03.png | Bin 0 -> 14507 bytes app/src/main/res/mipmap-xhdpi/people04.png | Bin 0 -> 10884 bytes app/src/main/res/mipmap-xhdpi/people05.png | Bin 0 -> 9022 bytes app/src/main/res/mipmap-xhdpi/person.png | Bin 0 -> 481596 bytes app/src/main/res/mipmap-xhdpi/sort_down.png | Bin 0 -> 274 bytes app/src/main/res/mipmap-xhdpi/sort_up.png | Bin 0 -> 268 bytes .../res/mipmap-xxhdpi/ic_action_return.png | Bin 0 -> 236 bytes .../mipmap-xxhdpi/ic_action_return_white.png | Bin 0 -> 527 bytes .../res/mipmap-xxhdpi/ic_action_share.png | Bin 0 -> 949 bytes .../mipmap-xxhdpi/ic_action_share_white.png | Bin 0 -> 1108 bytes .../res/mipmap-xxhdpi/ic_article_like.png | Bin 0 -> 3508 bytes .../res/mipmap-xxhdpi/ic_avatar_default.png | Bin 0 -> 2291 bytes .../main/res/mipmap-xxhdpi/ic_blog_icon.png | Bin 0 -> 1732 bytes .../res/mipmap-xxhdpi/ic_bookmark_normal.png | Bin 0 -> 3332 bytes .../mipmap-xxhdpi/ic_bookmark_selector.png | Bin 0 -> 3173 bytes .../res/mipmap-xxhdpi/ic_comment_normal.png | Bin 0 -> 3113 bytes app/src/main/res/mipmap-xxhdpi/ic_edit.png | Bin 0 -> 386 bytes .../main/res/mipmap-xxhdpi/ic_edit_bold.png | Bin 0 -> 971 bytes .../main/res/mipmap-xxhdpi/ic_edit_code.png | Bin 0 -> 901 bytes .../main/res/mipmap-xxhdpi/ic_edit_italic.png | Bin 0 -> 635 bytes .../main/res/mipmap-xxhdpi/ic_edit_line.png | Bin 0 -> 277 bytes .../main/res/mipmap-xxhdpi/ic_edit_link.png | Bin 0 -> 1189 bytes .../main/res/mipmap-xxhdpi/ic_edit_list.png | Bin 0 -> 345 bytes app/src/main/res/mipmap-xxhdpi/ic_email.png | Bin 0 -> 871 bytes .../main/res/mipmap-xxhdpi/ic_eye_white.png | Bin 0 -> 879 bytes .../res/mipmap-xxhdpi/ic_follow_normal.png | Bin 0 -> 833 bytes .../res/mipmap-xxhdpi/ic_follow_selector.png | Bin 0 -> 3757 bytes .../main/res/mipmap-xxhdpi/ic_github_icon.png | Bin 0 -> 1822 bytes app/src/main/res/mipmap-xxhdpi/ic_help.png | Bin 0 -> 1759 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7718 bytes app/src/main/res/mipmap-xxhdpi/ic_logo.png | Bin 0 -> 27677 bytes app/src/main/res/mipmap-xxhdpi/ic_logout.png | Bin 0 -> 717 bytes .../main/res/mipmap-xxhdpi/ic_main_hot.png | Bin 0 -> 1099 bytes .../main/res/mipmap-xxhdpi/ic_main_newest.png | Bin 0 -> 1596 bytes .../main/res/mipmap-xxhdpi/ic_main_nobody.png | Bin 0 -> 1411 bytes .../res/mipmap-xxhdpi/ic_main_palette.png | Bin 0 -> 1512 bytes .../res/mipmap-xxhdpi/ic_main_recommend.png | Bin 0 -> 1005 bytes .../main/res/mipmap-xxhdpi/ic_main_wiki.png | Bin 0 -> 1362 bytes .../main/res/mipmap-xxhdpi/ic_main_work.png | Bin 0 -> 2006 bytes .../main/res/mipmap-xxhdpi/ic_my_bookmark.png | Bin 0 -> 538 bytes .../main/res/mipmap-xxhdpi/ic_my_message.png | Bin 0 -> 922 bytes .../main/res/mipmap-xxhdpi/ic_my_reply.png | Bin 0 -> 909 bytes .../main/res/mipmap-xxhdpi/ic_my_topic.png | Bin 0 -> 871 bytes app/src/main/res/mipmap-xxhdpi/ic_point.png | Bin 0 -> 17722 bytes .../res/mipmap-xxhdpi/ic_send_default.png | Bin 0 -> 723 bytes .../main/res/mipmap-xxhdpi/ic_send_white.png | Bin 0 -> 446 bytes .../main/res/mipmap-xxhdpi/ic_settings.png | Bin 0 -> 1494 bytes .../main/res/mipmap-xxhdpi/ic_vote_down.png | Bin 0 -> 862 bytes .../res/mipmap-xxhdpi/ic_vote_down_normal.png | Bin 0 -> 16006 bytes .../res/mipmap-xxhdpi/ic_vote_down_select.png | Bin 0 -> 16046 bytes app/src/main/res/mipmap-xxhdpi/ic_vote_up.png | Bin 0 -> 878 bytes .../res/mipmap-xxhdpi/ic_vote_up_normal.png | Bin 0 -> 15912 bytes .../res/mipmap-xxhdpi/ic_vote_up_select.png | Bin 0 -> 15955 bytes .../main/res/mipmap-xxhdpi/ic_weibo_icon.png | Bin 0 -> 1880 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 10486 bytes app/src/main/res/values-v19/styles.xml | 115 ++ app/src/main/res/values-v21/styles.xml | 117 ++ app/src/main/res/values-w820dp/dimens.xml | 6 + app/src/main/res/values/attrs.xml | 45 + app/src/main/res/values/colors.xml | 57 + app/src/main/res/values/dimens.xml | 54 + app/src/main/res/values/strings.xml | 91 ++ app/src/main/res/values/styles.xml | 133 ++ build.gradle | 23 + gradle.properties.example | 28 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53637 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 160 +++ gradlew.bat | 90 ++ liraries/markdown/.gitignore | 3 + liraries/markdown/build.gradle | 31 + liraries/markdown/proguard-rules.pro | 17 + .../markdown/src/main/AndroidManifest.xml | 7 + .../generator/MarkdownSyntaxGenerator.java | 186 +++ .../markdown/model/MarkdownSyntaxModel.java | 36 + .../model/type/MarkdownSyntaxType.java | 23 + .../org/gemini/markdown/model/type/Range.java | 30 + .../gemini/markdown/syntax/SyntaxPattern.java | 21 + .../gemini/markdown/util/MarkdownUtil.java | 103 ++ .../markdown/view/MarkdownEditText.java | 43 + .../gemini/markdown/view/MarkdownEditor.java | 202 +++ .../view/MarkdownTextChangeWatcher.java | 59 + .../src/main/res/layout/mk_editor_layout.xml | 96 ++ .../src/main/res/values/mk_editor_style.xml | 6 + .../markdown/src/main/res/values/strings.xml | 3 + liraries/ptr-lib/AndroidManifest.xml | 12 + liraries/ptr-lib/build.gradle | 33 + liraries/ptr-lib/checkstyle.xml | 120 ++ liraries/ptr-lib/gradle-mvn-push.gradle | 117 ++ liraries/ptr-lib/libs/clog-1.0.2-sources.jar | Bin 0 -> 1604 bytes liraries/ptr-lib/libs/clog-1.0.2.jar | Bin 0 -> 4430 bytes liraries/ptr-lib/pom.xml | 106 ++ .../res/drawable-xhdpi/ptr_rotate_arrow.png | Bin 0 -> 4204 bytes .../res/drawable-xhdpi/tableview_loading2.png | Bin 0 -> 1991 bytes liraries/ptr-lib/res/drawable/pb_amin.xml | 7 + .../ptr-lib/res/drawable/pb_custom_shape.xml | 44 + .../cube_ptr_classic_default_header.xml | 58 + .../res/layout/cube_ptr_simple_loading.xml | 8 + .../ptr-lib/res/values-zh/cube_ptr_string.xml | 18 + .../ptr-lib/res/values/cube_ptr_attrs.xml | 35 + .../ptr-lib/res/values/cube_ptr_string.xml | 18 + liraries/ptr-lib/res/values/styles.xml | 12 + .../views/ptr/PtrClassicDefaultFooter.java | 51 + .../views/ptr/PtrClassicDefaultHeader.java | 301 ++++ .../cube/views/ptr/PtrClassicFrameLayout.java | 82 ++ .../cube/views/ptr/PtrDefaultHandler.java | 41 + .../cube/views/ptr/PtrDefaultHandler2.java | 47 + .../srain/cube/views/ptr/PtrFrameLayout.java | 1262 +++++++++++++++++ .../in/srain/cube/views/ptr/PtrHandler.java | 20 + .../in/srain/cube/views/ptr/PtrHandler2.java | 20 + .../in/srain/cube/views/ptr/PtrUIHandler.java | 35 + .../cube/views/ptr/PtrUIHandlerHolder.java | 159 +++ .../cube/views/ptr/PtrUIHandlerHook.java | 56 + .../cube/views/ptr/header/MaterialHeader.java | 182 +++ .../ptr/header/MaterialProgressDrawable.java | 758 ++++++++++ .../views/ptr/header/StoreHouseBarItem.java | 74 + .../views/ptr/header/StoreHouseHeader.java | 331 +++++ .../cube/views/ptr/header/StoreHousePath.java | 369 +++++ .../views/ptr/indicator/PtrIndicator.java | 222 +++ .../ptr/indicator/PtrTensionIndicator.java | 119 ++ .../in/srain/cube/views/ptr/util/PtrCLog.java | 292 ++++ .../cube/views/ptr/util/PtrLocalDisplay.java | 45 + settings.gradle | 1 + signing.properties.example | 7 + 320 files changed, 20218 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 README_ZH.md 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/assets/about_app.html create mode 100644 app/src/main/assets/js/ImageClickEvent.js create mode 100644 app/src/main/java/com/jun/elephant/Elephant.java create mode 100644 app/src/main/java/com/jun/elephant/api/Networks.java create mode 100644 app/src/main/java/com/jun/elephant/api/TokenApi.java create mode 100644 app/src/main/java/com/jun/elephant/api/TopicApi.java create mode 100644 app/src/main/java/com/jun/elephant/api/UserApi.java create mode 100644 app/src/main/java/com/jun/elephant/common/BaseActivity.java create mode 100644 app/src/main/java/com/jun/elephant/common/BaseFragment.java create mode 100644 app/src/main/java/com/jun/elephant/common/BaseFuncIml.java create mode 100644 app/src/main/java/com/jun/elephant/common/BaseWebViewActivity.java create mode 100644 app/src/main/java/com/jun/elephant/common/FragmentAdapter.java create mode 100644 app/src/main/java/com/jun/elephant/common/WebAppClient.java create mode 100644 app/src/main/java/com/jun/elephant/entity/DrawerMenuEntity.java create mode 100644 app/src/main/java/com/jun/elephant/entity/TokenEntity.java create mode 100644 app/src/main/java/com/jun/elephant/entity/topic/CategoryEntity.java create mode 100644 app/src/main/java/com/jun/elephant/entity/topic/TopicDetailEntity.java create mode 100644 app/src/main/java/com/jun/elephant/entity/topic/TopicEntity.java create mode 100644 app/src/main/java/com/jun/elephant/entity/topic/TopicListEntity.java create mode 100644 app/src/main/java/com/jun/elephant/entity/topic/TopicReplyEntity.java create mode 100644 app/src/main/java/com/jun/elephant/entity/user/MessageEntity.java create mode 100644 app/src/main/java/com/jun/elephant/entity/user/UserEntity.java create mode 100644 app/src/main/java/com/jun/elephant/entity/user/UserInfoEntity.java create mode 100644 app/src/main/java/com/jun/elephant/entity/user/UserMessageEntity.java create mode 100644 app/src/main/java/com/jun/elephant/global/Constants.java create mode 100644 app/src/main/java/com/jun/elephant/global/UserConstant.java create mode 100644 app/src/main/java/com/jun/elephant/mvpframe/BaseModel.java create mode 100644 app/src/main/java/com/jun/elephant/mvpframe/BasePresenter.java create mode 100644 app/src/main/java/com/jun/elephant/mvpframe/BaseView.java create mode 100644 app/src/main/java/com/jun/elephant/mvpframe/base/BaseFrameActivity.java create mode 100644 app/src/main/java/com/jun/elephant/mvpframe/base/BaseFrameFragment.java create mode 100644 app/src/main/java/com/jun/elephant/mvpframe/base/BaseFrameWebViewActivity.java create mode 100644 app/src/main/java/com/jun/elephant/mvpframe/rx/RxBus.java create mode 100644 app/src/main/java/com/jun/elephant/mvpframe/rx/RxManager.java create mode 100644 app/src/main/java/com/jun/elephant/mvpframe/rx/RxSchedulers.java create mode 100644 app/src/main/java/com/jun/elephant/mvpframe/util/TUtil.java create mode 100644 app/src/main/java/com/jun/elephant/ui/adapter/DrawerMenuAdapter.java create mode 100644 app/src/main/java/com/jun/elephant/ui/adapter/TopicListAdapter.java create mode 100644 app/src/main/java/com/jun/elephant/ui/adapter/UserMessageAdapter.java create mode 100644 app/src/main/java/com/jun/elephant/ui/login/LoginActivity.java create mode 100644 app/src/main/java/com/jun/elephant/ui/login/LoginContract.java create mode 100644 app/src/main/java/com/jun/elephant/ui/login/LoginModel.java create mode 100644 app/src/main/java/com/jun/elephant/ui/login/LoginPresenter.java create mode 100644 app/src/main/java/com/jun/elephant/ui/main/AboutAppActivity.java create mode 100644 app/src/main/java/com/jun/elephant/ui/main/MainActivity.java create mode 100644 app/src/main/java/com/jun/elephant/ui/main/PhotoShowActivity.java create mode 100644 app/src/main/java/com/jun/elephant/ui/main/SettingActivity.java create mode 100644 app/src/main/java/com/jun/elephant/ui/main/TopicPreviewActivity.java create mode 100644 app/src/main/java/com/jun/elephant/ui/main/WebViewActivity.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsActivity.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsContract.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsModel.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsPresenter.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/list/TopicContract.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/list/TopicListByForumFragment.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/list/TopicListByMeFragment.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/list/TopicListByUserFragment.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/list/TopicModel.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/list/TopicPresenter.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishActivity.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishContract.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishModel.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishPresenter.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/reply/TopicCommentListActivity.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/reply/TopicReplyContract.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/reply/TopicReplyModel.java create mode 100644 app/src/main/java/com/jun/elephant/ui/topic/reply/TopicReplyPresenter.java create mode 100644 app/src/main/java/com/jun/elephant/ui/user/info/UserInfoActivity.java create mode 100644 app/src/main/java/com/jun/elephant/ui/user/info/UserInfoContract.java create mode 100644 app/src/main/java/com/jun/elephant/ui/user/info/UserInfoEditActivity.java create mode 100644 app/src/main/java/com/jun/elephant/ui/user/info/UserInfoModel.java create mode 100644 app/src/main/java/com/jun/elephant/ui/user/info/UserInfoPresenter.java create mode 100644 app/src/main/java/com/jun/elephant/ui/user/message/MessageListContract.java create mode 100644 app/src/main/java/com/jun/elephant/ui/user/message/MessageModel.java create mode 100644 app/src/main/java/com/jun/elephant/ui/user/message/MessagePresenter.java create mode 100644 app/src/main/java/com/jun/elephant/ui/user/message/UserMessageActivity.java create mode 100644 app/src/main/java/com/jun/elephant/ui/user/reply/UserReplyFragment.java create mode 100644 app/src/main/java/com/jun/elephant/ui/widget/MultiStateView.java create mode 100644 app/src/main/java/com/jun/elephant/ui/widget/MySimpleDraweeView.java create mode 100644 app/src/main/java/com/jun/elephant/ui/widget/MySwipeRefreshLayout.java create mode 100644 app/src/main/java/com/jun/elephant/ui/widget/ThemeDialog.java create mode 100644 app/src/main/java/com/jun/elephant/ui/widget/VoteDialog.java create mode 100644 app/src/main/java/com/jun/elephant/util/FileUtils.java create mode 100644 app/src/main/java/com/jun/elephant/util/JLog.java create mode 100644 app/src/main/java/com/jun/elephant/util/MarkDownRenderer.java create mode 100644 app/src/main/java/com/jun/elephant/util/NetworkUtils.java create mode 100644 app/src/main/java/com/jun/elephant/util/OpenWebViewUtils.java create mode 100644 app/src/main/java/com/jun/elephant/util/SharePreferencesHelper.java create mode 100644 app/src/main/java/com/jun/elephant/util/ShareUtil.java create mode 100644 app/src/main/java/com/jun/elephant/util/SystemUtil.java create mode 100644 app/src/main/java/com/jun/elephant/util/ThemeUtil.java create mode 100644 app/src/main/res/drawable-v21/ripple.xml create mode 100644 app/src/main/res/drawable/btn1_normal.xml create mode 100644 app/src/main/res/drawable/btn1_selector.xml create mode 100644 app/src/main/res/drawable/btn2_tran_style.xml create mode 100644 app/src/main/res/drawable/btn_bg_white.xml create mode 100644 app/src/main/res/drawable/dialog_bg_style.xml create mode 100644 app/src/main/res/drawable/edt_line.xml create mode 100644 app/src/main/res/drawable/edt_line_gray.xml create mode 100644 app/src/main/res/drawable/edt_line_theme.xml create mode 100644 app/src/main/res/drawable/ic_place_black_24dp.xml create mode 100644 app/src/main/res/drawable/item_bg_line.xml create mode 100644 app/src/main/res/drawable/shadow.xml create mode 100644 app/src/main/res/drawable/shadow1.xml create mode 100644 app/src/main/res/layout/activity_about_app.xml create mode 100644 app/src/main/res/layout/activity_comment_list.xml create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/activity_setting.xml create mode 100644 app/src/main/res/layout/activity_show_photo.xml create mode 100644 app/src/main/res/layout/activity_topic_details.xml create mode 100644 app/src/main/res/layout/activity_topic_publish.xml create mode 100644 app/src/main/res/layout/activity_user_info.xml create mode 100644 app/src/main/res/layout/activity_user_info_edit.xml create mode 100644 app/src/main/res/layout/activity_user_message.xml create mode 100644 app/src/main/res/layout/activity_webview.xml create mode 100644 app/src/main/res/layout/common_avatar.xml create mode 100644 app/src/main/res/layout/common_toolbar.xml create mode 100644 app/src/main/res/layout/common_topic_list.xml create mode 100644 app/src/main/res/layout/common_webview.xml create mode 100644 app/src/main/res/layout/content_main.xml create mode 100644 app/src/main/res/layout/dialog_theme.xml create mode 100644 app/src/main/res/layout/dialog_vote.xml create mode 100644 app/src/main/res/layout/drawer_header.xml create mode 100644 app/src/main/res/layout/drawer_layout.xml create mode 100644 app/src/main/res/layout/fragment_topic_list.xml create mode 100644 app/src/main/res/layout/item_comment_list.xml create mode 100644 app/src/main/res/layout/item_drawer_menu.xml create mode 100644 app/src/main/res/layout/item_message.xml create mode 100644 app/src/main/res/layout/item_topic_list.xml create mode 100644 app/src/main/res/layout/shadow.xml create mode 100644 app/src/main/res/layout/shadow1.xml create mode 100644 app/src/main/res/layout/state_empty.xml create mode 100644 app/src/main/res/layout/state_error.xml create mode 100644 app/src/main/res/layout/state_loading.xml create mode 100644 app/src/main/res/menu/menu_main.xml create mode 100644 app/src/main/res/menu/menu_publish.xml create mode 100644 app/src/main/res/menu/menu_topic.xml create mode 100644 app/src/main/res/menu/menu_user_info.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/background.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_action_return.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_action_return_white.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_action_sahre_white.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_action_share.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_actionbar_menu.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_actionbar_menu_more.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_actionbar_menu_more_white.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_actionbar_menu_white.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_actionbar_notice.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_actionbar_notice_white.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_actionbar_search.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_actionbar_search_white.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_address.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_edit.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_edit_bold.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_edit_code.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_edit_img.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_edit_italic.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_edit_line.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_edit_link.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_edit_list.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_edit_quote.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_edit_title.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_email.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_eye_white.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_github_icon.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_help.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_logout.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_main_hot.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_main_newest.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_main_nobody.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_main_palette.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_main_recommend.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_main_wiki.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_main_work.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_my_bookmark.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_my_message.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_my_reply.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_my_topic.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_my_vote.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_send_default.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_send_white.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_settings.png create mode 100644 app/src/main/res/mipmap-xhdpi/people01.png create mode 100644 app/src/main/res/mipmap-xhdpi/people02.png create mode 100644 app/src/main/res/mipmap-xhdpi/people03.png create mode 100644 app/src/main/res/mipmap-xhdpi/people04.png create mode 100644 app/src/main/res/mipmap-xhdpi/people05.png create mode 100644 app/src/main/res/mipmap-xhdpi/person.png create mode 100644 app/src/main/res/mipmap-xhdpi/sort_down.png create mode 100644 app/src/main/res/mipmap-xhdpi/sort_up.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_action_return.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_action_return_white.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_action_share.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_action_share_white.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_article_like.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_avatar_default.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_blog_icon.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_bookmark_normal.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_bookmark_selector.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_comment_normal.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_edit.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_edit_bold.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_edit_code.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_edit_italic.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_edit_line.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_edit_link.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_edit_list.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_email.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_eye_white.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_follow_normal.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_follow_selector.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_github_icon.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_help.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_logo.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_logout.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_main_hot.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_main_newest.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_main_nobody.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_main_palette.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_main_recommend.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_main_wiki.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_main_work.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_my_bookmark.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_my_message.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_my_reply.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_my_topic.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_point.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_send_default.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_send_white.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_settings.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_vote_down.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_vote_down_normal.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_vote_down_select.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_vote_up.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_vote_up_normal.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_vote_up_select.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_weibo_icon.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.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-w820dp/dimens.xml create mode 100644 app/src/main/res/values/attrs.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/dimens.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 build.gradle create mode 100644 gradle.properties.example 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 liraries/markdown/.gitignore create mode 100644 liraries/markdown/build.gradle create mode 100644 liraries/markdown/proguard-rules.pro create mode 100644 liraries/markdown/src/main/AndroidManifest.xml create mode 100644 liraries/markdown/src/main/java/org/gemini/markdown/generator/MarkdownSyntaxGenerator.java create mode 100644 liraries/markdown/src/main/java/org/gemini/markdown/model/MarkdownSyntaxModel.java create mode 100644 liraries/markdown/src/main/java/org/gemini/markdown/model/type/MarkdownSyntaxType.java create mode 100644 liraries/markdown/src/main/java/org/gemini/markdown/model/type/Range.java create mode 100644 liraries/markdown/src/main/java/org/gemini/markdown/syntax/SyntaxPattern.java create mode 100644 liraries/markdown/src/main/java/org/gemini/markdown/util/MarkdownUtil.java create mode 100644 liraries/markdown/src/main/java/org/gemini/markdown/view/MarkdownEditText.java create mode 100644 liraries/markdown/src/main/java/org/gemini/markdown/view/MarkdownEditor.java create mode 100644 liraries/markdown/src/main/java/org/gemini/markdown/view/MarkdownTextChangeWatcher.java create mode 100644 liraries/markdown/src/main/res/layout/mk_editor_layout.xml create mode 100644 liraries/markdown/src/main/res/values/mk_editor_style.xml create mode 100644 liraries/markdown/src/main/res/values/strings.xml create mode 100644 liraries/ptr-lib/AndroidManifest.xml create mode 100644 liraries/ptr-lib/build.gradle create mode 100644 liraries/ptr-lib/checkstyle.xml create mode 100644 liraries/ptr-lib/gradle-mvn-push.gradle create mode 100644 liraries/ptr-lib/libs/clog-1.0.2-sources.jar create mode 100644 liraries/ptr-lib/libs/clog-1.0.2.jar create mode 100644 liraries/ptr-lib/pom.xml create mode 100644 liraries/ptr-lib/res/drawable-xhdpi/ptr_rotate_arrow.png create mode 100644 liraries/ptr-lib/res/drawable-xhdpi/tableview_loading2.png create mode 100644 liraries/ptr-lib/res/drawable/pb_amin.xml create mode 100644 liraries/ptr-lib/res/drawable/pb_custom_shape.xml create mode 100644 liraries/ptr-lib/res/layout/cube_ptr_classic_default_header.xml create mode 100644 liraries/ptr-lib/res/layout/cube_ptr_simple_loading.xml create mode 100644 liraries/ptr-lib/res/values-zh/cube_ptr_string.xml create mode 100644 liraries/ptr-lib/res/values/cube_ptr_attrs.xml create mode 100644 liraries/ptr-lib/res/values/cube_ptr_string.xml create mode 100644 liraries/ptr-lib/res/values/styles.xml create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicDefaultFooter.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicDefaultHeader.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicFrameLayout.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/PtrDefaultHandler.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/PtrDefaultHandler2.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/PtrFrameLayout.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/PtrHandler.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/PtrHandler2.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/PtrUIHandler.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/PtrUIHandlerHolder.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/PtrUIHandlerHook.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/header/MaterialHeader.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/header/MaterialProgressDrawable.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/header/StoreHouseBarItem.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/header/StoreHouseHeader.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/header/StoreHousePath.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/indicator/PtrIndicator.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/indicator/PtrTensionIndicator.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/util/PtrCLog.java create mode 100644 liraries/ptr-lib/src/in/srain/cube/views/ptr/util/PtrLocalDisplay.java create mode 100644 settings.gradle create mode 100644 signing.properties.example diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ff738c --- /dev/null +++ b/.gitignore @@ -0,0 +1,56 @@ +# Built application files +*.apk +*.ap_ + +# Files for the ART/Dalvik VM + +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ + + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio capturesptures folder +captures/ + +.idea + +# Keystore files +*.jks + +projectFilesBackup/ + +# Intellij +*.iml +.idea/workspace.xml + +ptr-lib/build + +app/libs/ + +README_ZH.html + +gradle.properties + +signing.properties \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ad4ae0f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,29 @@ +language: android + +before_install: + - rm -f gradle.properties + - cp gradle.properties.example gradle.properties + - chmod +x gradlew + +android: + components: + - tools + - build-tools-24.0.2 + - android-24 + - platform-tools + - extra-android-m2repository + +jdk: + - oraclejdk8 + +notifications: + email: false + +sudo: false + +cache: + directories: + - $HOME/.gradle + +script: + ./gradlew assembleDebug --stacktrace diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8adde18 --- /dev/null +++ b/README.md @@ -0,0 +1,132 @@ +

+ Welcome to follow me on GitHub or Weibo +

+ +GitHub: https://github.com/Freelander + +Weibo: http://weibo.com/gaojunhuang + +--- + +## Elephant + +

+ + + + + API + + + + + + + + + + +

+ +PHPHub is a Forum project written in Laravel 4.2, and it is also the project build up PHP & Laravel China community. + +Elephant is PHPHub Community Android unofficial client, App UI style follows the Google Material Design, architecture project using MVP mode, the data processing using RxJava + Retrofit network data processing. + +Why App named "Elephant"? Because the elephant is the PHP mascot, as the origin of history, you can go [here](http://www.phpchina.com/blog-56751-183726.html) to find out. + +#### [中文文档](https://github.com/Freelander/Elephant/blob/master/README_ZH.md) + +## PHPHub related projects + +You can checkout the others open source projects of PHPHub in the following list. + +* [PHPHub-iOS](https://github.com/Aufree/phphub-ios) by [@Aufree](https://github.com/Aufree) +* [PHPHub-Android](https://github.com/CycloneAxe/phphub-android) by [@Kelvin](https://github.com/CycloneAxe) and [@Xiaoxiaoyu](https://github.com/xiaoxiaoyu) +* [PHPHub-UI](https://github.com/phphub/phphub-ui) by [@Summer](https://github.com/summerblue) and [@Aufree](https://github.com/aufree) +* [PHPHub-Web](https://github.com/summerblue/phphub5) by [@Summer](https://github.com/summerblue) + +## Feature + +- [x] App UI style follows the google Material Design +- [x] Translucent status bar +- [x] Data processing using RxJava + Retrofit +- [x] Scan code login +- [x] WebView image click event JS injection +- [x] Project architecture using MVP mode +- [x] Publish topic using markdown editor +- [x] Support multiple theme style switch + +## Screenshots + +![](http://ww1.sinaimg.cn/large/006xB1lsgw1f8ofu9f0s8j31kw1zu1k9.jpg) + +## Build environment + +1. Min Android SDK version 4.0+ +2. Andriod Studio 2.2.1 +3. Gradle version 2.14.1 +4. Gradle plugin version 2.2.1 +5. Build tools version 24.0.2 + +## Build Instructions + +1.Download the source code; + +> $ git clone https://github.com/Freelander/Elephant.git + +2.Next, Make a copy of gradle.properties.example as gradle.properties and edit the information inside; + +> $ cp gradle.properties.example gradle.properties + +3.Finally, Will the project import Android Studio, click to run, I wish you good luck! + +## How should I Login? + +### Development Environment + +Scan this QRCode by using Elephant application. + +![](http://ww3.sinaimg.cn/large/76dc7f1bgw1exrg86f5ubj20ml0dsq45.jpg) + +### Production Environment + +Go to [PHPHub's official website](https://laravel-china.org/) and Login with GitHub. then find your QRCode in your personal page. It should look like this: + +![](https://dn-phphub.qbox.me/uploads/images/201609/05/1/LGYQoWp9kY.png) + +## Third-party Libraries + + Project | Introduction + -------- | ------ +[Logger](https://github.com/orhanobut/logger) | Simple, pretty and powerful logger for android +[Material-Dialogs](https://github.com/afollestad/material-dialogs) | A beautiful, easy-to-use, and customizable dialogs API +[BGABadgeView-Android](https://github.com/bingoogolapple/BGABadgeView-Android) | android badge view +[MultiStateView](https://github.com/Kennyc1012/MultiStateView) | Android View that displays different content based on its state. +[FloatingActionButton](https://github.com/makovkastar/FloatingActionButton) | Android floating action button which reacts on scrolling events +[commonadapter](https://github.com/bboyfeiyu/commonadapter) | The listview and recyleview common adapter +[glide-transformations](https://github.com/wasabeef/glide-transformations) | An Android transformation library providing a variety of image transformations for Glide +[writeily-pro](https://github.com/plafue/writeily-pro) | The minimalist Markdown editor for Android +[RxJava](https://github.com/ReactiveX/RxJava) | RxJava is a Java VM implementation of Reactive Extensions +[RxAndroid](https://github.com/ReactiveX/RxAndroid) | Android specific bindings for RxJava +[Retrofit](https://github.com/square/retrofit) | Type-safe HTTP client for Android and Java by Square +[Gson](https://github.com/google/gson) | Gson is a Java library that can be used to convert Java Objects into their JSON representation +[Prettytime](https://github.com/ocpsoft/prettytime) | Social Style Date and Time Formatting for Java +[barcodescanner](https://github.com/dm77/barcodescanner) | Barcode Scanner Libraries for Android +[fresco](https://github.com/facebook/fresco) | Fresco is a powerful system for displaying images in Android applications. +[butterknife](https://github.com/JakeWharton/butterknife) | Bind Android views and callbacks to fields and methods +[materialish-progress](https://github.com/pnikosis/materialish-progress) | A material style progress wheel compatible with 2.3 +[PhotoDraweeView](https://github.com/ongakuer/PhotoDraweeView) | PhotoView For Fresco +[cwac-anddown](https://github.com/commonsguy/cwac-anddown) | CWAC AndDown: Markdown Utility Library +[gm-mkdroid](https://github.com/geminiwen/gm-mkdroid) | A WYSIWYG MarkdownEditor on Android +[android-Ultra-Pull-To-Refresh-With-Load-More](https://github.com/captainbupt/android-Ultra-Pull-To-Refresh-With-Load-More) | This is a modification of the [Ultra-Pull-to-Refresh](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh) library which supports load-more for any view +[T-MVP](https://github.com/north2014/T-MVP) | Use generic to depthly decoupled MVP + +## Design + +UI design inspired by the ZHIHU, JUEJIN app. + +## License + +Copyright 2016 Freelander + +Licensed under the [Apache License2.0](https://github.com/Freelander/Elephant/blob/master/LICENSE) \ No newline at end of file diff --git a/README_ZH.md b/README_ZH.md new file mode 100644 index 0000000..16c91bd --- /dev/null +++ b/README_ZH.md @@ -0,0 +1,127 @@ +

+ 欢迎到 Github 或者微博关注我 +

+ +GitHub: https://github.com/Freelander + +Weibo: http://weibo.com/gaojunhuang + +--- + +## Elephant + +

+ + + + + + + + + + + + + + API + +

+ +PHPHub 是积极向上的 PHP & Laravel 开发者社区. + +大象是 PHPHub 社区非官方 Android 客户端, App UI 风格遵循了 Google Material Design 设计风格, 项目架构使用了 MVP 模式, 数据处理使用了 RxJava + Retrofit 技术. + +为什么给 App 取名为“大象”呢?因为大象是 PHP 的吉祥物, 至于由来历史, 可以去[这里](http://www.phpchina.com/blog-56751-183726.html)了解一下 + +## PHPHub 相关项目 + +* [PHPHub-iOS](https://github.com/Aufree/phphub-ios) by [@Aufree](https://github.com/Aufree) +* [PHPHub-Android](https://github.com/CycloneAxe/phphub-android) by [@Kelvin](https://github.com/CycloneAxe) and [@Xiaoxiaoyu](https://github.com/xiaoxiaoyu) +* [PHPHub-UI](https://github.com/phphub/phphub-ui) by [@Summer](https://github.com/summerblue) and [@Aufree](https://github.com/aufree) +* [PHPHub-Web](https://github.com/summerblue/phphub) by [@Summer](https://github.com/summerblue) + +## 特点 + +- [x] 界面设计遵循 Google 设计规范 +- [x] 沉浸式状态栏 +- [x] 数据处理使用了 RxJava + Retrofit +- [x] 二维码扫码登录 +- [x] WebView 图片点击事件 JS 注入 +- [x] 项目架构使用 MVP 模式 +- [x] 发布帖子支持使用 Markdown 语法编辑器 +- [x] 多主题切换 + +## Screenshots + +![](http://ww1.sinaimg.cn/large/006xB1lsgw1f8ofu9f0s8j31kw1zu1k9.jpg) + +## 运行环境 + +1. Min Android SDK version 4.0+ +2. Andriod Studio 2.2.1 +3. Gradle version 2.14.1 +4. Gradle plugin version 2.2.1 +5. Build tools version 24.0.2 + +## 安装方式 + +1.下载源码到本地; + +> $ git clone https://github.com/Freelander/Elephant.git + +2.下一步需要复制 gradle.properties.example 更名为 gradle.properties 并编辑 里面的相关信息; + +> $ cp gradle.properties.example gradle.properties + +3.最后将项目导入 Android Studio 运行即可, 祝你好运! + +## 登录帮助 + +### 开发环境 + +请使用以下二维码登录 + +![](http://ww3.sinaimg.cn/large/76dc7f1bgw1exrg86f5ubj20ml0dsq45.jpg) + +### 生产环境 + +打开 [PHPHub](https://laravel-china.org/) 桌面 Web 端, 进入 Web 版个人中心, 对准如下位置的二维码扫描: + +![](https://dn-phphub.qbox.me/uploads/images/201609/05/1/LGYQoWp9kY.png) + +## 使用到开源库 + + 项目名称 | 简介 + -------- | ------ +[Logger](https://github.com/orhanobut/logger) | 一个强大漂亮的Log输出日志,支持json格式化输出 +[Material-Dialogs](https://github.com/afollestad/material-dialogs) | 一个强大漂亮的Material Dialog +[BGABadgeView-Android](https://github.com/bingoogolapple/BGABadgeView-Android) | Android徽章控件 +[MultiStateView](https://github.com/Kennyc1012/MultiStateView) | 通用显示各种状态 View +[FloatingActionButton](https://github.com/makovkastar/FloatingActionButton) | 悬浮操作按钮库, 支持监听滑滚动事件 +[glide-transformations](https://github.com/wasabeef/glide-transformations) | 快速实现毛玻璃效果 +[writeily-pro](https://github.com/plafue/writeily-pro) | Markdown 文本编辑 Demo +[RxJava](https://github.com/ReactiveX/RxJava) | RxJava +[RxAndroid](https://github.com/ReactiveX/RxAndroid) | RxAndroid +[Retrofit](https://github.com/square/retrofit) | Retrofit +[Gson](https://github.com/google/gson) | Json 解析库 +[Prettytime](https://github.com/ocpsoft/prettytime) | 格式化日期时间 +[barcodescanner](https://github.com/dm77/barcodescanner) | 二维码扫描 +[fresco](https://github.com/facebook/fresco) | Facebook 开源的图片缓存库 +[butterknife](https://github.com/JakeWharton/butterknife) | 注解框架 +[materialish-progress](https://github.com/pnikosis/materialish-progress) | Material Design 风格进度条 +[PhotoDraweeView](https://github.com/ongakuer/PhotoDraweeView) | 基于 Fresco 的图片缩放控件 +[cwac-anddown](https://github.com/commonsguy/cwac-anddown) | Markdown 渲染 +[gm-mkdroid](https://github.com/geminiwen/gm-mkdroid) | 一个所见即所得的 Markdown 编辑器 +[android-Ultra-Pull-To-Refresh-With-Load-More](https://github.com/captainbupt/android-Ultra-Pull-To-Refresh-With-Load-More) | 这个是基于 [Ultra-Pull-to-Refresh](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh) 库修改增加支持加载更多的刷新控件 +[T-MVP](https://github.com/north2014/T-MVP) | 泛型深度解耦下的MVP大瘦身 + +## UI 设计 + +界面设计灵感来自于知乎, 掘金 App. + +## License + +Copyright 2016 Freelander + +Licensed under the [Apache License2.0](https://github.com/Freelander/Elephant/blob/master/LICENSE) diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..68e4fb6 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,3 @@ +/build +/src/androidTest +/src/test \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..51f647e --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,168 @@ +apply plugin: 'com.android.application' + +def releaseTime() { + return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC")) +} + +android { + compileSdkVersion 24 + buildToolsVersion "24.0.2" + + defaultConfig { + applicationId "com.jun.elephant" + minSdkVersion 15 + targetSdkVersion 24 + versionCode 3 + versionName "1.0" + + buildConfigField("String", "CLIENT_ID", "\"${CLIENT_ID}\"") + buildConfigField("String", "CLIENT_SECRET", "\"${CLIENT_SECRET}\"") + buildConfigField("String", "API_BASE_URL", "\"${API_BASE_URL}\"") + + println("CLIENT_ID ========== " + "\"${CLIENT_ID}\"") + println("CLIENT_SECRET ========== " + "\"${CLIENT_SECRET}\"") + println("API_BASE_URL ========== " + "\"${API_BASE_URL}\"") + println("UMENG_APPKEY ========== " + "\"${UMENG_APPKEY}\"") + } + + //忽略 lint 错误 + lintOptions { + abortOnError false + } + + signingConfigs { + + debug { + storeFile file("../debug.jks") + storePassword '111111' + keyAlias 'Jun' + keyPassword '111111' + } + + release { + def signingFile = file("../signing.properties") + if (signingFile.canRead()) { + def Properties properties = new Properties() + properties.load(new FileInputStream(signingFile)) + + try { + storeFile file(properties['STORE_FILE']) + storePassword properties['STORE_PASSWORD'] + keyAlias properties['KEY_ALIAS'] + keyPassword properties['KEY_PASSWORD'] + + println "RELEASE_BUILD: Signing..." + } catch (e) { + throw new InvalidUserDataException("You should define STORE_FILE and STORE_PASSWORD and KEY_ALIAS and KEY_PASSWORD in signing.properties.") + } + + } else { + println "RELEASE_BUILD: signing.properties not found" + } + } + } + + buildTypes { + debug { + minifyEnabled false + shrinkResources false + signingConfig signingConfigs.debug + //显示 Log + buildConfigField "boolean", "LOG_DEBUG", "true" + } + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release + //不显示 Log + buildConfigField "boolean", "LOG_DEBUG", "false" + //移除无用的 Resource + shrinkResources false + //资源文件按4字节对齐 + zipAlignEnabled true + //关闭调试 + debuggable false + + // 多渠道打包 + applicationVariants.all { variant -> + variant.outputs.each { output -> + def outputFile = output.outputFile + if (outputFile != null && outputFile.name.endsWith('.apk')) { + // 输出apk名称为Elephant_v1.0_2016-10-11_fir.apk + def fileName = "Elephant_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk" + output.outputFile = new File(outputFile.parent, fileName) + } + } + } + } + + //渠道 + productFlavors { + fir { + manifestPlaceholders = [UMENG_CHANNEL_VALUE: "fir", UMENG_APPKEY: UMENG_APPKEY] + } + } + } +} + +repositories { + flatDir { + dirs 'libs' + } + maven { + url "https://jitpack.io" + } + + jcenter() + mavenCentral() // GPUImage for Android + + maven { + url "https://s3.amazonaws.com/repo.commonsware.com" + } +} + +def SUPPORT_VERSION = "24.2.1" + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + compile 'com.android.support:appcompat-v7:' + SUPPORT_VERSION + compile 'com.android.support:design:' + SUPPORT_VERSION + compile 'com.android.support:cardview-v7:' + SUPPORT_VERSION + compile 'com.android.support:support-v4:' + SUPPORT_VERSION + compile 'com.android.support:recyclerview-v7:' + SUPPORT_VERSION + compile 'com.android.support:percent:' + SUPPORT_VERSION + compile project(':liraries:ptr-lib') + compile project(':liraries:markdown') + // If you want to use the GPU Filters + //RxJava 与 Retrofit + //辅助 + // compile 'eu.unicate.android:retroauth:1.0.4' + compile('com.github.afollestad.material-dialogs:core:0.8.5.5@aar') { + transitive = true + } + compile 'com.nineoldandroids:library:2.4.0' + compile 'cn.bingoogolapple:bga-badgeview:1.1.3@aar' + compile 'com.melnykov:floatingactionbutton:1.3.0' + compile 'jp.wasabeef:glide-transformations:2.0.0' + compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.3.0' + compile 'com.squareup.retrofit2:retrofit:2.1.0' + compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' + compile 'com.squareup.retrofit2:converter-gson:2.1.0' + compile 'io.reactivex:rxjava:1.1.7' + compile 'io.reactivex:rxandroid:1.2.1' + compile 'com.squareup.okhttp3:okhttp-urlconnection:3.4.1' + compile 'org.ocpsoft.prettytime:prettytime:4.0.1.Final' + compile 'me.dm7.barcodescanner:zxing:1.8.4' + compile 'com.facebook.fresco:fresco:0.13.0' + compile 'com.github.polok.localify:localify:1.0.0' + compile 'com.github.ongakuer:PhotoDraweeView:a14f105aaa' + compile 'com.commonsware.cwac:anddown:0.2.4' + compile 'com.umeng.analytics:analytics:latest.integration' + compile 'com.jakewharton:butterknife:7.0.1' + compile 'com.squareup.okio:okio:1.6.0' + compile 'com.google.code.gson:gson:2.4' + compile 'com.github.orhanobut:logger:1.12' + compile 'com.pnikosis:materialish-progress:1.7' + compile 'com.android.support:support-v4:24.2.1' + testCompile 'junit:junit:4.12' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..55f8eb6 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in D:\android-sdks/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 *; +#} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..66077a8 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/assets/about_app.html b/app/src/main/assets/about_app.html new file mode 100644 index 0000000..0e4a7a8 --- /dev/null +++ b/app/src/main/assets/about_app.html @@ -0,0 +1,265 @@ + + + + + + + 大象 App 介绍 + + + +

大象 App 介绍

+ +

+ 大象 App 是一款 PHPHub 社区第三方 Android 客户端, 该 App 界面设计遵循了 Google 的 Material Design 设计风格, 使用了 MVP 架构模式, + 使用 RxJava + Retrofit 对网络数据行处理. +

+ +

+ + App 下载地址 + +

+ +

+ 欢迎大家使用, 如果发现 bug 可通过 App 意见反馈, 或者通过微博以及 GitHub与我取得联系. +

+ +

+ + GitHub 开源地址 + +

+ +

欢迎各路大神 Starts, Pull, Fork, Request, Issues

+ +

使用到的开源库

+ + + +

开发环境

+ + + + + \ No newline at end of file diff --git a/app/src/main/assets/js/ImageClickEvent.js b/app/src/main/assets/js/ImageClickEvent.js new file mode 100644 index 0000000..772ffed --- /dev/null +++ b/app/src/main/assets/js/ImageClickEvent.js @@ -0,0 +1,8 @@ +javascript:(function() { + var images = document.getElementsByTagName('img'); + for(var i = 0; i < images.length; i++) { + images[i].onclick = function() { + {platform}.openImage(this.src) + } + } +})(); \ No newline at end of file diff --git a/app/src/main/java/com/jun/elephant/Elephant.java b/app/src/main/java/com/jun/elephant/Elephant.java new file mode 100644 index 0000000..936c7af --- /dev/null +++ b/app/src/main/java/com/jun/elephant/Elephant.java @@ -0,0 +1,79 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant; + +import android.app.Application; +import android.content.Context; +import android.os.Environment; + +import com.facebook.cache.disk.DiskCacheConfig; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.imagepipeline.core.ImagePipelineConfig; +import com.jun.elephant.util.JLog; + +import java.io.File; + +/** + * Created by Jun on 2016/4/10. + */ +public class Elephant extends Application { + + public static Context applicationContext; + + public static String APP_CACHE_PATH = "Elephant/cache"; + + @Override + public void onCreate() { + super.onCreate(); + applicationContext = this; + + /** + * 设置 Fresco 图片缓存的路径 + */ + DiskCacheConfig diskCacheConfig = DiskCacheConfig.newBuilder(getApplicationContext()) + .setBaseDirectoryPath(getOwnCacheDirectory(this, APP_CACHE_PATH)) + .build(); + ImagePipelineConfig config = ImagePipelineConfig.newBuilder(getApplicationContext()) + .setMainDiskCacheConfig(diskCacheConfig) + .setSmallImageDiskCacheConfig(diskCacheConfig) + .build(); + + //初始化 Fresco 图片缓存库 + Fresco.initialize(this, config); + //初始化日志输出工具 + JLog.initialize(BuildConfig.LOG_DEBUG); + } + + public static File getOwnCacheDirectory(Context context, String cachePath) { + File appCacheDir = null; + + if ("mounted".equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) { + appCacheDir = new File(Environment.getExternalStorageDirectory(), cachePath); + } + + if (appCacheDir == null || !appCacheDir.exists() && !appCacheDir.mkdirs()) { + appCacheDir = context.getCacheDir(); + } + + return appCacheDir; + } + + public static boolean hasExternalStoragePermission(Context context) { + int perm = context.checkCallingOrSelfPermission("android.permission.WRITE_EXTERNAL_STORAGE"); + return perm == 0; + } + +} diff --git a/app/src/main/java/com/jun/elephant/api/Networks.java b/app/src/main/java/com/jun/elephant/api/Networks.java new file mode 100644 index 0000000..7cecfa6 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/api/Networks.java @@ -0,0 +1,152 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.api; + +import android.text.TextUtils; + +import com.jun.elephant.BuildConfig; +import com.jun.elephant.util.JLog; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.concurrent.TimeUnit; + +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.Buffer; +import okio.BufferedSource; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; +import retrofit2.converter.gson.GsonConverterFactory; + +/** + * Created by Jun on 2016/7/27. + */ +public class Networks { + + private static final int DEFAULT_TIMEOUT = 5; + + private static Retrofit retrofit; + + private static TokenApi mTokenApi; + + private static TopicApi mTopicApi; + + private static UserApi mUserApi; + + private static String mToken = ""; + + public static String getToken() { + return mToken; + } + + public static void setToken(String mToken) { + Networks.mToken = mToken; + } + + private static Networks mNetworks; + + public static Networks getInstance() { + if (mNetworks == null) { + mNetworks = new Networks(); + } + return mNetworks; + } + + public TokenApi getTokenApi() { + return mTokenApi == null ? configRetrofit(TokenApi.class, true): mTokenApi; + } + + + public TopicApi getTopicApi() { + return mTopicApi == null ? configRetrofit(TopicApi.class, false) : mTopicApi; + } + + public UserApi getUserApi() { + return mUserApi == null ? configRetrofit(UserApi.class, false) : mUserApi; + } + + private T configRetrofit(Class service, boolean isGetToken) { + retrofit = new Retrofit.Builder() + .baseUrl(BuildConfig.API_BASE_URL) + .client(configClient(isGetToken)) + .addConverterFactory(GsonConverterFactory.create()) + .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) + .build(); + + return retrofit.create(service); + + } + + private OkHttpClient configClient(final boolean isGetToken) { + OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder(); + + //为所有请求添加头部 Header 配置的拦截器 + Interceptor headerIntercept = new Interceptor() { + @Override + public Response intercept(Chain chain) throws IOException { + Request.Builder builder = chain.request().newBuilder(); + builder.addHeader("X-Client-Platform", "Android"); + builder.addHeader("X-Client-Version", BuildConfig.VERSION_NAME); + builder.addHeader("X-Client-Build", String.valueOf(BuildConfig.VERSION_CODE)); + + builder.removeHeader("Accept"); + if (isGetToken) { + builder.addHeader("Accept", "application/vnd.PHPHub.v1+json"); + } else { + builder.addHeader("Accept", "application/vnd.OralMaster.v1+json"); + } + + if (!TextUtils.isEmpty(mToken)) { + builder.addHeader("Authorization", "Bearer " + mToken); + } + + Request request = builder.build(); + + return chain.proceed(request); + } + }; + + // Log信息拦截器 + if (BuildConfig.LOG_DEBUG) { + Interceptor loggingIntercept = new Interceptor() { + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + Response response = chain.proceed(request); + ResponseBody responseBody = response.body(); + BufferedSource source = responseBody.source(); + source.request(Long.MAX_VALUE); // Buffer the entire body. + Buffer buffer = source.buffer(); + Charset UTF8 = Charset.forName("UTF-8"); + JLog.logJ("REQUEST_JSON", buffer.clone().readString(UTF8)); + JLog.logi("REQUEST_URL", request.toString()); + return response; + } + }; + okHttpClient.addInterceptor(loggingIntercept); + } + + okHttpClient.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); + okHttpClient.addNetworkInterceptor(headerIntercept); + + return okHttpClient.build(); + } + +} diff --git a/app/src/main/java/com/jun/elephant/api/TokenApi.java b/app/src/main/java/com/jun/elephant/api/TokenApi.java new file mode 100644 index 0000000..e493bc9 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/api/TokenApi.java @@ -0,0 +1,60 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.api; + + +import com.jun.elephant.entity.TokenEntity; + +import retrofit2.http.Field; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.POST; +import rx.Observable; + +/** + * Created by Jun on 2016/6/27. + */ +public interface TokenApi { + + /** + * client_credentials 认证 + * @param authType + * @param clientId + * @param clientSecret + * @return + */ + @FormUrlEncoded + @POST("oauth/access_token") + Observable getToken(@Field("grant_type") String authType, + @Field("client_id") String clientId, + @Field("client_secret") String clientSecret); + + /** + * login_token 认证 + * @param authType + * @param clientId + * @param clientSecret + * @param username + * @param loginToken + * @return + */ + @FormUrlEncoded + @POST("oauth/access_token") + Observable getToken(@Field("grant_type") String authType, + @Field("client_id") String clientId, + @Field("client_secret") String clientSecret, + @Field("username") String username, + @Field("login_token") String loginToken); +} diff --git a/app/src/main/java/com/jun/elephant/api/TopicApi.java b/app/src/main/java/com/jun/elephant/api/TopicApi.java new file mode 100644 index 0000000..793614e --- /dev/null +++ b/app/src/main/java/com/jun/elephant/api/TopicApi.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.api; + +import com.google.gson.JsonObject; +import com.jun.elephant.entity.topic.CategoryEntity; +import com.jun.elephant.entity.topic.TopicDetailEntity; +import com.jun.elephant.entity.topic.TopicListEntity; +import com.jun.elephant.entity.topic.TopicReplyEntity; + +import java.util.Map; + +import retrofit2.http.GET; +import retrofit2.http.POST; +import retrofit2.http.Path; +import retrofit2.http.QueryMap; +import rx.Observable; + +/** + * Created by Jun on 2016/4/9. + */ +public interface TopicApi { + + @GET("topics/{topicId}") + Observable getTopicDetail(@Path("topicId") int topicId, + @QueryMap Map options); + + @GET("topics") + Observable getTopics(@QueryMap Map options); + + @POST("topics/{topicId}/vote-up") + Observable voteUp(@Path("topicId") int topicId); + + @POST("topics/{topicId}/vote-down") + Observable voteDown(@Path("topicId") int topicId); + + @POST("topics") + Observable publishTopic(@QueryMap Map options); + + @GET("categories") + Observable getCategories(); + + @POST("replies") + Observable publishReply(@QueryMap Map options); +} diff --git a/app/src/main/java/com/jun/elephant/api/UserApi.java b/app/src/main/java/com/jun/elephant/api/UserApi.java new file mode 100644 index 0000000..6516691 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/api/UserApi.java @@ -0,0 +1,61 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.api; + +import com.jun.elephant.entity.topic.TopicListEntity; +import com.jun.elephant.entity.user.UserEntity; +import com.jun.elephant.entity.user.UserInfoEntity; +import com.jun.elephant.entity.user.UserMessageEntity; + +import java.util.Map; + +import retrofit2.http.Body; +import retrofit2.http.GET; +import retrofit2.http.PUT; +import retrofit2.http.Path; +import retrofit2.http.QueryMap; +import rx.Observable; + +/** + * Created by Jun on 2016/4/16. + */ +public interface UserApi { + + @GET("me") + Observable getUserInfo(); + + @GET("users/{userId}") + Observable getUserInfoById(@Path("userId") int userId); + + @GET("users/{userId}/following") + Observable getAttentions(@Path("userId") int userId); + + @GET("user/{userId}/votes") + Observable getVotes(@Path("userId") int userId, + @QueryMap Map options); + + @GET("user/{userId}/topics") + Observable getTopics(@Path("userId") int userId, + @QueryMap Map options); + + @GET("me/notifications") + Observable getMyMessage(@QueryMap Map options); + + @PUT("users/{userId}") + Observable saveUserInfo(@Path("userId") int userId, + @Body UserEntity userInfo); + +} diff --git a/app/src/main/java/com/jun/elephant/common/BaseActivity.java b/app/src/main/java/com/jun/elephant/common/BaseActivity.java new file mode 100644 index 0000000..00ece69 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/common/BaseActivity.java @@ -0,0 +1,255 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.common; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.KeyEvent; +import android.view.MenuItem; +import android.view.View; +import android.widget.Toast; + +import com.afollestad.materialdialogs.MaterialDialog; +import com.jun.elephant.R; +import com.jun.elephant.global.Constants; +import com.jun.elephant.global.UserConstant; +import com.jun.elephant.util.JLog; +import com.jun.elephant.util.ThemeUtil; +import com.umeng.analytics.MobclickAgent; + +/** + * Created by Jun on 2016/4/29. + */ +public class BaseActivity extends AppCompatActivity implements BaseFuncIml , View.OnClickListener { + + protected Fragment mCurrFragment; + + private int mFragmentId; + + private static final String TAG = "BaseActivity"; + + private UserConstant mUserConstant; + + private MaterialDialog.Builder mLoadingDialog; + + public ThemeUtil mThemeUtil; + + private long mExitTime; + + private boolean isExit = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + initTheme(); + super.onCreate(savedInstanceState); + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + initData(); + initView(); + initListener(); + initLoad(); + } + + @Override + protected void onStart() { + JLog.logd(TAG, "onStart"); + super.onStart(); + } + + @Override + protected void onResume() { + JLog.logd(TAG, "onResume"); + super.onResume(); + MobclickAgent.onPageStart(getClass().getName()); + MobclickAgent.onResume(this); + } + + @Override + protected void onPause() { + JLog.logd(TAG, "onPause"); + super.onPause(); + MobclickAgent.onPageEnd(getClass().getName()); + MobclickAgent.onPause(this); + } + + @Override + protected void onStop() { + JLog.logd(TAG, "onStop"); + super.onStop(); + } + + @Override + protected void onDestroy() { + JLog.logd(TAG, "onDestroy"); + super.onDestroy(); + } + + public void setFrameId(int mFragmentId) { + this.mFragmentId = mFragmentId; + } + + protected void toFragment(Fragment toFragment) { + if (mCurrFragment == null) { + showShortToast("mCurrFragment is null!"); + return; + } + + if (toFragment == null) { + showShortToast("toFragment is null!"); + return; + } + + if (toFragment.isAdded()) { + getSupportFragmentManager().beginTransaction().hide(mCurrFragment) + .show(toFragment).commit(); + } else { + getSupportFragmentManager().beginTransaction().hide(mCurrFragment) + .add(mFragmentId, toFragment).show(toFragment) + .commit(); + } + + mCurrFragment = toFragment; + + } + + public Fragment getCurrFragment() { + return mCurrFragment; + } + + public void setCurrFragment(Fragment mCurrFragment) { + this.mCurrFragment = mCurrFragment; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return super.onOptionsItemSelected(item); + } + + @Override + public void initData() { + mUserConstant = UserConstant.getInstance(this); + } + + @Override + public void initView() { + initLoadingDialog(); + } + + private void initLoadingDialog() { + mLoadingDialog = new MaterialDialog.Builder(this); + mLoadingDialog.title("请稍候"); + mLoadingDialog.progress(true, 0); + } + + @Override + public void initListener() { + + } + + @Override + public void initLoad() { + + } + + private void initTheme() { + mThemeUtil = ThemeUtil.getInstance(this); + String theme = mThemeUtil.getTheme(); + switch (theme) { + case Constants.Theme.Blue: + setTheme(R.style.BlueTheme); + break; + + case Constants.Theme.White: + setTheme(R.style.WhiteTheme); + break; + + case Constants.Theme.Gray: + setTheme(R.style.GrayTheme); + break; + + default: + setTheme(R.style.BlueTheme); + break; + } + } + + @Override + public void onClick(View v) { + + } + + protected void showShortToast(String msg) { + Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); + } + + protected void openActivity(Class toActivity) { + openActivity(toActivity, null); + } + + protected void openActivity(Class toActivity, Bundle parameter) { + Intent intent = new Intent(this, toActivity); + if (parameter != null) { + intent.putExtras(parameter); + } + startActivity(intent); + + } + + public UserConstant getUserConstant() { + return mUserConstant; + } + + public MaterialDialog.Builder getLoadingDialog() { + return mLoadingDialog; + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + if (isExit) { + if ((System.currentTimeMillis() - mExitTime) > 2000) { + showShortToast("再按一次退出程序"); + mExitTime = System.currentTimeMillis(); + } else { + finish(); + } + return true; + } + } + return super.onKeyDown(keyCode, event); + } + + public void setExit(boolean isExit) { + this.isExit = isExit; + } + + protected void setToolbar(Toolbar toolbar, String title) { + toolbar.setTitle(title); + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + } + +} diff --git a/app/src/main/java/com/jun/elephant/common/BaseFragment.java b/app/src/main/java/com/jun/elephant/common/BaseFragment.java new file mode 100644 index 0000000..5173c85 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/common/BaseFragment.java @@ -0,0 +1,105 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.common; + +import android.content.Intent; +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 android.widget.Toast; + +import com.jun.elephant.global.UserConstant; + + +/** + * Created by Jun on 2016/5/2. + */ +public class BaseFragment extends Fragment implements BaseFuncIml { + + private View mContentView; + + private ViewGroup container; + + private UserConstant mUserConstant; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + initData(); + initView(); + initListener(); + initLoad(); + this.container = container; + return mContentView; + } + + public void setContentView(int viewId) { + this.mContentView = getActivity().getLayoutInflater().inflate(viewId, container, false); + } + + public View getContentView() { + return mContentView; + } + + protected void showShortToast(String pMsg) { + Toast.makeText(getActivity(), pMsg, Toast.LENGTH_SHORT).show(); + } + + @Override + public void initData() { + mUserConstant = UserConstant.getInstance(getContext()); + } + + @Override + public void initView() { + + } + + @Override + public void initListener() { + + } + + @Override + public void initLoad() { + + } + + protected void openActivity(Class toActivity) { + openActivity(toActivity, null); + } + + protected void openActivity(Class toActivity, Bundle parameter) { + Intent intent = new Intent(getActivity(), toActivity); + if (parameter != null) { + intent.putExtras(parameter); + } + startActivity(intent); + + } + + public UserConstant getUserConstant() { + return mUserConstant; + } +} diff --git a/app/src/main/java/com/jun/elephant/common/BaseFuncIml.java b/app/src/main/java/com/jun/elephant/common/BaseFuncIml.java new file mode 100644 index 0000000..487dd93 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/common/BaseFuncIml.java @@ -0,0 +1,34 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.common; + +/** + * Created by Jun on 2016/10/17. + */ + +public interface BaseFuncIml { + /** 初始化数据方法 */ + void initData(); + + /** 初始化UI控件方法 */ + void initView(); + + /** 初始化事件监听方法 */ + void initListener(); + + /** 初始化界面加载 */ + void initLoad(); +} diff --git a/app/src/main/java/com/jun/elephant/common/BaseWebViewActivity.java b/app/src/main/java/com/jun/elephant/common/BaseWebViewActivity.java new file mode 100644 index 0000000..64d85f4 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/common/BaseWebViewActivity.java @@ -0,0 +1,187 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.common; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Bundle; +import android.text.TextUtils; +import android.webkit.JavascriptInterface; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import com.github.polok.localify.LocalifyClient; +import com.github.polok.localify.module.LocalifyModule; +import com.jun.elephant.api.Networks; +import com.jun.elephant.global.Constants; +import com.jun.elephant.ui.main.PhotoShowActivity; +import com.jun.elephant.ui.widget.MultiStateView; +import com.orhanobut.logger.Logger; + +import java.util.HashMap; +import java.util.Map; + +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action1; +import rx.functions.Func1; +import rx.schedulers.Schedulers; + + +public abstract class BaseWebViewActivity extends BaseActivity { + + protected final static String PLATFORM = "Android"; + + protected static class WebAppClient extends WebViewClient { + private Context context; + + private MultiStateView multiStateView; + + private WebView contentView; + + private WebSettings settings; + + public WebAppClient(Context context, + MultiStateView multiStateView, + WebView contentView) { + this.context = context; + this.multiStateView = multiStateView; + this.contentView = contentView; + + initSetting(); + } + + private void initSetting() { + settings = contentView.getSettings(); + + settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); + settings.setJavaScriptEnabled(true); + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + Uri uri = Uri.parse(url); + if (uri.getHost().contains(Constants.PHPHUB_HOST)) { + HashMap segments = new HashMap<>(); + String key = null; + for (String segment : uri.getPathSegments()) { + if (key == null) { + key = segment; + } else { + segments.put(key, segment); + key = null; + } + } + if (segments.size() > 0) { + if (segments.containsKey(Constants.PHPHUB_TOPIC_PATH) && !TextUtils.isEmpty(segments.get(Constants.PHPHUB_TOPIC_PATH))) { + url = String.format("%s%s?id=%s", Constants.DEEP_LINK_PREFIX, Constants.PHPHUB_TOPIC_PATH, segments.get(Constants.PHPHUB_TOPIC_PATH)); + } else if (segments.containsKey(Constants.PHPHUB_USER_PATH) && !TextUtils.isEmpty(segments.get(Constants.PHPHUB_USER_PATH))) { + url = String.format("%s%s?id=%s", Constants.DEEP_LINK_PREFIX, Constants.PHPHUB_USER_PATH, segments.get(Constants.PHPHUB_USER_PATH)); + } + } + } + + if (url.contains(Constants.DEEP_LINK_PREFIX)) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + if (intent.resolveActivity(context.getPackageManager()) != null) { + context.startActivity(intent); + } else { + context.startActivity(Intent.createChooser(intent, "请选择浏览器")); + } + } else { +// Intent intentToLaunch = WebViewPageActivity.getCallingIntent(context, webUrl); +// context.startActivity(intentToLaunch); +// navigator.navigateToWebView(context, url); + } + return true; + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + if (multiStateView != null) { + multiStateView.setViewState(MultiStateView.VIEW_STATE_LOADING); + } + } + + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + if (multiStateView != null) { + multiStateView.setViewState(MultiStateView.VIEW_STATE_CONTENT); + } + addImageClickEvent(); + } + + private void addImageClickEvent() { + LocalifyModule localify = new LocalifyClient.Builder() + .withAssetManager(context.getAssets()) + .build() + .localify(); + + localify.rx() + .loadAssetsFile("js/ImageClickEvent.js") + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .filter(new Func1() { + @Override + public Boolean call(String javascript) { + return !TextUtils.isEmpty(javascript); + } + }) + .subscribe(new Action1() { + @Override + public void call(String javascript) { + contentView.loadUrl(javascript.replace("{platform}", PLATFORM)); + } + }, new Action1() { + @Override + public void call(Throwable throwable) { + Logger.e(throwable.toString()); + } + }); + } + } + + protected static class WebAppInterface { + private Context context; + + + public WebAppInterface(Context context) { + this.context = context; + } + + @JavascriptInterface + public void openImage(String url) { + Bundle bundle = new Bundle(); + bundle.putString(Constants.Key.TOPIC_IMAGE_URL, url); + Intent intent = new Intent(context, PhotoShowActivity.class); + intent.putExtras(bundle); + context.startActivity(intent); + + } + } + + public Map getAuth() { + Map header = new HashMap<>(); + header.put("Authorization", "Bearer " + Networks.getToken()); + return header; + } + +} diff --git a/app/src/main/java/com/jun/elephant/common/FragmentAdapter.java b/app/src/main/java/com/jun/elephant/common/FragmentAdapter.java new file mode 100644 index 0000000..1eeee69 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/common/FragmentAdapter.java @@ -0,0 +1,57 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.common; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Jun on 2016/3/2. + */ +public class FragmentAdapter extends FragmentPagerAdapter { + + private List mFragments = new ArrayList<>(); + + private List mTitles = new ArrayList<>(); + + public FragmentAdapter(FragmentManager fm) { + super(fm); + } + + public void addFragment(Fragment fragment, String title) { + mFragments.add(fragment); + mTitles.add(title); + } + + @Override + public Fragment getItem(int position) { + return mFragments.get(position); + } + + @Override + public int getCount() { + return mFragments.size(); + } + + @Override + public CharSequence getPageTitle(int position) { + return mTitles.get(position); + } +} diff --git a/app/src/main/java/com/jun/elephant/common/WebAppClient.java b/app/src/main/java/com/jun/elephant/common/WebAppClient.java new file mode 100644 index 0000000..2508432 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/common/WebAppClient.java @@ -0,0 +1,156 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.common; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.text.TextUtils; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import com.github.polok.localify.LocalifyClient; +import com.github.polok.localify.module.LocalifyModule; +import com.jun.elephant.ui.widget.MultiStateView; +import com.jun.elephant.global.Constants; +import com.orhanobut.logger.Logger; + +import java.util.HashMap; + +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action1; +import rx.functions.Func1; +import rx.schedulers.Schedulers; + +/** + * Created by Jun on 2016/5/8. + */ +public class WebAppClient extends WebViewClient { + + protected final static String PLATFORM = "Android"; + + private Context context; + + private MultiStateView multiStateView; + + private WebView contentView; + + private WebSettings settings; + + public WebAppClient(Context context, + MultiStateView multiStateView, + WebView contentView) { + this.context = context; + this.multiStateView = multiStateView; + this.contentView = contentView; + + initSetting(); + } + + private void initSetting() { + settings = contentView.getSettings(); + + settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); + settings.setJavaScriptEnabled(true); + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + Uri uri = Uri.parse(url); + if (uri.getHost().contains(Constants.PHPHUB_HOST)) { + HashMap segments = new HashMap<>(); + String key = null; + for (String segment : uri.getPathSegments()) { + if (key == null) { + key = segment; + } else { + segments.put(key, segment); + key = null; + } + } + if (segments.size() > 0) { + if (segments.containsKey(Constants.PHPHUB_TOPIC_PATH) && !TextUtils.isEmpty(segments.get(Constants.PHPHUB_TOPIC_PATH))) { + url = String.format("%s%s?id=%s", Constants.DEEP_LINK_PREFIX, Constants.PHPHUB_TOPIC_PATH, segments.get(Constants.PHPHUB_TOPIC_PATH)); + } else if (segments.containsKey(Constants.PHPHUB_USER_PATH) && !TextUtils.isEmpty(segments.get(Constants.PHPHUB_USER_PATH))) { + url = String.format("%s%s?id=%s", Constants.DEEP_LINK_PREFIX, Constants.PHPHUB_USER_PATH, segments.get(Constants.PHPHUB_USER_PATH)); + } + } + } + + if (url.contains(Constants.DEEP_LINK_PREFIX)) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + if (intent.resolveActivity(context.getPackageManager()) != null) { + context.startActivity(intent); + } else { + context.startActivity(Intent.createChooser(intent, "请选择浏览器")); + } + } else { +// Intent intentToLaunch = WebViewPageActivity.getCallingIntent(context, webUrl); +// context.startActivity(intentToLaunch); +// navigator.navigateToWebView(context, url); + } + return true; + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + if (multiStateView != null) { + multiStateView.setViewState(MultiStateView.VIEW_STATE_LOADING); + } + } + + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + if (multiStateView != null) { + multiStateView.setViewState(MultiStateView.VIEW_STATE_CONTENT); + } + addImageClickEvent(); + } + + private void addImageClickEvent() { + LocalifyModule localify = new LocalifyClient.Builder() + .withAssetManager(context.getAssets()) + .build() + .localify(); + + localify.rx() + .loadAssetsFile("js/ImageClickEvent.js") + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .filter(new Func1() { + @Override + public Boolean call(String javascript) { + return !TextUtils.isEmpty(javascript); + } + }) + .subscribe(new Action1() { + @Override + public void call(String javascript) { + contentView.loadUrl(javascript.replace("{platform}", PLATFORM)); + } + }, new Action1() { + @Override + public void call(Throwable throwable) { + Logger.e(throwable.toString()); + } + }); + } +} diff --git a/app/src/main/java/com/jun/elephant/entity/DrawerMenuEntity.java b/app/src/main/java/com/jun/elephant/entity/DrawerMenuEntity.java new file mode 100644 index 0000000..b6f47c9 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/entity/DrawerMenuEntity.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.entity; + +/** + * Created by Jun on 2016/6/1. + */ +public class DrawerMenuEntity { + private String title; + private int icon; + + public DrawerMenuEntity(String title, int icon) { + this.title = title; + this.icon = icon; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public int getIcon() { + return icon; + } + + public void setIcon(int icon) { + this.icon = icon; + } +} diff --git a/app/src/main/java/com/jun/elephant/entity/TokenEntity.java b/app/src/main/java/com/jun/elephant/entity/TokenEntity.java new file mode 100644 index 0000000..b5a571c --- /dev/null +++ b/app/src/main/java/com/jun/elephant/entity/TokenEntity.java @@ -0,0 +1,65 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.entity; + +/** + * Created by Jun on 2016/3/27. + */ +public class TokenEntity { + + /** + * access_token : 5QLxZKYZu49C2iE38NouIMoKDfdpfmjvuJ40VRVj + * token_type : Bearer + * expires_in : 864000 + */ + + private String access_token; + private String token_type; + private String expires_in; + private String refresh_token; + + public String getAccess_token() { + return access_token; + } + + public void setAccess_token(String access_token) { + this.access_token = access_token; + } + + public String getToken_type() { + return token_type; + } + + public void setToken_type(String token_type) { + this.token_type = token_type; + } + + public String getExpires_in() { + return expires_in; + } + + public void setExpires_in(String expires_in) { + this.expires_in = expires_in; + } + + public String getRefresh_token() { + return refresh_token; + } + + public void setRefresh_token(String refresh_token) { + this.refresh_token = refresh_token; + } +} diff --git a/app/src/main/java/com/jun/elephant/entity/topic/CategoryEntity.java b/app/src/main/java/com/jun/elephant/entity/topic/CategoryEntity.java new file mode 100644 index 0000000..b2081f1 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/entity/topic/CategoryEntity.java @@ -0,0 +1,95 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.entity.topic; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +/** + * Created by Jun on 2016/6/20. + */ +public class CategoryEntity { + + /** + * id : 1 + * name : PHP + * parent_node : 0 + */ + + private List data; + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public static class Category implements Parcelable { + private int id; + private String name; + + 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; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.id); + dest.writeString(this.name); + } + + public Category() { + } + + protected Category(Parcel in) { + this.id = in.readInt(); + this.name = in.readString(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public Category createFromParcel(Parcel source) { + return new Category(source); + } + + @Override + public Category[] newArray(int size) { + return new Category[size]; + } + }; + } +} diff --git a/app/src/main/java/com/jun/elephant/entity/topic/TopicDetailEntity.java b/app/src/main/java/com/jun/elephant/entity/topic/TopicDetailEntity.java new file mode 100644 index 0000000..694ee6b --- /dev/null +++ b/app/src/main/java/com/jun/elephant/entity/topic/TopicDetailEntity.java @@ -0,0 +1,31 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.entity.topic; + +/** + * Created by Jun on 2016/5/4. + */ +public class TopicDetailEntity { + private TopicEntity data; + + public TopicEntity getData() { + return data; + } + + public void setData(TopicEntity data) { + this.data = data; + } +} diff --git a/app/src/main/java/com/jun/elephant/entity/topic/TopicEntity.java b/app/src/main/java/com/jun/elephant/entity/topic/TopicEntity.java new file mode 100644 index 0000000..6a41bd3 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/entity/topic/TopicEntity.java @@ -0,0 +1,546 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.entity.topic; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by Jun on 2016/5/3. + */ +public class TopicEntity implements Parcelable { + + private int id; + private String title; + @SerializedName("is_excellent") + private boolean isExcellent; + @SerializedName("reply_count") + private int replyCount; + @SerializedName("updated_at") + private String updatedAt; + @SerializedName("created_at") + private String createdAt; + @SerializedName("vote_count") + private int voteCount; + + private boolean favorite; + + private boolean attention; + + @SerializedName("vote_down") + private boolean voteDown; + + @SerializedName("vote_up") + private boolean voteUp; + + private LinksBean links; + + private UserBean user; + + @SerializedName("last_reply_user") + private LastReplyUser lastReplyUser; + + private Category category; + + public Category getCategory() { + return category; + } + + public void setCategory(Category category) { + this.category = category; + } + + public LastReplyUser getLastReplyUser() { + return lastReplyUser; + } + + public void setLastReplyUser(LastReplyUser lastReplyUser) { + this.lastReplyUser = lastReplyUser; + } + + public UserBean getUser() { + return user; + } + + public void setUser(UserBean user) { + this.user = user; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public boolean isIsExcellent() { + return isExcellent; + } + + public void setIsExcellent(boolean isExcellent) { + this.isExcellent = isExcellent; + } + + public int getReplyCount() { + return replyCount; + } + + public void setReplyCount(int replyCount) { + this.replyCount = replyCount; + } + + public String getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(String updatedAt) { + this.updatedAt = updatedAt; + } + + public String getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + + public int getVoteCount() { + return voteCount; + } + + public void setVoteCount(int voteCount) { + this.voteCount = voteCount; + } + + public LinksBean getLinks() { + return links; + } + + public void setLinks(LinksBean links) { + this.links = links; + } + + public boolean isFavorite() { + return favorite; + } + + public void setFavorite(boolean favorite) { + this.favorite = favorite; + } + + public boolean isAttention() { + return attention; + } + + public void setAttention(boolean attention) { + this.attention = attention; + } + + public boolean isVoteDown() { + return voteDown; + } + + public void setVoteDown(boolean voteDown) { + this.voteDown = voteDown; + } + + public boolean isVoteUp() { + return voteUp; + } + + public void setVoteUp(boolean voteUp) { + this.voteUp = voteUp; + } + + public static class LinksBean implements Parcelable { + @SerializedName("details_web_view") + private String detailsWebView; + @SerializedName("replies_web_view") + private String repliesWebView; + @SerializedName("web_url") + private String webUrl; + + public String getDetailsWebView() { + return detailsWebView; + } + + public void setDetailsWebView(String detailsWebView) { + this.detailsWebView = detailsWebView; + } + + public String getRepliesWebView() { + return repliesWebView; + } + + public void setRepliesWebView(String repliesWebView) { + this.repliesWebView = repliesWebView; + } + + public String getWebUrl() { + return webUrl; + } + + public void setWebUrl(String webUrl) { + this.webUrl = webUrl; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.detailsWebView); + dest.writeString(this.repliesWebView); + dest.writeString(this.webUrl); + } + + public LinksBean() { + } + + protected LinksBean(Parcel in) { + this.detailsWebView = in.readString(); + this.repliesWebView = in.readString(); + this.webUrl = in.readString(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public LinksBean createFromParcel(Parcel source) { + return new LinksBean(source); + } + + @Override + public LinksBean[] newArray(int size) { + return new LinksBean[size]; + } + }; + } + + public static class UserBean implements Parcelable { + + public DataEntity data; + + public DataEntity getData() { + return data; + } + + public void setData(DataEntity data) { + this.data = data; + } + + public static class DataEntity implements Parcelable { + private String id; + private String name; + private String avatar; + private String introduction; + + public String getIntroduction() { + return introduction; + } + + public void setIntroduction(String introduction) { + this.introduction = introduction; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.id); + dest.writeString(this.name); + dest.writeString(this.avatar); + dest.writeString(this.introduction); + } + + public DataEntity() { + } + + protected DataEntity(Parcel in) { + this.id = in.readString(); + this.name = in.readString(); + this.avatar = in.readString(); + this.introduction = in.readString(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public DataEntity createFromParcel(Parcel source) { + return new DataEntity(source); + } + + @Override + public DataEntity[] newArray(int size) { + return new DataEntity[size]; + } + }; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(this.data, flags); + } + + public UserBean() { + } + + protected UserBean(Parcel in) { + this.data = in.readParcelable(DataEntity.class.getClassLoader()); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public UserBean createFromParcel(Parcel source) { + return new UserBean(source); + } + + @Override + public UserBean[] newArray(int size) { + return new UserBean[size]; + } + }; + } + + public static class LastReplyUser implements Parcelable { + + private DataEntity data; + + public DataEntity getData() { + return data; + } + + public void setData(DataEntity data) { + this.data = data; + } + + public static class DataEntity implements Parcelable { + private String id; + private String name; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.id); + dest.writeString(this.name); + } + + public DataEntity() { + } + + protected DataEntity(Parcel in) { + this.id = in.readString(); + this.name = in.readString(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public DataEntity createFromParcel(Parcel source) { + return new DataEntity(source); + } + + @Override + public DataEntity[] newArray(int size) { + return new DataEntity[size]; + } + }; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(this.data, flags); + } + + public LastReplyUser() { + } + + protected LastReplyUser(Parcel in) { + this.data = in.readParcelable(DataEntity.class.getClassLoader()); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public LastReplyUser createFromParcel(Parcel source) { + return new LastReplyUser(source); + } + + @Override + public LastReplyUser[] newArray(int size) { + return new LastReplyUser[size]; + } + }; + } + + public static class Category implements Parcelable { + + private CategoryEntity.Category data; + + public CategoryEntity.Category getData() { + return data; + } + + public void setData(CategoryEntity.Category data) { + this.data = data; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(this.data, flags); + } + + public Category() { + } + + protected Category(Parcel in) { + this.data = in.readParcelable(CategoryEntity.Category.class.getClassLoader()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Category createFromParcel(Parcel source) { + return new Category(source); + } + + @Override + public Category[] newArray(int size) { + return new Category[size]; + } + }; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.id); + dest.writeString(this.title); + dest.writeByte(isExcellent ? (byte) 1 : (byte) 0); + dest.writeInt(this.replyCount); + dest.writeString(this.updatedAt); + dest.writeString(this.createdAt); + dest.writeInt(this.voteCount); + dest.writeParcelable(this.links, flags); + dest.writeParcelable(this.user, flags); + dest.writeParcelable(this.lastReplyUser, flags); + dest.writeParcelable(this.category, flags); + } + + public TopicEntity() { + } + + protected TopicEntity(Parcel in) { + this.id = in.readInt(); + this.title = in.readString(); + this.isExcellent = in.readByte() != 0; + this.replyCount = in.readInt(); + this.updatedAt = in.readString(); + this.createdAt = in.readString(); + this.voteCount = in.readInt(); + this.links = in.readParcelable(LinksBean.class.getClassLoader()); + this.user = in.readParcelable(UserBean.class.getClassLoader()); + this.lastReplyUser = in.readParcelable(LastReplyUser.class.getClassLoader()); + this.category = in.readParcelable(Category.class.getClassLoader()); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public TopicEntity createFromParcel(Parcel source) { + return new TopicEntity(source); + } + + @Override + public TopicEntity[] newArray(int size) { + return new TopicEntity[size]; + } + }; +} diff --git a/app/src/main/java/com/jun/elephant/entity/topic/TopicListEntity.java b/app/src/main/java/com/jun/elephant/entity/topic/TopicListEntity.java new file mode 100644 index 0000000..f78e7be --- /dev/null +++ b/app/src/main/java/com/jun/elephant/entity/topic/TopicListEntity.java @@ -0,0 +1,46 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.entity.topic; + +import java.util.List; + +/** + * Created by Jun on 2016/3/27. + */ +public class TopicListEntity { + + /** + * id : 1300 + * title : 督促唱几句 + * is_excellent : false + * reply_count : 0 + * updated_at : 2016-03-24 18:29:29 + * created_at : 2016-03-24 18:29:21 + * vote_count : 1 + * links : {"details_web_view":"https://staging_api.phphub.org/v1/topics/1300/web_view","replies_web_view":"https://staging_api.phphub.org/v1/topics/1300/replies/web_view","web_url":"http://phphub.org/topics/1300"} + */ + + private List data; + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + +} diff --git a/app/src/main/java/com/jun/elephant/entity/topic/TopicReplyEntity.java b/app/src/main/java/com/jun/elephant/entity/topic/TopicReplyEntity.java new file mode 100644 index 0000000..e2ea9f0 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/entity/topic/TopicReplyEntity.java @@ -0,0 +1,112 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.entity.topic; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by Jun on 2016/5/5. + */ +public class TopicReplyEntity { + + private ReplyEntity data; + + public ReplyEntity getData() { + return data; + } + + public void setData(ReplyEntity data) { + this.data = data; + } + + public static class ReplyEntity { + @SerializedName("topic_id") + int topicId; + + String body; + + @SerializedName("user_id") + int userId; + + @SerializedName("body_original") + String bodyOriginal; + + @SerializedName("updated_at") + String updatedAt; + + @SerializedName("created_at") + String createdAt; + + int id; + + public int getTopicId() { + return topicId; + } + + public void setTopicId(int topicId) { + this.topicId = topicId; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public String getBodyOriginal() { + return bodyOriginal; + } + + public void setBodyOriginal(String bodyOriginal) { + this.bodyOriginal = bodyOriginal; + } + + public String getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(String updatedAt) { + this.updatedAt = updatedAt; + } + + public String getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + } + +} diff --git a/app/src/main/java/com/jun/elephant/entity/user/MessageEntity.java b/app/src/main/java/com/jun/elephant/entity/user/MessageEntity.java new file mode 100644 index 0000000..bf9eebf --- /dev/null +++ b/app/src/main/java/com/jun/elephant/entity/user/MessageEntity.java @@ -0,0 +1,140 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.entity.user; + +import com.google.gson.annotations.SerializedName; +import com.jun.elephant.entity.topic.TopicDetailEntity; + +/** + * Created by Jun on 2016/5/6. + */ +public class MessageEntity { + private int id; + + private String type; + + private String body; + + @SerializedName("topic_id") + private int topicId; + + @SerializedName("reply_id") + private int replyId; + + @SerializedName("created_at") + private String createdAt; + + @SerializedName("from_user_id") + private int fromUserId; + + @SerializedName("type_msg") + private String typeMsg; + + private String message; + + @SerializedName("from_user") + private UserInfoEntity fromUserEntity; + + private TopicDetailEntity topic; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public int getTopicId() { + return topicId; + } + + public void setTopicId(int topicId) { + this.topicId = topicId; + } + + public int getReplyId() { + return replyId; + } + + public void setReplyId(int replyId) { + this.replyId = replyId; + } + + public String getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + + public int getFromUserId() { + return fromUserId; + } + + public void setFromUserId(int fromUserId) { + this.fromUserId = fromUserId; + } + + public String getTypeMsg() { + return typeMsg; + } + + public void setTypeMsg(String typeMsg) { + this.typeMsg = typeMsg; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public UserInfoEntity getFromUserEntity() { + return fromUserEntity; + } + + public void setFromUserEntity(UserInfoEntity fromUserEntity) { + this.fromUserEntity = fromUserEntity; + } + + public TopicDetailEntity getTopic() { + return topic; + } + + public void setTopic(TopicDetailEntity topic) { + this.topic = topic; + } +} diff --git a/app/src/main/java/com/jun/elephant/entity/user/UserEntity.java b/app/src/main/java/com/jun/elephant/entity/user/UserEntity.java new file mode 100644 index 0000000..3e7dff1 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/entity/user/UserEntity.java @@ -0,0 +1,319 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.entity.user; + + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Created by Jun on 2016/5/6. + */ +public class UserEntity implements Parcelable { + private int id; + private String name; + private String avatar; + private int topic_count; + private int reply_count; + private int notification_count; + private boolean is_banned; + private String twitter_account; + private String company; + private String city; + private String email; + private String signature; + private String introduction; + private String github_name; + private String github_url; + private String real_name; + private String personal_website; + private String created_at; + private String updated_at; + /** + * replies_web_view : https://staging_api.phphub.org/v1/users/1/replies/web_view + */ + + private LinksBean links; + + 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 String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public int getTopic_count() { + return topic_count; + } + + public void setTopic_count(int topic_count) { + this.topic_count = topic_count; + } + + public int getReply_count() { + return reply_count; + } + + public void setReply_count(int reply_count) { + this.reply_count = reply_count; + } + + public int getNotification_count() { + return notification_count; + } + + public void setNotification_count(int notification_count) { + this.notification_count = notification_count; + } + + public boolean isIs_banned() { + return is_banned; + } + + public void setIs_banned(boolean is_banned) { + this.is_banned = is_banned; + } + + public String getTwitter_account() { + return twitter_account; + } + + public void setTwitter_account(String twitter_account) { + this.twitter_account = twitter_account; + } + + public String getCompany() { + return company; + } + + public void setCompany(String company) { + this.company = company; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public String getIntroduction() { + return introduction; + } + + public void setIntroduction(String introduction) { + this.introduction = introduction; + } + + public String getGithub_name() { + return github_name; + } + + public void setGithub_name(String github_name) { + this.github_name = github_name; + } + + public String getGithub_url() { + return github_url; + } + + public void setGithub_url(String github_url) { + this.github_url = github_url; + } + + public String getReal_name() { + return real_name; + } + + public void setReal_name(String real_name) { + this.real_name = real_name; + } + + public String getPersonal_website() { + return personal_website; + } + + public void setPersonal_website(String personal_website) { + this.personal_website = personal_website; + } + + public String getCreated_at() { + return created_at; + } + + public void setCreated_at(String created_at) { + this.created_at = created_at; + } + + public String getUpdated_at() { + return updated_at; + } + + public void setUpdated_at(String updated_at) { + this.updated_at = updated_at; + } + + public LinksBean getLinks() { + return links; + } + + public void setLinks(LinksBean links) { + this.links = links; + } + + public static class LinksBean implements Parcelable { + private String replies_web_view; + + public String getReplies_web_view() { + return replies_web_view; + } + + public void setReplies_web_view(String replies_web_view) { + this.replies_web_view = replies_web_view; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.replies_web_view); + } + + public LinksBean() { + } + + protected LinksBean(Parcel in) { + this.replies_web_view = in.readString(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public LinksBean createFromParcel(Parcel source) { + return new LinksBean(source); + } + + @Override + public LinksBean[] newArray(int size) { + return new LinksBean[size]; + } + }; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.id); + dest.writeString(this.name); + dest.writeString(this.avatar); + dest.writeInt(this.topic_count); + dest.writeInt(this.reply_count); + dest.writeInt(this.notification_count); + dest.writeByte(is_banned ? (byte) 1 : (byte) 0); + dest.writeString(this.twitter_account); + dest.writeString(this.company); + dest.writeString(this.city); + dest.writeString(this.email); + dest.writeString(this.signature); + dest.writeString(this.introduction); + dest.writeString(this.github_name); + dest.writeString(this.github_url); + dest.writeString(this.real_name); + dest.writeString(this.personal_website); + dest.writeString(this.created_at); + dest.writeString(this.updated_at); + dest.writeParcelable(this.links, flags); + } + + public UserEntity() { + } + + protected UserEntity(Parcel in) { + this.id = in.readInt(); + this.name = in.readString(); + this.avatar = in.readString(); + this.topic_count = in.readInt(); + this.reply_count = in.readInt(); + this.notification_count = in.readInt(); + this.is_banned = in.readByte() != 0; + this.twitter_account = in.readString(); + this.company = in.readString(); + this.city = in.readString(); + this.email = in.readString(); + this.signature = in.readString(); + this.introduction = in.readString(); + this.github_name = in.readString(); + this.github_url = in.readString(); + this.real_name = in.readString(); + this.personal_website = in.readString(); + this.created_at = in.readString(); + this.updated_at = in.readString(); + this.links = in.readParcelable(LinksBean.class.getClassLoader()); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public UserEntity createFromParcel(Parcel source) { + return new UserEntity(source); + } + + @Override + public UserEntity[] newArray(int size) { + return new UserEntity[size]; + } + }; +} diff --git a/app/src/main/java/com/jun/elephant/entity/user/UserInfoEntity.java b/app/src/main/java/com/jun/elephant/entity/user/UserInfoEntity.java new file mode 100644 index 0000000..922561e --- /dev/null +++ b/app/src/main/java/com/jun/elephant/entity/user/UserInfoEntity.java @@ -0,0 +1,87 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.entity.user; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Created by Jun on 2016/6/15. + */ +public class UserInfoEntity implements Parcelable { + + /** + * id : 1 + * name : Summer + * avatar : http://phphub.org/uploads/avatars/1_1443413935.jpeg + * topic_count : 88 + * reply_count : 645 + * notification_count : 0 + * is_banned : false + * twitter_account : Summe + * company : + * city : 南京 + * email : summer.alex07@gmail.com + * signature : Little knowledge is dangerous + * introduction : A little knowledge is a dangerous thing + * github_name : summerblue + * github_url : https://github.com/summerblue + * real_name : I love it + * personal_website : summerblue.me + * created_at : 2014-08-18 01:00:03 + * updated_at : 2016-04-15 12:53:08 + * links : {"replies_web_view":"https://staging_api.phphub.org/v1/users/1/replies/web_view"} + */ + + private UserEntity data; + + public UserEntity getData() { + return data; + } + + public void setData(UserEntity data) { + this.data = data; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(this.data, flags); + } + + public UserInfoEntity() { + } + + protected UserInfoEntity(Parcel in) { + this.data = in.readParcelable(UserEntity.class.getClassLoader()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public UserInfoEntity createFromParcel(Parcel source) { + return new UserInfoEntity(source); + } + + @Override + public UserInfoEntity[] newArray(int size) { + return new UserInfoEntity[size]; + } + }; +} diff --git a/app/src/main/java/com/jun/elephant/entity/user/UserMessageEntity.java b/app/src/main/java/com/jun/elephant/entity/user/UserMessageEntity.java new file mode 100644 index 0000000..0d9ebab --- /dev/null +++ b/app/src/main/java/com/jun/elephant/entity/user/UserMessageEntity.java @@ -0,0 +1,33 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.entity.user; + +import java.util.List; + +/** + * Created by Jun on 2016/5/6. + */ +public class UserMessageEntity { + private List data; + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } +} diff --git a/app/src/main/java/com/jun/elephant/global/Constants.java b/app/src/main/java/com/jun/elephant/global/Constants.java new file mode 100644 index 0000000..1ec788c --- /dev/null +++ b/app/src/main/java/com/jun/elephant/global/Constants.java @@ -0,0 +1,101 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.global; + +/** + * Created by Jun on 2016/4/9. + */ +public class Constants { + + public static final int PER_PAGE = 15; + + public static final String PHPHUB_HOST = "phphub.org"; + + public static final String PHPHUB_USER_PATH = "users"; + + public static final String PHPHUB_TOPIC_PATH = "topics"; + + public static final String DEEP_LINK_PREFIX = "phphub://"; + + public static final String MD_HTML_PREFIX = " "; + + public static final String MD_HTML_SUFFIX = ""; + + public static final String ABOUT_PHPHUB = "https://laravel-china.org/about"; + + public static final String ABOUT_ME = "https://github.com/Freelander"; + + public static final String OPEN_SOURCE = "https://github.com/Freelander/Elephant"; + + public static final String CONTACT_INFO = "huanggaojun13@gmail.com"; + + public static final String LOGIN_HELP = "http://7xnqwn.com1.z0.glb.clouddn.com/index.html"; + + public interface Key { + String TOPIC = "topic"; + String TOPIC_TYPE = "topic_type"; + String USER_DATA = "user_data"; + String IS_LOGIN = "is_login"; + String COMMENT_URL = "comment_url"; + String WEB_URL = "web_url"; + String WEB_TITLE = "web_title"; + String TOPIC_IMAGE_URL = "topic_image_url"; + String TOKEN = "token"; + String TOPIC_ID = "topic_id"; + String USER_ID = "user_id"; + String PREVIEW_TOPIC_TITLE = "preview_topic_title"; + String PREVIEW_TOPIC_CONTENT = "preview_topic_content"; + String THEME_MODE = "theme_mode"; + } + + public interface Token { + String AUTH_TYPE_GUEST = "client_credentials"; + String AUTH_TYPE_USER = "login_token"; + String AUTH_TYPE_REFRESH = "refresh_token"; + } + + public interface Topic { + String EXCELLENT = "excellent";//推荐 + String NEWEST = "newest";//最新 + String VOTE = "vote";//热门 + String NOBODY = "nobody";//零回复 + String WIKI = "wiki";//社区WIKI + String JOBS = "jobs";//热门招聘 + } + + public interface User { + int USER_TOPIC_FOLLOW = 1; + int USER_TOPIC_VOTES = 2; //赞过话题 + int USER_TOPIC_MY = 3; // + } + + public interface TopicOpt { + int TOPIC_VOTE_UP = 5; //赞同话题 + int TOPIC_VOTE_DOWN = 6;//反对话题 + } + + public interface Activity { + int LoginActivity = 1000; + int UserInfoEditActivity = 1001; + } + + public interface Theme { + String Blue = "blue"; + String White = "white"; + String Gray = "gray"; + } + +} diff --git a/app/src/main/java/com/jun/elephant/global/UserConstant.java b/app/src/main/java/com/jun/elephant/global/UserConstant.java new file mode 100644 index 0000000..3a2f533 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/global/UserConstant.java @@ -0,0 +1,82 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.global; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.SharedPreferences; + +import com.google.gson.Gson; +import com.jun.elephant.entity.user.UserInfoEntity; + +/** + * Created by Jun on 2016/4/16. + */ +@SuppressLint("CommitPrefEdits") +public class UserConstant { + + private static final String TAG = "UserConstant"; + + private static UserConstant mUserConstant; + + private SharedPreferences mPreferences; + + private SharedPreferences.Editor mEditor; + + private Gson mGson; + + public static UserConstant getInstance(Context context) { + if (mUserConstant == null) + mUserConstant = new UserConstant(context); + + return mUserConstant; + } + + private UserConstant(Context context) { + mPreferences = context.getSharedPreferences(TAG, Context.MODE_PRIVATE); + mGson = new Gson(); + } + + + public boolean saveUserData(String value) { + mEditor = mPreferences.edit(); + mEditor.putString(Constants.Key.USER_DATA, value); + + return mEditor.commit() && setIsLogin(true); + } + + public UserInfoEntity getUserData() { + String temp = mPreferences.getString(Constants.Key.USER_DATA, ""); + return mGson.fromJson(temp, UserInfoEntity.class); + } + + public boolean logout() { + mEditor = mPreferences.edit(); + mEditor.remove(Constants.Key.USER_DATA); + mEditor.putBoolean(Constants.Key.IS_LOGIN, false); + return mEditor.commit() && setIsLogin(false); + } + + public boolean isLogin() { + return mPreferences.getBoolean(Constants.Key.IS_LOGIN, false); + } + + private boolean setIsLogin(boolean login) { + mEditor = mPreferences.edit(); + mEditor.putBoolean(Constants.Key.IS_LOGIN, login); + return mEditor.commit(); + } +} diff --git a/app/src/main/java/com/jun/elephant/mvpframe/BaseModel.java b/app/src/main/java/com/jun/elephant/mvpframe/BaseModel.java new file mode 100644 index 0000000..152af79 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/mvpframe/BaseModel.java @@ -0,0 +1,7 @@ +package com.jun.elephant.mvpframe; + +/** + * Created by baixiaokang on 16/5/1. + */ +public interface BaseModel { +} diff --git a/app/src/main/java/com/jun/elephant/mvpframe/BasePresenter.java b/app/src/main/java/com/jun/elephant/mvpframe/BasePresenter.java new file mode 100644 index 0000000..32b51c1 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/mvpframe/BasePresenter.java @@ -0,0 +1,26 @@ +package com.jun.elephant.mvpframe; + +import android.content.Context; + +import com.jun.elephant.mvpframe.rx.RxManager; + + +/** + * Created by baixiaokang on 16/4/22. + */ +public abstract class BasePresenter { + public Context context; + public M mModel; + public V mView; + public RxManager mRxManager = new RxManager(); + + public void setVM(V v, M m) { + this.mView = v; + this.mModel = m; + } + + + public void onDestroy() { + mRxManager.clear(); + } +} diff --git a/app/src/main/java/com/jun/elephant/mvpframe/BaseView.java b/app/src/main/java/com/jun/elephant/mvpframe/BaseView.java new file mode 100644 index 0000000..00f40bd --- /dev/null +++ b/app/src/main/java/com/jun/elephant/mvpframe/BaseView.java @@ -0,0 +1,12 @@ +package com.jun.elephant.mvpframe; + +/** + * Created by baixiaokang on 16/4/22. + */ +public interface BaseView { + void onRequestStart(); + void onRequestError(String msg); + void onRequestEnd(); + void onInternetError(); + +} diff --git a/app/src/main/java/com/jun/elephant/mvpframe/base/BaseFrameActivity.java b/app/src/main/java/com/jun/elephant/mvpframe/base/BaseFrameActivity.java new file mode 100644 index 0000000..12d6c3e --- /dev/null +++ b/app/src/main/java/com/jun/elephant/mvpframe/base/BaseFrameActivity.java @@ -0,0 +1,49 @@ +package com.jun.elephant.mvpframe.base; + +import android.os.Bundle; + +import com.jun.elephant.common.BaseActivity; +import com.jun.elephant.mvpframe.BaseModel; +import com.jun.elephant.mvpframe.BasePresenter; +import com.jun.elephant.mvpframe.BaseView; +import com.jun.elephant.mvpframe.util.TUtil; +import com.jun.elephant.util.JLog; + + +/** + * Created by quan on 16/9/1. + */ + +public abstract class BaseFrameActivity

extends BaseActivity implements BaseView { + + public P mPresenter; + + public M mModel; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mPresenter = TUtil.getT(this, 0); + mModel = TUtil.getT(this, 1); + if (this instanceof BaseView) { + mPresenter.setVM(this, mModel); + } + } + + @Override + protected void onDestroy() { + if (mPresenter != null) mPresenter.onDestroy(); + super.onDestroy(); + } + + @Override + public void onInternetError() { +// showShortToast("网络异常"); + } + + @Override + public void onRequestError(String msg) { +// showShortToast(msg); + JLog.e("REQUEST_ERROR ==== ", msg); + } +} diff --git a/app/src/main/java/com/jun/elephant/mvpframe/base/BaseFrameFragment.java b/app/src/main/java/com/jun/elephant/mvpframe/base/BaseFrameFragment.java new file mode 100644 index 0000000..3808ca6 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/mvpframe/base/BaseFrameFragment.java @@ -0,0 +1,52 @@ +package com.jun.elephant.mvpframe.base; + +import android.os.Bundle; + +import com.jun.elephant.common.BaseFragment; +import com.jun.elephant.mvpframe.BaseModel; +import com.jun.elephant.mvpframe.BasePresenter; +import com.jun.elephant.mvpframe.BaseView; +import com.jun.elephant.mvpframe.util.TUtil; +import com.jun.elephant.util.JLog; + + +/** + * Created by quan on 16/9/20. + */ + +public abstract class BaseFrameFragment

extends BaseFragment implements BaseView { + + public P mPresenter; + + public M mModel; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mPresenter = TUtil.getT(this, 0); + mModel = TUtil.getT(this, 1); + if (this instanceof BaseView) { + mPresenter.setVM(this, mModel); + } + + } + + @Override + public void onDestroy() { + if (mPresenter != null) mPresenter.onDestroy(); + super.onDestroy(); + } + + @Override + public void onInternetError() { +// showShortToast("网络异常"); + } + + @Override + public void onRequestError(String msg) { +// showShortToast(msg); + JLog.e("REQUEST_ERROR ==== ", msg); + } + +} diff --git a/app/src/main/java/com/jun/elephant/mvpframe/base/BaseFrameWebViewActivity.java b/app/src/main/java/com/jun/elephant/mvpframe/base/BaseFrameWebViewActivity.java new file mode 100644 index 0000000..76a4296 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/mvpframe/base/BaseFrameWebViewActivity.java @@ -0,0 +1,62 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.mvpframe.base; + +import android.os.Bundle; + +import com.jun.elephant.common.BaseWebViewActivity; +import com.jun.elephant.mvpframe.BaseModel; +import com.jun.elephant.mvpframe.BasePresenter; +import com.jun.elephant.mvpframe.BaseView; +import com.jun.elephant.mvpframe.util.TUtil; + +/** + * Created by Jun on 2016/10/15. + */ +public abstract class BaseFrameWebViewActivity

extends BaseWebViewActivity implements BaseView { + + public P mPresenter; + + public M mModel; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mPresenter = TUtil.getT(this, 0); + mModel = TUtil.getT(this, 1); + if (this instanceof BaseView) { + mPresenter.setVM(this, mModel); + } + } + + @Override + protected void onDestroy() { + if (mPresenter != null) mPresenter.onDestroy(); + super.onDestroy(); + } + + @Override + public void onInternetError() { +// showShortToast("网络异常"); + } + + @Override + public void onRequestError(String msg) { + System.out.println("request msg ======= [" + msg + "]"); +// showShortToast(msg); + } + +} diff --git a/app/src/main/java/com/jun/elephant/mvpframe/rx/RxBus.java b/app/src/main/java/com/jun/elephant/mvpframe/rx/RxBus.java new file mode 100644 index 0000000..6c58f12 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/mvpframe/rx/RxBus.java @@ -0,0 +1,132 @@ +package com.jun.elephant.mvpframe.rx; + +import android.support.annotation.NonNull; + +import com.jun.elephant.util.JLog; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + + +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action1; +import rx.subjects.PublishSubject; +import rx.subjects.Subject; + +/** + * 用RxJava实现的EventBus + * + * @author baixiaokang + */ +public class RxBus { + private static RxBus instance; + + public static synchronized RxBus $() { + if (null == instance) { + instance = new RxBus(); + } + return instance; + } + + private RxBus() { + } + + @SuppressWarnings("rawtypes") + private ConcurrentHashMap> subjectMapper = new ConcurrentHashMap>(); + + /** + * 订阅事件源 + * + * @param mObservable + * @param mAction1 + * @return + */ + public RxBus OnEvent(Observable mObservable, Action1 mAction1) { + mObservable.observeOn(AndroidSchedulers.mainThread()).subscribe(mAction1, new Action1() { + @Override + public void call(Throwable throwable) { + throwable.printStackTrace(); + } + }); + return $(); + } + + /** + * 注册事件源 + * + * @param tag + * @return + */ + @SuppressWarnings({"rawtypes"}) + public Observable register(@NonNull Object tag) { + List subjectList = subjectMapper.get(tag); + if (null == subjectList) { + subjectList = new ArrayList(); + subjectMapper.put(tag, subjectList); + } + Subject subject; + subjectList.add(subject = PublishSubject.create()); + JLog.d("register", tag + " size:" + subjectList.size()); + return subject; + } + + @SuppressWarnings("rawtypes") + public void unregister(@NonNull Object tag) { + List subjects = subjectMapper.get(tag); + if (null != subjects) { + subjectMapper.remove(tag); + } + } + + /** + * 取消监听 + * + * @param tag + * @param observable + * @return + */ + @SuppressWarnings("rawtypes") + public RxBus unregister(@NonNull Object tag, + @NonNull Observable observable) { + if (null == observable) + return $(); + List subjects = subjectMapper.get(tag); + if (null != subjects) { + subjects.remove((Subject) observable); + if (isEmpty(subjects)) { + subjectMapper.remove(tag); + JLog.d("unregister", tag + " size:" + subjects.size()); + } + } + return $(); + } + + public void post(@NonNull Object content) { + post(content.getClass().getName(), content); + } + + /** + * 触发事件 + * + * @param content + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public void post(@NonNull Object tag, @NonNull Object content) { + JLog.d("post", "eventName: " + tag); + List subjectList = subjectMapper.get(tag); + if (!isEmpty(subjectList)) { + for (Subject subject : subjectList) { + subject.onNext(content); + JLog.d("onEvent", "eventName: " + tag); + } + } + } + + @SuppressWarnings("rawtypes") + public static boolean isEmpty(Collection collection) { + return null == collection || collection.isEmpty(); + } +} diff --git a/app/src/main/java/com/jun/elephant/mvpframe/rx/RxManager.java b/app/src/main/java/com/jun/elephant/mvpframe/rx/RxManager.java new file mode 100644 index 0000000..0d8567f --- /dev/null +++ b/app/src/main/java/com/jun/elephant/mvpframe/rx/RxManager.java @@ -0,0 +1,49 @@ +package com.jun.elephant.mvpframe.rx; + +import java.util.HashMap; +import java.util.Map; + +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action1; +import rx.subscriptions.CompositeSubscription; + +/** + * 用于管理RxBus的事件和Rxjava相关代码的生命周期处理 + * Created by baixiaokang on 16/4/28. + */ +public class RxManager { + + public RxBus mRxBus = RxBus.$(); + private Map> mObservables = new HashMap<>();// 管理观察源 + private CompositeSubscription mCompositeSubscription = new CompositeSubscription();// 管理订阅者者 + + + public void on(String eventName, Action1 action1) { + Observable mObservable = mRxBus.register(eventName); + mObservables.put(eventName, mObservable); + mCompositeSubscription + .add(mObservable.observeOn(AndroidSchedulers.mainThread()) + .subscribe(action1, new Action1() { + @Override + public void call(Throwable throwable) { + throwable.printStackTrace(); + } + })); + } + + public void add(Subscription m) { + mCompositeSubscription.add(m); + } + + public void clear() { + mCompositeSubscription.unsubscribe();// 取消订阅 + for (Map.Entry> entry : mObservables.entrySet()) + mRxBus.unregister(entry.getKey(), entry.getValue());// 移除观察 + } + + public void post(Object tag, Object content) { + mRxBus.post(tag, content); + } +} diff --git a/app/src/main/java/com/jun/elephant/mvpframe/rx/RxSchedulers.java b/app/src/main/java/com/jun/elephant/mvpframe/rx/RxSchedulers.java new file mode 100644 index 0000000..964ccab --- /dev/null +++ b/app/src/main/java/com/jun/elephant/mvpframe/rx/RxSchedulers.java @@ -0,0 +1,35 @@ +package com.jun.elephant.mvpframe.rx; + +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; + +/** + * Created by baixiaokang on 16/5/6. + */ +public class RxSchedulers { +// public static Observable.Transformer handleDialog(final Context context) { +// return tObservable -> { +// tObservable.subscribe(new ProgressSubscriber<>(context)); +// return tObservable; +// }; +// +// } + + public static Observable.Transformer io_main() { + + return new Observable.Transformer() { + @Override + public Observable call(Observable tObservable) { + + return tObservable + //生产线程 + .subscribeOn(Schedulers.io()) + //消费线程 + .observeOn(AndroidSchedulers.mainThread()); + } + }; + + + } +} diff --git a/app/src/main/java/com/jun/elephant/mvpframe/util/TUtil.java b/app/src/main/java/com/jun/elephant/mvpframe/util/TUtil.java new file mode 100644 index 0000000..98a3719 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/mvpframe/util/TUtil.java @@ -0,0 +1,32 @@ +package com.jun.elephant.mvpframe.util; + +import java.lang.reflect.ParameterizedType; + +/** + * Created by baixiaokang on 16/4/30. + */ +public class TUtil { + public static T getT(Object o, int i) { + try { + return ((Class) ((ParameterizedType) (o.getClass() + .getGenericSuperclass())).getActualTypeArguments()[i]) + .newInstance(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (ClassCastException e) { + e.printStackTrace(); + } + return null; + } + + public static Class forName(String className) { + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/adapter/DrawerMenuAdapter.java b/app/src/main/java/com/jun/elephant/ui/adapter/DrawerMenuAdapter.java new file mode 100644 index 0000000..bcd1aa3 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/adapter/DrawerMenuAdapter.java @@ -0,0 +1,104 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.adapter; + +import android.content.Context; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.jun.elephant.R; +import com.jun.elephant.entity.DrawerMenuEntity; + +import java.util.List; + +/** + * Created by Jun on 2016/3/9. + */ +public class DrawerMenuAdapter extends RecyclerView.Adapter { + + private Context mContext; + + private List mMenuList; + + private int mSelectPosition = 0; + + private OnMenuItemClickListener onMenuItemClickListener; + + public DrawerMenuAdapter(Context context, List menuList) { + this.mContext = context; + this.mMenuList = menuList; + } + + public void setSelectPosition(int mSelectPosition) { + this.mSelectPosition = mSelectPosition; + } + + @Override + public DrawerHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return new DrawerHolder(LayoutInflater.from(mContext).inflate(R.layout.item_drawer_menu, parent, false)); + } + + @Override + public void onBindViewHolder(final DrawerHolder holder, int position) { + DrawerMenuEntity item = mMenuList.get(position); + holder.mMenuText.setText(item.getTitle()); + holder.mMenuIcon.setImageResource(item.getIcon()); + + if (mSelectPosition == position) { + holder.itemView.setBackgroundColor(ContextCompat.getColor(mContext, R.color.line_space)); + } else { + holder.itemView.setBackgroundColor(ContextCompat.getColor(mContext, R.color.text_white)); + } + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onMenuItemClickListener != null) + onMenuItemClickListener.onMenuItemClick(holder.getAdapterPosition()); + } + }); + } + + @Override + public int getItemCount() { + return mMenuList.size(); + } + + class DrawerHolder extends RecyclerView.ViewHolder { + + private TextView mMenuText; + private ImageView mMenuIcon; + + public DrawerHolder(View itemView) { + super(itemView); + mMenuText = (TextView) itemView.findViewById(R.id.drawer_menu_tv); + mMenuIcon = (ImageView) itemView.findViewById(R.id.drawer_menu_ic); + } + } + + public interface OnMenuItemClickListener { + void onMenuItemClick(int position); + } + + public void setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) { + this.onMenuItemClickListener = onMenuItemClickListener; + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/adapter/TopicListAdapter.java b/app/src/main/java/com/jun/elephant/ui/adapter/TopicListAdapter.java new file mode 100644 index 0000000..d1d237f --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/adapter/TopicListAdapter.java @@ -0,0 +1,129 @@ +package com.jun.elephant.ui.adapter; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.jun.elephant.R; +import com.jun.elephant.entity.topic.TopicEntity; +import com.jun.elephant.ui.topic.details.TopicDetailsActivity; +import com.jun.elephant.ui.user.info.UserInfoActivity; +import com.jun.elephant.ui.widget.MySimpleDraweeView; + +import org.ocpsoft.prettytime.PrettyTime; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Locale; + +import cn.bingoogolapple.badgeview.BGABadgeRelativeLayout; + + +/** + * Created by Jun on 2016/9/23. + */ + +public class TopicListAdapter extends RecyclerView.Adapter { + + private List mDatas; + + private Context mContext; + + private Locale mLocale; + + private PrettyTime mPrettyTime; + + private SimpleDateFormat mDateFormat; + + @SuppressLint("SimpleDateFormat") + public TopicListAdapter(Context context, List datas) { + this.mContext = context; + this.mDatas = datas; + mLocale = mContext.getResources().getConfiguration().locale; + mPrettyTime = new PrettyTime(mLocale); + mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } + + @Override + public TopicViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return new TopicViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_topic_list, parent, false)); + } + + @Override + public void onBindViewHolder(TopicViewHolder holder, int position) { + final TopicEntity item = mDatas.get(position); + + String mCommentCount = String.valueOf(item.getReplyCount()); + StringBuilder temp = new StringBuilder(); + + if (item.getReplyCount() > 99) { + mCommentCount = "99+"; + } + + if (item.getCategory() != null) { + temp.append(item.getCategory().getData().getName()); + } + + if (item.getLastReplyUser() != null) { + temp.append(" • ").append("最后由").append(item.getLastReplyUser().getData().getName()); + } + + if (item.getUpdatedAt() != null) { + String dateStr = item.getUpdatedAt(); + try { + String prettyTimeString = mPrettyTime.format(mDateFormat.parse(dateStr)); + temp.append(" • ").append(prettyTimeString); + } catch (ParseException e) { + e.printStackTrace(); + } + } + + String avatarUrl = item.getUser().getData().getAvatar(); + if (avatarUrl != null) { + holder.mUserImg.setImageUrl(avatarUrl); + } + + holder.mTitleTv.setText(item.getTitle()); + holder.mTimeTv.setText(temp.toString()); + holder.mBgaRl.showTextBadge(mCommentCount); + + holder.mBgaRl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mContext.startActivity(TopicDetailsActivity.newIntent(mContext, item)); + } + }); + + holder.mUserImg.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mContext.startActivity(UserInfoActivity.newIntent(mContext, Integer.parseInt(item.getUser().getData().getId()))); + } + }); + } + + @Override + public int getItemCount() { + return mDatas.size(); + } + + class TopicViewHolder extends RecyclerView.ViewHolder { + private BGABadgeRelativeLayout mBgaRl; + private MySimpleDraweeView mUserImg; + private TextView mTitleTv; + private TextView mTimeTv; + + TopicViewHolder(View itemView) { + super(itemView); + mBgaRl = (BGABadgeRelativeLayout) itemView.findViewById(R.id.bgaRl); + mTimeTv = (TextView) itemView.findViewById(R.id.content_time_tv); + mTitleTv = (TextView) itemView.findViewById(R.id.content_title_tv); + mUserImg = (MySimpleDraweeView) itemView.findViewById(R.id.user_img_iv); + } + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/adapter/UserMessageAdapter.java b/app/src/main/java/com/jun/elephant/ui/adapter/UserMessageAdapter.java new file mode 100644 index 0000000..a37e8c8 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/adapter/UserMessageAdapter.java @@ -0,0 +1,130 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.adapter; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.jun.elephant.R; +import com.jun.elephant.entity.topic.TopicEntity; +import com.jun.elephant.entity.user.MessageEntity; +import com.jun.elephant.entity.user.UserEntity; +import com.jun.elephant.ui.topic.details.TopicDetailsActivity; +import com.jun.elephant.ui.user.info.UserInfoActivity; +import com.jun.elephant.ui.widget.MySimpleDraweeView; + +import org.ocpsoft.prettytime.PrettyTime; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Locale; + +/** + * Created by Jun on 2016/5/6. + */ +public class UserMessageAdapter extends RecyclerView.Adapter { + + private Context mContext; + + private List mMessageList; + + private Locale mLocale; + + private PrettyTime mPrettyTime; + + private SimpleDateFormat mDateFormat; + + @SuppressLint("SimpleDateFormat") + public UserMessageAdapter(Context context, List messageList) { + this.mContext = context; + this.mMessageList = messageList; + mLocale = mContext.getResources().getConfiguration().locale; + mPrettyTime = new PrettyTime(mLocale); + mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } + + @Override + public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return new MessageViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_message, parent, false)); + } + + @Override + public void onBindViewHolder(MessageViewHolder holder, int position) { + final MessageEntity mEntity = mMessageList.get(position); + final UserEntity mUserEntity = mEntity.getFromUserEntity().getData(); + TopicEntity mTopicEntity = mEntity.getTopic().getData(); + + String msg = mEntity.getMessage(); + + String msgDate = mUserEntity.getName(); + if (mEntity.getCreatedAt() != null) { + + String dateStr = mEntity.getCreatedAt(); + + try { + msgDate += " • " + mPrettyTime.format(mDateFormat.parse(dateStr)); + } catch (ParseException e) { + e.printStackTrace(); + } + } + + holder.mMsgDateTv.setText(msgDate); + holder.mAvatarIv.setImageUrl(mUserEntity.getAvatar()); + if (!TextUtils.isEmpty(msg)) { + holder.mReplyTv.setText(msg); + } + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mContext.startActivity(TopicDetailsActivity.newIntent(mContext, mEntity.getTopic().getData())); + } + }); + + holder.mAvatarIv.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mContext.startActivity(UserInfoActivity.newIntent(mContext, mUserEntity.getId())); + } + }); + + } + + @Override + public int getItemCount() { + return mMessageList.size(); + } + + class MessageViewHolder extends RecyclerView.ViewHolder { + private MySimpleDraweeView mAvatarIv; + private TextView mMsgDateTv; + private TextView mReplyTv; + + public MessageViewHolder(View itemView) { + super(itemView); + mAvatarIv = (MySimpleDraweeView) itemView.findViewById(R.id.user_img_iv); + mMsgDateTv = (TextView) itemView.findViewById(R.id.msg_date_tv); + mReplyTv = (TextView) itemView.findViewById(R.id.msg_details_tv); + } + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/login/LoginActivity.java b/app/src/main/java/com/jun/elephant/ui/login/LoginActivity.java new file mode 100644 index 0000000..5b7f0c7 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/login/LoginActivity.java @@ -0,0 +1,105 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.login; + +import android.content.Intent; +import android.os.Bundle; +import android.text.TextUtils; + +import com.afollestad.materialdialogs.MaterialDialog; +import com.google.zxing.Result; +import com.jun.elephant.entity.user.UserInfoEntity; +import com.jun.elephant.mvpframe.base.BaseFrameActivity; +import com.jun.elephant.global.Constants; +import com.jun.elephant.util.JLog; + +import me.dm7.barcodescanner.zxing.ZXingScannerView; + +/** + * Created by Jun on 2016/10/14. + */ +public class LoginActivity extends BaseFrameActivity implements ZXingScannerView.ResultHandler, LoginContract.View { + + private ZXingScannerView mScannerView; + + private MaterialDialog mLoadingDialog; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mScannerView = new ZXingScannerView(this); + setContentView(mScannerView); + } + + @Override + public void initView() { + super.initView(); + getLoadingDialog().content("正在登录..."); + mLoadingDialog = getLoadingDialog().build(); + } + + @Override + public void onResume() { + super.onResume(); + mScannerView.setResultHandler(this); + mScannerView.startCamera(); + } + + @Override + public void onPause() { + super.onPause(); + mScannerView.stopCamera(); + } + + @Override + public void handleResult(Result result) { + String mUserName = ""; + String mLoginToken = ""; + String s = result.getText(); + if (!TextUtils.isEmpty(s) && s.contains(",")) { + String[] data = s.split(",", 2); + if (data.length == 2) { + mUserName = data[0]; + mLoginToken = data[1]; + } + } + + JLog.d("Login info: ", "UserName === " + mUserName + " LoginToken ======= "+ mLoginToken); + + mPresenter.login(this, mUserName, mLoginToken); + } + + @Override + public void onRequestStart() { + mLoadingDialog.show(); + } + + @Override + public void onRequestEnd() { + mLoadingDialog.dismiss(); + } + + @Override + public void onLoginSuccess(UserInfoEntity userInfoEntity) { + Bundle bundle = new Bundle(); + bundle.putParcelable(Constants.Key.USER_DATA, userInfoEntity); + Intent intent = new Intent(); + intent.putExtras(bundle); + setResult(RESULT_OK, intent); + finish(); + } + +} diff --git a/app/src/main/java/com/jun/elephant/ui/login/LoginContract.java b/app/src/main/java/com/jun/elephant/ui/login/LoginContract.java new file mode 100644 index 0000000..929c741 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/login/LoginContract.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.login; + +import android.content.Context; + +import com.jun.elephant.entity.TokenEntity; +import com.jun.elephant.entity.user.UserInfoEntity; +import com.jun.elephant.mvpframe.BaseModel; +import com.jun.elephant.mvpframe.BasePresenter; +import com.jun.elephant.mvpframe.BaseView; + +import rx.Observable; + +/** + * Created by Jun on 2016/9/19. + */ + +public interface LoginContract { + + interface Model extends BaseModel { + Observable getLoginToken(Context context, String userName, String loginToken); + + Observable login(); + } + + interface View extends BaseView { + void onLoginSuccess(UserInfoEntity userInfoEntity); + } + + abstract class Presenter extends BasePresenter { + public abstract void login(Context context, String userName, String loginToken); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/login/LoginModel.java b/app/src/main/java/com/jun/elephant/ui/login/LoginModel.java new file mode 100644 index 0000000..8afc4e6 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/login/LoginModel.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.login; + +import android.content.Context; + +import com.jun.elephant.BuildConfig; +import com.jun.elephant.api.Networks; +import com.jun.elephant.entity.TokenEntity; +import com.jun.elephant.entity.user.UserInfoEntity; +import com.jun.elephant.global.Constants; + +import rx.Observable; + +/** + * Created by Jun on 2016/10/14. + */ +public class LoginModel implements LoginContract.Model { + + /** + * 获取登录状态 Token,扫描后得到的 userName, loginToken + * @param context + * @param userName + * @param loginToken + * @return + */ + @Override + public Observable getLoginToken(Context context, String userName, String loginToken) { + return Networks.getInstance().getTokenApi().getToken( + Constants.Token.AUTH_TYPE_USER, + BuildConfig.CLIENT_ID, + BuildConfig.CLIENT_SECRET, + userName, loginToken); + } + + /** + * 登录 + * @return + */ + @Override + public Observable login() { + return Networks.getInstance().getUserApi().getUserInfo(); + } + +} diff --git a/app/src/main/java/com/jun/elephant/ui/login/LoginPresenter.java b/app/src/main/java/com/jun/elephant/ui/login/LoginPresenter.java new file mode 100644 index 0000000..60c0f22 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/login/LoginPresenter.java @@ -0,0 +1,77 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.login; + +import android.content.Context; + +import com.jun.elephant.Elephant; +import com.jun.elephant.api.Networks; +import com.jun.elephant.entity.TokenEntity; +import com.jun.elephant.entity.user.UserInfoEntity; +import com.jun.elephant.global.Constants; +import com.jun.elephant.util.JLog; +import com.jun.elephant.util.SharePreferencesHelper; + +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action0; +import rx.functions.Action1; +import rx.functions.Func1; +import rx.schedulers.Schedulers; + +/** + * Created by Jun on 2016/10/14. + */ +public class LoginPresenter extends LoginContract.Presenter { + + @Override + public void login(Context context, String userName, String loginToken) { + mRxManager.add(mModel.getLoginToken(context, userName, loginToken) + .subscribeOn(Schedulers.io()) + .doOnSubscribe(new Action0() { + @Override + public void call() { + mView.onRequestStart(); + } + }) + .flatMap(new Func1>() { + @Override + public Observable call(TokenEntity tokenEntity) { + JLog.logd("login_token: ", tokenEntity.getAccess_token()); + Networks.setToken(tokenEntity.getAccess_token()); + SharePreferencesHelper.getInstance(Elephant.applicationContext) + .putString(Constants.Key.TOKEN, tokenEntity.getAccess_token()); + return mModel.login(); + } + }) + .subscribeOn(AndroidSchedulers.mainThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Action1() { + @Override + public void call(UserInfoEntity userInfoEntity) { + mView.onLoginSuccess(userInfoEntity); + } + }, new Action1() { + @Override + public void call(Throwable throwable) { + mView.onInternetError(); + mView.onRequestEnd(); + } + }) + + ); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/main/AboutAppActivity.java b/app/src/main/java/com/jun/elephant/ui/main/AboutAppActivity.java new file mode 100644 index 0000000..3f2dcd3 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/main/AboutAppActivity.java @@ -0,0 +1,96 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.main; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.support.design.widget.CollapsingToolbarLayout; +import android.support.v7.widget.Toolbar; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.TextView; + +import com.jun.elephant.R; +import com.jun.elephant.common.BaseActivity; +import com.jun.elephant.util.SystemUtil; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Created by Jun on 2016/4/20. + */ +public class AboutAppActivity extends BaseActivity { + + @Bind(R.id.version_tv) + TextView mVersionTv; + @Bind(R.id.toolbar) + Toolbar mToolbar; + @Bind(R.id.collapsing_toolbar) + CollapsingToolbarLayout mCollapsingToolbar; + @Bind(R.id.webView) + WebView mWebView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_about_app); + ButterKnife.bind(this); + } + + @Override + public void initView() { + super.initView(); + setToolbar(mToolbar, ""); + mCollapsingToolbar.setTitle(getResources().getString(R.string.app_about_app)); + + mVersionTv.setText(String.valueOf("Version " + SystemUtil.getVersionName(this))); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING); + } else { + mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); + } + mWebView.setWebViewClient(new AboutClient()); + } + + @Override + public void initLoad() { + super.initLoad(); + mWebView.loadUrl("file:///android_asset/about_app.html"); + } + + private class AboutClient extends WebViewClient { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + if (url != null && (url.startsWith("http://") || url.startsWith("https://"))) { + openBrowser(view.getContext(), url); + return true; + } else { + return false; + } + } + } + + private void openBrowser(Context context, String url) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + context.startActivity(intent); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/main/MainActivity.java b/app/src/main/java/com/jun/elephant/ui/main/MainActivity.java new file mode 100644 index 0000000..f5ef644 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/main/MainActivity.java @@ -0,0 +1,477 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.main; + +import android.content.Intent; +import android.content.res.Resources; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.content.ContextCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.afollestad.materialdialogs.DialogAction; +import com.afollestad.materialdialogs.MaterialDialog; +import com.google.gson.Gson; +import com.jun.elephant.R; +import com.jun.elephant.api.Networks; +import com.jun.elephant.common.BaseActivity; +import com.jun.elephant.util.OpenWebViewUtils; +import com.jun.elephant.entity.DrawerMenuEntity; +import com.jun.elephant.entity.user.UserInfoEntity; +import com.jun.elephant.global.Constants; +import com.jun.elephant.global.UserConstant; +import com.jun.elephant.ui.adapter.DrawerMenuAdapter; +import com.jun.elephant.ui.topic.list.TopicListByMeFragment; +import com.jun.elephant.ui.login.LoginActivity; +import com.jun.elephant.ui.user.info.UserInfoActivity; +import com.jun.elephant.ui.user.message.UserMessageActivity; +import com.jun.elephant.ui.topic.list.TopicListByForumFragment; +import com.jun.elephant.ui.widget.MySimpleDraweeView; +import com.jun.elephant.ui.widget.ThemeDialog; +import com.jun.elephant.util.SharePreferencesHelper; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * Created by Jun on 2016/3/1. + */ +public class MainActivity extends BaseActivity implements Toolbar.OnMenuItemClickListener, + ThemeDialog.OnThemeChangeListener, + DrawerMenuAdapter.OnMenuItemClickListener, + MaterialDialog.SingleButtonCallback { + + @Bind(R.id.toolBar) + Toolbar mToolBar; + @Bind(R.id.container) + FrameLayout mContainer; + @Bind(R.id.main_content) + LinearLayout mMainContentLl; + @Bind(R.id.name_tv) + TextView mNameTv; + @Bind(R.id.user_email_tv) + TextView mUserEmailTv; + @Bind(R.id.menu_down_iv) + ImageView mMenuDownIv; + @Bind(R.id.drawer_menu_rc) + RecyclerView mDrawerMenuRc; + @Bind(R.id.navigation_view) + LinearLayout mNavigationView; + @Bind(R.id.drawerLayout) + DrawerLayout mDrawerLayout; + @Bind(R.id.user_iv) + MySimpleDraweeView mUserImgIv; + @Bind(R.id.login_tv) + TextView mLoginTv; + @Bind(R.id.login_success_rl) + RelativeLayout mLoginShowRl; + + private List mMainMenuList, mMyMenuList; + + private DrawerMenuAdapter mMenuAdapter; + + private TopicListByForumFragment mCommonFragment; + + private TopicListByMeFragment mTopicListByMeFragment; + + private UserInfoEntity mUserInfoEntity; + + private ThemeDialog mThemeDialog; + + private MaterialDialog.Builder mExitLoginDialog; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + ButterKnife.bind(this); + setFrameId(R.id.container); + setExit(true); + } + + @Override + public void initData() { + super.initData(); + + mThemeDialog = new ThemeDialog(this); + mExitLoginDialog = new MaterialDialog.Builder(this); + + mCommonFragment = new TopicListByForumFragment(); + mTopicListByMeFragment = new TopicListByMeFragment(); + + String token = SharePreferencesHelper.getInstance(this).getString(Constants.Key.TOKEN); + if (!TextUtils.isEmpty(token)) { + Networks.setToken(token); + } + + mMainMenuList = new ArrayList<>(); + mMainMenuList.add(new DrawerMenuEntity(getString(R.string.menu_recommend), R.mipmap.ic_main_recommend)); + mMainMenuList.add(new DrawerMenuEntity(getString(R.string.menu_hot), R.mipmap.ic_main_hot)); + mMainMenuList.add(new DrawerMenuEntity(getString(R.string.menu_newest), R.mipmap.ic_main_newest)); + mMainMenuList.add(new DrawerMenuEntity(getString(R.string.menu_nobody), R.mipmap.ic_main_nobody)); + mMainMenuList.add(new DrawerMenuEntity(getString(R.string.menu_jobs), R.mipmap.ic_main_work)); + mMainMenuList.add(new DrawerMenuEntity(getString(R.string.menu_wiki), R.mipmap.ic_main_wiki)); + + mMyMenuList = new ArrayList<>(); + mMyMenuList.add(new DrawerMenuEntity(getString(R.string.menu_vote), R.mipmap.ic_my_vote)); + mMyMenuList.add(new DrawerMenuEntity(getString(R.string.menu_topic), R.mipmap.ic_my_topic)); + mMyMenuList.add(new DrawerMenuEntity(getString(R.string.menu_revert), R.mipmap.ic_my_reply)); + + mMenuAdapter = new DrawerMenuAdapter(this, mMainMenuList); + + } + + @Override + public void initView() { + initExitLoginDialog(); + initToolbar(); + + if (getUserConstant().isLogin()) { + mUserInfoEntity = UserConstant.getInstance(this).getUserData(); + + Uri uri = Uri.parse(mUserInfoEntity.getData().getAvatar()); + mUserImgIv.setImageURI(uri); + mUserEmailTv.setText(mUserInfoEntity.getData().getEmail()); + mNameTv.setText(mUserInfoEntity.getData().getName()); + + mLoginShowRl.setVisibility(View.VISIBLE); + mLoginTv.setVisibility(View.GONE); + + mMainMenuList.addAll(mMyMenuList); + } + + RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this); + mDrawerMenuRc.setLayoutManager(layoutManager); + mDrawerMenuRc.setAdapter(mMenuAdapter); + + Bundle bundle = new Bundle(); + bundle.putString(Constants.Key.TOPIC_TYPE, Constants.Topic.EXCELLENT); + mCommonFragment.setArguments(bundle); + setCurrFragment(mCommonFragment); + toFragment(mCommonFragment); + } + + private void initToolbar() { + mToolBar.inflateMenu(R.menu.menu_main); + Resources.Theme theme = getTheme(); + TypedValue navIcon = new TypedValue(); + TypedValue overFlowIcon = new TypedValue(); + + theme.resolveAttribute(R.attr.navIcon, navIcon, true); + theme.resolveAttribute(R.attr.overFlowIcon, overFlowIcon, true); + + mToolBar.setNavigationIcon(navIcon.resourceId); + mToolBar.setOverflowIcon(ContextCompat.getDrawable(this, overFlowIcon.resourceId)); + } + + private void initExitLoginDialog() { + mExitLoginDialog.content(getString(R.string.dialog_exit_login)) + .positiveColorRes(R.color.colorPrimary) + .negativeColorRes(R.color.colorPrimary) + .positiveText(getString(R.string.dialog_exit_positive_text)) + .negativeText(getString(R.string.dialog_exit_negative_text)); + } + + @Override + public void initListener() { + mToolBar.setOnMenuItemClickListener(this); + mToolBar.setNavigationOnClickListener(this); + mThemeDialog.setOnThemeChangeListener(this); + mMenuAdapter.setOnMenuItemClickListener(this); + mExitLoginDialog.onPositive(this); + } + + private void toUserFragment() { + toFragment(mTopicListByMeFragment); + if (mTopicListByMeFragment.isAdded()) { + mTopicListByMeFragment.getData(); + } + } + + private void toCommonFragment() { + toFragment(mCommonFragment); + if (mCommonFragment.isAdded()) { + mCommonFragment.getData(); + } + } + + @OnClick({R.id.login_tv, R.id.login_success_rl, R.id.user_iv, R.id.setting_ll, R.id.login_help_ll}) + @Override + public void onClick(View v) { + super.onClick(v); + switch (v.getId()) { + case R.id.login_success_rl: + mExitLoginDialog.show(); + break; + + case R.id.login_tv: + case R.id.user_iv: + if (getUserConstant().isLogin()) { + startActivity(UserInfoActivity.newIntent(this, getUserConstant().getUserData().getData().getId())); + } else { + Intent intent = new Intent(this, LoginActivity.class); + startActivityForResult(intent, Constants.Activity.LoginActivity); + } + break; + case R.id.setting_ll: + mThemeDialog.show(); + break; + case R.id.login_help_ll: + OpenWebViewUtils.loginHelp(this); + break; + default: + if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) { + mDrawerLayout.closeDrawers(); + } else { + mDrawerLayout.openDrawer(Gravity.LEFT); + } + break; + } + } + + /** + * 退出登录 dialog 按钮点击监听事件 + * @param dialog + * @param which + */ + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + switch (which) { + case POSITIVE: + if (getUserConstant().logout()) { //退出登录 + mLoginShowRl.setVisibility(View.GONE); + mLoginTv.setVisibility(View.VISIBLE); + mMainMenuList.removeAll(mMyMenuList); + mMenuAdapter.notifyDataSetChanged(); + mUserImgIv.setImageURI(Uri.parse("res://com.jun.elephant/" + R.color.main_bg)); + dialog.dismiss(); + } + break; + case NEGATIVE: + dialog.dismiss(); + break; + } + } + + /** + * DrawerLayout menu click callback + * @param position menu click position + */ + @Override + public void onMenuItemClick(int position) { + switch (position) { + case 0: + switchFragment(getString(R.string.menu_recommend), Constants.Topic.EXCELLENT); + break; + case 1: + switchFragment(getString(R.string.menu_hot), Constants.Topic.VOTE); + break; + case 2: + switchFragment(getString(R.string.menu_newest), Constants.Topic.NEWEST); + break; + case 3: + switchFragment(getString(R.string.menu_nobody), Constants.Topic.NOBODY); + break; + case 4: + switchFragment(getString(R.string.menu_jobs), Constants.Topic.JOBS); + break; + case 5: + switchFragment(getString(R.string.menu_wiki), Constants.Topic.WIKI); + break; + case 6: + mToolBar.setTitle(getString(R.string.menu_vote)); + mTopicListByMeFragment.TYPE = Constants.User.USER_TOPIC_VOTES; + toUserFragment(); + break; + case 7: + mToolBar.setTitle(getString(R.string.menu_topic)); + mTopicListByMeFragment.TYPE = Constants.User.USER_TOPIC_MY; + toUserFragment(); + break; + case 8: + OpenWebViewUtils.myReply(MainActivity.this, mUserInfoEntity.getData().getLinks().getReplies_web_view()); + break; + } + + if (position != 8) { + mMenuAdapter.setSelectPosition(position); + mMenuAdapter.notifyDataSetChanged(); + mDrawerLayout.closeDrawers(); + } + } + + /** + * toolbar menu click callback + * @param item menu item + * @return true + */ + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_notice: + if (getUserConstant().isLogin()) { + openActivity(UserMessageActivity.class); + } else { + showShortToast(getString(R.string.toast_no_login)); + } + break; + + case R.id.action_about: + OpenWebViewUtils.aboutMe(this); + break; + + case R.id.action_search: + showShortToast(getString(R.string.toast_adorn)); + break; + + case R.id.action_settings: + openActivity(SettingActivity.class); + break; + } + return true; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (resultCode == RESULT_OK && requestCode == Constants.Activity.LoginActivity) { + mUserInfoEntity = data.getExtras().getParcelable(Constants.Key.USER_DATA); + + assert mUserInfoEntity != null; + Uri uri = Uri.parse(mUserInfoEntity.getData().getAvatar()); + mUserImgIv.setImageURI(uri); + mUserEmailTv.setText(mUserInfoEntity.getData().getEmail()); + mNameTv.setText(mUserInfoEntity.getData().getName()); + + getUserConstant().saveUserData(new Gson().toJson(mUserInfoEntity)); + + mLoginShowRl.setVisibility(View.VISIBLE); + mLoginTv.setVisibility(View.GONE); + + mMainMenuList.addAll(mMyMenuList); + mMenuAdapter.notifyDataSetChanged(); + } + } + + /** + * 切换 fragment 操作 + * @param title 标题 + * @param type 类型 + */ + private void switchFragment(String title, String type) { + mCommonFragment.TYPE = type; + mToolBar.setTitle(title); + toCommonFragment(); + } + + /** + * 改变主题 + */ + private void changeTheme() { + refreshUI(); + mCommonFragment.refreshUI(); + mDrawerLayout.closeDrawers(); + } + + /** + * 刷新 UI + * 遍历所有view,替换相对应主题颜色 + */ + private void refreshUI() { + TypedValue themeColor = new TypedValue(); //主题 + TypedValue statusColor = new TypedValue(); //状态栏 + TypedValue toolbarTextColor = new TypedValue(); //状态栏字体颜色 + TypedValue navIcon = new TypedValue(); //toolbar 导航图标 + TypedValue searchIcon = new TypedValue(); //toolbar 搜索图标 + TypedValue noticeIcon = new TypedValue(); //toolbar 通知图标 + TypedValue overFlowIcon = new TypedValue(); //toolbar 更多图标 + + //获取切换后的主题,以及主题相对应对的属性值 + Resources.Theme theme = getTheme(); + theme.resolveAttribute(R.attr.elephantTheme, themeColor, true); + theme.resolveAttribute(R.attr.elephantStatus, statusColor, true); + theme.resolveAttribute(R.attr.elephantToolbarText, toolbarTextColor, true); + theme.resolveAttribute(R.attr.navIcon, navIcon, true); + theme.resolveAttribute(R.attr.menuSearch, searchIcon, true); + theme.resolveAttribute(R.attr.menuNotice, noticeIcon, true); + theme.resolveAttribute(R.attr.overFlowIcon, overFlowIcon, true); + + //切换到主题相对应的图标以及颜色值 + mToolBar.getMenu().findItem(R.id.action_search).setIcon(searchIcon.resourceId); + mToolBar.getMenu().findItem(R.id.action_notice).setIcon(noticeIcon.resourceId); + mToolBar.setNavigationIcon(navIcon.resourceId); + mToolBar.setOverflowIcon(ContextCompat.getDrawable(this, overFlowIcon.resourceId)); + mToolBar.setBackgroundColor(ContextCompat.getColor(this, themeColor.resourceId)); + mToolBar.setTitleTextColor(ContextCompat.getColor(this, toolbarTextColor.resourceId)); + + changeStatusColor(statusColor.resourceId); + } + + /** + * 切换状态栏颜色值 + * @param colorValue 颜色值 + */ + private void changeStatusColor(int colorValue) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Window window = getWindow(); + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(ContextCompat.getColor(this, colorValue)); + } + } + + @Override + public void onChangeTheme(View view) { + switch (view.getId()) { + case R.id.theme_blue: + setTheme(R.style.BlueTheme); //设置主题 + mThemeUtil.setTheme(Constants.Theme.Blue); //保存当前主题 + break; + case R.id.theme_gray: + setTheme(R.style.GrayTheme); + mThemeUtil.setTheme(Constants.Theme.Gray); + break; + case R.id.theme_white: + setTheme(R.style.WhiteTheme); + mThemeUtil.setTheme(Constants.Theme.White); + break; + } + changeTheme(); + } + +} diff --git a/app/src/main/java/com/jun/elephant/ui/main/PhotoShowActivity.java b/app/src/main/java/com/jun/elephant/ui/main/PhotoShowActivity.java new file mode 100644 index 0000000..5264a7f --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/main/PhotoShowActivity.java @@ -0,0 +1,87 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.main; + +import android.graphics.drawable.Animatable; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; + +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder; +import com.facebook.drawee.controller.BaseControllerListener; +import com.facebook.imagepipeline.image.ImageInfo; +import com.jun.elephant.R; +import com.jun.elephant.common.BaseActivity; +import com.jun.elephant.global.Constants; + +import butterknife.Bind; +import butterknife.ButterKnife; +import me.relex.photodraweeview.PhotoDraweeView; + +/** + * Created by Jun on 2016/4/19. + */ +public class PhotoShowActivity extends BaseActivity { + + @Bind(R.id.toolBar) + Toolbar mToolbar; + @Bind(R.id.photo_iv) + PhotoDraweeView mPhotoIv; + + private String mImageUrl; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_show_photo); + ButterKnife.bind(this); + } + + @Override + public void initData() { + super.initData(); + mImageUrl = getIntent().getExtras().getString(Constants.Key.TOPIC_IMAGE_URL); + } + + @Override + public void initView() { + super.initView(); + setToolbar(mToolbar, getString(R.string.app_photo_show)); + } + + @Override + public void initLoad() { + super.initLoad(); + + if (!TextUtils.isEmpty(mImageUrl)) { + PipelineDraweeControllerBuilder controller = Fresco.newDraweeControllerBuilder(); + controller.setUri(mImageUrl); + controller.setOldController(mPhotoIv.getController()); + controller.setControllerListener(new BaseControllerListener() { + @Override + public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) { + super.onFinalImageSet(id, imageInfo, animatable); + if (imageInfo == null || mPhotoIv == null) { + return; + } + mPhotoIv.update(imageInfo.getWidth(), imageInfo.getHeight()); + } + }); + mPhotoIv.setController(controller.build()); + } + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/main/SettingActivity.java b/app/src/main/java/com/jun/elephant/ui/main/SettingActivity.java new file mode 100644 index 0000000..4d3532d --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/main/SettingActivity.java @@ -0,0 +1,154 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.main; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.widget.TextView; + +import com.afollestad.materialdialogs.DialogAction; +import com.afollestad.materialdialogs.MaterialDialog; +import com.jun.elephant.util.FileUtils; +import com.jun.elephant.util.ShareUtil; +import com.jun.elephant.util.SystemUtil; +import com.jun.elephant.Elephant; +import com.jun.elephant.R; +import com.jun.elephant.common.BaseActivity; +import com.jun.elephant.util.OpenWebViewUtils; +import com.jun.elephant.global.Constants; + +import java.io.File; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * Created by Jun on 2016/3/22. + */ +public class SettingActivity extends BaseActivity implements MaterialDialog.SingleButtonCallback { + + @Bind(R.id.toolBar) + Toolbar mToolBar; + @Bind(R.id.version_tv) + TextView mVersionTv; + @Bind(R.id.cache_size_tv) + TextView mCacheSizeTv; + + private MaterialDialog.Builder mDialog; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_setting); + ButterKnife.bind(this); + } + + @Override + public void initData() { + super.initData(); + mDialog = new MaterialDialog.Builder(this); + } + + @Override + public void initView() { + super.initView(); + setToolbar(mToolBar, getString(R.string.app_setting)); + initCleanCacheDialog(); + + mVersionTv.setText(String.valueOf(getString(R.string.app_name) + " " + SystemUtil.getVersionName(this))); + mCacheSizeTv.setText(getCacheSize()); + } + + @OnClick({R.id.feedback_tv, R.id.clear_cache_ll, R.id.about_app_tv, R.id.get_open_source_tv, R.id.about_phphub_tv, R.id.about_me_tv}) + public void onClick(View view) { + switch (view.getId()) { + case R.id.feedback_tv: + ShareUtil.feedback(this, Constants.CONTACT_INFO); + break; + + case R.id.clear_cache_ll: + mDialog.show(); + break; + + case R.id.get_open_source_tv: + OpenWebViewUtils.getOpenSource(this); + break; + + case R.id.about_phphub_tv: + OpenWebViewUtils.aboutPHPHub(this); + break; + + case R.id.about_me_tv: + OpenWebViewUtils.aboutMe(this); + break; + + case R.id.about_app_tv: + openActivity(AboutAppActivity.class); + break; + + default: + finish(); + break; + } + } + + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + switch (which) { + case POSITIVE: + cleanCache(); + break; + case NEGATIVE: + dialog.dismiss(); + break; + } + } + + private void initCleanCacheDialog() { + mDialog.content(getString(R.string.dialog_clean_cache)); + mDialog.negativeText(getString(R.string.dialog_clean_negative_text)); + mDialog.positiveText(getString(R.string.dialog_clean_positive_text)); + mDialog.positiveColorRes(R.color.colorPrimary); + mDialog.negativeColorRes(R.color.colorPrimary); + mDialog.onPositive(this); + } + + /** + * 获取缓存大小 + * @return 缓存大小,例 1.63MB + */ + private String getCacheSize() { + File dirCacheFile = Elephant.getOwnCacheDirectory(this, Elephant.APP_CACHE_PATH); + return FileUtils.getCacheDirSize(dirCacheFile); + } + + /** + * 清空缓存 + */ + private void cleanCache() { + File dirCacheFile = Elephant.getOwnCacheDirectory(this, Elephant.APP_CACHE_PATH); + if (FileUtils.deleteDir(dirCacheFile)) { // 删掉缓存目录所有文件 + mCacheSizeTv.setText(""); + showShortToast(getString(R.string.toast_clean_success)); + } else { + showShortToast(getString(R.string.toast_clean_fail)); + } + } + +} diff --git a/app/src/main/java/com/jun/elephant/ui/main/TopicPreviewActivity.java b/app/src/main/java/com/jun/elephant/ui/main/TopicPreviewActivity.java new file mode 100644 index 0000000..bf2a13b --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/main/TopicPreviewActivity.java @@ -0,0 +1,91 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.main; + +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.webkit.WebView; + +import com.jun.elephant.ui.widget.MultiStateView; +import com.jun.elephant.R; +import com.jun.elephant.common.BaseWebViewActivity; +import com.jun.elephant.global.Constants; +import com.jun.elephant.util.MarkDownRenderer; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Created by Jun on 2016/5/16. + */ +public class TopicPreviewActivity extends BaseWebViewActivity { + @Bind(R.id.webView) + WebView mWebView; + @Bind(R.id.toolBar) + Toolbar mToolBar; + @Bind(R.id.multiStateView) + MultiStateView mMultiStateView; + + private String title; + + private String content; + + private MarkDownRenderer markDownRenderer; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_webview); + ButterKnife.bind(this); + } + + @Override + public void initData() { + super.initData(); + title = getIntent().getExtras().getString(Constants.Key.PREVIEW_TOPIC_TITLE); + content = getIntent().getExtras().getString(Constants.Key.PREVIEW_TOPIC_CONTENT); + + markDownRenderer = new MarkDownRenderer(); + } + + @Override + public void initView() { + super.initView(); + mToolBar.setTitle(title); + mToolBar.setNavigationIcon(R.mipmap.ic_action_return); + } + + @Override + public void initListener() { + super.initListener(); + mToolBar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + mWebView.setWebViewClient(new WebAppClient(this, mMultiStateView, mWebView)); + } + + @Override + public void initLoad() { + super.initLoad(); + String markdownHtml = markDownRenderer.renderMarkdown(content); + mWebView.getSettings().setDefaultTextEncodingName("utf-8"); + mWebView.loadData(markdownHtml, "text/html", "UTF-8"); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/main/WebViewActivity.java b/app/src/main/java/com/jun/elephant/ui/main/WebViewActivity.java new file mode 100644 index 0000000..7b7d145 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/main/WebViewActivity.java @@ -0,0 +1,101 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.main; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.webkit.WebView; + +import com.jun.elephant.ui.widget.MultiStateView; +import com.jun.elephant.R; +import com.jun.elephant.common.BaseWebViewActivity; +import com.jun.elephant.global.Constants; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Created by Jun on 2016/4/18. + */ +public class WebViewActivity extends BaseWebViewActivity { + + @Bind(R.id.toolBar) + Toolbar mToolBar; + @Bind(R.id.webView) + WebView mWebView; + @Bind(R.id.multiStateView) + MultiStateView mMultiStateView; + + private String mWebUrl, mTitle; + + public static Intent newIntent(Context context, String title, String url) { + Intent intent = new Intent(context, WebViewActivity.class); + intent.putExtra(Constants.Key.WEB_TITLE, title); + intent.putExtra(Constants.Key.WEB_URL, url); + return intent; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_webview); + ButterKnife.bind(this); + } + + @Override + public void initData() { + super.initData(); + mWebUrl = getIntent().getStringExtra(Constants.Key.WEB_URL); + mTitle = getIntent().getStringExtra(Constants.Key.WEB_TITLE); + } + + @Override + public void initView() { + super.initView(); + mToolBar.setTitle(mTitle); + } + + @Override + public void initLoad() { + super.initLoad(); + + mWebView.setWebViewClient(new WebAppClient(this, mMultiStateView, mWebView)); + mWebView.loadUrl(mWebUrl, getAuth()); + } + + @Override + public void initListener() { + super.initListener(); + mToolBar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + } + + @Override + public void finish() { + if (mWebView.canGoBack()) { + mWebView.goBack(); + } else { + super.finish(); + } + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsActivity.java b/app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsActivity.java new file mode 100644 index 0000000..e302b17 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsActivity.java @@ -0,0 +1,316 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.topic.details; + +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.view.MenuItem; +import android.view.View; +import android.webkit.WebView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.jun.elephant.R; +import com.jun.elephant.entity.topic.TopicDetailEntity; +import com.jun.elephant.entity.topic.TopicEntity; +import com.jun.elephant.mvpframe.base.BaseFrameWebViewActivity; +import com.jun.elephant.global.Constants; +import com.jun.elephant.ui.topic.reply.TopicCommentListActivity; +import com.jun.elephant.ui.user.info.UserInfoActivity; +import com.jun.elephant.ui.widget.MultiStateView; +import com.jun.elephant.ui.widget.MySimpleDraweeView; +import com.jun.elephant.ui.widget.VoteDialog; +import com.jun.elephant.util.ShareUtil; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * Created by Jun on 2016/3/13. + */ +public class TopicDetailsActivity extends BaseFrameWebViewActivity implements + Toolbar.OnMenuItemClickListener, VoteDialog.OnVoteDialogClickListener, TopicDetailsContract.View { + + @Bind(R.id.toolBar) + Toolbar mToolBar; + @Bind(R.id.webView) + WebView mWebView; + @Bind(R.id.point_tv) + TextView mPointTv; + @Bind(R.id.comment_tv) + TextView mCommentTv; + @Bind(R.id.favorite_tv) + TextView mFavoriteTv; + @Bind(R.id.follow_tv) + TextView mFollowTv; + @Bind(R.id.user_img_iv) + MySimpleDraweeView mUserImgIv; + @Bind(R.id.user_name_tv) + TextView mUserNameTv; + @Bind(R.id.user_desc_tv) + TextView mUserDescTv; + @Bind(R.id.multiStateView) + MultiStateView mMultiStateView; + + private TopicEntity mTopicEntity; + + private VoteDialog mVoteDialog; + + public static Intent newIntent(Context context, TopicEntity topicEntity) { + Intent intent = new Intent(context, TopicDetailsActivity.class); + Bundle bundle = new Bundle(); + bundle.putParcelable(Constants.Key.TOPIC, topicEntity); + intent.putExtras(bundle); + return intent; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_topic_details); + ButterKnife.bind(this); + } + + @Override + public void initData() { + super.initData(); + mTopicEntity = getIntent().getExtras().getParcelable(Constants.Key.TOPIC); + mVoteDialog = new VoteDialog(this); + } + + @Override + public void initView() { + super.initView(); + setToolbar(mToolBar, ""); + mToolBar.inflateMenu(R.menu.menu_topic); + + if (getUserConstant().isLogin()) { + mPresenter.getDetailsInfo(mTopicEntity.getId()); + } else { + initViewDetail(mTopicEntity); + } + + } + + @Override + public void initListener() { + super.initListener(); + mToolBar.setOnMenuItemClickListener(this); + mVoteDialog.setOnVoteDialogClickListener(this); + } + + private void initViewDetail(TopicEntity mTopicEntity) { + RelativeLayout mUserInfoRl = (RelativeLayout) mToolBar.getChildAt(1); + mUserInfoRl.setVisibility(View.VISIBLE); + + mUserImgIv.setImageUrl(mTopicEntity.getUser().getData().getAvatar()); + + if (!TextUtils.isEmpty(mTopicEntity.getUser().getData().getIntroduction())) { + mUserDescTv.setText(mTopicEntity.getUser().getData().getIntroduction()); + mUserDescTv.setVisibility(View.VISIBLE); + } + + mUserNameTv.setText(mTopicEntity.getUser().getData().getName()); + mCommentTv.setText(String.valueOf(mTopicEntity.getReplyCount())); + mPointTv.setText(String.valueOf(mTopicEntity.getVoteCount())); + + mWebView.setWebViewClient(new WebAppClient(this, mMultiStateView, mWebView)); + mWebView.loadUrl(mTopicEntity.getLinks().getDetailsWebView(), getAuth()); + mWebView.addJavascriptInterface(new WebAppInterface(this), PLATFORM); + + if (getUserConstant().isLogin()) { + if (mTopicEntity.isAttention()) { + setDrawableTop(mFollowTv, R.mipmap.ic_follow_selector); + } else { + setDrawableTop(mFollowTv, R.mipmap.ic_follow_normal); + } + + if (mTopicEntity.isFavorite()) { + setDrawableTop(mFavoriteTv, R.mipmap.ic_bookmark_selector); + } else { + setDrawableTop(mFavoriteTv, R.mipmap.ic_bookmark_normal); + } + + if (mTopicEntity.isVoteDown()) { + changeVoteStyle(Constants.TopicOpt.TOPIC_VOTE_DOWN); + } + + if (mTopicEntity.isVoteUp()) { + changeVoteStyle(Constants.TopicOpt.TOPIC_VOTE_UP); + } + } + + } + + @OnClick({R.id.point_tv, R.id.comment_tv, R.id.favorite_tv, R.id.follow_tv, R.id.user_img_iv}) + public void onClick(View view) { + switch (view.getId()) { + case R.id.point_tv: + if (!getUserConstant().isLogin()) { + showShortToast(getString(R.string.toast_no_login)); + return; + } + + mVoteDialog.setTopicEntity(mTopicEntity); + mVoteDialog.show(); + break; + case R.id.comment_tv: + startActivity(TopicCommentListActivity.newIntent(this, mTopicEntity.getLinks().getRepliesWebView(), mTopicEntity.getId())); + break; + case R.id.user_img_iv: + startActivity(UserInfoActivity.newIntent(this, Integer.parseInt(mTopicEntity.getUser().getData().getId()))); + break; + } + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_share: + ShareUtil.share(this, "分享 " + mTopicEntity.getUser().getData().getName() + " 的文章" + mTopicEntity.getTitle() + + " " + mTopicEntity.getLinks().getWebUrl() + "「来自:大象」"); + break; + } + return true; + } + + @Override + public void onVoteDownClick() { + mPresenter.optStatusType(Constants.TopicOpt.TOPIC_VOTE_DOWN, mTopicEntity.getId()); + } + + @Override + public void onVoteUpClick() { + mPresenter.optStatusType(Constants.TopicOpt.TOPIC_VOTE_UP, mTopicEntity.getId()); + } + + @Override + public void optVoteUpSuccess(int voteCount) { + optVote(Constants.TopicOpt.TOPIC_VOTE_UP); + } + + @Override + public void optVoteDownSuccess(int voteCount) { + optVote(Constants.TopicOpt.TOPIC_VOTE_DOWN); + } + + @Override + public void onRequestStart() { + + } + + @Override + public void onRequestEnd() { + + } + + @Override + public void onInternetError() { + super.onInternetError(); + showShortToast(getString(R.string.toast_opt_fail)); + } + + private void optVote(int type) { + int voteCount = mTopicEntity.getVoteCount(); + + switch (type) { + case Constants.TopicOpt.TOPIC_VOTE_DOWN: + if (mTopicEntity.isVoteDown()) { + mTopicEntity.setVoteDown(false); + changeVoteStyle(-1); + voteCount++; + } else { + + if (mTopicEntity.isVoteUp()) { + voteCount -= 2; + } else { + voteCount--; + } + mTopicEntity.setVoteDown(true); + changeVoteStyle(type); + } + mTopicEntity.setVoteUp(false); + break; + case Constants.TopicOpt.TOPIC_VOTE_UP: + if (mTopicEntity.isVoteUp()) { + mTopicEntity.setVoteUp(false); + changeVoteStyle(-1); + voteCount--; + } else { + if (mTopicEntity.isVoteDown()) { + voteCount += 2; + } else { + voteCount++; + } + mTopicEntity.setVoteUp(true); + changeVoteStyle(type); + } + mTopicEntity.setVoteDown(false); + break; + } + + mTopicEntity.setVoteCount(voteCount); + mPointTv.setText(String.valueOf(voteCount)); + + if (mVoteDialog.isShowing()) + mVoteDialog.dismiss(); + } + + private void changeVoteStyle(int type) { + + mPointTv.setBackgroundResource(R.drawable.btn1_selector); + mPointTv.setTextColor(ContextCompat.getColor(this, R.color.text_white)); + + switch (type) { + case Constants.TopicOpt.TOPIC_VOTE_DOWN: + setDrawableLeft(mPointTv, R.mipmap.ic_vote_down); + break; + case Constants.TopicOpt.TOPIC_VOTE_UP: + setDrawableLeft(mPointTv, R.mipmap.ic_vote_up); + break; + default: + setDrawableLeft(mPointTv, R.mipmap.ic_point); + mPointTv.setBackgroundResource(R.drawable.btn1_normal); + mPointTv.setTextColor(ContextCompat.getColor(this, R.color.bg_gray)); + break; + } + + } + + private void setDrawableTop(TextView textView, int resId) { + Drawable drawable = ContextCompat.getDrawable(this, resId); + drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight()); + textView.setCompoundDrawables(null, drawable, null, null); + } + + private void setDrawableLeft(TextView textView, int resId) { + Drawable drawable = ContextCompat.getDrawable(this, resId); + drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight()); + textView.setCompoundDrawables(drawable, null, null, null); + } + + @Override + public void getDetailsInfo(TopicDetailEntity topicDetailEntity) { + mTopicEntity = topicDetailEntity.getData(); + initViewDetail(topicDetailEntity.getData()); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsContract.java b/app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsContract.java new file mode 100644 index 0000000..66e1c74 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsContract.java @@ -0,0 +1,52 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.topic.details; + +import com.google.gson.JsonObject; +import com.jun.elephant.entity.topic.TopicDetailEntity; +import com.jun.elephant.mvpframe.BaseModel; +import com.jun.elephant.mvpframe.BasePresenter; +import com.jun.elephant.mvpframe.BaseView; + +import rx.Observable; + +/** + * Created by Jun on 2016/10/17. + */ +public interface TopicDetailsContract { + interface Model extends BaseModel { + + Observable getDetailsInfo(int topicId); + + Observable voteUp(int topicId); + + Observable voteDown(int topicId); + } + + interface View extends BaseView { + void getDetailsInfo(TopicDetailEntity topicDetailEntity); + + void optVoteUpSuccess(int voteCount); + + void optVoteDownSuccess(int voteCount); + } + + abstract class Presenter extends BasePresenter { + public abstract void getDetailsInfo(int topicId); + + public abstract void optStatusType(int type, int topicId); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsModel.java b/app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsModel.java new file mode 100644 index 0000000..3ca8b85 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsModel.java @@ -0,0 +1,60 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.topic.details; + +import com.google.gson.JsonObject; +import com.jun.elephant.api.Networks; +import com.jun.elephant.entity.topic.TopicDetailEntity; +import com.jun.elephant.mvpframe.rx.RxSchedulers; + +import java.util.HashMap; +import java.util.Map; + +import rx.Observable; + +/** + * Created by Jun on 2016/10/17. + */ +public class TopicDetailsModel implements TopicDetailsContract.Model { + + private Map getOptions() { + Map options = new HashMap<>(); + options.put("include", "user,node"); + options.put("columns", "root(excerpt),user(signature)"); + return options; + } + + @Override + public Observable getDetailsInfo(int topicId) { + return Networks.getInstance().getTopicApi() + .getTopicDetail(topicId, getOptions()) + .compose(RxSchedulers.io_main()); + } + + @Override + public Observable voteUp(int topicId) { + return Networks.getInstance().getTopicApi() + .voteUp(topicId) + .compose(RxSchedulers.io_main()); + } + + @Override + public Observable voteDown(int topicId) { + return Networks.getInstance().getTopicApi() + .voteDown(topicId) + .compose(RxSchedulers.io_main()); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsPresenter.java b/app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsPresenter.java new file mode 100644 index 0000000..fb71893 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/details/TopicDetailsPresenter.java @@ -0,0 +1,107 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.topic.details; + +import com.google.gson.JsonObject; +import com.jun.elephant.entity.topic.TopicDetailEntity; +import com.jun.elephant.global.Constants; + +import rx.Observer; + +/** + * Created by Jun on 2016/10/17. + */ +public class TopicDetailsPresenter extends TopicDetailsContract.Presenter { + + private Observer mTopicDetailObserver = new Observer() { + + @Override + public void onCompleted() { + mView.onRequestEnd(); + } + + @Override + public void onError(Throwable e) { + mView.onRequestError(e.toString()); + mView.onInternetError(); + } + + @Override + public void onNext(TopicDetailEntity topicDetailEntity) { + mView.getDetailsInfo(topicDetailEntity); + } + }; + + private Observer mVoteUpObserver = new Observer() { + + @Override + public void onCompleted() { + mView.onRequestEnd(); + } + + @Override + public void onError(Throwable e) { + mView.onRequestError(e.toString()); + mView.onInternetError(); + } + + @Override + public void onNext(JsonObject jsonObject) { + boolean status = jsonObject.get("vote-up").getAsBoolean(); + int voteCount = jsonObject.get("vote_count").getAsInt(); + if (status) mView.optVoteUpSuccess(voteCount); + } + }; + + private Observer mVoteDownObserver = new Observer() { + + @Override + public void onCompleted() { + mView.onRequestEnd(); + } + + @Override + public void onError(Throwable e) { + mView.onRequestError(e.toString()); + mView.onInternetError(); + } + + @Override + public void onNext(JsonObject jsonObject) { + boolean status = jsonObject.get("vote-down").getAsBoolean(); + int voteCount = jsonObject.get("vote_count").getAsInt(); + if (status) mView.optVoteDownSuccess(voteCount); + } + }; + + @Override + public void getDetailsInfo(int topicId) { + mRxManager.add(mModel.getDetailsInfo(topicId).subscribe(mTopicDetailObserver)); + } + + @Override + public void optStatusType(int type, int topicId) { + switch (type) { + case Constants.TopicOpt.TOPIC_VOTE_DOWN: + mRxManager.add(mModel.voteDown(topicId).subscribe(mVoteDownObserver)); + break; + + case Constants.TopicOpt.TOPIC_VOTE_UP: + mRxManager.add(mModel.voteUp(topicId).subscribe(mVoteUpObserver)); + break; + } + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/list/TopicContract.java b/app/src/main/java/com/jun/elephant/ui/topic/list/TopicContract.java new file mode 100644 index 0000000..ce844a2 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/list/TopicContract.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.topic.list; + +import com.jun.elephant.entity.TokenEntity; +import com.jun.elephant.entity.topic.TopicListEntity; +import com.jun.elephant.mvpframe.BaseModel; +import com.jun.elephant.mvpframe.BasePresenter; +import com.jun.elephant.mvpframe.BaseView; + +import rx.Observable; + +/** + * Created by Jun on 2016/10/14. + */ + +public interface TopicContract { + + interface Model extends BaseModel { + + Observable getTokenByForum(); + + Observable getTopicByForum(String type, int pageIndex); + + Observable getUserTopicPraise(int userId, int pageIndex); + + Observable getUserTopicShare(int userId, int pageIndex); + + Observable getUserFollowUser(int userId, int pageIndex); + } + + interface View extends BaseView { + void refreshTopicList(TopicListEntity topicListEntity); + + void loadMoreTopicList(TopicListEntity topicListEntity); + } + + abstract class Presenter extends BasePresenter { + + abstract void getTopicListByUser(int type, int userId, int pageIndex); + + abstract void getTopicListByForum(String type, int pageIndex); + + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/list/TopicListByForumFragment.java b/app/src/main/java/com/jun/elephant/ui/topic/list/TopicListByForumFragment.java new file mode 100644 index 0000000..9f038f1 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/list/TopicListByForumFragment.java @@ -0,0 +1,234 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.topic.list; + +import android.content.res.Resources; +import android.os.Bundle; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; + +import com.jun.elephant.R; +import com.jun.elephant.entity.topic.TopicEntity; +import com.jun.elephant.entity.topic.TopicListEntity; +import com.jun.elephant.mvpframe.base.BaseFrameFragment; +import com.jun.elephant.global.Constants; +import com.jun.elephant.ui.topic.publish.TopicPublishActivity; +import com.jun.elephant.ui.adapter.TopicListAdapter; +import com.jun.elephant.ui.widget.MultiStateView; +import com.jun.elephant.ui.widget.MySwipeRefreshLayout; +import com.melnykov.fab.FloatingActionButton; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import cn.bingoogolapple.badgeview.BGABadgeRelativeLayout; +import in.srain.cube.views.ptr.PtrDefaultHandler2; +import in.srain.cube.views.ptr.PtrFrameLayout; + +/** + * Created by Jun on 2016/7/9. + */ +public class TopicListByForumFragment extends BaseFrameFragment implements TopicContract.View { + + @Bind(R.id.recyclerView) + RecyclerView mRecyclerView; + @Bind(R.id.swipe_layout) + MySwipeRefreshLayout mSwipeLayout; + @Bind(R.id.multiStateView) + MultiStateView mMultiStateView; + @Bind(R.id.fab) + FloatingActionButton mFabBtn; + + private TopicListAdapter mAdapter; + + private List mTopicList; + + public String TYPE = Constants.Topic.EXCELLENT; + + private int mPageIndex = 1; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_topic_list); + ButterKnife.bind(this, getContentView()); + } + + @Override + public void initData() { + super.initData(); + mTopicList = new ArrayList<>(); + mAdapter = new TopicListAdapter(getContext(), mTopicList); + } + + @Override + public void initView() { + super.initView(); + mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + mRecyclerView.setAdapter(mAdapter); + } + + @Override + public void initListener() { + super.initListener(); + mFabBtn.attachToRecyclerView(mRecyclerView); + + mSwipeLayout.setPtrHandler(new PtrDefaultHandler2() { + @Override + public void onLoadMoreBegin(PtrFrameLayout ptrFrameLayout) { + mPageIndex++; + mPresenter.getTopicListByForum(TYPE, mPageIndex); + } + + @Override + public void onRefreshBegin(PtrFrameLayout ptrFrameLayout) { + mPageIndex = 1; + mPresenter.getTopicListByForum(TYPE, mPageIndex); + } + }); + + mFabBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (getUserConstant().isLogin()) { + openActivity(TopicPublishActivity.class); + } else { + showShortToast("先登录..."); + } + } + }); + } + + @Override + public void initLoad() { + super.initLoad(); +// mMultiStateView.setViewState(MultiStateView.VIEW_STATE_LOADING); + + getData(); + } + + public void getData() { + int state = mMultiStateView.getViewState(); + if (state == MultiStateView.VIEW_STATE_EMPTY || state == MultiStateView.VIEW_STATE_ERROR) { + mPageIndex = 1; + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_LOADING); + mPresenter.getTopicListByForum(TYPE, mPageIndex); + return; + } + mSwipeLayout.post(new Runnable() { + @Override + public void run() { + mSwipeLayout.autoRefresh(); + } + }); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + ButterKnife.unbind(this); + } + + @Override + public void refreshTopicList(TopicListEntity topicListEntity) { + if (topicListEntity.getData().size() == 0) { + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_EMPTY); + return; + } + + mTopicList.clear(); + mTopicList.addAll(topicListEntity.getData()); + mAdapter.notifyDataSetChanged(); + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_CONTENT); + } + + @Override + public void loadMoreTopicList(final TopicListEntity topicListEntity) { + List temp = topicListEntity.getData(); + if (temp.size() != 0) { + mTopicList.addAll(temp); + mAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onRequestStart() { + + } + + @Override + public void onRequestEnd() { + mSwipeLayout.refreshComplete(); + } + + @Override + public void onRequestError(String msg) { + super.onRequestError(msg); + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_ERROR); + } + + public void refreshUI() { + TypedValue badgeColor = new TypedValue(); + TypedValue floatBtnColor = new TypedValue(); + Resources.Theme theme = getContext().getTheme(); + theme.resolveAttribute(R.attr.elephantBadge, badgeColor, true); + theme.resolveAttribute(R.attr.elephantFloatBtn, floatBtnColor, true); + + int childCount = mRecyclerView.getChildCount(); + for (int childIndex = 0; childIndex < childCount; childIndex++) { + ViewGroup childView = (ViewGroup) mRecyclerView.getChildAt(childIndex); + BGABadgeRelativeLayout bgarl = (BGABadgeRelativeLayout) childView.findViewById(R.id.bgaRl); + bgarl.getBadgeViewHelper().setBadgeBgColorInt(ContextCompat.getColor(getContext(), badgeColor.resourceId)); + + } + + mFabBtn.setColorNormalResId(floatBtnColor.resourceId); + mFabBtn.setColorPressedResId(floatBtnColor.resourceId); + + //让 RecyclerView 缓存在 Pool 中的 Item 失效 + //那么,如果是ListView,要怎么做呢?这里的思路是通过反射拿到 AbsListView 类中的 RecycleBin 对象,然后同样再用反射去调用 clear 方法 + Class recyclerViewClass = RecyclerView.class; + try { + Field declaredField = recyclerViewClass.getDeclaredField("mRecycler"); + declaredField.setAccessible(true); + Method declaredMethod = Class.forName(RecyclerView.Recycler.class.getName()).getDeclaredMethod("clear", (Class[]) new Class[0]); + declaredMethod.setAccessible(true); + declaredMethod.invoke(declaredField.get(mRecyclerView), new Object[0]); + RecyclerView.RecycledViewPool recycledViewPool = mRecyclerView.getRecycledViewPool(); + recycledViewPool.clear(); + + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/list/TopicListByMeFragment.java b/app/src/main/java/com/jun/elephant/ui/topic/list/TopicListByMeFragment.java new file mode 100644 index 0000000..945c6ff --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/list/TopicListByMeFragment.java @@ -0,0 +1,175 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.topic.list; + +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +import com.jun.elephant.R; +import com.jun.elephant.entity.topic.TopicEntity; +import com.jun.elephant.entity.topic.TopicListEntity; +import com.jun.elephant.global.Constants; +import com.jun.elephant.mvpframe.base.BaseFrameFragment; +import com.jun.elephant.ui.adapter.TopicListAdapter; +import com.jun.elephant.ui.topic.publish.TopicPublishActivity; +import com.jun.elephant.ui.widget.MultiStateView; +import com.jun.elephant.ui.widget.MySwipeRefreshLayout; +import com.melnykov.fab.FloatingActionButton; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import in.srain.cube.views.ptr.PtrDefaultHandler2; +import in.srain.cube.views.ptr.PtrFrameLayout; + +/** + * Created by Jun on 2016/4/18. + */ +public class TopicListByMeFragment extends BaseFrameFragment implements TopicContract.View { + + @Bind(R.id.recyclerView) + RecyclerView mRecyclerView; + @Bind(R.id.swipe_layout) + MySwipeRefreshLayout mSwipeLayout; + @Bind(R.id.multiStateView) + MultiStateView mMultiStateView; + @Bind(R.id.fab) + FloatingActionButton mFabBtn; + + private List mTopicList; + + public int TYPE = Constants.User.USER_TOPIC_MY; + + private int mUserId; + + private TopicListAdapter mAdapter; + + private int mPageIndex = 1; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_topic_list); + ButterKnife.bind(this, getContentView()); + } + + @Override + public void initData() { + super.initData(); + mTopicList = new ArrayList<>(); + mAdapter = new TopicListAdapter(getContext(), mTopicList); + mUserId = getUserConstant().getUserData().getData().getId(); + } + + @Override + public void initView() { + super.initView(); + mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + mRecyclerView.setAdapter(mAdapter); + } + + @Override + public void initListener() { + super.initListener(); + mFabBtn.attachToRecyclerView(mRecyclerView); + + mSwipeLayout.setPtrHandler(new PtrDefaultHandler2() { + @Override + public void onLoadMoreBegin(PtrFrameLayout ptrFrameLayout) { + mPageIndex ++; + mPresenter.getTopicListByUser(TYPE, mUserId, mPageIndex); + } + + @Override + public void onRefreshBegin(PtrFrameLayout ptrFrameLayout) { + mPageIndex = 1; + mPresenter.getTopicListByUser(TYPE, mUserId, mPageIndex); + } + }); + + mFabBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + openActivity(TopicPublishActivity.class); + } + }); + } + + @Override + public void initLoad() { + super.initLoad(); + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_LOADING); + getData(); + } + + public void getData() { + int state = mMultiStateView.getViewState(); + if (state == MultiStateView.VIEW_STATE_EMPTY || state == MultiStateView.VIEW_STATE_ERROR) { + mPageIndex = 1; + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_LOADING); + mPresenter.getTopicListByUser(TYPE, mUserId, mPageIndex); + return; + } + mSwipeLayout.post(new Runnable() { + @Override + public void run() { + mSwipeLayout.autoRefresh(); + } + }); + } + + @Override + public void refreshTopicList(TopicListEntity topicListEntity) { + if (topicListEntity.getData().size() == 0) { + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_EMPTY); + return; + } + + mTopicList.clear(); + mTopicList.addAll(topicListEntity.getData()); + mAdapter.notifyDataSetChanged(); + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_CONTENT); + } + + @Override + public void loadMoreTopicList(TopicListEntity topicListEntity) { + List temp = topicListEntity.getData(); + if (temp.size() != 0) { + mTopicList.addAll(temp); + mAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onRequestStart() { + + } + + @Override + public void onRequestEnd() { + if (mSwipeLayout != null) mSwipeLayout.refreshComplete(); + } + + @Override + public void onRequestError(String msg) { + super.onRequestError(msg); + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_ERROR); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/list/TopicListByUserFragment.java b/app/src/main/java/com/jun/elephant/ui/topic/list/TopicListByUserFragment.java new file mode 100644 index 0000000..cf9f116 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/list/TopicListByUserFragment.java @@ -0,0 +1,173 @@ +package com.jun.elephant.ui.topic.list; + +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; + +import com.jun.elephant.R; +import com.jun.elephant.entity.topic.TopicEntity; +import com.jun.elephant.entity.topic.TopicListEntity; +import com.jun.elephant.global.Constants; +import com.jun.elephant.mvpframe.base.BaseFrameFragment; +import com.jun.elephant.ui.adapter.TopicListAdapter; +import com.jun.elephant.ui.widget.MultiStateView; +import com.jun.elephant.ui.widget.MySwipeRefreshLayout; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import in.srain.cube.views.ptr.PtrDefaultHandler2; +import in.srain.cube.views.ptr.PtrFrameLayout; + +/** + * Created by Jun on 2016/3/18. + */ +public class TopicListByUserFragment extends BaseFrameFragment implements TopicContract.View { + + @Bind(R.id.recyclerView) + RecyclerView mRecyclerView; + @Bind(R.id.multiStateView) + MultiStateView mMultiStateView; + @Bind(R.id.swipe_layout) + MySwipeRefreshLayout mSwipeLayout; + + private TopicListAdapter mAdapter; + + private List mTopicList; + + private int mUserId, mType; + + private int mPageIndex = 1; + + /** + * + * @param userId 用户 Id + * @param type 用户分享、赞过的话题 + * @return bundle + */ + public static Bundle newBundle(int userId, int type) { + Bundle bundle = new Bundle(); + bundle.putInt(Constants.Key.USER_ID, userId); + bundle.putInt(Constants.Key.TOPIC_TYPE, type); + return bundle; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.common_topic_list); + ButterKnife.bind(this, getContentView()); + } + + @Override + public void initData() { + super.initData(); + mUserId = getArguments().getInt(Constants.Key.USER_ID); + mType = getArguments().getInt(Constants.Key.TOPIC_TYPE); + + mTopicList = new ArrayList<>(); + mAdapter = new TopicListAdapter(getContext(), mTopicList); + } + + @Override + public void initView() { + super.initView(); + mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + mRecyclerView.setAdapter(mAdapter); + mSwipeLayout.setMode(PtrFrameLayout.Mode.LOAD_MORE); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + ButterKnife.unbind(this); + } + + @Override + public void initListener() { + super.initListener(); + + mSwipeLayout.setPtrHandler(new PtrDefaultHandler2() { + @Override + public void onLoadMoreBegin(PtrFrameLayout ptrFrameLayout) { + mPageIndex ++; + mPresenter.getTopicListByUser(mType, mUserId, mPageIndex); + + } + + @Override + public void onRefreshBegin(PtrFrameLayout ptrFrameLayout) { + mPageIndex = 1; + mPresenter.getTopicListByUser(mType, mUserId, mPageIndex); + } + }); + + } + + @Override + public void initLoad() { + super.initLoad(); + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_LOADING); + + getData(); + } + + public void getData() { + mSwipeLayout.post(new Runnable() { + @Override + public void run() { + mSwipeLayout.autoRefresh(); + } + }); + } + + @Override + public void refreshTopicList(TopicListEntity topicListEntity) { + if (mRecyclerView == null) + return; + + if (topicListEntity.getData().size() == 0) { + mSwipeLayout.refreshComplete(); + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_EMPTY); + return; + } + + mTopicList.clear(); + mTopicList.addAll(topicListEntity.getData()); + mRecyclerView.setAdapter(mAdapter); + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_CONTENT); + } + + @Override + public void loadMoreTopicList(TopicListEntity topicListEntity) { + List temp = topicListEntity.getData(); + if (temp.size() != 0) { + mTopicList.addAll(temp); + mAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onRequestStart() { + + } + + @Override + public void onRequestEnd() { + if (mSwipeLayout != null) mSwipeLayout.refreshComplete(); + } + + @Override + public void onInternetError() { + super.onInternetError(); + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_ERROR); + } + + @Override + public void onRequestError(String msg) { + super.onRequestError(msg); + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_ERROR); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/list/TopicModel.java b/app/src/main/java/com/jun/elephant/ui/topic/list/TopicModel.java new file mode 100644 index 0000000..aeb25ea --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/list/TopicModel.java @@ -0,0 +1,121 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.topic.list; + +import com.jun.elephant.BuildConfig; +import com.jun.elephant.api.Networks; +import com.jun.elephant.entity.TokenEntity; +import com.jun.elephant.entity.topic.TopicListEntity; +import com.jun.elephant.mvpframe.rx.RxSchedulers; +import com.jun.elephant.global.Constants; + +import java.util.HashMap; +import java.util.Map; + +import rx.Observable; + +/** + * Created by Jun on 2016/10/14. + */ +public class TopicModel implements TopicContract.Model { + + @Override + public Observable getTokenByForum() { + return Networks.getInstance().getTokenApi().getToken( + Constants.Token.AUTH_TYPE_GUEST, + BuildConfig.CLIENT_ID, + BuildConfig.CLIENT_SECRET) + .compose(RxSchedulers.io_main()); + } + + @Override + public Observable getTopicByForum(String type, int pageIndex) { + return Networks.getInstance().getTopicApi() + .getTopics(getOptionsByForum(type, pageIndex)) + .compose(RxSchedulers.io_main()); + } + + /** + * 获取用户点赞话题列表 + * @param userId + * @param pageIndex + * @return + */ + @Override + public Observable getUserTopicPraise(int userId, int pageIndex) { + return Networks.getInstance().getUserApi() + .getVotes(userId, getOptionsByUser(pageIndex)) + .compose(RxSchedulers.io_main()); + } + + /** + * 获取用户分享话题列表 + * @param userId + * @param pageIndex + * @return + */ + @Override + public Observable getUserTopicShare(int userId, int pageIndex) { + return Networks.getInstance().getUserApi() + .getTopics(userId, getOptionsByUser(pageIndex)) + .compose(RxSchedulers.io_main()); + } + + /** + * 获取用户关注的人列表,备注:这个是预留接口,后台未提供相关接口 + * @param userId + * @param pageIndex + * @return + */ + @Override + public Observable getUserFollowUser(int userId, int pageIndex) { + return Networks.getInstance().getUserApi() + .getAttentions(userId) + .compose(RxSchedulers.io_main()); + } + + + /** + * 获取用户话题列表相关配置 + * @param pageIndex + * @return + */ + private Map getOptionsByUser(int pageIndex) { + Map options = new HashMap<>(); + options.put("include", "category,user,node,last_reply_user"); + options.put("per_page", String.valueOf(Constants.PER_PAGE)); + options.put("page", String.valueOf(pageIndex)); + + return options; + } + + /** + * 论坛话题列表相关设置 + * @param filters + * @param pageIndex + * @return + */ + private Map getOptionsByForum(String filters, int pageIndex) { + Map options = new HashMap(); + options.put("include", "category,user,node,last_reply_user"); + options.put("per_page", String.valueOf(Constants.PER_PAGE)); + options.put("filters", filters); + options.put("page", String.valueOf(pageIndex)); + options.put("columns", "user(signature)"); + + return options; + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/list/TopicPresenter.java b/app/src/main/java/com/jun/elephant/ui/topic/list/TopicPresenter.java new file mode 100644 index 0000000..12737cd --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/list/TopicPresenter.java @@ -0,0 +1,125 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.topic.list; + +import android.text.TextUtils; + +import com.jun.elephant.Elephant; +import com.jun.elephant.api.Networks; +import com.jun.elephant.entity.TokenEntity; +import com.jun.elephant.entity.topic.TopicListEntity; +import com.jun.elephant.global.Constants; +import com.jun.elephant.util.JLog; +import com.jun.elephant.util.SharePreferencesHelper; + +import rx.Observable; +import rx.Observer; +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action1; +import rx.functions.Func1; +import rx.schedulers.Schedulers; + +/** + * Created by Jun on 2016/10/15. + */ +public class TopicPresenter extends TopicContract.Presenter { + + public Observer getTopicListObserver(final int pageIndex) { + return new Observer() { + @Override + public void onCompleted() { + mView.onRequestEnd(); + } + + @Override + public void onError(Throwable e) { + mView.onRequestError(e.toString()); + mView.onInternetError(); + } + + @Override + public void onNext(TopicListEntity topicListEntity) { + if (pageIndex == 1) { + mView.refreshTopicList(topicListEntity); + } else { + mView.loadMoreTopicList(topicListEntity); + } + } + }; + } + + @Override + void getTopicListByUser(int type, int userId, int pageIndex) { + switch (type) { + case Constants.User.USER_TOPIC_VOTES: + mRxManager.add(mModel.getUserTopicPraise(userId, pageIndex) + .subscribe(getTopicListObserver(pageIndex))); + break; + case Constants.User.USER_TOPIC_FOLLOW: + mRxManager.add(mModel.getUserFollowUser(userId, pageIndex) + .subscribe(getTopicListObserver(pageIndex))); + break; + case Constants.User.USER_TOPIC_MY: + mRxManager.add(mModel.getUserTopicShare(userId, pageIndex) + .subscribe(getTopicListObserver(pageIndex))); + break; + } + } + + @Override + void getTopicListByForum(final String type, final int pageIndex) { + mRxManager.add(Observable.just(null) + .flatMap(new Func1>() { + @Override + public Observable call(Object o) { + return TextUtils.isEmpty(Networks.getToken()) + ? Observable.error(new NullPointerException("Token is null")) + : mModel.getTopicByForum(type, pageIndex); + } + }) + .retryWhen(new Func1, Observable>() { + @Override + public Observable call(Observable observable) { + return observable.flatMap(new Func1>() { + @Override + public Observable call(Throwable throwable) { + if (throwable instanceof NullPointerException || + throwable instanceof IllegalArgumentException) { + return mModel.getTokenByForum() + .doOnNext(new Action1() { + @Override + public void call(TokenEntity tokenEntity) { + Networks.setToken(tokenEntity.getAccess_token()); + JLog.logd("Token ==== ", tokenEntity.getAccess_token()); + SharePreferencesHelper.getInstance(Elephant.applicationContext) + .putString(Constants.Key.TOKEN, tokenEntity.getAccess_token()); + } + + }); + } + + return Observable.just(throwable); + + } + }); + + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(getTopicListObserver(pageIndex))); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishActivity.java b/app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishActivity.java new file mode 100644 index 0000000..ae26a02 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishActivity.java @@ -0,0 +1,283 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.topic.publish; + +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.text.Editable; +import android.text.TextUtils; +import android.view.MenuItem; +import android.view.View; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.afollestad.materialdialogs.MaterialDialog; +import com.jun.elephant.R; +import com.jun.elephant.entity.topic.CategoryEntity; +import com.jun.elephant.entity.topic.TopicEntity; +import com.jun.elephant.mvpframe.base.BaseFrameActivity; +import com.jun.elephant.global.Constants; +import com.jun.elephant.ui.main.TopicPreviewActivity; +import com.jun.elephant.ui.topic.details.TopicDetailsActivity; +import com.jun.elephant.util.JLog; + +import org.gemini.markdown.util.MarkdownUtil; +import org.gemini.markdown.view.MarkdownEditText; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * Created by Jun on 2016/8/24. + */ +public class TopicPublishActivity extends BaseFrameActivity + implements Toolbar.OnMenuItemClickListener, TopicPublishContract.View { + + @Bind(R.id.toolBar) + Toolbar mToolBar; + @Bind(R.id.topic_title_edt) + EditText mTopicTitleEdt; + @Bind(R.id.topic_node_tv) + TextView mTopicNodeTv; + @Bind(R.id.topic_content_edt) + MarkdownEditText mTopicContentEdt; + @Bind(R.id.edit_opt_ll) + LinearLayout mEditOptLl; + + private MaterialDialog.Builder mNodeListDialog; + private MaterialDialog mLoadingDialog; + + private MarkdownUtil mMarkdownUtil; + + private List mCategory; + + private String nodeId; + + private List mCategoryList; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_topic_publish); + ButterKnife.bind(this); + } + + @Override + public void initData() { + super.initData(); + mMarkdownUtil = new MarkdownUtil(mTopicContentEdt); + + mCategory = new ArrayList<>(); + } + + @Override + public void initView() { + super.initView(); + setToolbar(mToolBar, getString(R.string.app_publish)); + mToolBar.inflateMenu(R.menu.menu_publish); + + mLoadingDialog = getLoadingDialog().content("正在发布...").build(); + } + + private void initListDialog() { + + if (mCategoryList == null) + return; + + mNodeListDialog = new MaterialDialog.Builder(this); + mNodeListDialog.title("请选择节点"); + mNodeListDialog.titleColorRes(R.color.text_common); + mNodeListDialog.positiveColorRes(R.color.colorPrimary); + mNodeListDialog.positiveText("再看看"); + + if (!mCategoryList.isEmpty()) { + mNodeListDialog.items(mCategoryList); + } + + mNodeListDialog.itemsCallback(new MaterialDialog.ListCallback() { + @Override + public void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) { + JLog.logd("node click position", " ===" + which); + nodeId = String.valueOf(mCategory.get(which).getId()); + mTopicNodeTv.setText(text); + } + }); + + mNodeListDialog.show(); + } + + @Override + public void initListener() { + super.initListener(); + mToolBar.setOnMenuItemClickListener(this); + + mTopicContentEdt.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus) { + mEditOptLl.setVisibility(View.VISIBLE); + } else { + mEditOptLl.setVisibility(View.GONE); + } + } + }); + } + + @OnClick({R.id.topic_node_tv, + R.id.edit_bold_ib, + R.id.edit_code_ib, + R.id.edit_img_ib, + R.id.edit_italic_ib, + R.id.edit_line_ib, + R.id.edit_link_ib, + R.id.edit_list_ib, + R.id.edit_quote_ib, + R.id.edit_title_ib + }) + @Override + public void onClick(View v) { + super.onClick(v); + Editable e = mTopicContentEdt.getText(); + switch (v.getId()) { + case R.id.topic_node_tv: +// mNodeListDialog.show(); + initListDialog(); + break; + case R.id.edit_bold_ib: + mMarkdownUtil.insertStrong(e); + break; + case R.id.edit_code_ib: + mMarkdownUtil.insertCodeBlock(e); + break; + case R.id.edit_img_ib: + mMarkdownUtil.insertImage(e); + break; + case R.id.edit_italic_ib: + mMarkdownUtil.insertItalic(e); + break; + case R.id.edit_line_ib: + mMarkdownUtil.insertHorizontalLine(e); + break; + case R.id.edit_link_ib: + mMarkdownUtil.insertLink(e); + break; + case R.id.edit_list_ib: + mMarkdownUtil.insertList(e); + break; + case R.id.edit_quote_ib: + mMarkdownUtil.insertBlockquotes(e); + break; + case R.id.edit_title_ib: + mMarkdownUtil.insertTitle(e); + break; + default: + finish(); + break; + } + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_publish: + publishTopic(); + break; + case R.id.action_preview: +// FileUtil.saveNote(elephantApplication.getTopicDir(),mTopicContentEdt.getText().toString(), +// mTopicTitleEdt.getText().toString()); + Bundle bundle = new Bundle(); + bundle.putString(Constants.Key.PREVIEW_TOPIC_TITLE, mTopicTitleEdt.getText().toString()); + bundle.putString(Constants.Key.PREVIEW_TOPIC_CONTENT, mTopicContentEdt.getText().toString().replace("\n-", "\n\n-")); + openActivity(TopicPreviewActivity.class, bundle); + break; + } + return true; + } + + @Override + public void initLoad() { + super.initLoad(); + mPresenter.getCategory(); + } + + public void publishTopic() { + String title = mTopicTitleEdt.getText().toString(); + String body = mTopicContentEdt.getText().toString(); + if (TextUtils.isEmpty(title)) { + showShortToast("请填写标题"); + return; + } + + if (TextUtils.isEmpty(body)) { + showShortToast("请输入内容"); + return; + } + + if (TextUtils.isEmpty(nodeId)) { + showShortToast("请选择发布类型"); + return; + } + + mPresenter.publishTopic(title, body, nodeId); + + } + + @Override + public void getCategory(CategoryEntity categoryEntity) { + this.mCategory = categoryEntity.getData(); + + if (!mCategory.isEmpty()) { + mCategoryList = new ArrayList<>(); + for (int i = 0; i < mCategory.size(); i++) { + mCategoryList.add(i, mCategory.get(i).getName()); + } + } + } + + @Override + public void publishTopicSuccess(TopicEntity topicEntity) { + if (topicEntity != null) { + + Bundle bundle = new Bundle(); + bundle.putParcelable(Constants.Key.TOPIC, topicEntity); + openActivity(TopicDetailsActivity.class, bundle); + + finish(); + } + } + + @Override + public void onRequestStart() { + mLoadingDialog.show(); + } + + @Override + public void onRequestEnd() { + mLoadingDialog.dismiss(); + } + + @Override + public void onRequestError(String msg) { + super.onRequestError(msg); + JLog.e("publish fail msg === ", msg); + showShortToast(getString(R.string.toast_publish_fail)); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishContract.java b/app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishContract.java new file mode 100644 index 0000000..3affde9 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishContract.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.topic.publish; + +import com.jun.elephant.entity.topic.CategoryEntity; +import com.jun.elephant.entity.topic.TopicDetailEntity; +import com.jun.elephant.entity.topic.TopicEntity; +import com.jun.elephant.mvpframe.BaseModel; +import com.jun.elephant.mvpframe.BasePresenter; +import com.jun.elephant.mvpframe.BaseView; + +import rx.Observable; + +/** + * Created by Jun on 2016/10/15. + */ +public interface TopicPublishContract { + + interface Model extends BaseModel { + Observable getCategories(); + + Observable publishTopic(String title, String body, String categoryId); + } + + interface View extends BaseView { + + void getCategory(CategoryEntity categoryEntity); + + void publishTopicSuccess(TopicEntity topicEntity); + + } + + abstract class Presenter extends BasePresenter { + + public abstract void getCategory(); + + public abstract void publishTopic(String title, String body, String categoryId); + + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishModel.java b/app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishModel.java new file mode 100644 index 0000000..947e0cf --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishModel.java @@ -0,0 +1,51 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.topic.publish; + +import com.jun.elephant.api.Networks; +import com.jun.elephant.entity.topic.CategoryEntity; +import com.jun.elephant.entity.topic.TopicDetailEntity; +import com.jun.elephant.mvpframe.rx.RxSchedulers; + +import java.util.HashMap; +import java.util.Map; + +import rx.Observable; + +/** + * Created by Jun on 2016/10/15. + */ +public class TopicPublishModel implements TopicPublishContract.Model { + + @Override + public Observable getCategories() { + return Networks.getInstance().getTopicApi() + .getCategories() + .compose(RxSchedulers.io_main()); + } + + @Override + public Observable publishTopic(String title, String body, String categoryId) { + Map options = new HashMap<>(); + options.put("title", title); + options.put("body", body); + options.put("node_id", categoryId); + + return Networks.getInstance().getTopicApi() + .publishTopic(options) + .compose(RxSchedulers.io_main()); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishPresenter.java b/app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishPresenter.java new file mode 100644 index 0000000..7f3733b --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/publish/TopicPublishPresenter.java @@ -0,0 +1,74 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.topic.publish; + +import com.jun.elephant.entity.topic.CategoryEntity; +import com.jun.elephant.entity.topic.TopicDetailEntity; + +import rx.Observer; + +/** + * Created by Jun on 2016/10/19. + */ +public class TopicPublishPresenter extends TopicPublishContract.Presenter { + + private Observer mCategoryObserver = new Observer() { + @Override + public void onCompleted() { + mView.onRequestEnd(); + } + + @Override + public void onError(Throwable e) { + mView.onRequestError(e.toString()); + mView.onInternetError(); + } + + @Override + public void onNext(CategoryEntity categoryEntity) { + mView.getCategory(categoryEntity); + } + }; + + private Observer mTopicDetailObserver = new Observer() { + @Override + public void onCompleted() { + mView.onRequestEnd(); + } + + @Override + public void onError(Throwable e) { + mView.onRequestError(e.toString()); + mView.onInternetError(); + } + + @Override + public void onNext(TopicDetailEntity topicDetailEntity) { + mView.publishTopicSuccess(topicDetailEntity.getData()); + } + }; + + @Override + public void getCategory() { + mRxManager.add(mModel.getCategories().subscribe(mCategoryObserver)); + } + + @Override + public void publishTopic(String title, String body, String categoryId) { + mRxManager.add(mModel.publishTopic(title, body, categoryId) + .subscribe(mTopicDetailObserver)); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/reply/TopicCommentListActivity.java b/app/src/main/java/com/jun/elephant/ui/topic/reply/TopicCommentListActivity.java new file mode 100644 index 0000000..4e3c55e --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/reply/TopicCommentListActivity.java @@ -0,0 +1,128 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.topic.reply; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.widget.AppCompatEditText; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.view.View; +import android.webkit.WebView; +import android.widget.LinearLayout; + +import com.jun.elephant.R; +import com.jun.elephant.entity.topic.TopicReplyEntity; +import com.jun.elephant.mvpframe.base.BaseFrameWebViewActivity; +import com.jun.elephant.global.Constants; +import com.jun.elephant.ui.widget.MultiStateView; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * Created by Jun on 2016/3/14. + */ +public class TopicCommentListActivity extends BaseFrameWebViewActivity + implements TopicReplyContract.View { + @Bind(R.id.toolBar) + Toolbar mToolBar; + @Bind(R.id.multiStateView) + MultiStateView mMultiStateView; + @Bind(R.id.webView) + WebView mWebView; + @Bind(R.id.bottom_rl) + LinearLayout mBottomRl; + @Bind(R.id.comment_edt) + AppCompatEditText mCommentEdt; + + private String mCommentUrl; + + private int mTopicId; + + public static Intent newIntent(Context context, String commentUrl, int topicId) { + Intent intent = new Intent(context, TopicCommentListActivity.class); + intent.putExtra(Constants.Key.COMMENT_URL, commentUrl); + intent.putExtra(Constants.Key.TOPIC_ID, topicId); + return intent; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_comment_list); + ButterKnife.bind(this); + } + + @Override + public void initData() { + super.initData(); + mCommentUrl = getIntent().getStringExtra(Constants.Key.COMMENT_URL); + mTopicId = getIntent().getIntExtra(Constants.Key.TOPIC_ID, 1); + } + + @Override + public void initView() { + super.initView(); + setToolbar(mToolBar, getString(R.string.app_comment_list)); + } + + @Override + public void initLoad() { + super.initLoad(); + + mWebView.setWebViewClient(new WebAppClient(this, mMultiStateView, mWebView)); + mWebView.loadUrl(mCommentUrl, getAuth()); + mWebView.addJavascriptInterface(new WebAppInterface(this), PLATFORM); + } + + @OnClick({R.id.send_iv}) + @Override + public void onClick(View v) { + super.onClick(v); + switch (v.getId()) { + case R.id.send_iv: + if (getUserConstant().isLogin()) { + if (TextUtils.isEmpty(mCommentEdt.getText())) { + showShortToast(getString(R.string.toast_no_content)); + return; + } + + mPresenter.reply(mTopicId, mCommentEdt.getText().toString()); + } else { + showShortToast(getString(R.string.toast_no_login)); + } + break; + } + } + + @Override + public void replySuccess(TopicReplyEntity topicReplyEntity) { + if (topicReplyEntity.getData() != null) mWebView.loadUrl(mCommentUrl, getAuth()); + } + + @Override + public void onRequestStart() { + + } + + @Override + public void onRequestEnd() { + + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/reply/TopicReplyContract.java b/app/src/main/java/com/jun/elephant/ui/topic/reply/TopicReplyContract.java new file mode 100644 index 0000000..a0ffdd6 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/reply/TopicReplyContract.java @@ -0,0 +1,41 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.topic.reply; + +import com.jun.elephant.entity.topic.TopicReplyEntity; +import com.jun.elephant.mvpframe.BaseModel; +import com.jun.elephant.mvpframe.BasePresenter; +import com.jun.elephant.mvpframe.BaseView; + +import rx.Observable; + +/** + * Created by Jun on 2016/10/15. + */ + +public interface TopicReplyContract { + interface Model extends BaseModel { + Observable reply(int topicId, String body); + } + + interface View extends BaseView { + void replySuccess(TopicReplyEntity topicReplyEntity); + } + + abstract class Presenter extends BasePresenter { + public abstract void reply(int topicId, String body); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/reply/TopicReplyModel.java b/app/src/main/java/com/jun/elephant/ui/topic/reply/TopicReplyModel.java new file mode 100644 index 0000000..544ebdf --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/reply/TopicReplyModel.java @@ -0,0 +1,44 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.topic.reply; + +import com.jun.elephant.api.Networks; +import com.jun.elephant.entity.topic.TopicReplyEntity; + +import java.util.HashMap; +import java.util.Map; + +import rx.Observable; + +/** + * Created by Jun on 2016/10/15. + */ +public class TopicReplyModel implements TopicReplyContract.Model { + + private Map getOptions(int topicId, String body) { + Map options = new HashMap(); + options.put("topic_id", String.valueOf(topicId)); + options.put("body", body); + + return options; + } + + @Override + public Observable reply(int topicId, String body) { + return Networks.getInstance().getTopicApi() + .publishReply(getOptions(topicId, body)); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/topic/reply/TopicReplyPresenter.java b/app/src/main/java/com/jun/elephant/ui/topic/reply/TopicReplyPresenter.java new file mode 100644 index 0000000..51bd64f --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/topic/reply/TopicReplyPresenter.java @@ -0,0 +1,60 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.topic.reply; + +import com.jun.elephant.entity.topic.TopicReplyEntity; + +import rx.Observer; +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action0; + +/** + * Created by Jun on 2016/10/15. + */ +public class TopicReplyPresenter extends TopicReplyContract.Presenter { + + private Observer mReplyObserver = new Observer() { + + @Override + public void onCompleted() { + mView.onRequestEnd(); + } + + @Override + public void onError(Throwable e) { + mView.onRequestError(e.toString()); + mView.onInternetError(); + } + + @Override + public void onNext(TopicReplyEntity topicReplyEntity) { + mView.replySuccess(topicReplyEntity); + } + }; + + @Override + public void reply(int topicId, String body) { + mRxManager.add(mModel.reply(topicId, body) + .doOnSubscribe(new Action0() { + @Override + public void call() { + mView.onRequestStart(); + } + }) + .subscribeOn(AndroidSchedulers.mainThread()) + .subscribe(mReplyObserver)); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoActivity.java b/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoActivity.java new file mode 100644 index 0000000..76c444a --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoActivity.java @@ -0,0 +1,351 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.user.info; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.support.design.widget.AppBarLayout; +import android.support.design.widget.CollapsingToolbarLayout; +import android.support.design.widget.TabLayout; +import android.support.v4.view.ViewPager; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.bumptech.glide.Glide; +import com.facebook.drawee.view.SimpleDraweeView; +import com.jun.elephant.R; +import com.jun.elephant.common.FragmentAdapter; +import com.jun.elephant.entity.user.UserEntity; +import com.jun.elephant.entity.user.UserInfoEntity; +import com.jun.elephant.mvpframe.base.BaseFrameActivity; +import com.jun.elephant.global.Constants; +import com.jun.elephant.ui.main.WebViewActivity; +import com.jun.elephant.ui.user.reply.UserReplyFragment; +import com.jun.elephant.ui.topic.list.TopicListByUserFragment; +import com.jun.elephant.util.ShareUtil; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; +import jp.wasabeef.glide.transformations.BlurTransformation; + +/** + * Created by Jun on 2016/10/14. + */ +public class UserInfoActivity extends BaseFrameActivity implements UserInfoContract.View, Toolbar.OnMenuItemClickListener { + + @Bind(R.id.main_bg_iv) + ImageView mUserImgBg; + @Bind(R.id.toolBar) + Toolbar mToolBar; + @Bind(R.id.user_name_tv) + TextView mUserNameTv; + @Bind(R.id.user_real_name_tv) + TextView mUserRealNameTv; + @Bind(R.id.tabLayout) + TabLayout mTabLayout; + @Bind(R.id.viewPager) + ViewPager mViewPager; + @Bind(R.id.collapsing_toolbar) + CollapsingToolbarLayout mCollapsingToolbar; + @Bind(R.id.appBarLayout) + AppBarLayout mAppBarLayout; + @Bind(R.id.user_address_tv) + TextView mUserAddressTv; + @Bind(R.id.user_avatar_civ) + SimpleDraweeView mUserAvatarCiv; + @Bind(R.id.user_info_ll) + LinearLayout mUserInfoLl; + @Bind(R.id.signature_tv) + TextView mSignatureTv; + @Bind(R.id.user_share_tv) + TextView mUserShareTv; + @Bind(R.id.user_reply_tv) + TextView mUserReplyTv; + @Bind(R.id.user_follow_tv) + TextView mUserFollowTv; + @Bind(R.id.edit_tv) + TextView mSettingTv; + @Bind(R.id.user_focus_ll) + LinearLayout mUserFocusLl; + @Bind(R.id.page_title_tv) + TextView mPageTitleTv; + + private TopicListByUserFragment mTopicFragment; + private TopicListByUserFragment mFollowFragment; + private TopicListByUserFragment mVoteFragment; + private UserReplyFragment mReplyFragment; + + private boolean isShowUserInfo = true; + private boolean isShowUserFoucs = true; + private boolean isShowUserDesc = true; + private boolean isShowTitle = true; + + private UserEntity mUserEntity; + + private int mUserId; + + public static Intent newIntent(Context context, int userId) { + Intent intent = new Intent(context, UserInfoActivity.class); + intent.putExtra(Constants.Key.USER_ID, userId); + return intent; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setTheme(R.style.Custom_Theme); + setContentView(R.layout.activity_user_info); + ButterKnife.bind(this); + } + + @Override + public void initData() { + super.initData(); + mUserId = getIntent().getIntExtra(Constants.Key.USER_ID, 1); + } + + @Override + public void initView() { + setToolbar(mToolBar, ""); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + Window window = getWindow(); + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(Color.TRANSPARENT); + } + + mToolBar.inflateMenu(R.menu.menu_user_info); + alphaView(mPageTitleTv, 200, 4); + + mAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() { + @Override + public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { + float f = ((float) Math.abs(verticalOffset)) / (float) (mAppBarLayout.getTotalScrollRange()); + + appBarScrollChange(f); + } + }); + + } + + @Override + public void initListener() { + super.initListener(); + mToolBar.setOnMenuItemClickListener(this); + } + + @Override + public void initLoad() { + super.initLoad(); + mPresenter.getUserInfoById(mUserId); + } + + @OnClick({R.id.user_avatar_civ, R.id.edit_tv}) + @Override + public void onClick(View v) { + super.onClick(v); + switch (v.getId()) { + case R.id.user_avatar_civ: + break; + case R.id.edit_tv: + startActivityForResult(UserInfoEditActivity.newIntent(this, mUserEntity), Constants.Activity.UserInfoEditActivity); + break; + } + } + + private void setupViewPager() { + List titles = new ArrayList<>(); + titles.add("分享"); + titles.add("关注"); + titles.add("赞过"); + titles.add("回复"); + mTabLayout.addTab(mTabLayout.newTab().setText(titles.get(0))); + mTabLayout.addTab(mTabLayout.newTab().setText(titles.get(1))); + mTabLayout.addTab(mTabLayout.newTab().setText(titles.get(2))); + mTabLayout.addTab(mTabLayout.newTab().setText(titles.get(3))); + + Bundle bundleReply = new Bundle(); + bundleReply.putString(Constants.Key.WEB_URL, mUserEntity.getLinks().getReplies_web_view()); + + mTopicFragment = new TopicListByUserFragment(); + mFollowFragment = new TopicListByUserFragment(); + mVoteFragment = new TopicListByUserFragment(); + mReplyFragment = new UserReplyFragment(); + + mTopicFragment.setArguments(TopicListByUserFragment.newBundle(mUserId, Constants.User.USER_TOPIC_MY)); + mFollowFragment.setArguments(TopicListByUserFragment.newBundle(mUserId, Constants.User.USER_TOPIC_FOLLOW)); + mVoteFragment.setArguments(TopicListByUserFragment.newBundle(mUserId, Constants.User.USER_TOPIC_VOTES)); + mReplyFragment.setArguments(bundleReply); + + FragmentAdapter fragmentAdapter = new FragmentAdapter(getSupportFragmentManager()); + fragmentAdapter.addFragment(mTopicFragment, titles.get(0)); + fragmentAdapter.addFragment(mFollowFragment, titles.get(1)); + fragmentAdapter.addFragment(mVoteFragment, titles.get(2)); + fragmentAdapter.addFragment(mReplyFragment, titles.get(3)); + + mViewPager.setAdapter(fragmentAdapter); + mViewPager.setOffscreenPageLimit(4); + mTabLayout.setupWithViewPager(mViewPager); + mTabLayout.setTabsFromPagerAdapter(fragmentAdapter); + } + + private void alphaView(View view, long duration, int i) { + view.animate().alpha(i == 0 ? 1.0f : 0.0f).setDuration(duration).start(); + } + + private void appBarScrollChange(float f) { + if (f >= 0.1f) { + if (isShowUserInfo) { + alphaView(mUserInfoLl, 200, 4); + isShowUserInfo = false; + } + } else if (!isShowUserInfo){ + alphaView(mUserInfoLl, 200, 0); + isShowUserInfo = true; + } + + if (f >= 0.5f) { + if (isShowUserDesc) { + alphaView(mSignatureTv, 200, 4); + isShowUserDesc = false; + } + } else if (!isShowUserDesc) { + alphaView(mSignatureTv, 200, 0); + mSignatureTv.animate().alpha(0.5f); + isShowUserDesc = true; + } + + if (f >= 0.7f) { + if (isShowUserFoucs) { + alphaView(mUserFocusLl, 200, 4); + isShowUserFoucs = false; + } + } else if (!isShowUserFoucs) { + alphaView(mUserFocusLl, 200, 0); + isShowUserFoucs = true; + + } + + if (f >= 0.86f) { + if (!isShowTitle) { + alphaView(mPageTitleTv, 200, 0); + isShowTitle = true; + } + } else if (isShowTitle) { + alphaView(mPageTitleTv, 200, 4); + isShowTitle = false; + } + } + + private void initUserInfo(UserEntity userEntity) { + Glide.with(this).load(userEntity.getAvatar()) + .bitmapTransform(new BlurTransformation(this, 5)) + .into(mUserImgBg); + mCollapsingToolbar.setTitle(userEntity.getName()); + mPageTitleTv.setText(userEntity.getName()); + mUserNameTv.setText(userEntity.getName()); + mUserAvatarCiv.setImageURI(Uri.parse(userEntity.getAvatar())); + mUserShareTv.setText(String.valueOf(userEntity.getTopic_count())); + mUserReplyTv.setText(String.valueOf(userEntity.getReply_count())); + mSignatureTv.setText(userEntity.getIntroduction()); + + if (!TextUtils.isEmpty(userEntity.getCity())) { + mUserAddressTv.setText(userEntity.getCity()); + mUserAddressTv.setVisibility(View.VISIBLE); + } + + if (getUserConstant().isLogin()) { + if (userEntity.getId() == getUserConstant().getUserData().getData().getId()) { + mSettingTv.setVisibility(View.VISIBLE); + } + } + + if (!TextUtils.isEmpty(mUserEntity.getGithub_url())) { + mToolBar.getMenu().findItem(R.id.action_github).setVisible(true); + } + + if (!TextUtils.isEmpty(mUserEntity.getEmail())) { + mToolBar.getMenu().findItem(R.id.action_email).setVisible(true); + } + + if (!TextUtils.isEmpty(mUserEntity.getPersonal_website())) { + mToolBar.getMenu().findItem(R.id.action_blog).setVisible(true); + } + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_github: + startActivity(WebViewActivity.newIntent(this, mUserEntity.getGithub_name(), mUserEntity.getGithub_url())); + break; + case R.id.action_blog: + String temp = mUserEntity.getPersonal_website(); + if (!temp.contains("http://") && !temp.contains("https://")) { + temp = "http://" + temp; + } + startActivity(WebViewActivity.newIntent(this, mUserEntity.getName(), temp)); + break; + case R.id.action_email: + ShareUtil.feedback(this, mUserEntity.getEmail()); + break; + } + return true; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == Constants.Activity.UserInfoEditActivity && resultCode == RESULT_OK) { + UserEntity userEntity = data.getExtras().getParcelable(Constants.Key.USER_DATA); + this.mUserEntity = userEntity; + initUserInfo(userEntity); + } + } + + @Override + public void onRequestStart() { + + } + + @Override + public void onRequestEnd() { + + } + + @Override + public void getUserInfo(UserInfoEntity userInfoEntity) { + mUserEntity = userInfoEntity.getData(); + initUserInfo(mUserEntity); + setupViewPager(); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoContract.java b/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoContract.java new file mode 100644 index 0000000..19ee61e --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoContract.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.user.info; + +import com.jun.elephant.entity.user.UserEntity; +import com.jun.elephant.entity.user.UserInfoEntity; +import com.jun.elephant.mvpframe.BaseModel; +import com.jun.elephant.mvpframe.BasePresenter; +import com.jun.elephant.mvpframe.BaseView; + +import rx.Observable; + +/** + * Created by Jun on 2016/10/14. + */ +public interface UserInfoContract { + + interface Model extends BaseModel { + Observable getUserInfoById(int userId); + + Observable saveUserInfoById(int userId, UserEntity userEntity); + } + + interface View extends BaseView { + void getUserInfo(UserInfoEntity userInfoEntity); + } + + abstract class Presenter extends BasePresenter { + + public abstract void getUserInfoById(int userId); + + public abstract void saveUserInfoById(int userId, UserEntity userEntity); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoEditActivity.java b/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoEditActivity.java new file mode 100644 index 0000000..6c98db2 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoEditActivity.java @@ -0,0 +1,307 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.user.info; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.Toolbar; +import android.text.InputType; +import android.text.TextUtils; +import android.view.View; +import android.widget.TextView; + +import com.afollestad.materialdialogs.MaterialDialog; +import com.google.gson.Gson; +import com.jun.elephant.R; +import com.jun.elephant.entity.user.UserEntity; +import com.jun.elephant.entity.user.UserInfoEntity; +import com.jun.elephant.mvpframe.base.BaseFrameActivity; +import com.jun.elephant.global.Constants; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * Created by Jun on 2016/10/14. + */ +public class UserInfoEditActivity extends BaseFrameActivity implements UserInfoContract.View { + + @Bind(R.id.toolBar) + Toolbar mToolBar; + @Bind(R.id.name_tv) + TextView mUserNameTv; + @Bind(R.id.signature_tv) + TextView mUserSignatureTv; + @Bind(R.id.user_address_tv) + TextView mUserAddressTv; + @Bind(R.id.user_github_tv) + TextView mUserGithubTv; + @Bind(R.id.user_blog_tv) + TextView mUserBlogTv; + @Bind(R.id.user_twitter_tv) + TextView mUserTwitterTv; + @Bind(R.id.intro_tv) + TextView mIntroTv; + + private MaterialDialog.Builder mInputDialog; + + private UserEntity mUserEntity; + + private MaterialDialog mLoadingDialog; + + private boolean isChange; //记录是否修改过个人信息 + + public static Intent newIntent(Context context, UserEntity userEntity) { + Intent intent = new Intent(context, UserInfoEditActivity.class); + Bundle bundle = new Bundle(); + bundle.putParcelable(Constants.Key.USER_DATA, userEntity); + intent.putExtras(bundle); + return intent; + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_user_info_edit); + ButterKnife.bind(this); + } + + @Override + public void initData() { + super.initData(); + mUserEntity = getIntent().getExtras().getParcelable(Constants.Key.USER_DATA); + } + + @Override + public void initView() { + super.initView(); + setToolbar(mToolBar, getString(R.string.app_edit_info)); + getLoadingDialog().content(getString(R.string.dialog_modify_loading)); + mLoadingDialog = getLoadingDialog().build(); + + initPersonalInfo(); + + initDialog(); + } + + private void initPersonalInfo() { + String signature = mUserEntity.getSignature(); + String city = mUserEntity.getCity(); + String blog = mUserEntity.getPersonal_website(); + String twitter = mUserEntity.getTwitter_account(); + String github = mUserEntity.getGithub_name(); + String name = mUserEntity.getName(); + String intro = mUserEntity.getIntroduction(); + + if (name != null) { + mUserNameTv.setText(name); + } + + if (city != null) { + mUserAddressTv.setText(city); + } + + if (signature != null) { + mUserSignatureTv.setText(signature); + } + + if (intro != null) { + mIntroTv.setText(intro); + } + + if (blog != null) { + mUserBlogTv.setText(blog); + } + + if (twitter != null) { + mUserTwitterTv.setText(twitter); + } + + if (github != null) { + mUserGithubTv.setText(github); + } + } + + private void initDialog() { + mInputDialog = new MaterialDialog.Builder(this); + mInputDialog.negativeText(getString(R.string.dialog_btn_cancel)); + mInputDialog.positiveText(getString(R.string.dialog_btn_confirm)); + mInputDialog.positiveColorRes(R.color.colorPrimary); + mInputDialog.negativeColorRes(R.color.colorPrimary); + mInputDialog.titleColorRes(R.color.text_common); + mInputDialog.inputType(InputType.TYPE_CLASS_TEXT); + mInputDialog.widgetColorRes(R.color.colorPrimary); + } + + @OnClick({R.id.user_name_ll, R.id.user_signature_ll, R.id.user_intro_ll, R.id.user_address_ll, R.id.user_github_ll, R.id.user_blog_ll, R.id.user_twitter_ll}) + public void onClick(View view) { + int id = view.getId(); + switch (id) { + case R.id.user_name_ll: + case R.id.user_signature_ll: + case R.id.user_intro_ll: + case R.id.user_address_ll: + case R.id.user_github_ll: + case R.id.user_blog_ll: + case R.id.user_twitter_ll: + showDialogs(id); + break; + default: + finish(); + break; + } + } + + @Override + public void finish() { + if (isChange) { + Intent intent = new Intent(); + Bundle bundle = new Bundle(); + bundle.putParcelable(Constants.Key.USER_DATA, mUserEntity); + intent.putExtras(bundle); + setResult(RESULT_OK, intent); + } + super.finish(); + } + + private void showDialogs(int id) { + switch (id) { + case R.id.user_name_ll: + mInputDialog.title(getString(R.string.dialog_edit_name)); + mInputDialog.input("", mUserEntity.getName(),nameCallback); + break; + case R.id.user_signature_ll: + mInputDialog.title(getString(R.string.dialog_edit_signature)); + mInputDialog.input("", mUserEntity.getSignature(), signatureCallback); + break; + case R.id.user_intro_ll: + mInputDialog.title(getString(R.string.dialog_edit_intro)); + mInputDialog.input("", mUserEntity.getIntroduction(), introCallback); + break; + case R.id.user_address_ll: + mInputDialog.title(getString(R.string.dialog_edit_address)); + mInputDialog.input("", mUserEntity.getCity(), addressCallback); + break; + case R.id.user_github_ll: + mInputDialog.title(getString(R.string.dialog_edit_github)); + mInputDialog.input("", mUserEntity.getGithub_name(), githubCallback); + break; + case R.id.user_blog_ll: + mInputDialog.title(getString(R.string.dialog_edit_blog)); + mInputDialog.input("", mUserEntity.getPersonal_website(), blogCallback); + break; + case R.id.user_twitter_ll: + mInputDialog.title(getString(R.string.dialog_edit_twitter)); + mInputDialog.input("", mUserEntity.getTwitter_account(), twitterCallback); + break; + } + + mInputDialog.show(); + } + + MaterialDialog.InputCallback nameCallback = new MaterialDialog.InputCallback() { + @Override + public void onInput(@NonNull MaterialDialog dialog, CharSequence input) { + if (!TextUtils.isEmpty(input)) { + mUserEntity.setName(input.toString()); + mPresenter.saveUserInfoById(mUserEntity.getId(), mUserEntity); + } + } + }; + + MaterialDialog.InputCallback introCallback = new MaterialDialog.InputCallback() { + @Override + public void onInput(@NonNull MaterialDialog dialog, CharSequence input) { + if (!TextUtils.isEmpty(input)) { + mUserEntity.setIntroduction(input.toString()); + mPresenter.saveUserInfoById(mUserEntity.getId(), mUserEntity); + } + } + }; + + MaterialDialog.InputCallback signatureCallback = new MaterialDialog.InputCallback() { + @Override + public void onInput(@NonNull MaterialDialog dialog, CharSequence input) { + if (!TextUtils.isEmpty(input)) { + mUserEntity.setSignature(input.toString()); + mPresenter.saveUserInfoById(mUserEntity.getId(), mUserEntity); + } + } + }; + + MaterialDialog.InputCallback addressCallback = new MaterialDialog.InputCallback() { + @Override + public void onInput(@NonNull MaterialDialog dialog, CharSequence input) { + if (!TextUtils.isEmpty(input)) { + mUserEntity.setCity(input.toString()); + mPresenter.saveUserInfoById(mUserEntity.getId(), mUserEntity); + } + } + }; + + MaterialDialog.InputCallback githubCallback = new MaterialDialog.InputCallback() { + @Override + public void onInput(@NonNull MaterialDialog dialog, CharSequence input) { + if (!TextUtils.isEmpty(input)) { + mUserEntity.setGithub_name(input.toString()); + mPresenter.saveUserInfoById(mUserEntity.getId(), mUserEntity); + } + } + }; + + MaterialDialog.InputCallback blogCallback = new MaterialDialog.InputCallback() { + @Override + public void onInput(@NonNull MaterialDialog dialog, CharSequence input) { + if (!TextUtils.isEmpty(input)) { + mUserEntity.setPersonal_website(input.toString()); + mPresenter.saveUserInfoById(mUserEntity.getId(), mUserEntity); + } + } + }; + + MaterialDialog.InputCallback twitterCallback = new MaterialDialog.InputCallback() { + @Override + public void onInput(@NonNull MaterialDialog dialog, CharSequence input) { + if (!TextUtils.isEmpty(input)) { + mUserEntity.setTwitter_account(input.toString()); + mPresenter.saveUserInfoById(mUserEntity.getId(), mUserEntity); + } + } + }; + + @Override + public void getUserInfo(UserInfoEntity userInfoEntity) { + getUserConstant().saveUserData(new Gson().toJson(userInfoEntity)); + mUserEntity = userInfoEntity.getData(); + initPersonalInfo(); + isChange = true; + showShortToast(getString(R.string.toast_modify_success)); + } + + @Override + public void onRequestStart() { + mLoadingDialog.show(); + } + + @Override + public void onRequestEnd() { + mLoadingDialog.dismiss(); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoModel.java b/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoModel.java new file mode 100644 index 0000000..8bbf925 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoModel.java @@ -0,0 +1,51 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.user.info; + +import com.jun.elephant.api.Networks; +import com.jun.elephant.entity.user.UserEntity; +import com.jun.elephant.entity.user.UserInfoEntity; +import com.jun.elephant.mvpframe.rx.RxSchedulers; + +import rx.Observable; + +/** + * Created by Jun on 2016/10/14. + */ +public class UserInfoModel implements UserInfoContract.Model { + /** + * 根据用户 Id 获取用户信息 + * @param userId + * @return + */ + @Override + public Observable getUserInfoById(int userId) { + return Networks.getInstance().getUserApi().getUserInfoById(userId) + .compose(RxSchedulers.io_main()); + } + + /** + * 保存修改过后的用户信息 + * @param userId + * @param userEntity + * @return + */ + @Override + public Observable saveUserInfoById(int userId, UserEntity userEntity) { + return Networks.getInstance().getUserApi().saveUserInfo(userId, userEntity) + .compose(RxSchedulers.io_main()); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoPresenter.java b/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoPresenter.java new file mode 100644 index 0000000..5cb3235 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/user/info/UserInfoPresenter.java @@ -0,0 +1,68 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.user.info; + +import com.jun.elephant.entity.user.UserEntity; +import com.jun.elephant.entity.user.UserInfoEntity; + +import rx.Observer; +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action0; + +/** + * Created by Jun on 2016/10/14. + */ +public class UserInfoPresenter extends UserInfoContract.Presenter { + + public Observer getUserInfoObserver() { + return new Observer() { + @Override + public void onCompleted() { + mView.onRequestEnd(); + } + + @Override + public void onError(Throwable e) { + mView.onRequestError(e.getMessage()); + } + + @Override + public void onNext(UserInfoEntity userInfoEntity) { + mView.getUserInfo(userInfoEntity); + } + }; + } + + @Override + public void getUserInfoById(int userId) { + mRxManager.add(mModel.getUserInfoById(userId).subscribe(getUserInfoObserver())); + } + + @Override + public void saveUserInfoById(int userId, UserEntity userEntity) { + mRxManager.add(mModel.saveUserInfoById(userId, userEntity) + .doOnSubscribe(new Action0() { + @Override + public void call() { + mView.onRequestStart(); + } + }) + .subscribeOn(AndroidSchedulers.mainThread()) + .subscribe(getUserInfoObserver()) + + ); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/user/message/MessageListContract.java b/app/src/main/java/com/jun/elephant/ui/user/message/MessageListContract.java new file mode 100644 index 0000000..39ee9a0 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/user/message/MessageListContract.java @@ -0,0 +1,28 @@ +package com.jun.elephant.ui.user.message; + +import com.jun.elephant.entity.user.UserMessageEntity; +import com.jun.elephant.mvpframe.BaseModel; +import com.jun.elephant.mvpframe.BasePresenter; +import com.jun.elephant.mvpframe.BaseView; + +import rx.Observable; + +/** + * Created by Jun on 2016/9/10. + */ + +public interface MessageListContract { + interface Model extends BaseModel { + Observable getUserMessage(int pageIndex); + } + + interface View extends BaseView { + void refreshMessageList(UserMessageEntity userMessageEntity); + + void loadMoreMessageList(UserMessageEntity userMessageEntity); + } + + abstract class Presenter extends BasePresenter { + public abstract void getMessageList(int pageIndex); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/user/message/MessageModel.java b/app/src/main/java/com/jun/elephant/ui/user/message/MessageModel.java new file mode 100644 index 0000000..a5ea313 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/user/message/MessageModel.java @@ -0,0 +1,49 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.user.message; + +import com.jun.elephant.api.Networks; +import com.jun.elephant.entity.user.UserMessageEntity; +import com.jun.elephant.mvpframe.rx.RxSchedulers; +import com.jun.elephant.global.Constants; + +import java.util.HashMap; +import java.util.Map; + +import rx.Observable; + +/** + * Created by Jun on 2016/10/15. + */ +public class MessageModel implements MessageListContract.Model { + + @Override + public Observable getUserMessage(int pageIndex) { + return Networks.getInstance().getUserApi() + .getMyMessage(getOptions(pageIndex)) + .compose(RxSchedulers.io_main()); + } + + + private Map getOptions(int pageIndex) { + Map options = new HashMap<>(); + options.put("per_page", String.valueOf(Constants.PER_PAGE)); + options.put("include", "from_user,topic"); + options.put("page", String.valueOf(pageIndex)); + + return options; + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/user/message/MessagePresenter.java b/app/src/main/java/com/jun/elephant/ui/user/message/MessagePresenter.java new file mode 100644 index 0000000..85e6d26 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/user/message/MessagePresenter.java @@ -0,0 +1,56 @@ +/* + * Copyright 2016 Freelander + * + * 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. + */ +package com.jun.elephant.ui.user.message; + +import com.jun.elephant.entity.user.UserMessageEntity; + +import rx.Observer; + +/** + * Created by Jun on 2016/10/15. + */ +public class MessagePresenter extends MessageListContract.Presenter { + + private Observer getMessageObserver(final int pageIndex) { + return new Observer() { + @Override + public void onCompleted() { + mView.onRequestEnd(); + } + + @Override + public void onError(Throwable e) { + mView.onRequestError(e.toString()); + mView.onInternetError(); + } + + @Override + public void onNext(UserMessageEntity userMessageEntity) { + if (pageIndex == 1) { + mView.refreshMessageList(userMessageEntity); + } else { + mView.loadMoreMessageList(userMessageEntity); + } + } + }; + } + + + @Override + public void getMessageList(int pageIndex) { + mRxManager.add(mModel.getUserMessage(pageIndex).subscribe(getMessageObserver(pageIndex))); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/user/message/UserMessageActivity.java b/app/src/main/java/com/jun/elephant/ui/user/message/UserMessageActivity.java new file mode 100644 index 0000000..d592290 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/user/message/UserMessageActivity.java @@ -0,0 +1,141 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.user.message; + +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; + +import com.jun.elephant.R; +import com.jun.elephant.entity.user.MessageEntity; +import com.jun.elephant.entity.user.UserMessageEntity; +import com.jun.elephant.mvpframe.base.BaseFrameActivity; +import com.jun.elephant.ui.adapter.UserMessageAdapter; +import com.jun.elephant.ui.widget.MultiStateView; +import com.jun.elephant.ui.widget.MySwipeRefreshLayout; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import in.srain.cube.views.ptr.PtrDefaultHandler2; +import in.srain.cube.views.ptr.PtrFrameLayout; + +/** + * Created by Jun on 2016/5/6. + */ +public class UserMessageActivity extends BaseFrameActivity implements MessageListContract.View { + + @Bind(R.id.toolBar) + Toolbar mToolBar; + @Bind(R.id.recyclerView) + RecyclerView mRecyclerView; + @Bind(R.id.multiStateView) + MultiStateView mMultiStateView; + @Bind(R.id.swipe_layout) + MySwipeRefreshLayout mSwipeLayout; + + private UserMessageAdapter mAdapter; + + private List mMessageList; + + private int mPageIndex = 1; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_user_message); + ButterKnife.bind(this); + } + + @Override + public void initData() { + super.initData(); + mMessageList = new ArrayList<>(); + mAdapter = new UserMessageAdapter(this, mMessageList); + } + + @Override + public void initView() { + super.initView(); + setToolbar(mToolBar, getString(R.string.app_user_message)); + mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + mRecyclerView.setAdapter(mAdapter); + } + + @Override + public void initListener() { + super.initListener(); + mSwipeLayout.setPtrHandler(new PtrDefaultHandler2() { + @Override + public void onLoadMoreBegin(PtrFrameLayout ptrFrameLayout) { + mPageIndex ++; + mPresenter.getMessageList(mPageIndex); + } + + @Override + public void onRefreshBegin(PtrFrameLayout ptrFrameLayout) { + mPageIndex = 1; + mPresenter.getMessageList(mPageIndex); + } + }); + + } + + @Override + public void initLoad() { + super.initLoad(); + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_LOADING); + + mPresenter.getMessageList(mPageIndex); + } + + @Override + public void refreshMessageList(UserMessageEntity userMessageEntity) { + if (userMessageEntity.getData().size() == 0){ + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_EMPTY); + return; + } + mMessageList.clear(); + mMessageList.addAll(userMessageEntity.getData()); + mAdapter.notifyDataSetChanged(); + } + + @Override + public void loadMoreMessageList(UserMessageEntity userMessageEntity) { + mMessageList.addAll(userMessageEntity.getData()); + mAdapter.notifyDataSetChanged(); + } + + @Override + public void onRequestStart() { + + } + + @Override + public void onRequestEnd() { + mSwipeLayout.refreshComplete(); + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_CONTENT); + } + + @Override + public void onRequestError(String msg) { + super.onRequestError(msg); + mMultiStateView.setViewState(MultiStateView.VIEW_STATE_ERROR); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/user/reply/UserReplyFragment.java b/app/src/main/java/com/jun/elephant/ui/user/reply/UserReplyFragment.java new file mode 100644 index 0000000..b9f1e0a --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/user/reply/UserReplyFragment.java @@ -0,0 +1,76 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.user.reply; + +import android.os.Bundle; +import android.webkit.WebView; + +import com.jun.elephant.ui.widget.MultiStateView; +import com.jun.elephant.R; +import com.jun.elephant.api.Networks; +import com.jun.elephant.common.BaseFragment; +import com.jun.elephant.common.WebAppClient; +import com.jun.elephant.global.Constants; + +import java.util.HashMap; +import java.util.Map; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Created by Jun on 2016/5/8. + */ +public class UserReplyFragment extends BaseFragment { + @Bind(R.id.webView) + WebView mWebView; + @Bind(R.id.multiStateView) + MultiStateView mMultiStateView; + + private String mWebUrl; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.common_webview); + ButterKnife.bind(this, getContentView()); + } + + @Override + public void initData() { + super.initData(); + mWebUrl = getArguments().getString(Constants.Key.WEB_URL); + } + + @Override + public void initLoad() { + super.initLoad(); + mWebView.setWebViewClient(new WebAppClient(getContext(), mMultiStateView, mWebView)); + mWebView.loadUrl(mWebUrl, getAuth()); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + ButterKnife.unbind(this); + } + + public Map getAuth() { + Map header = new HashMap<>(); + header.put("Authorization", "Bearer " + Networks.getToken()); + return header; + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/widget/MultiStateView.java b/app/src/main/java/com/jun/elephant/ui/widget/MultiStateView.java new file mode 100644 index 0000000..08d5bf8 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/widget/MultiStateView.java @@ -0,0 +1,350 @@ +package com.jun.elephant.ui.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.support.annotation.IntDef; +import android.support.annotation.LayoutRes; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import com.jun.elephant.R; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +/** + * View that contains 4 different states: Content, Error, Empty, and Loading.
+ * Each state has their own separate layout which can be shown/hidden by setting + * the {@link ViewState} accordingly + * Every MultiStateView MUST contain a content view. The content view + * is obtained from whatever is inside of the tags of the view via its XML declaration + */ +public class MultiStateView extends FrameLayout { + + public static final int VIEW_STATE_CONTENT = 0; + + public static final int VIEW_STATE_ERROR = 1; + + public static final int VIEW_STATE_EMPTY = 2; + + public static final int VIEW_STATE_LOADING = 3; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({VIEW_STATE_CONTENT, VIEW_STATE_ERROR, VIEW_STATE_EMPTY, VIEW_STATE_LOADING}) + public @interface ViewState { + } + + private LayoutInflater mInflater; + + private View mContentView; + + private View mLoadingView; + + private View mErrorView; + + private View mEmptyView; + + @ViewState + private int mViewState = VIEW_STATE_CONTENT; + + public MultiStateView(Context context) { + this(context, null); + } + + public MultiStateView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public MultiStateView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(attrs); + } + + private void init(AttributeSet attrs) { + mInflater = LayoutInflater.from(getContext()); + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MultiStateView); + + int loadingViewResId = a.getResourceId(R.styleable.MultiStateView_msv_loadingView, -1); + if (loadingViewResId > -1) { + mLoadingView = mInflater.inflate(loadingViewResId, this, false); + addView(mLoadingView, mLoadingView.getLayoutParams()); + } + + int emptyViewResId = a.getResourceId(R.styleable.MultiStateView_msv_emptyView, -1); + if (emptyViewResId > -1) { + mEmptyView = mInflater.inflate(emptyViewResId, this, false); + addView(mEmptyView, mEmptyView.getLayoutParams()); + } + + int errorViewResId = a.getResourceId(R.styleable.MultiStateView_msv_errorView, -1); + if (errorViewResId > -1) { + mErrorView = mInflater.inflate(errorViewResId, this, false); + addView(mErrorView, mErrorView.getLayoutParams()); + } + + int viewState = a.getInt(R.styleable.MultiStateView_msv_viewState, VIEW_STATE_CONTENT); + + switch (viewState) { + case VIEW_STATE_CONTENT: + mViewState = VIEW_STATE_CONTENT; + break; + + case VIEW_STATE_ERROR: + mViewState = VIEW_STATE_ERROR; + break; + + case VIEW_STATE_EMPTY: + mViewState = VIEW_STATE_EMPTY; + break; + + case VIEW_STATE_LOADING: + mViewState = VIEW_STATE_LOADING; + break; + } + + a.recycle(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mContentView == null) throw new IllegalArgumentException("Content view is not defined"); + setView(); + } + + /* All of the addView methods have been overridden so that it can obtain the content view via XML + It is NOT recommended to add views into MultiStateView via the addView methods, but rather use + any of the setViewForState methods to set views for their given ViewState accordingly */ + @Override + public void addView(View child) { + if (isValidContentView(child)) mContentView = child; + super.addView(child); + } + + @Override + public void addView(View child, int index) { + if (isValidContentView(child)) mContentView = child; + super.addView(child, index); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (isValidContentView(child)) mContentView = child; + super.addView(child, index, params); + } + + @Override + public void addView(View child, ViewGroup.LayoutParams params) { + if (isValidContentView(child)) mContentView = child; + super.addView(child, params); + } + + @Override + public void addView(View child, int width, int height) { + if (isValidContentView(child)) mContentView = child; + super.addView(child, width, height); + } + + @Override + protected boolean addViewInLayout(View child, int index, ViewGroup.LayoutParams params) { + if (isValidContentView(child)) mContentView = child; + return super.addViewInLayout(child, index, params); + } + + @Override + protected boolean addViewInLayout(View child, int index, ViewGroup.LayoutParams params, boolean preventRequestLayout) { + if (isValidContentView(child)) mContentView = child; + return super.addViewInLayout(child, index, params, preventRequestLayout); + } + + /** + * Returns the {@link View} associated with the {@link com.kennyc.view.MultiStateView.ViewState} + * + * @param state The {@link com.kennyc.view.MultiStateView.ViewState} with to return the view for + * @return The {@link View} associated with the {@link com.kennyc.view.MultiStateView.ViewState}, null if no view is present + */ + @Nullable + public View getView(@ViewState int state) { + switch (state) { + case VIEW_STATE_LOADING: + return mLoadingView; + + case VIEW_STATE_CONTENT: + return mContentView; + + case VIEW_STATE_EMPTY: + return mEmptyView; + + case VIEW_STATE_ERROR: + return mErrorView; + + default: + return null; + } + } + + /** + * Returns the current {@link com.kennyc.view.MultiStateView.ViewState} + * + * @return + */ + @ViewState + public int getViewState() { + return mViewState; + } + + /** + * Sets the current {@link com.kennyc.view.MultiStateView.ViewState} + * + * @param state The {@link com.kennyc.view.MultiStateView.ViewState} to set {@link MultiStateView} to + */ + public void setViewState(@ViewState int state) { + if (state != mViewState) { + mViewState = state; + setView(); + } + } + + /** + * Shows the {@link View} based on the {@link com.kennyc.view.MultiStateView.ViewState} + */ + private void setView() { + switch (mViewState) { + case VIEW_STATE_LOADING: + if (mLoadingView == null) { + throw new NullPointerException("Loading View"); + } + + mLoadingView.setVisibility(View.VISIBLE); + if (mContentView != null) mContentView.setVisibility(View.GONE); + if (mErrorView != null) mErrorView.setVisibility(View.GONE); + if (mEmptyView != null) mEmptyView.setVisibility(View.GONE); + break; + + case VIEW_STATE_EMPTY: + if (mEmptyView == null) { + throw new NullPointerException("Empty View"); + } + + mEmptyView.setVisibility(View.VISIBLE); + if (mLoadingView != null) mLoadingView.setVisibility(View.GONE); + if (mErrorView != null) mErrorView.setVisibility(View.GONE); + if (mContentView != null) mContentView.setVisibility(View.GONE); + break; + + case VIEW_STATE_ERROR: + if (mErrorView == null) { + throw new NullPointerException("Error View"); + } + + mErrorView.setVisibility(View.VISIBLE); + if (mLoadingView != null) mLoadingView.setVisibility(View.GONE); + if (mContentView != null) mContentView.setVisibility(View.GONE); + if (mEmptyView != null) mEmptyView.setVisibility(View.GONE); + break; + + case VIEW_STATE_CONTENT: + default: + if (mContentView == null) { + // Should never happen, the view should throw an exception if no content view is present upon creation + throw new NullPointerException("Content View"); + } + + mContentView.setVisibility(View.VISIBLE); + if (mLoadingView != null) mLoadingView.setVisibility(View.GONE); + if (mErrorView != null) mErrorView.setVisibility(View.GONE); + if (mEmptyView != null) mEmptyView.setVisibility(View.GONE); + break; + } + } + + /** + * Checks if the given {@link View} is valid for the Content View + * + * @param view The {@link View} to check + * @return + */ + private boolean isValidContentView(View view) { + if (mContentView != null && mContentView != view) { + return false; + } + + return view != mLoadingView && view != mErrorView && view != mEmptyView; + } + + /** + * Sets the view for the given view state + * + * @param view The {@link View} to use + * @param state The {@link com.kennyc.view.MultiStateView.ViewState}to set + * @param switchToState If the {@link com.kennyc.view.MultiStateView.ViewState} should be switched to + */ + public void setViewForState(View view, @ViewState int state, boolean switchToState) { + switch (state) { + case VIEW_STATE_LOADING: + if (mLoadingView != null) removeView(mLoadingView); + mLoadingView = view; + addView(mLoadingView); + break; + + case VIEW_STATE_EMPTY: + if (mEmptyView != null) removeView(mEmptyView); + mEmptyView = view; + addView(mEmptyView); + break; + + case VIEW_STATE_ERROR: + if (mErrorView != null) removeView(mErrorView); + mErrorView = view; + addView(mErrorView); + break; + + case VIEW_STATE_CONTENT: + if (mContentView != null) removeView(mContentView); + mContentView = view; + addView(mContentView); + break; + } + + if (switchToState) setViewState(state); + } + + /** + * Sets the {@link View} for the given {@link com.kennyc.view.MultiStateView.ViewState} + * + * @param view The {@link View} to use + * @param state The {@link com.kennyc.view.MultiStateView.ViewState} to set + */ + public void setViewForState(View view, @ViewState int state) { + setViewForState(view, state, false); + } + + /** + * Sets the {@link View} for the given {@link com.kennyc.view.MultiStateView.ViewState} + * + * @param layoutRes Layout resource id + * @param state The {@link com.kennyc.view.MultiStateView.ViewState} to set + * @param switchToState If the {@link com.kennyc.view.MultiStateView.ViewState} should be switched to + */ + public void setViewForState(@LayoutRes int layoutRes, @ViewState int state, boolean switchToState) { + if (mInflater == null) mInflater = LayoutInflater.from(getContext()); + View view = mInflater.inflate(layoutRes, this, false); + setViewForState(view, state, switchToState); + } + + /** + * Sets the {@link View} for the given {@link com.kennyc.view.MultiStateView.ViewState} + * + * @param layoutRes Layout resource id + * @param state The {@link View} state to set + */ + public void setViewForState(@LayoutRes int layoutRes, @ViewState int state) { + setViewForState(layoutRes, state, false); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/widget/MySimpleDraweeView.java b/app/src/main/java/com/jun/elephant/ui/widget/MySimpleDraweeView.java new file mode 100644 index 0000000..4f9e642 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/widget/MySimpleDraweeView.java @@ -0,0 +1,65 @@ +package com.jun.elephant.ui.widget; + +import android.content.Context; +import android.net.Uri; +import android.util.AttributeSet; + +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.backends.pipeline.PipelineDraweeController; +import com.facebook.drawee.generic.GenericDraweeHierarchy; +import com.facebook.drawee.view.SimpleDraweeView; +import com.facebook.imagepipeline.common.ResizeOptions; +import com.facebook.imagepipeline.request.ImageRequest; +import com.facebook.imagepipeline.request.ImageRequestBuilder; + +/** + * Created by Jun on 2016/9/27. + */ + +public class MySimpleDraweeView extends SimpleDraweeView { + + public MySimpleDraweeView(Context context, GenericDraweeHierarchy hierarchy) { + super(context, hierarchy); + } + + public MySimpleDraweeView(Context context) { + super(context); + } + + public MySimpleDraweeView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void setImageURI(Uri uri) { + + ImageRequest request = ImageRequestBuilder + .newBuilderWithSource(uri) + .setLocalThumbnailPreviewsEnabled(true) + .setProgressiveRenderingEnabled(false) + .setResizeOptions(new ResizeOptions(50, 50)) + .build(); +/* + DraweeController controller = Fresco.newDraweeControllerBuilder() + .setUri(uri) + .setTapToRetryEnabled(true) + .setImageRequest(request) + .setOldController(getController()) + .build();*/ + + PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder() + .setUri(uri) + .setTapToRetryEnabled(true) + .setImageRequest(request) + .setOldController(getController()) + .build(); + + setController(controller); + + super.setImageURI(uri); + } + + public void setImageUrl(String url) { + setImageURI(Uri.parse(url)); + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/widget/MySwipeRefreshLayout.java b/app/src/main/java/com/jun/elephant/ui/widget/MySwipeRefreshLayout.java new file mode 100644 index 0000000..3dc9443 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/widget/MySwipeRefreshLayout.java @@ -0,0 +1,118 @@ + +package com.jun.elephant.ui.widget; + +import android.content.Context; +import android.graphics.Color; +import android.util.AttributeSet; +import android.view.ViewGroup; + +import in.srain.cube.views.ptr.PtrFrameLayout; +import in.srain.cube.views.ptr.header.MaterialHeader; + +/** + * 刷新控件 + * Created by Jun on 2016/3/9. + * + * PtrFrameLayout: 使用自定义头布局或者尾布局 + * PtrClassicFrameLayout:使用默认样式 + */ +public class MySwipeRefreshLayout extends PtrFrameLayout { + + private int[] colors = new int[]{Color.parseColor("#6E9AC1"), Color.parseColor("#6E9AC1"), + Color.parseColor("#6E9AC1"), Color.parseColor("#6E9AC1") }; + + public MySwipeRefreshLayout(Context context) { + super(context); + + this.initConfig(); + this.initHeader(); + this.initFooter(); + } + + public MySwipeRefreshLayout(Context context, AttributeSet attrs) { + super(context, attrs); + + this.initConfig(); + this.initHeader(); + this.initFooter(); + } + + private void initFooter() { + + MaterialHeader footer = new MaterialHeader(getContext()); + footer.setColorSchemeColors(colors); + footer.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + footer.setPadding(0, 20, 0, 25); + footer.setPtrFrameLayout(this); + + this.addPtrUIHandler(footer); + this.setFooterView(footer); + } + + private void initHeader() { + MaterialHeader header = new MaterialHeader(getContext()); + header.setColorSchemeColors(colors); + header.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + header.setPadding(0, 25, 0, 20); + header.setPtrFrameLayout(this); + + this.addPtrUIHandler(header); + this.setHeaderView(header); + } + + private void initConfig() { + /** + * 阻尼系数 + * 默认: 1.7f,越大,感觉下拉时越吃力 + */ + setResistance(1.7f); + + /** + * 触发刷新时移动的位置比例 + * 默认,1.2f,移动达到头部高度1.2倍时可触发刷新操作 + */ + setRatioOfHeaderHeightToRefresh(1.2f); + + /** + * 回弹延时 + * 默认 200ms,回弹到刷新高度所用时间 + */ + setDurationToClose(200); + + /** + * 头部回弹时间 + * 默认1000ms + */ + setDurationToCloseHeader(1000); + + /** + * 刷新是保持头部 + * 默认值 true + */ + setKeepHeaderWhenRefresh(true); + + /** + * 下拉刷新 / 释放刷新 + * 默认为释放刷新 false + */ + setPullToRefresh(true); + + /** + * 刷新时,保持内容不动,仅头部下移, 使用 Material Design 风格才好看一点 + * 默认 false + */ + setPinContent(true); + + /** + * 刷新模式 + * 默认 TOP:只支持下拉 + * Bottom:只支持上拉 + * BOTH:两种同时支持 + */ + setMode(Mode.BOTH); + + + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/widget/ThemeDialog.java b/app/src/main/java/com/jun/elephant/ui/widget/ThemeDialog.java new file mode 100644 index 0000000..7d8a198 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/widget/ThemeDialog.java @@ -0,0 +1,79 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.widget; + +import android.app.Dialog; +import android.content.Context; +import android.graphics.Rect; +import android.view.LayoutInflater; +import android.view.View; +import android.view.Window; + +import com.jun.elephant.R; + +/** + * Created by Jun on 2016/9/22. + */ + +public class ThemeDialog extends Dialog implements View.OnClickListener { + + private Context mContext; + + private OnThemeChangeListener onThemeChangeListener; + + public ThemeDialog(Context context) { + this(context, R.style.MyDialog_style); + + } + + public ThemeDialog(Context context, int themeResId) { + super(context, themeResId); + this.mContext = context; + initContentView(); + } + + private void initContentView() { + View view = LayoutInflater.from(mContext).inflate(R.layout.dialog_theme, null); + + Rect displayRectangle = new Rect(); + Window window = getWindow(); + window.getDecorView().getWindowVisibleDisplayFrame(displayRectangle); + view.setMinimumWidth((int)(displayRectangle.width() * 0.8f)); + window.setBackgroundDrawableResource(R.color.dialog_bg); + + setContentView(view); + + view.findViewById(R.id.theme_blue).setOnClickListener(this); + view.findViewById(R.id.theme_gray).setOnClickListener(this); + view.findViewById(R.id.theme_white).setOnClickListener(this); + } + + @Override + public void onClick(View v) { + if (onThemeChangeListener != null) { + onThemeChangeListener.onChangeTheme(v); + dismiss(); + } + } + + public interface OnThemeChangeListener { + void onChangeTheme(View view); + } + + public void setOnThemeChangeListener(OnThemeChangeListener onThemeChangeListener) { + this.onThemeChangeListener = onThemeChangeListener; + } +} diff --git a/app/src/main/java/com/jun/elephant/ui/widget/VoteDialog.java b/app/src/main/java/com/jun/elephant/ui/widget/VoteDialog.java new file mode 100644 index 0000000..8796df3 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/ui/widget/VoteDialog.java @@ -0,0 +1,121 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.ui.widget; + +import android.app.Dialog; +import android.content.Context; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.v4.content.ContextCompat; +import android.view.LayoutInflater; +import android.view.View; +import android.view.Window; +import android.widget.TextView; + +import com.jun.elephant.R; +import com.jun.elephant.entity.topic.TopicEntity; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * 投票会话框 + * Created by Jun on 2016/5/4. + */ +public class VoteDialog extends Dialog implements View.OnClickListener { + + public OnVoteDialogClickListener mOnVoteDialogClickListener; + @Bind(R.id.vote_up_tv) + TextView mVoteUpTv; + @Bind(R.id.vote_down_tv) + TextView mVoteDownTv; + private Context mContext; + private TopicEntity mTopicEntity; + + public VoteDialog(Context context) { + super(context, R.style.MyDialog_style); + mContext = context; + + initContentView(); + ButterKnife.bind(this); + initListener(); + } + + private void initContentView() { + View view = LayoutInflater.from(mContext).inflate(R.layout.dialog_vote, null); + + Rect displayRectangle = new Rect(); + Window window = getWindow(); + window.getDecorView().getWindowVisibleDisplayFrame(displayRectangle); + view.setMinimumWidth((int) (displayRectangle.width() * 0.8f)); + window.setBackgroundDrawableResource(R.color.dialog_bg); + + setContentView(view); + } + + public void setTopicEntity(TopicEntity mTopicEntity) { + this.mTopicEntity = mTopicEntity; + initView(); + } + + private void initView() { + if (mTopicEntity.isVoteDown()) { + setDrawableTop(mVoteDownTv, R.mipmap.ic_vote_down_select); + } else { + setDrawableTop(mVoteDownTv, R.mipmap.ic_vote_down_normal); + } + + if (mTopicEntity.isVoteUp()) { + setDrawableTop(mVoteUpTv, R.mipmap.ic_vote_up_select); + } else { + setDrawableTop(mVoteUpTv, R.mipmap.ic_vote_up_normal); + } + } + + private void initListener() { + mVoteDownTv.setOnClickListener(this); + mVoteUpTv.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.vote_down_tv: + if (mOnVoteDialogClickListener != null) + mOnVoteDialogClickListener.onVoteDownClick(); + break; + case R.id.vote_up_tv: + if (mOnVoteDialogClickListener != null) mOnVoteDialogClickListener.onVoteUpClick(); + break; + } + } + + private void setDrawableTop(TextView textView, int resId) { + Drawable drawable = ContextCompat.getDrawable(mContext, resId); + drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight()); + textView.setCompoundDrawables(null, drawable, null, null); + } + + public void setOnVoteDialogClickListener(OnVoteDialogClickListener onVoteDialogClickListener) { + mOnVoteDialogClickListener = onVoteDialogClickListener; + } + + public interface OnVoteDialogClickListener { + void onVoteDownClick(); + + void onVoteUpClick(); + } +} diff --git a/app/src/main/java/com/jun/elephant/util/FileUtils.java b/app/src/main/java/com/jun/elephant/util/FileUtils.java new file mode 100644 index 0000000..044a8d8 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/util/FileUtils.java @@ -0,0 +1,267 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.util; + +import java.io.File; +import java.util.Locale; + +/** + * 文件帮助类 + * Created by Jun on 2016/9/28. + */ + +public class FileUtils { + + public static final int BYTE = 1; + /** + * KB与Byte的倍数 + */ + public static final int KB = 1024; + /** + * MB与Byte的倍数 + */ + public static final int MB = 1048576; + /** + * GB与Byte的倍数 + */ + public static final int GB = 1073741824; + + + private FileUtils() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + /** + * 判断字符串是否为null或全为空格 + * + * @param s 待校验字符串 + * @return {@code true}: null或全空格
{@code false}: 不为null且不全空格 + */ + public static boolean isSpace(String s) { + return (s == null || s.trim().length() == 0); + } + + /** + * 根据文件路径获取文件 + * + * @param filePath 文件路径 + * @return 文件 + */ + public static File getFileByPath(String filePath) { + return isSpace(filePath) ? null : new File(filePath); + } + + /** + * 判断文件是否存在 + * + * @param filePath 文件路径 + * @return {@code true}: 存在
{@code false}: 不存在 + */ + public static boolean isFileExists(String filePath) { + return isFileExists(getFileByPath(filePath)); + } + + /** + * 判断文件是否存在 + * + * @param file 文件 + * @return {@code true}: 存在
{@code false}: 不存在 + */ + public static boolean isFileExists(File file) { + return file != null && file.exists(); + } + + /** + * 判断是否是目录 + * + * @param dirPath 目录路径 + * @return {@code true}: 是
{@code false}: 否 + */ + public static boolean isDir(String dirPath) { + return isDir(getFileByPath(dirPath)); + } + + /** + * 判断是否是目录 + * + * @param file 文件 + * @return {@code true}: 是
{@code false}: 否 + */ + public static boolean isDir(File file) { + return isFileExists(file) && file.isDirectory(); + } + + /** + * 判断是否是文件 + * + * @param filePath 文件路径 + * @return {@code true}: 是
{@code false}: 否 + */ + public static boolean isFile(String filePath) { + return isFile(getFileByPath(filePath)); + } + + /** + * 判断是否是文件 + * + * @param file 文件 + * @return {@code true}: 是
{@code false}: 否 + */ + public static boolean isFile(File file) { + return isFileExists(file) && file.isFile(); + } + + /** + * 判断目录是否存在,不存在则判断是否创建成功 + * + * @param dirPath 文件路径 + * @return {@code true}: 存在或创建成功
{@code false}: 不存在或创建失败 + */ + public static boolean createOrExistsDir(String dirPath) { + return createOrExistsDir(getFileByPath(dirPath)); + } + + /** + * 判断目录是否存在,不存在则判断是否创建成功 + * + * @param file 文件 + * @return {@code true}: 存在或创建成功
{@code false}: 不存在或创建失败 + */ + public static boolean createOrExistsDir(File file) { + // 如果存在,是目录则返回true,是文件则返回false,不存在则返回是否创建成功 + return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); + } + + /** + * 获取文件大小 + * + * @param filePath 文件路径 + * @return 文件大小 + */ + public static String getFileSize(String filePath) { + return getFileSize(getFileByPath(filePath)); + } + + /** + * 获取文件大小 + *

例如:getFileSize(file, ConstUtils.MB); 返回文件大小单位为MB

+ * + * @param file 文件 + * @return 文件大小 + */ + public static String getFileSize(File file) { + if (!isFileExists(file)) return ""; + return byte2FitSize(file.length()); + } + + /** + * 字节数转合适大小 + *

保留3位小数

+ * + * @param byteNum 字节数 + * @return 1...1024 unit + */ + public static String byte2FitSize(long byteNum) { + if (byteNum < 0) { + return "shouldn't be less than zero!"; + } else if (byteNum < KB) { + return String.format(Locale.getDefault(), "%.2fB", (double) byteNum); + } else if (byteNum < MB) { + return String.format(Locale.getDefault(), "%.2fKB", (double) byteNum / KB); + } else if (byteNum < GB) { + return String.format(Locale.getDefault(), "%.2fMB", (double) byteNum / MB); + } else { + return String.format(Locale.getDefault(), "%.2fGB", (double) byteNum / GB); + } + } + + /** + * 获得某个目录下所有文件大小 + * @param dirPath 目录路径 + * @return 目录大小(String 类型) + */ + public static String getCacheDirSize(File file) { + return byte2FitSize(getDirSize(file)); + } + + /** + * 获取某个目录下所有文件大小 + * @param dirPath 目录 + * @return 目录大小(long 类型) + */ + public static long getDirSize(File file) { + + if (file == null) return 0; + + if (!isDir(file)) return 0; + + long dirSize = 0; + File[] files = file.listFiles(); + for (int i = 0; i < files.length; i++) { + if (files[i].isFile()) { //文件 + dirSize += files[i].length(); + } else if (files[i].isDirectory()) {//如果还有文件夹 + dirSize += getDirSize(files[i]);//不停回调 + } + } + + return dirSize; + } + + /** + * 删除目录 + * + * @param dir 目录 + * @return {@code true}: 删除成功
{@code false}: 删除失败 + */ + public static boolean deleteDir(File dir) { + if (dir == null) return false; + // 目录不存在返回true + if (!dir.exists()) return true; + // 不是目录返回false + if (!dir.isDirectory()) return false; + // 现在文件存在且是文件夹 + File[] files = dir.listFiles(); + for (File file : files) { + if (file.isFile()) { + if (!deleteFile(file)) return false; + } else if (file.isDirectory()) { + if (!deleteDir(file)) return false; + } + } + return dir.delete(); + } + + /** + * 删除文件 + * + * @param srcFilePath 文件路径 + * @return {@code true}: 删除成功
{@code false}: 删除失败 + */ + public static boolean deleteFile(String srcFilePath) { + return deleteFile(getFileByPath(srcFilePath)); + } + + /** + * 删除文件 + * + * @param file 文件 + * @return {@code true}: 删除成功
{@code false}: 删除失败 + */ + public static boolean deleteFile(File file) { + return file != null && (!file.exists() || file.isFile() && file.delete()); + } +} diff --git a/app/src/main/java/com/jun/elephant/util/JLog.java b/app/src/main/java/com/jun/elephant/util/JLog.java new file mode 100644 index 0000000..148e50a --- /dev/null +++ b/app/src/main/java/com/jun/elephant/util/JLog.java @@ -0,0 +1,125 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.util; + +import android.util.Log; + +import com.orhanobut.logger.Logger; + +/** + * 日志管理工具 + * Created by Jun on 2016/2/25. + */ +public class JLog { + + public static boolean DEBUG ; + + public static void initialize(boolean isDebug) { + JLog.DEBUG = isDebug; + } + + public static void v(String tag, String message) { + if(DEBUG) { + Log.v(tag, message); + } + } + + public static void d(String tag, String message) { + if(DEBUG) { + Log.d(tag, message); + } + } + + public static void i(String tag, String message) { + if(DEBUG) { + Log.i(tag, message); + } + } + + public static void w(String tag, String message) { + if(DEBUG) { + Log.w(tag, message); + } + } + + public static void e(String tag, String message) { + if(DEBUG) { + Log.e(tag, message); + } + } + + public static void e(String tag, String message, Exception e) { + if(DEBUG) { + Log.e(tag, message, e); + } + } + + + /** + * 使用 Logger 工具 + */ + + public static void logv(String tag, String message) { + if (DEBUG) { + Logger.init(tag); + Logger.v(message); + } + } + + public static void logd(String tag, String message) { + if(DEBUG) { + Logger.init(tag); + Logger.d(message); + } + } + + public static void logi(String tag, String message) { + if(DEBUG) { + Logger.init(tag); + Logger.i(message); + } + } + + public static void logw(String tag, String message) { + if(DEBUG) { + Logger.init(tag); + Logger.w(message); + } + } + + public static void loge(String tag, String message) { + if(DEBUG) { + Logger.init(tag); + Logger.e(message); + + + } + } + + public static void loge(String tag, String message, Exception e) { + if(DEBUG) { + Logger.init(tag); + Logger.e(message, e); + } + } + + public static void logJ(String tag, String message) { + if (DEBUG) { + Logger.init(tag); + Logger.json(message); + } + } +} diff --git a/app/src/main/java/com/jun/elephant/util/MarkDownRenderer.java b/app/src/main/java/com/jun/elephant/util/MarkDownRenderer.java new file mode 100644 index 0000000..37a17b3 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/util/MarkDownRenderer.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.util; + +import com.commonsware.cwac.anddown.AndDown; +import com.jun.elephant.global.Constants; + + +public class MarkDownRenderer { + AndDown andDown = new AndDown(); + + public String renderMarkdown(String markdownRaw) { + return Constants.MD_HTML_PREFIX + + andDown.markdownToHtml(markdownRaw) + + Constants.MD_HTML_SUFFIX; + } + +// private String themeStringFromContext(Context context) { +// String theme = getThemeFromPrefs(context); +// if (!theme.equals("")) { +// if (theme.equals(context.getString(R.string.theme_dark))) { +// return Constants.DARK_MD_HTML_PREFIX; +// } else { +// return Constants.MD_HTML_PREFIX; +// } +// } +// return ""; +// } +// +// private String getThemeFromPrefs(Context context) { +// return PreferenceManager.getDefaultSharedPreferences(context) +// .getString(context.getString(R.string.pref_theme_key), ""); +// } +} diff --git a/app/src/main/java/com/jun/elephant/util/NetworkUtils.java b/app/src/main/java/com/jun/elephant/util/NetworkUtils.java new file mode 100644 index 0000000..32ea36b --- /dev/null +++ b/app/src/main/java/com/jun/elephant/util/NetworkUtils.java @@ -0,0 +1,98 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.util; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.telephony.TelephonyManager; + +/** + * Created by Jun on 2016/1/7. + */ +public class NetworkUtils { + public static boolean isNetworkAvailable(Context c) { + Context context = c.getApplicationContext(); + // 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理) + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + + if (connectivityManager == null) { + return false; + } else { + // 获取NetworkInfo对象 + NetworkInfo[] networkInfo = connectivityManager.getAllNetworkInfo(); + + if (networkInfo != null && networkInfo.length > 0) { + for (NetworkInfo aNetworkInfo : networkInfo) { +// System.out.println(i + "===状态===" + networkInfo[i].getState()); +// System.out.println(i + "===类型===" + networkInfo[i].getTypeName()); + // 判断当前网络状态是否为连接状态 + if (aNetworkInfo.getState() == NetworkInfo.State.CONNECTED) { + return true; + } + } + } + } + return false; + } + + /** + * 判断WIFI是否打开 + * @param context + * @return + */ + public static boolean isWifiEnabled(Context context) { + ConnectivityManager mgrConn = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + TelephonyManager mgrTel = (TelephonyManager) context + .getSystemService(Context.TELEPHONY_SERVICE); + return ((mgrConn.getActiveNetworkInfo() != null && mgrConn + .getActiveNetworkInfo().getState() == NetworkInfo.State.CONNECTED) || mgrTel + .getNetworkType() == TelephonyManager.NETWORK_TYPE_UMTS); + } + + /** + * 判断是否是3G网络 + * @param context + * @return + */ + public static boolean is3rd(Context context) { + ConnectivityManager cm = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkINfo = cm.getActiveNetworkInfo(); + if (networkINfo != null + && networkINfo.getType() == ConnectivityManager.TYPE_MOBILE) { + return true; + } + return false; + } + + /** + * 判断是wifi还是3g网络 + * @param context + * @return + */ + public static boolean isWifi(Context context) { + ConnectivityManager cm = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkINfo = cm.getActiveNetworkInfo(); + if (networkINfo != null + && networkINfo.getType() == ConnectivityManager.TYPE_WIFI) { + return true; + } + return false; + } +} diff --git a/app/src/main/java/com/jun/elephant/util/OpenWebViewUtils.java b/app/src/main/java/com/jun/elephant/util/OpenWebViewUtils.java new file mode 100644 index 0000000..c68ca7c --- /dev/null +++ b/app/src/main/java/com/jun/elephant/util/OpenWebViewUtils.java @@ -0,0 +1,96 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.util; + +import android.content.Context; +import android.content.Intent; + +import com.jun.elephant.R; +import com.jun.elephant.global.Constants; +import com.jun.elephant.ui.main.WebViewActivity; + +/** + * Created by Jun on 2016/9/29. + */ + +public class OpenWebViewUtils { + + /** + * 跳转到关于 PHPHub WebView 界面 + * @param context 上下文 + */ + public static void aboutPHPHub(Context context) { + String title = context.getString(R.string.about_phphub); + String url = Constants.ABOUT_PHPHUB; + Intent intent = WebViewActivity.newIntent(context, title, url); + context.startActivity(intent); + } + + /** + * 跳转到关于我 WebView 界面 + * @param context 上下文 + */ + public static void aboutMe(Context context) { + String title = context.getString(R.string.about_me); + String url = Constants.ABOUT_ME; + Intent intent = WebViewActivity.newIntent(context, title, url); + context.startActivity(intent); + } + + /** + * 跳转到获取源代码 WebView 界面 + * @param context 上下文 + */ + public static void getOpenSource(Context context) { + String title = context.getString(R.string.open_source); + String url = Constants.OPEN_SOURCE; + Intent intent = WebViewActivity.newIntent(context, title, url); + context.startActivity(intent); + } + + /** + * 跳转到登录帮助 WebView 界面 + * @param context 上下文 + */ + public static void loginHelp(Context context) { + String title = context.getString(R.string.menu_login_help); + String url = Constants.LOGIN_HELP; + Intent intent = WebViewActivity.newIntent(context, title, url); + context.startActivity(intent); + } + + /** + * 跳转到我回复 WebView 界面 + * @param context 上下文 + * @param url 我回复的链接地址 + */ + public static void myReply(Context context, String url) { + String title = context.getString(R.string.menu_revert); + Intent intent = WebViewActivity.newIntent(context, title, url); + context.startActivity(intent); + } + + /** + * 跳转到个人博客 + * @param context 上下文 + * @param title 博客名 + * @param url 博客地址 + */ + public static void blog(Context context, String title, String url) { + Intent intent = WebViewActivity.newIntent(context, title, url); + context.startActivity(intent); + } +} diff --git a/app/src/main/java/com/jun/elephant/util/SharePreferencesHelper.java b/app/src/main/java/com/jun/elephant/util/SharePreferencesHelper.java new file mode 100644 index 0000000..5f80a22 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/util/SharePreferencesHelper.java @@ -0,0 +1,93 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.util; + +import android.content.Context; +import android.content.SharedPreferences; + +/** + * Created by Jun on 2016/5/3. + */ +public class SharePreferencesHelper { + + private static final String TAG = "SharePreferencesHelper"; + + private SharedPreferences mPreferences; + + private SharedPreferences.Editor mEditor; + + private static SharePreferencesHelper mSPHelper; + + public static SharePreferencesHelper getInstance(Context context) { + if (mSPHelper == null) + mSPHelper = new SharePreferencesHelper(context); + + return mSPHelper; + } + + private SharePreferencesHelper(Context context) { + mPreferences = context.getSharedPreferences(TAG, Context.MODE_APPEND); + } + + public boolean putString(String key, String value) { + mEditor = mPreferences.edit(); + mEditor.putString(key, value); + return mEditor.commit(); + } + + public String getString(String key) { + return mPreferences.getString(key, ""); + } + + public String getString(String key, String defValue) { + return mPreferences.getString(key, defValue); + } + + public boolean removeString(String key) { + mEditor = mPreferences.edit(); + mEditor.remove(key); + return mEditor.commit(); + } + + public boolean putInt(String key, int value) { + mEditor = mPreferences.edit(); + mEditor.putInt(key, value); + return mEditor.commit(); + } + + public int getInt(String key) { + return mPreferences.getInt(key, 0); + } + + public int getInt(String key, int defValue) { + return mPreferences.getInt(key, defValue); + } + + public boolean putBoolean(String key, boolean value) { + mEditor = mPreferences.edit(); + mEditor.putBoolean(key, value); + return mEditor.commit(); + } + + public boolean getBoolean(String key) { + return mPreferences.getBoolean(key, false); + } + + public boolean getBoolean(String key, boolean defValue) { + return mPreferences.getBoolean(key, defValue); + } + +} diff --git a/app/src/main/java/com/jun/elephant/util/ShareUtil.java b/app/src/main/java/com/jun/elephant/util/ShareUtil.java new file mode 100644 index 0000000..6397dcd --- /dev/null +++ b/app/src/main/java/com/jun/elephant/util/ShareUtil.java @@ -0,0 +1,61 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.util; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.widget.Toast; + +import com.jun.elephant.R; + +import java.util.List; + +/** + * Created by Jun on 2016/4/21. + */ +public class ShareUtil { + + /** + * 分享 + */ + public static void share(Context context, String content){ + Intent intent=new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.share)); + intent.putExtra(Intent.EXTRA_TEXT, content); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(Intent.createChooser(intent, context.getString(R.string.share))); + } + + /** + * 反馈 + */ + public static void feedback(Context context, String email) { + Uri uri = Uri.parse("mailto:" + email); + final Intent intent = new Intent(Intent.ACTION_SENDTO, uri); + PackageManager pm = context.getPackageManager(); + List infos = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + if (infos == null || infos.size() <= 0){ + Toast.makeText(context, context.getString(R.string.no_email), Toast.LENGTH_SHORT).show(); + return; + } + context.startActivity(intent); + } + +} diff --git a/app/src/main/java/com/jun/elephant/util/SystemUtil.java b/app/src/main/java/com/jun/elephant/util/SystemUtil.java new file mode 100644 index 0000000..019a6af --- /dev/null +++ b/app/src/main/java/com/jun/elephant/util/SystemUtil.java @@ -0,0 +1,44 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.util; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.text.TextUtils; + +/** + * 获取系统信息工具类 + * Created by Jun on 2016/4/21. + */ +public class SystemUtil { + /** + * 获取当前应用的版本号 + */ + public static String getVersionName(Context context) { + try { + PackageManager packageManager = context.getPackageManager(); + PackageInfo packInfo = packageManager.getPackageInfo(context.getPackageName(),0); + String version = packInfo.versionName; + if (!TextUtils.isEmpty(version)) { + return version; + } + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } +} diff --git a/app/src/main/java/com/jun/elephant/util/ThemeUtil.java b/app/src/main/java/com/jun/elephant/util/ThemeUtil.java new file mode 100644 index 0000000..7e05a18 --- /dev/null +++ b/app/src/main/java/com/jun/elephant/util/ThemeUtil.java @@ -0,0 +1,76 @@ +/* + * Copyright 2016 Freelander + *

+ * 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. + */ +package com.jun.elephant.util; + +import android.content.Context; + +import com.jun.elephant.global.Constants; + +/** + * 主题切换帮助类 + * Created by Jun on 2016/9/21. + */ + +public class ThemeUtil { + private SharePreferencesHelper mSPHelper; + + private static ThemeUtil mThemeUtil; + + public static ThemeUtil getInstance(Context context) { + if (mThemeUtil == null) { + mThemeUtil = new ThemeUtil(context); + } + + return mThemeUtil; + } + + public ThemeUtil(Context context) { + super(); + mSPHelper = SharePreferencesHelper.getInstance(context); + } + + /** + * 保存主题设置 + * @param theme 主题 + */ + public void setTheme(String theme) { + mSPHelper.putString(Constants.Key.THEME_MODE, theme); + } + + /** + * 取出当前主题 + * @return 主题 + */ + public String getTheme() { + return mSPHelper.getString(Constants.Key.THEME_MODE, Constants.Theme.Blue); + } + + /** + * 判断是否是蓝色主题 + * @return + */ + public boolean isBlueTheme() { + return getTheme().equals(Constants.Theme.Blue); + } + + /** + * 判断是否是白色主题 + * @return + */ + public boolean isWhiteTheme() { + return getTheme().equals(Constants.Theme.White); + } +} diff --git a/app/src/main/res/drawable-v21/ripple.xml b/app/src/main/res/drawable-v21/ripple.xml new file mode 100644 index 0000000..3c66d04 --- /dev/null +++ b/app/src/main/res/drawable-v21/ripple.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn1_normal.xml b/app/src/main/res/drawable/btn1_normal.xml new file mode 100644 index 0000000..d04f277 --- /dev/null +++ b/app/src/main/res/drawable/btn1_normal.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn1_selector.xml b/app/src/main/res/drawable/btn1_selector.xml new file mode 100644 index 0000000..22d95ff --- /dev/null +++ b/app/src/main/res/drawable/btn1_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn2_tran_style.xml b/app/src/main/res/drawable/btn2_tran_style.xml new file mode 100644 index 0000000..ebcecbe --- /dev/null +++ b/app/src/main/res/drawable/btn2_tran_style.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_bg_white.xml b/app/src/main/res/drawable/btn_bg_white.xml new file mode 100644 index 0000000..ef6d505 --- /dev/null +++ b/app/src/main/res/drawable/btn_bg_white.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/dialog_bg_style.xml b/app/src/main/res/drawable/dialog_bg_style.xml new file mode 100644 index 0000000..d60b0df --- /dev/null +++ b/app/src/main/res/drawable/dialog_bg_style.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/edt_line.xml b/app/src/main/res/drawable/edt_line.xml new file mode 100644 index 0000000..82419b1 --- /dev/null +++ b/app/src/main/res/drawable/edt_line.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/edt_line_gray.xml b/app/src/main/res/drawable/edt_line_gray.xml new file mode 100644 index 0000000..d4eecbe --- /dev/null +++ b/app/src/main/res/drawable/edt_line_gray.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/edt_line_theme.xml b/app/src/main/res/drawable/edt_line_theme.xml new file mode 100644 index 0000000..d4e1d61 --- /dev/null +++ b/app/src/main/res/drawable/edt_line_theme.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_place_black_24dp.xml b/app/src/main/res/drawable/ic_place_black_24dp.xml new file mode 100644 index 0000000..a964276 --- /dev/null +++ b/app/src/main/res/drawable/ic_place_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/item_bg_line.xml b/app/src/main/res/drawable/item_bg_line.xml new file mode 100644 index 0000000..24f52b8 --- /dev/null +++ b/app/src/main/res/drawable/item_bg_line.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shadow.xml b/app/src/main/res/drawable/shadow.xml new file mode 100644 index 0000000..b65adb2 --- /dev/null +++ b/app/src/main/res/drawable/shadow.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shadow1.xml b/app/src/main/res/drawable/shadow1.xml new file mode 100644 index 0000000..3b9e563 --- /dev/null +++ b/app/src/main/res/drawable/shadow1.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about_app.xml b/app/src/main/res/layout/activity_about_app.xml new file mode 100644 index 0000000..c68b9d1 --- /dev/null +++ b/app/src/main/res/layout/activity_about_app.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_comment_list.xml b/app/src/main/res/layout/activity_comment_list.xml new file mode 100644 index 0000000..ced5624 --- /dev/null +++ b/app/src/main/res/layout/activity_comment_list.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..c006ea4 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_setting.xml b/app/src/main/res/layout/activity_setting.xml new file mode 100644 index 0000000..d874587 --- /dev/null +++ b/app/src/main/res/layout/activity_setting.xml @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_show_photo.xml b/app/src/main/res/layout/activity_show_photo.xml new file mode 100644 index 0000000..45cfdbc --- /dev/null +++ b/app/src/main/res/layout/activity_show_photo.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_topic_details.xml b/app/src/main/res/layout/activity_topic_details.xml new file mode 100644 index 0000000..a1ae2e5 --- /dev/null +++ b/app/src/main/res/layout/activity_topic_details.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_topic_publish.xml b/app/src/main/res/layout/activity_topic_publish.xml new file mode 100644 index 0000000..00f828a --- /dev/null +++ b/app/src/main/res/layout/activity_topic_publish.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_user_info.xml b/app/src/main/res/layout/activity_user_info.xml new file mode 100644 index 0000000..dab8128 --- /dev/null +++ b/app/src/main/res/layout/activity_user_info.xml @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_user_info_edit.xml b/app/src/main/res/layout/activity_user_info_edit.xml new file mode 100644 index 0000000..154905d --- /dev/null +++ b/app/src/main/res/layout/activity_user_info_edit.xml @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +