├── .gitignore
├── README.md
├── album
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── licrafter
│ │ └── album
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── boxing_impl
│ │ │ ├── WindowManagerHelper.java
│ │ │ ├── adapter
│ │ │ ├── BoxingAlbumAdapter.java
│ │ │ └── BoxingMediaAdapter.java
│ │ │ ├── ui
│ │ │ ├── BoxingActivity.java
│ │ │ ├── BoxingBottomSheetActivity.java
│ │ │ ├── BoxingBottomSheetFragment.java
│ │ │ ├── BoxingRawImageFragment.java
│ │ │ ├── BoxingViewActivity.java
│ │ │ └── BoxingViewFragment.java
│ │ │ └── view
│ │ │ ├── HackyViewPager.java
│ │ │ ├── MediaItemLayout.java
│ │ │ └── SpacesItemDecoration.java
│ └── res
│ │ ├── anim
│ │ ├── fade_in.xml
│ │ └── fade_out.xml
│ │ ├── drawable-hdpi
│ │ ├── ic_broken_image.png
│ │ ├── ic_camera.png
│ │ ├── ic_camera_white.png
│ │ ├── ic_check_black.png
│ │ ├── ic_checked.png
│ │ ├── ic_default_image.png
│ │ └── ic_play.png
│ │ ├── drawable-xhdpi
│ │ ├── ic_broken_image.png
│ │ ├── ic_camera.png
│ │ ├── ic_camera_white.png
│ │ ├── ic_check_black.png
│ │ ├── ic_checked.png
│ │ ├── ic_default_image.png
│ │ └── ic_play.png
│ │ ├── drawable-xxhdpi
│ │ ├── ic_broken_image.png
│ │ ├── ic_camera.png
│ │ ├── ic_camera_white.png
│ │ ├── ic_check_black.png
│ │ ├── ic_checked.png
│ │ ├── ic_default_image.png
│ │ └── ic_play.png
│ │ ├── drawable
│ │ ├── ic_unchecked.xml
│ │ ├── selector_button_solid.xml
│ │ └── shape_popup_background_material.xml
│ │ ├── layout
│ │ ├── activity_boxing.xml
│ │ ├── activity_boxing_bottom_sheet.xml
│ │ ├── activity_boxing_view.xml
│ │ ├── fragmant_boxing_view.xml
│ │ ├── fragment_boxing_bottom_sheet.xml
│ │ ├── fragment_boxing_raw_image.xml
│ │ ├── layout_album.xml
│ │ ├── layout_album_item.xml
│ │ ├── layout_app_bar.xml
│ │ ├── layout_empty_txt.xml
│ │ ├── layout_media_item.xml
│ │ ├── layout_recycleview_header.xml
│ │ ├── layout_recycleview_item.xml
│ │ └── layout_simple_media_item.xml
│ │ ├── menu
│ │ ├── activity_image_viewer.xml
│ │ └── activity_pick_flow.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── licrafter
│ └── album
│ └── ExampleUnitTest.java
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── licrafter
│ │ └── cnode
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ ├── css
│ │ │ └── github.css
│ │ ├── html
│ │ │ └── markdown.html
│ │ └── js
│ │ │ ├── jquery-3.1.1.min.js
│ │ │ ├── markdownview.js
│ │ │ └── marked.min.js
│ ├── java
│ │ └── com
│ │ │ └── licrafter
│ │ │ └── cnode
│ │ │ ├── BaseApplication.java
│ │ │ ├── api
│ │ │ ├── CNodeApi.java
│ │ │ ├── Urls.java
│ │ │ └── service
│ │ │ │ └── CNodeService.java
│ │ │ ├── base
│ │ │ ├── BaseActivity.java
│ │ │ └── BaseFragment.java
│ │ │ ├── cache
│ │ │ └── UserCache.java
│ │ │ ├── model
│ │ │ ├── AccessTokenBody.java
│ │ │ ├── CollectionBody.java
│ │ │ ├── LoginBody.java
│ │ │ ├── LoginResultModel.java
│ │ │ ├── MarkResultModel.java
│ │ │ ├── NotificationModel.java
│ │ │ ├── PostTopicResultModel.java
│ │ │ ├── ReplyBody.java
│ │ │ ├── ReplyResultModel.java
│ │ │ ├── TabModel.java
│ │ │ ├── TopicDetailModel.java
│ │ │ ├── UnReadCountModel.java
│ │ │ ├── UserDetailModel.java
│ │ │ └── entity
│ │ │ │ ├── Author.java
│ │ │ │ ├── Notification.java
│ │ │ │ ├── PostTopic.java
│ │ │ │ ├── Reply.java
│ │ │ │ ├── TAB.java
│ │ │ │ ├── Topic.java
│ │ │ │ ├── TopicDetail.java
│ │ │ │ └── UserDetail.java
│ │ │ ├── mvp
│ │ │ ├── presenter
│ │ │ │ ├── BasePresenter.java
│ │ │ │ ├── LoginPresenter.java
│ │ │ │ ├── MarkdownEditPresenter.java
│ │ │ │ ├── NotificationCenterPresenter.java
│ │ │ │ ├── TopicDetailPresenter.java
│ │ │ │ ├── TopicListPresenter.java
│ │ │ │ └── UserDetailPresenter.java
│ │ │ └── view
│ │ │ │ └── MvpView.java
│ │ │ ├── ui
│ │ │ ├── activity
│ │ │ │ ├── LoginActivity.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── MarkdownEditActivity.java
│ │ │ │ ├── NotificationCenterActivity.java
│ │ │ │ ├── QRActivity.java
│ │ │ │ └── TopicDetailActivity.java
│ │ │ └── fragment
│ │ │ │ ├── CategoryFragment.java
│ │ │ │ ├── HomePageFragment.java
│ │ │ │ ├── MdEditFragment.java
│ │ │ │ ├── MdPreviewFragment.java
│ │ │ │ ├── MineFragment.java
│ │ │ │ ├── MineTopicListFragment.java
│ │ │ │ └── TopicListFragment.java
│ │ │ ├── utils
│ │ │ ├── BitmapCompressUtil.java
│ │ │ ├── DateUtils.java
│ │ │ ├── DipConvertUtils.java
│ │ │ ├── FragmentUtils.java
│ │ │ ├── ImageLoader.java
│ │ │ ├── IntentUtils.java
│ │ │ ├── LogUtils.java
│ │ │ ├── PermissionUtils.java
│ │ │ ├── SharedPreferenceUtils.java
│ │ │ ├── StatusBarUtils.java
│ │ │ ├── SwipeRefreshUtils.java
│ │ │ ├── TopicDividerDecoration.java
│ │ │ └── http
│ │ │ │ ├── CustomCookieJar.java
│ │ │ │ ├── HttpClient.java
│ │ │ │ ├── PersistentCookieStore.java
│ │ │ │ └── SerializableHttpCookie.java
│ │ │ └── widget
│ │ │ ├── BoxingPicassoLoader.java
│ │ │ ├── BoxingUcrop.java
│ │ │ ├── CNodeWebView.java
│ │ │ ├── HorizontalOperatorView.java
│ │ │ ├── MarkdownPreview.java
│ │ │ ├── NotScrollViewPager.java
│ │ │ ├── dialog
│ │ │ ├── InsertLinkDialog.java
│ │ │ └── UploadingDialog.java
│ │ │ ├── richEditView
│ │ │ └── engine
│ │ │ │ └── PerformEditable.java
│ │ │ └── richTextView
│ │ │ ├── AsyncImageGetter.java
│ │ │ └── RichTextView.java
│ └── res
│ │ ├── anim
│ │ ├── fragment_pop_in.xml
│ │ ├── fragment_pop_out.xml
│ │ ├── fragment_translate_in.xml
│ │ └── fragment_translate_out.xml
│ │ ├── color
│ │ └── color_bottom_navigation_item.xml
│ │ ├── drawable
│ │ ├── bg_gray_rect.xml
│ │ ├── bg_operator_clicked.xml
│ │ ├── bg_red_dot.xml
│ │ ├── bg_topic_tag.xml
│ │ └── bg_topic_tag_normal.xml
│ │ ├── layout
│ │ ├── activity_login.xml
│ │ ├── activity_main.xml
│ │ ├── activity_markdown_edit.xml
│ │ ├── activity_notification_center.xml
│ │ ├── activity_qr.xml
│ │ ├── activity_topic_detail.xml
│ │ ├── fragment_category.xml
│ │ ├── fragment_home_page.xml
│ │ ├── fragment_md_edit.xml
│ │ ├── fragment_md_preview.xml
│ │ ├── fragment_mine.xml
│ │ ├── fragment_mine_topic_list.xml
│ │ ├── fragment_topic_list.xml
│ │ ├── include_comments_bottom_sheet.xml
│ │ ├── item_detail_header.xml
│ │ ├── item_empty.xml
│ │ ├── item_footer.xml
│ │ ├── item_notification.xml
│ │ ├── item_reply.xml
│ │ ├── item_reply_count.xml
│ │ ├── item_topic.xml
│ │ ├── layout_insert_link_dialog.xml
│ │ ├── layout_operator.xml
│ │ ├── layout_uploading_dialog.xml
│ │ ├── toolbar_common.xml
│ │ └── toolbar_create_topic.xml
│ │ ├── menu
│ │ ├── menu_bottom_navigation.xml
│ │ ├── menu_send_post.xml
│ │ └── menu_topic_detail.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_code.png
│ │ ├── ic_collected.png
│ │ ├── ic_comment.png
│ │ ├── ic_comment_gray.png
│ │ ├── ic_create_topic.png
│ │ ├── ic_empty.png
│ │ ├── ic_font_blod.png
│ │ ├── ic_font_italic.png
│ │ ├── ic_gray_dot.png
│ │ ├── ic_image.png
│ │ ├── ic_launcher.png
│ │ ├── ic_link.png
│ │ ├── ic_more_infomation.png
│ │ ├── ic_notification.png
│ │ ├── ic_ol.png
│ │ ├── ic_preview.png
│ │ ├── ic_profit_bg.jpg
│ │ ├── ic_quote.png
│ │ ├── ic_reply.png
│ │ ├── ic_send.png
│ │ ├── ic_settings.png
│ │ ├── ic_share.png
│ │ ├── ic_tab_category.png
│ │ ├── ic_tab_home_page.png
│ │ ├── ic_tab_mine.png
│ │ ├── ic_tab_notification.png
│ │ ├── ic_ul.png
│ │ ├── ic_uncollected.png
│ │ ├── ic_unup.png
│ │ ├── ic_up.png
│ │ └── ic_visit.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-v19
│ │ └── dimens.xml
│ │ ├── values-v21
│ │ └── styles.xml
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── arrays.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── licrafter
│ └── cnode
│ └── ExampleUnitTest.java
├── art
├── 01.jpg
├── 02.jpg
├── 03.jpg
├── 04.jpg
├── 05.jpg
├── 06.jpg
├── 07.jpg
└── 08.jpg
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | .idea/
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CNode-android
2 | a unofficial android client of [https://cnodejs.org/](https://cnodejs.org/)
3 |
4 | [](https://github.com/shellljx/Cnode-android)
5 | [](https://github.com/shellljx/CNode-android/issues)
6 | 
7 |
8 | # Apk Download
9 |
10 | [https://fir.im/r26f](https://fir.im/r26f)
11 |
12 | # Completed
13 |
14 | * 扫码登录功能
15 | * 获取文章等信息并展示功能
16 | * Markdown编辑器以及预览功能
17 | * 发表文章和评论,以及点赞等功能
18 | * 对Fragment的内存恢复做了处理
19 | * 图片压缩上传
20 |
21 | # Screenshots
22 | 






23 |
24 | # Thanks to the open source project
25 | * [RxJava](https://github.com/ReactiveX/RxJava)
26 | * [Butterknife](https://github.com/JakeWharton/butterknife)
27 | * [Picasso](https://github.com/square/picasso)
28 | * [Retrofix](https://github.com/square/retrofit)
29 | * [RoundedImageview](https://github.com/vinc3m1/RoundedImageView)
30 | * [Zxing-android-embedded](https://github.com/journeyapps/zxing-android-embedded)
31 | * [Avoscloud-sdk](https://github.com/leancloud/leancloud-sdk)
32 | * [Bilibili-Boxing](https://github.com/Bilibili/boxing)
33 |
--------------------------------------------------------------------------------
/album/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/album/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion '26.0.2'
6 |
7 | defaultConfig {
8 | minSdkVersion 16
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | compile fileTree(dir: 'libs', include: ['*.jar'])
26 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
27 | exclude group: 'com.android.support', module: 'support-annotations'
28 | })
29 | compile 'com.android.support:appcompat-v7:25.3.1'
30 | compile 'com.android.support:design:25.3.1'
31 | compile('com.chensl.rotatephotoview:rotatephotoview:1.0.5') {
32 | exclude group: 'com.android.support', module: 'appcompat'
33 | exclude group: 'com.android.support', module: 'support-v4'
34 | exclude module: 'recyclerview-v7'
35 | }
36 | compile 'com.bilibili:boxing:0.5.0'
37 | testCompile 'junit:junit:4.12'
38 | }
39 |
--------------------------------------------------------------------------------
/album/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk-macosx/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/album/src/androidTest/java/com/licrafter/album/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.album;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.licrafter.album.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/album/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
12 |
13 |
14 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/album/src/main/java/boxing_impl/WindowManagerHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Bilibili
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package boxing_impl;
19 |
20 | import android.content.Context;
21 | import android.util.DisplayMetrics;
22 | import android.util.TypedValue;
23 | import android.view.Display;
24 | import android.view.WindowManager;
25 |
26 | /**
27 | * @author ChenSL
28 | */
29 | public class WindowManagerHelper {
30 | private static WindowManager getWindowManager(Context context) {
31 | Object service = context.getSystemService(Context.WINDOW_SERVICE);
32 | if (service == null)
33 | return null;
34 |
35 | return (WindowManager) service;
36 | }
37 |
38 | private static Display getDefaultDisplay(Context context) {
39 | WindowManager wm = getWindowManager(context);
40 | if (wm == null)
41 | return null;
42 |
43 | return wm.getDefaultDisplay();
44 | }
45 |
46 | public static int getScreenHeight(Context context) {
47 | DisplayMetrics dm = getDisplayMetrics(context);
48 | if (dm != null) {
49 | return dm.heightPixels;
50 | }
51 | return 0;
52 | }
53 |
54 | public static int getScreenWidth(Context context) {
55 | DisplayMetrics dm = getDisplayMetrics(context);
56 | if (dm != null) {
57 | return dm.widthPixels;
58 | }
59 | return 0;
60 | }
61 |
62 | private static DisplayMetrics getDisplayMetrics(Context context) {
63 | Display display = getDefaultDisplay(context);
64 | if (display != null) {
65 | DisplayMetrics result = new DisplayMetrics();
66 | display.getMetrics(result);
67 | return result;
68 | }
69 | return null;
70 | }
71 |
72 | public static int getStatusBarHeight(Context context) {
73 | int result = 0;
74 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
75 | if (resourceId > 0) {
76 | result = context.getResources().getDimensionPixelSize(resourceId);
77 | }
78 | return result;
79 | }
80 |
81 | public static int getToolbarHeight(Context context) {
82 | TypedValue tv = new TypedValue();
83 | if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
84 | return TypedValue.complexToDimensionPixelSize(tv.data, context.getResources().getDisplayMetrics());
85 | }
86 | return 0;
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/album/src/main/java/boxing_impl/view/HackyViewPager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Bilibili
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package boxing_impl.view;
19 |
20 | import android.content.Context;
21 | import android.support.v4.view.ViewPager;
22 | import android.util.AttributeSet;
23 | import android.view.MotionEvent;
24 |
25 | /**
26 | * https://github.com/chrisbanes/PhotoView/issues/35
27 | */
28 | public class HackyViewPager extends ViewPager {
29 | private boolean mIsLocked;
30 |
31 | public HackyViewPager(Context context) {
32 | super(context);
33 | mIsLocked = false;
34 | }
35 |
36 | public HackyViewPager(Context context, AttributeSet attrs) {
37 | super(context, attrs);
38 | mIsLocked = false;
39 | }
40 |
41 | @Override
42 | public boolean onInterceptTouchEvent(MotionEvent ev) {
43 | if (!mIsLocked) {
44 | try {
45 | return super.onInterceptTouchEvent(ev);
46 | } catch (IllegalArgumentException e) {
47 | return false;
48 | }
49 | }
50 | return false;
51 | }
52 |
53 | @Override
54 | public boolean onTouchEvent(MotionEvent event) {
55 | return !mIsLocked && super.onTouchEvent(event);
56 | }
57 |
58 | public boolean isLocked() {
59 | return mIsLocked;
60 | }
61 |
62 | public void setLocked(boolean isLocked) {
63 | this.mIsLocked = isLocked;
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/album/src/main/res/anim/fade_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
26 |
27 |
--------------------------------------------------------------------------------
/album/src/main/res/anim/fade_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
26 |
27 |
--------------------------------------------------------------------------------
/album/src/main/res/drawable-hdpi/ic_broken_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-hdpi/ic_broken_image.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-hdpi/ic_camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-hdpi/ic_camera.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-hdpi/ic_camera_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-hdpi/ic_camera_white.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-hdpi/ic_check_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-hdpi/ic_check_black.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-hdpi/ic_checked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-hdpi/ic_checked.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-hdpi/ic_default_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-hdpi/ic_default_image.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-hdpi/ic_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-hdpi/ic_play.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xhdpi/ic_broken_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xhdpi/ic_broken_image.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xhdpi/ic_camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xhdpi/ic_camera.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xhdpi/ic_camera_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xhdpi/ic_camera_white.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xhdpi/ic_check_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xhdpi/ic_check_black.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xhdpi/ic_checked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xhdpi/ic_checked.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xhdpi/ic_default_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xhdpi/ic_default_image.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xhdpi/ic_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xhdpi/ic_play.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xxhdpi/ic_broken_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xxhdpi/ic_broken_image.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xxhdpi/ic_camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xxhdpi/ic_camera.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xxhdpi/ic_camera_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xxhdpi/ic_camera_white.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xxhdpi/ic_check_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xxhdpi/ic_check_black.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xxhdpi/ic_checked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xxhdpi/ic_checked.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xxhdpi/ic_default_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xxhdpi/ic_default_image.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable-xxhdpi/ic_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/album/src/main/res/drawable-xxhdpi/ic_play.png
--------------------------------------------------------------------------------
/album/src/main/res/drawable/ic_unchecked.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
21 |
22 |
23 |
24 |
27 |
28 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/album/src/main/res/drawable/selector_button_solid.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 | -
21 |
22 |
23 |
24 |
25 |
26 |
27 | -
28 |
29 |
30 |
31 |
32 |
33 | -
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/album/src/main/res/drawable/shape_popup_background_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
21 |
22 |
24 |
26 |
27 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/activity_boxing.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
24 |
25 |
31 |
32 |
43 |
44 |
45 |
50 |
51 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/activity_boxing_bottom_sheet.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
24 |
25 |
29 |
30 |
31 |
32 |
38 |
39 |
40 |
41 |
49 |
50 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/activity_boxing_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
27 |
28 |
34 |
35 |
45 |
46 |
47 |
55 |
56 |
62 |
63 |
69 |
70 |
82 |
83 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/fragmant_boxing_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
25 |
26 |
33 |
34 |
37 |
38 |
45 |
46 |
51 |
52 |
62 |
63 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/fragment_boxing_bottom_sheet.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
24 |
25 |
31 |
32 |
40 |
41 |
49 |
50 |
51 |
52 |
58 |
59 |
67 |
68 |
71 |
72 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/fragment_boxing_raw_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
22 |
23 |
29 |
30 |
37 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/layout_album.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
24 |
25 |
31 |
32 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/layout_album_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
26 |
27 |
35 |
36 |
49 |
50 |
60 |
61 |
71 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/layout_app_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
26 |
27 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/layout_empty_txt.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/layout_media_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
23 |
24 |
30 |
31 |
38 |
39 |
49 |
50 |
62 |
63 |
73 |
74 |
75 |
81 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/layout_recycleview_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
27 |
28 |
36 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/layout_recycleview_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
24 |
--------------------------------------------------------------------------------
/album/src/main/res/layout/layout_simple_media_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
26 |
--------------------------------------------------------------------------------
/album/src/main/res/menu/activity_image_viewer.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
--------------------------------------------------------------------------------
/album/src/main/res/menu/activity_pick_flow.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
--------------------------------------------------------------------------------
/album/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 | #000000
21 | #ff212121
22 | #44000000
23 | #FF4081
24 |
25 | #999999
26 | #333333
27 |
28 | #ffffff
29 | #fafafa
30 | #eaeaea
31 |
32 | #000000
33 | #1b1b1b
34 | #26000000
35 | #33000000
36 | #3c000000
37 |
38 |
--------------------------------------------------------------------------------
/album/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 | 16dp
20 | 16dp
21 |
22 | 16sp
23 | 14sp
24 | 12sp
25 | 10sp
26 |
27 | 8dp
28 | 4dp
29 | 2dp
30 | 2dp
31 |
32 |
--------------------------------------------------------------------------------
/album/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 | 确定
20 | 拍照
21 |
22 | %1$s/%2$s
23 | %1$s/%2$s 确定
24 | (%1$s/%2$s) 确定
25 | (%d)
26 |
27 | 你最多只能选择九张图片
28 | 您选择的gif图片过大,请压缩后再上传
29 | 视频单选
30 | 图片多选
31 | 图片单选加裁剪
32 | 图片单选
33 | 视频相册
34 | 完成
35 | 开始选图
36 | 肥肠抱歉,加载出错啦
37 | 需要访问你的存储设备来选择图片,请在“系统设置”或授权对话框中允许“存储空间”权限。
38 | 需要访问你的相机来拍照,请在“系统设置”或授权对话框中允许“使用相机”权限。
39 | 设备存储读取出错或暂不可用,请稍候重试
40 | 肥肠抱歉,裁剪出错啦
41 | 相机不可用,请检查相机权限!
42 | 处理中
43 | 默认相册
44 | 什么也找不到
45 | 您最多只能选择九张图片
46 |
47 |
48 |
--------------------------------------------------------------------------------
/album/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 |
25 |
26 |
27 |
33 |
34 |
38 |
39 |
45 |
46 |
47 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/album/src/test/java/com/licrafter/album/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.album;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | signingConfigs {
5 | release {
6 | keyAlias 'cnode'
7 | keyPassword 'licrafter'
8 | storeFile file('/Users/lijx/apk_sign/android_cnode.jks')
9 | storePassword 'licrafter'
10 | }
11 | }
12 | compileSdkVersion 25
13 | buildToolsVersion '26.0.2'
14 | defaultConfig {
15 | applicationId "com.licrafter.cnode"
16 | minSdkVersion 16
17 | targetSdkVersion 25
18 | versionCode 1
19 | versionName "1.0"
20 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
21 | }
22 | buildTypes {
23 | release {
24 | minifyEnabled true
25 | shrinkResources true
26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
27 | signingConfig signingConfigs.release
28 | proguardFile '/Users/lijx/AndroidStudioProjects/github/CNode/app/proguard-rules.pro'
29 | }
30 | }
31 | //为了解决部分第三方库重复打包了META-INF的问题
32 | packagingOptions {
33 | exclude 'META-INF/LICENSE.txt'
34 | exclude 'META-INF/NOTICE.txt'
35 | }
36 | lintOptions {
37 | checkReleaseBuilds false
38 | abortOnError false
39 | }
40 | android.applicationVariants.all { variant ->
41 | Date date = new Date();
42 |
43 | variant.outputs.all {
44 | outputFileName = "CNode-${variant.name}-${variant.versionName}-${date.format("yyyy-MM-dd")}.apk"
45 | }
46 | }
47 |
48 | productFlavors {
49 | }
50 | }
51 |
52 | def buildTime() {
53 | def date = new Date()
54 | def formattedDate = date.format('yyyyMMdd')
55 | return formattedDate
56 | }
57 |
58 | dependencies {
59 | compile fileTree(include: ['*.jar'], dir: 'libs')
60 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
61 | exclude group: 'com.android.support', module: 'support-annotations'
62 | })
63 | compile 'com.android.support:design:25.3.1'
64 | compile 'com.android.support:appcompat-v7:25.3.1'
65 | testCompile 'junit:junit:4.12'
66 | compile 'com.jakewharton:butterknife:8.5.1'
67 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
68 | compile 'com.squareup.picasso:picasso:2.5.2'
69 | compile 'io.reactivex:rxandroid:1.2.1'
70 | compile 'io.reactivex:rxjava:1.1.10'
71 | compile 'com.squareup.retrofit2:retrofit:2.1.0'
72 | compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
73 | compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
74 | compile 'com.squareup.retrofit2:converter-gson:2.1.0'
75 | compile 'com.makeramen:roundedimageview:2.3.0'
76 | compile 'com.journeyapps:zxing-android-embedded:3.4.0'
77 | compile('com.yalantis:ucrop:2.2.0') {
78 | exclude group: 'com.android.support'
79 | exclude group: 'com.squareup.okio'
80 | exclude group: 'com.squareup.okhttp3'
81 | }
82 | // LeanCloud 基础包
83 | compile 'cn.leancloud.android:avoscloud-sdk:v3.+'
84 | compile project(path: ':album')
85 | }
86 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/licrafter/cnode/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.licrafter.cnode", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
25 |
26 |
31 |
32 |
35 |
36 |
37 |
38 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
50 |
51 |
55 |
56 |
60 |
61 |
65 |
66 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/app/src/main/assets/js/markdownview.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | //给无http前缀的图片链接加上http
4 | $("img").each(function(){
5 | if($(this).attr('src').indexOf('http')<0){
6 | $(this).attr('src','https:'+$(this).attr('src'));
7 | }
8 | });
9 |
10 | //js调用android本地代码显示大图
11 | $("img").each(function(){
12 | $(this).click(function(){
13 | window.cnode.showImageDetail($(this).attr('src'));
14 | });
15 | });
16 |
17 | $('abbr').each(function() {
18 | $(this).data('title', $(this).attr('title'));
19 | $(this).removeAttr('title');
20 | });
21 |
22 | $('abbr').mouseover(function() {
23 | $('#tooltip').css('display', 'inline-block');
24 | $('#tooltip').html($(this).data('title'));
25 | $('#tooltip').css('margin-left', -$('#tooltip').outerWidth() / 2);
26 | });
27 |
28 | $('abbr').click(function() {
29 | $(this).mouseover();
30 | $('#tooltip').animate({opacity: 0.9},{duration: 2000, complete: function() {
31 | $('#tooltip').fadeOut(1000);
32 | }});
33 |
34 | });
35 |
36 | $('abbr').mouseout(function() {
37 | $('#tooltip').css('display', 'none');
38 | });
39 |
40 | });
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/BaseApplication.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | import com.avos.avoscloud.AVOSCloud;
7 | import com.bilibili.boxing.BoxingCrop;
8 | import com.bilibili.boxing.BoxingMediaLoader;
9 | import com.bilibili.boxing.loader.IBoxingMediaLoader;
10 | import com.licrafter.cnode.utils.SharedPreferenceUtils;
11 | import com.licrafter.cnode.widget.BoxingPicassoLoader;
12 | import com.licrafter.cnode.widget.BoxingUcrop;
13 |
14 | /**
15 | * author: shell
16 | * date 2017/2/24 下午3:27
17 | **/
18 | public class BaseApplication extends Application {
19 |
20 | public static Context mContext;
21 | public static String APP_ID = "V2ewxmFalb07Lj1GFFYuWfQF-gzGzoHsz";
22 | public static String APP_KEY = "5A0DVS5BnD5HGftmjOXVtFuG";
23 |
24 | @Override
25 | public void onCreate() {
26 | super.onCreate();
27 | mContext = getApplicationContext();
28 | SharedPreferenceUtils.init(this);
29 | AVOSCloud.initialize(this, APP_ID, APP_KEY);
30 | AVOSCloud.setDebugLogEnabled(true);
31 | IBoxingMediaLoader loader = new BoxingPicassoLoader();
32 | BoxingMediaLoader.getInstance().init(loader);
33 | BoxingCrop.getInstance().init(new BoxingUcrop());
34 | }
35 |
36 | public static Context getContext() {
37 | return mContext;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/api/CNodeApi.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.api;
2 |
3 | import com.google.gson.GsonBuilder;
4 | import com.licrafter.cnode.api.service.CNodeService;
5 | import com.licrafter.cnode.utils.http.HttpClient;
6 |
7 | import retrofit2.Retrofit;
8 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
9 | import retrofit2.converter.gson.GsonConverterFactory;
10 |
11 | /**
12 | * author: shell
13 | * date 2017/2/24 下午2:56
14 | **/
15 | public class CNodeApi {
16 |
17 | private static CNodeService cnodeInstance;
18 |
19 | public static CNodeService getCNodeService() {
20 | if (cnodeInstance == null) {
21 | initCNodeService();
22 | }
23 | return cnodeInstance;
24 | }
25 |
26 | private static void initCNodeService() {
27 | Retrofit retrofit = new Retrofit.Builder()
28 | .baseUrl(Urls.BASE_URL)
29 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
30 | .addConverterFactory(GsonConverterFactory.create(new GsonBuilder()
31 | .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
32 | .serializeNulls()
33 | .create()))
34 | .client(HttpClient.INSTANCE.getOkHttpClient())
35 | .build();
36 | cnodeInstance = retrofit.create(CNodeService.class);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/api/Urls.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.api;
2 |
3 | /**
4 | * author: shell
5 | * date 2017/2/24 下午2:57
6 | **/
7 | public class Urls {
8 | public static final String BASE_URL = " https://cnodejs.org/api/v1/";
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/base/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.base;
2 |
3 | import android.os.Build;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.WindowManager;
7 |
8 | import com.licrafter.cnode.utils.StatusBarUtils;
9 |
10 | import butterknife.ButterKnife;
11 |
12 | /**
13 | * author: shell
14 | * date 2017/2/22 下午4:52
15 | **/
16 | public abstract class BaseActivity extends AppCompatActivity {
17 |
18 | @Override
19 | public void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | setContentView(getContentView());
22 | StatusBarUtils.initStatusBar(this);
23 | ButterKnife.bind(this);
24 | initView(savedInstanceState);
25 | setListeners();
26 | bind();
27 | }
28 |
29 | @Override
30 | protected void onDestroy() {
31 | super.onDestroy();
32 | unBind();
33 | }
34 |
35 | /**
36 | * 返回content layout id
37 | *
38 | * @return
39 | */
40 | public abstract int getContentView();
41 |
42 | public abstract void initView(Bundle savedInstanceState);
43 |
44 | public abstract void setListeners();
45 |
46 | public abstract void bind();
47 |
48 | public abstract void unBind();
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/base/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.base;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.support.v4.app.Fragment;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 |
11 | import butterknife.ButterKnife;
12 |
13 | /**
14 | * author: shell
15 | * date 2017/2/24 上午11:27
16 | **/
17 | public abstract class BaseFragment extends Fragment {
18 |
19 | protected boolean mPrepared;
20 | protected boolean mVisibleToUser;
21 | private BaseActivity mActivity;
22 |
23 | @Override
24 | public void onAttach(Activity activity) {
25 | super.onAttach(activity);
26 | mActivity = (BaseActivity) activity;
27 | }
28 |
29 | @Override
30 | public void setUserVisibleHint(boolean isVisibleToUser) {
31 | super.setUserVisibleHint(isVisibleToUser);
32 | mVisibleToUser = isVisibleToUser;
33 | tryLoad();
34 | }
35 |
36 | @Nullable
37 | @Override
38 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
39 | View root = inflater.inflate(setContentView(), container, false);
40 | ButterKnife.bind(this, root);
41 | mPrepared = true;
42 | return root;
43 | }
44 |
45 | @Override
46 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
47 | super.onViewCreated(view, savedInstanceState);
48 | initView(view);
49 | bind();
50 | tryLoad();
51 | setListeners();
52 | }
53 |
54 | private void tryLoad() {
55 | if (mVisibleToUser && mPrepared) {
56 | lazyLoad();
57 | }
58 | }
59 |
60 | public abstract int setContentView();
61 |
62 | public abstract void lazyLoad();
63 |
64 | public abstract void initView(View root);
65 |
66 | public abstract void setListeners();
67 |
68 | public abstract void bind();
69 |
70 | public abstract void unBind();
71 |
72 | @Override
73 | public void onDestroyView() {
74 | super.onDestroyView();
75 | mPrepared = false;
76 | unBind();
77 | }
78 |
79 | public BaseActivity getBaseActivity() {
80 | return mActivity;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/cache/UserCache.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.cache;
2 |
3 | import com.licrafter.cnode.utils.SharedPreferenceUtils;
4 |
5 | /**
6 | * author: shell
7 | * date 2017/2/24 下午3:30
8 | **/
9 | public class UserCache {
10 |
11 | public static void cache(String id, String name, String avatar_url, String token) {
12 | SharedPreferenceUtils.save("user_id", id);
13 | SharedPreferenceUtils.save("avatar_url", avatar_url);
14 | SharedPreferenceUtils.save("user_name", name);
15 | SharedPreferenceUtils.save("user_token", token);
16 | }
17 |
18 | public static String getUserId() {
19 | return SharedPreferenceUtils.getString("user_id", null);
20 | }
21 |
22 | public static String getUserName() {
23 | return SharedPreferenceUtils.getString("user_name", null);
24 | }
25 |
26 | public static String getAvatarUrl() {
27 | return SharedPreferenceUtils.getString("avatar_url", null);
28 | }
29 |
30 | public static String getUserToken() {
31 | return SharedPreferenceUtils.getString("user_token", null);
32 | }
33 |
34 | public static void clear() {
35 | SharedPreferenceUtils.clearAll();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/AccessTokenBody.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | /**
7 | * Created by lijx on 2017/4/12.
8 | */
9 |
10 | public class AccessTokenBody {
11 | @SerializedName("accesstoken")
12 | @Expose
13 | private String accesstoken;
14 |
15 | public AccessTokenBody(String accesstoken) {
16 | this.accesstoken = accesstoken;
17 | }
18 |
19 | public String getAccesstoken() {
20 | return accesstoken;
21 | }
22 |
23 | public void setAccesstoken(String accesstoken) {
24 | this.accesstoken = accesstoken;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/CollectionBody.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | /**
7 | * Created by lijx on 2017/4/12.
8 | */
9 |
10 | public class CollectionBody {
11 |
12 | @SerializedName("accesstoken")
13 | @Expose
14 | private String accesstoken;
15 | @SerializedName("topic_id")
16 | @Expose
17 | private String topic_id;
18 |
19 | public CollectionBody(String accesstoken, String topic_id) {
20 | this.accesstoken = accesstoken;
21 | this.topic_id = topic_id;
22 | }
23 |
24 | public String getAccesstoken() {
25 | return accesstoken;
26 | }
27 |
28 | public void setAccesstoken(String accesstoken) {
29 | this.accesstoken = accesstoken;
30 | }
31 |
32 | public String getTopic_id() {
33 | return topic_id;
34 | }
35 |
36 | public void setTopic_id(String topic_id) {
37 | this.topic_id = topic_id;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/LoginBody.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | /**
7 | * author: shell
8 | * date 2017/3/13 下午8:26
9 | **/
10 | public class LoginBody {
11 |
12 | @SerializedName("accesstoken")
13 | @Expose
14 | private String accesstoken;
15 |
16 | public String getAccesstoken() {
17 | return accesstoken;
18 | }
19 |
20 | public void setAccesstoken(String accesstoken) {
21 | this.accesstoken = accesstoken;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/LoginResultModel.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | /**
7 | * author: shell
8 | * date 2017/3/13 下午8:52
9 | **/
10 | public class LoginResultModel {
11 |
12 | @SerializedName("success")
13 | @Expose
14 | private Boolean success;
15 | @SerializedName("loginname")
16 | @Expose
17 | private String loginname;
18 | @SerializedName("avatar_url")
19 | @Expose
20 | private String avatar_url;
21 | @SerializedName("id")
22 | @Expose
23 | private String id;
24 |
25 | public Boolean getSuccess() {
26 | return success;
27 | }
28 |
29 | public void setSuccess(Boolean success) {
30 | this.success = success;
31 | }
32 |
33 | public String getLoginname() {
34 | return loginname;
35 | }
36 |
37 | public void setLoginname(String loginname) {
38 | this.loginname = loginname;
39 | }
40 |
41 | public String getAvatar_url() {
42 | return avatar_url.contains("http") | avatar_url.contains("https") ? avatar_url : "https:" + avatar_url;
43 | }
44 |
45 | public void setAvatar_url(String avatar_url) {
46 | this.avatar_url = avatar_url;
47 | }
48 |
49 | public String getId() {
50 | return id;
51 | }
52 |
53 | public void setId(String id) {
54 | this.id = id;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/MarkResultModel.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | /**
7 | * Created by lijx on 2017/4/12.
8 | */
9 |
10 | public class MarkResultModel {
11 |
12 | @SerializedName("success")
13 | @Expose
14 | private String success;
15 | @SerializedName("marked_msgs")
16 | @Expose
17 | private MarkedMsg[] marked_msgs;
18 |
19 | public String getSuccess() {
20 | return success;
21 | }
22 |
23 | public void setSuccess(String success) {
24 | this.success = success;
25 | }
26 |
27 | public MarkedMsg[] getMarked_msgs() {
28 | return marked_msgs;
29 | }
30 |
31 | public void setMarked_msgs(MarkedMsg[] marked_msgs) {
32 | this.marked_msgs = marked_msgs;
33 | }
34 |
35 | public class MarkedMsg {
36 | @SerializedName("id")
37 | @Expose
38 | private String id;
39 |
40 | public String getId() {
41 | return id;
42 | }
43 |
44 | public void setId(String id) {
45 | this.id = id;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/NotificationModel.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 | import com.licrafter.cnode.model.entity.Notification;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * author: shell
12 | * date 2017/3/16 下午5:28
13 | **/
14 | public class NotificationModel {
15 |
16 | @SerializedName("data")
17 | @Expose
18 | private Data data;
19 |
20 | public Data getData() {
21 | return data;
22 | }
23 |
24 | public void setData(Data data) {
25 | this.data = data;
26 | }
27 |
28 | public static class Data {
29 | @SerializedName("has_read_messages")
30 | @Expose
31 | private List has_read_messages = new ArrayList<>();
32 | @SerializedName("hasnot_read_messages")
33 | @Expose
34 | private List hasnot_read_messages = new ArrayList<>();
35 |
36 | public List getHas_read_messages() {
37 | return has_read_messages;
38 | }
39 |
40 | public void setHas_read_messages(List has_read_messages) {
41 | this.has_read_messages = has_read_messages;
42 | }
43 |
44 | public List getHasnot_read_messages() {
45 | return hasnot_read_messages;
46 | }
47 |
48 | public void setHasnot_read_messages(List hasnot_read_messages) {
49 | this.hasnot_read_messages = hasnot_read_messages;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/PostTopicResultModel.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | /**
7 | * Created by lijx on 2017/4/10.
8 | */
9 |
10 | public class PostTopicResultModel {
11 |
12 | @SerializedName("success")
13 | @Expose
14 | private boolean success;
15 | @SerializedName("topic_id")
16 | @Expose
17 | private String topic_id;
18 |
19 | public boolean isSuccess() {
20 | return success;
21 | }
22 |
23 | public void setSuccess(boolean success) {
24 | this.success = success;
25 | }
26 |
27 | public String getTopic_id() {
28 | return topic_id;
29 | }
30 |
31 | public void setTopic_id(String topic_id) {
32 | this.topic_id = topic_id;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/ReplyBody.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | /**
7 | * Created by lijx on 2017/4/14.
8 | */
9 |
10 | public class ReplyBody {
11 |
12 | @SerializedName("accesstoken")
13 | @Expose
14 | private String accesstoken;
15 | @SerializedName("content")
16 | @Expose
17 | private String content;
18 | @SerializedName("reply_id")
19 | @Expose
20 | private String reply_id;
21 |
22 | public ReplyBody(String accesstoken, String content, String reply_id) {
23 | this.accesstoken = accesstoken;
24 | this.content = content;
25 | this.reply_id = reply_id;
26 | }
27 |
28 | public String getAccesstoken() {
29 | return accesstoken;
30 | }
31 |
32 | public void setAccesstoken(String accesstoken) {
33 | this.accesstoken = accesstoken;
34 | }
35 |
36 | public String getContent() {
37 | return content;
38 | }
39 |
40 | public void setContent(String content) {
41 | this.content = content;
42 | }
43 |
44 | public String getReply_id() {
45 | return reply_id;
46 | }
47 |
48 | public void setReply_id(String reply_id) {
49 | this.reply_id = reply_id;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/ReplyResultModel.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | /**
7 | * Created by lijx on 2017/4/14.
8 | */
9 |
10 | public class ReplyResultModel {
11 |
12 | @SerializedName("success")
13 | @Expose
14 | private Boolean success;
15 | @SerializedName("reply_id")
16 | @Expose
17 | private String reply_id;
18 |
19 | public Boolean getSuccess() {
20 | return success;
21 | }
22 |
23 | public void setSuccess(Boolean success) {
24 | this.success = success;
25 | }
26 |
27 | public String getReply_id() {
28 | return reply_id;
29 | }
30 |
31 | public void setReply_id(String reply_id) {
32 | this.reply_id = reply_id;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/TabModel.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 | import com.licrafter.cnode.model.entity.Topic;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * author: shell
12 | * date 2017/2/24 下午3:50
13 | **/
14 | public class TabModel {
15 |
16 | @SerializedName("success")
17 | @Expose
18 | private boolean success;
19 | @SerializedName("data")
20 | @Expose
21 | private List data = new ArrayList<>();
22 |
23 | public boolean isSuccess() {
24 | return success;
25 | }
26 |
27 | public void setSuccess(boolean success) {
28 | this.success = success;
29 | }
30 |
31 | public List getData() {
32 | return data;
33 | }
34 |
35 | public void setData(List data) {
36 | this.data = data;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/TopicDetailModel.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 | import com.licrafter.cnode.model.entity.TopicDetail;
6 |
7 | /**
8 | * author: shell
9 | * date 2017/3/1 上午10:51
10 | **/
11 | public class TopicDetailModel {
12 |
13 | @Expose
14 | @SerializedName("success")
15 | private boolean success;
16 |
17 | @Expose
18 | @SerializedName("data")
19 | private TopicDetail data;
20 |
21 | public boolean isSuccess() {
22 | return success;
23 | }
24 |
25 | public void setSuccess(boolean success) {
26 | this.success = success;
27 | }
28 |
29 | public TopicDetail getData() {
30 | return data;
31 | }
32 |
33 | public void setData(TopicDetail data) {
34 | this.data = data;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/UnReadCountModel.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | /**
7 | * author: shell
8 | * date 2017/3/16 下午5:02
9 | **/
10 | public class UnReadCountModel {
11 |
12 | @SerializedName("data")
13 | @Expose
14 | private Integer data;
15 | @SerializedName("success")
16 | @Expose
17 | private Boolean success;
18 |
19 | public Boolean getSuccess() {
20 | return success;
21 | }
22 |
23 | public void setSuccess(Boolean success) {
24 | this.success = success;
25 | }
26 |
27 | public Integer getData() {
28 | return data;
29 | }
30 |
31 | public void setData(Integer data) {
32 | this.data = data;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/UserDetailModel.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 | import com.licrafter.cnode.model.entity.UserDetail;
6 |
7 | /**
8 | * author: shell
9 | * date 2017/3/6 下午2:55
10 | **/
11 | public class UserDetailModel {
12 |
13 | @SerializedName("success")
14 | @Expose
15 | private boolean success;
16 |
17 | @SerializedName("data")
18 | @Expose
19 | private UserDetail data;
20 |
21 | public boolean isSuccess() {
22 | return success;
23 | }
24 |
25 | public void setSuccess(boolean success) {
26 | this.success = success;
27 | }
28 |
29 | public UserDetail getData() {
30 | return data;
31 | }
32 |
33 | public void setData(UserDetail data) {
34 | this.data = data;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/entity/Author.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model.entity;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | /**
7 | * author: shell
8 | * date 2017/2/24 下午3:55
9 | **/
10 | public class Author {
11 |
12 | @SerializedName("loginname")
13 | @Expose
14 | private String loginname;
15 | @SerializedName("avatar_url")
16 | @Expose
17 | private String avatar_url;
18 |
19 | public String getLoginname() {
20 | return loginname;
21 | }
22 |
23 | public void setLoginname(String loginname) {
24 | this.loginname = loginname;
25 | }
26 |
27 | public String getAvatar_url() {
28 | return avatar_url.contains("http") | avatar_url.contains("https") ? avatar_url : "https:" + avatar_url;
29 | }
30 |
31 | public void setAvatar_url(String avatar_url) {
32 | this.avatar_url = avatar_url;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/entity/Notification.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model.entity;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | /**
7 | * author: shell
8 | * date 2017/3/16 下午5:29
9 | **/
10 | public class Notification {
11 |
12 | @SerializedName("id")
13 | @Expose
14 | private String id;
15 | @SerializedName("type")
16 | @Expose
17 | private String type;
18 | @SerializedName("has_read")
19 | @Expose
20 | private Boolean has_read;
21 |
22 | @SerializedName("author")
23 | @Expose
24 | private Author author;
25 | @SerializedName("topic")
26 | @Expose
27 | private Topic topic;
28 | @SerializedName("reply")
29 | @Expose
30 | private Reply reply;
31 |
32 | public String getId() {
33 | return id;
34 | }
35 |
36 | public void setId(String id) {
37 | this.id = id;
38 | }
39 |
40 | public Boolean getHas_read() {
41 | return has_read;
42 | }
43 |
44 | public void setHas_read(Boolean has_read) {
45 | this.has_read = has_read;
46 | }
47 |
48 | public String getType() {
49 | return type;
50 | }
51 |
52 | public void setType(String type) {
53 | this.type = type;
54 | }
55 |
56 | public Author getAuthor() {
57 | return author;
58 | }
59 |
60 | public void setAuthor(Author author) {
61 | this.author = author;
62 | }
63 |
64 | public Topic getTopic() {
65 | return topic;
66 | }
67 |
68 | public void setTopic(Topic topic) {
69 | this.topic = topic;
70 | }
71 |
72 | public Reply getReply() {
73 | return reply;
74 | }
75 |
76 | public void setReply(Reply reply) {
77 | this.reply = reply;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/entity/PostTopic.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model.entity;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | /**
7 | * Created by lijx on 2017/4/10.
8 | */
9 |
10 | public class PostTopic {
11 |
12 | @SerializedName("accesstoken")
13 | @Expose
14 | private String accesstoken;
15 | @SerializedName("title")
16 | @Expose
17 | private String title;
18 | @SerializedName("tab")
19 | @Expose
20 | private String tab;
21 | @SerializedName("content")
22 | @Expose
23 | private String content;
24 |
25 | public PostTopic(String accesstoken, String title, String tab, String content) {
26 | this.accesstoken = accesstoken;
27 | this.title = title;
28 | this.tab = tab;
29 | this.content = content;
30 | }
31 |
32 | public String getAccesstoken() {
33 | return accesstoken;
34 | }
35 |
36 | public void setAccesstoken(String accesstoken) {
37 | this.accesstoken = accesstoken;
38 | }
39 |
40 | public String getTitle() {
41 | return title;
42 | }
43 |
44 | public void setTitle(String title) {
45 | this.title = title;
46 | }
47 |
48 | public String getTab() {
49 | return tab;
50 | }
51 |
52 | public void setTab(String tab) {
53 | this.tab = tab;
54 | }
55 |
56 | public String getContent() {
57 | return content;
58 | }
59 |
60 | public void setContent(String content) {
61 | this.content = content;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/entity/Reply.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model.entity;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | import java.util.ArrayList;
7 | import java.util.Date;
8 | import java.util.List;
9 |
10 | /**
11 | * author: shell
12 | * date 2017/3/1 上午10:57
13 | **/
14 | public class Reply {
15 |
16 | @Expose
17 | @SerializedName("id")
18 | private String id;
19 |
20 | @Expose
21 | @SerializedName("author")
22 | private Author author;
23 |
24 | @Expose
25 | @SerializedName("content")
26 | private String content;
27 |
28 | @Expose
29 | @SerializedName("create_at")
30 | private Date create_at;
31 |
32 | @Expose
33 | @SerializedName("reply_id")
34 | private String reply_id;
35 |
36 | @Expose
37 | @SerializedName("ups")
38 | private List ups = new ArrayList<>();
39 |
40 | public String getId() {
41 | return id;
42 | }
43 |
44 | public void setId(String id) {
45 | this.id = id;
46 | }
47 |
48 | public Author getAuthor() {
49 | return author;
50 | }
51 |
52 | public void setAuthor(Author author) {
53 | this.author = author;
54 | }
55 |
56 | public String getContent() {
57 | return content;
58 | }
59 |
60 | public void setContent(String content) {
61 | this.content = content;
62 | }
63 |
64 | public Date getCreate_at() {
65 | return create_at;
66 | }
67 |
68 | public void setCreate_at(Date create_at) {
69 | this.create_at = create_at;
70 | }
71 |
72 | public String getReply_id() {
73 | return reply_id;
74 | }
75 |
76 | public void setReply_id(String reply_id) {
77 | this.reply_id = reply_id;
78 | }
79 |
80 | public List getUps() {
81 | return ups;
82 | }
83 |
84 | public void setUps(List ups) {
85 | this.ups = ups;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/entity/TAB.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model.entity;
2 |
3 | /**
4 | * Created by lijx on 2017/4/11.
5 | */
6 |
7 | public enum TAB {
8 | SHARE("share", "分享"), ASK("ask", "问答"), JOB("job", "招聘"), GOOD("good", "精华");
9 |
10 | private String zhName;
11 | private String enName;
12 |
13 | TAB(String enName, String zhName) {
14 | this.enName = enName;
15 | this.zhName = zhName;
16 | }
17 |
18 | public String getZhName() {
19 | return zhName;
20 | }
21 |
22 | public String getEnName() {
23 | return enName;
24 | }
25 |
26 | public static TAB ValueOf(String name) {
27 | switch (name) {
28 | case "share":
29 | case "分享":
30 | return SHARE;
31 | case "ask":
32 | case "问答":
33 | return ASK;
34 | case "job":
35 | case "招聘":
36 | return JOB;
37 | case "good":
38 | case "精华":
39 | return GOOD;
40 | default:
41 | return SHARE;
42 | }
43 | }
44 |
45 | @Override
46 | public String toString() {
47 | return zhName + "(" + enName + ")";
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/model/entity/UserDetail.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.model.entity;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | import java.util.Date;
7 | import java.util.List;
8 |
9 | /**
10 | * author: shell
11 | * date 2017/3/6 下午3:41
12 | **/
13 | public class UserDetail {
14 |
15 | @SerializedName("loginname")
16 | @Expose
17 | private String loginname;
18 | @SerializedName("avatar_url")
19 | @Expose
20 | private String avatar_url;
21 | @SerializedName("githubUsername")
22 | @Expose
23 | private String githubUsername;
24 | @SerializedName("create_at")
25 | @Expose
26 | private Date create_at;
27 | @SerializedName("score")
28 | @Expose
29 | private Integer score;
30 |
31 | @SerializedName("recent_topics")
32 | @Expose
33 | private List recent_topics;
34 |
35 | public List getRecent_replies() {
36 | return recent_replies;
37 | }
38 |
39 | public void setRecent_replies(List recent_replies) {
40 | this.recent_replies = recent_replies;
41 | }
42 |
43 | @SerializedName("recent_replies")
44 | @Expose
45 | private List recent_replies;
46 |
47 | public String getAvatar_url() {
48 | return avatar_url;
49 | }
50 |
51 | public void setAvatar_url(String avatar_url) {
52 | this.avatar_url = avatar_url;
53 | }
54 |
55 | public String getLoginname() {
56 | return loginname;
57 | }
58 |
59 | public void setLoginname(String loginname) {
60 | this.loginname = loginname;
61 | }
62 |
63 | public String getGithubUsername() {
64 | return githubUsername;
65 | }
66 |
67 | public void setGithubUsername(String githubUsername) {
68 | this.githubUsername = githubUsername;
69 | }
70 |
71 | public Date getCreate_at() {
72 | return create_at;
73 | }
74 |
75 | public void setCreate_at(Date create_at) {
76 | this.create_at = create_at;
77 | }
78 |
79 | public Integer getScore() {
80 | return score;
81 | }
82 |
83 | public void setScore(Integer score) {
84 | this.score = score;
85 | }
86 |
87 | public List getRecent_topics() {
88 | return recent_topics;
89 | }
90 |
91 | public void setRecent_topics(List recent_topics) {
92 | this.recent_topics = recent_topics;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/mvp/presenter/BasePresenter.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.mvp.presenter;
2 |
3 | import com.licrafter.cnode.mvp.view.MvpView;
4 |
5 | import rx.subscriptions.CompositeSubscription;
6 |
7 | /**
8 | * author: shell
9 | * date 2017/2/24 下午4:17
10 | **/
11 | public class BasePresenter {
12 |
13 | private T mvpView;
14 | public CompositeSubscription mCompositeSubscription;
15 |
16 | public void attachView(T view) {
17 | this.mvpView = view;
18 | this.mCompositeSubscription = new CompositeSubscription();
19 | }
20 |
21 | public void detachView() {
22 | mvpView = null;
23 | mCompositeSubscription.unsubscribe();
24 | mCompositeSubscription = null;
25 | }
26 |
27 | public boolean isViewAttached() {
28 | return mvpView != null;
29 | }
30 |
31 | public T getView() {
32 | return mvpView;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/mvp/presenter/LoginPresenter.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.mvp.presenter;
2 |
3 | import com.licrafter.cnode.api.CNodeApi;
4 | import com.licrafter.cnode.model.LoginBody;
5 | import com.licrafter.cnode.model.LoginResultModel;
6 | import com.licrafter.cnode.ui.activity.LoginActivity;
7 | import com.licrafter.cnode.utils.LogUtils;
8 |
9 | import rx.Subscriber;
10 | import rx.android.schedulers.AndroidSchedulers;
11 | import rx.schedulers.Schedulers;
12 |
13 | /**
14 | * author: shell
15 | * date 2017/3/13 下午8:22
16 | **/
17 | public class LoginPresenter extends BasePresenter {
18 |
19 | public void login(String token) {
20 | LoginBody body = new LoginBody();
21 | body.setAccesstoken(token);
22 |
23 | mCompositeSubscription.add(CNodeApi.getCNodeService().login(body)
24 | .subscribeOn(Schedulers.io())
25 | .observeOn(AndroidSchedulers.mainThread())
26 | .subscribe(new Subscriber() {
27 | @Override
28 | public void onCompleted() {
29 |
30 | }
31 |
32 | @Override
33 | public void onError(Throwable e) {
34 | LogUtils.error(LoginPresenter.class.getName(), e.toString());
35 | }
36 |
37 | @Override
38 | public void onNext(LoginResultModel model) {
39 | getView().notifyLoginSuccess(model);
40 | }
41 | }));
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/mvp/presenter/NotificationCenterPresenter.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.mvp.presenter;
2 |
3 | import com.licrafter.cnode.api.CNodeApi;
4 | import com.licrafter.cnode.cache.UserCache;
5 | import com.licrafter.cnode.model.AccessTokenBody;
6 | import com.licrafter.cnode.model.MarkResultModel;
7 | import com.licrafter.cnode.model.NotificationModel;
8 | import com.licrafter.cnode.ui.activity.NotificationCenterActivity;
9 |
10 | import rx.Subscriber;
11 | import rx.android.schedulers.AndroidSchedulers;
12 | import rx.functions.Action0;
13 | import rx.schedulers.Schedulers;
14 |
15 | /**
16 | * Created by lijx on 2017/4/11.
17 | */
18 |
19 | public class NotificationCenterPresenter extends BasePresenter {
20 |
21 | public void getAllNotification() {
22 | mCompositeSubscription.add(CNodeApi.getCNodeService().getAllNotifications(UserCache.getUserToken(), true)
23 | .subscribeOn(Schedulers.io())
24 | .observeOn(AndroidSchedulers.mainThread())
25 | .doOnTerminate(new Action0() {
26 | @Override
27 | public void call() {
28 | getView().stopRefresh();
29 | }
30 | })
31 | .subscribe(new Subscriber() {
32 | @Override
33 | public void onCompleted() {
34 |
35 | }
36 |
37 | @Override
38 | public void onError(Throwable e) {
39 | getView().onFailed(e);
40 | }
41 |
42 | @Override
43 | public void onNext(NotificationModel notificationModel) {
44 | getView().notifyGetSuccess(notificationModel);
45 | }
46 | }));
47 | }
48 |
49 | public void readAll() {
50 | mCompositeSubscription.add(CNodeApi.getCNodeService()
51 | .markAllMsg(new AccessTokenBody(UserCache.getUserToken()))
52 | .subscribeOn(Schedulers.io())
53 | .observeOn(AndroidSchedulers.mainThread())
54 | .subscribe(new Subscriber() {
55 | @Override
56 | public void onCompleted() {
57 |
58 | }
59 |
60 | @Override
61 | public void onError(Throwable e) {
62 |
63 | }
64 |
65 | @Override
66 | public void onNext(MarkResultModel markResultModel) {
67 |
68 | }
69 | }));
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/mvp/presenter/TopicListPresenter.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.mvp.presenter;
2 |
3 | import com.licrafter.cnode.api.CNodeApi;
4 | import com.licrafter.cnode.model.TabModel;
5 | import com.licrafter.cnode.ui.fragment.TopicListFragment;
6 |
7 | import rx.Observable;
8 | import rx.Subscriber;
9 | import rx.android.schedulers.AndroidSchedulers;
10 | import rx.schedulers.Schedulers;
11 |
12 | /**
13 | * author: shell
14 | * date 2017/2/24 下午4:28
15 | **/
16 | public class TopicListPresenter extends BasePresenter {
17 |
18 | private static final int PAGE_LIMIT = 50;
19 | private int mPageIndex = 1;
20 | private String mTab;
21 | private boolean mHasNextPage = true;
22 | private boolean mLoading = false;
23 |
24 |
25 | public void refresh() {
26 | mPageIndex = 1;
27 | getHomePage(mPageIndex, true);
28 | }
29 |
30 | public void loadNextPage() {
31 | getHomePage(mPageIndex, false);
32 | }
33 |
34 |
35 | private void getHomePage(final int pageIndex, final boolean refresh) {
36 | if (mLoading) {
37 | return;
38 | }
39 | mLoading = true;
40 | mCompositeSubscription.add(getObservable(pageIndex).subscribeOn(Schedulers.io())
41 | .observeOn(AndroidSchedulers.mainThread())
42 | .subscribe(new Subscriber() {
43 | @Override
44 | public void onCompleted() {
45 | mLoading = false;
46 | }
47 |
48 | @Override
49 | public void onError(Throwable e) {
50 | getView().onFailed(e);
51 | }
52 |
53 | @Override
54 | public void onNext(TabModel tabModel) {
55 | mPageIndex++;
56 | mHasNextPage = tabModel.getData().size() >= PAGE_LIMIT;
57 |
58 | if (tabModel.isSuccess()) {
59 | getView().notifySuccess(tabModel, refresh);
60 | } else {
61 | getView().onFailed(new Throwable("获取列表失败"));
62 | }
63 | }
64 | }));
65 |
66 | }
67 |
68 | private Observable getObservable(int pageIndex) {
69 | if (mTab == null) {
70 | return CNodeApi.getCNodeService().getTopicPage(pageIndex, PAGE_LIMIT, true);
71 | } else {
72 | return CNodeApi.getCNodeService().getTabByName(mTab, pageIndex, PAGE_LIMIT, true);
73 | }
74 | }
75 |
76 | public void setTab(String tab) {
77 | mTab = tab;
78 | }
79 |
80 | public boolean isLoading() {
81 | return mLoading;
82 | }
83 |
84 | public boolean hasNextPage() {
85 | return mHasNextPage;
86 | }
87 |
88 | public int getPageIndex() {
89 | return mPageIndex;
90 | }
91 |
92 | public void setPageIndex(int pageIndex) {
93 | mPageIndex = pageIndex;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/mvp/presenter/UserDetailPresenter.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.mvp.presenter;
2 |
3 | import com.licrafter.cnode.api.CNodeApi;
4 | import com.licrafter.cnode.cache.UserCache;
5 | import com.licrafter.cnode.model.UnReadCountModel;
6 | import com.licrafter.cnode.model.UserDetailModel;
7 | import com.licrafter.cnode.ui.fragment.MineFragment;
8 |
9 | import rx.Subscriber;
10 | import rx.android.schedulers.AndroidSchedulers;
11 | import rx.schedulers.Schedulers;
12 |
13 | /**
14 | * author: shell
15 | * date 2017/3/6 下午2:51
16 | **/
17 | public class UserDetailPresenter extends BasePresenter {
18 |
19 | public void getUserDetail(String userName) {
20 | mCompositeSubscription.add(CNodeApi.getCNodeService().getUserDetailByName(userName)
21 | .subscribeOn(Schedulers.io())
22 | .observeOn(AndroidSchedulers.mainThread())
23 | .subscribe(new Subscriber() {
24 | @Override
25 | public void onCompleted() {
26 |
27 | }
28 |
29 | @Override
30 | public void onError(Throwable e) {
31 |
32 | }
33 |
34 | @Override
35 | public void onNext(UserDetailModel userDetailModel) {
36 | getView().notifyGetUserSuccess(userDetailModel);
37 | }
38 | }));
39 | }
40 |
41 | public void getUnReadCount() {
42 | if (UserCache.getUserToken() == null) {
43 | return;
44 | }
45 | mCompositeSubscription.add(CNodeApi.getCNodeService().getUnReadCount(UserCache.getUserToken())
46 | .subscribeOn(Schedulers.io())
47 | .observeOn(AndroidSchedulers.mainThread())
48 | .subscribe(new Subscriber() {
49 | @Override
50 | public void onCompleted() {
51 |
52 | }
53 |
54 | @Override
55 | public void onError(Throwable e) {
56 |
57 | }
58 |
59 | @Override
60 | public void onNext(UnReadCountModel model) {
61 | getView().notifyUnReadCount(model);
62 | }
63 | }));
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/mvp/view/MvpView.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.mvp.view;
2 |
3 | /**
4 | * author: shell
5 | * date 2017/2/24 下午4:17
6 | **/
7 | public interface MvpView {
8 | void onFailed(Throwable e);
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/ui/activity/QRActivity.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.ui.activity;
2 |
3 | import android.os.Bundle;
4 | import android.view.KeyEvent;
5 |
6 | import com.journeyapps.barcodescanner.CaptureManager;
7 | import com.journeyapps.barcodescanner.DecoratedBarcodeView;
8 | import com.licrafter.cnode.R;
9 | import com.licrafter.cnode.base.BaseActivity;
10 |
11 | import butterknife.BindView;
12 |
13 | /**
14 | * author: shell
15 | * date 2017/3/13 上午11:26
16 | **/
17 | public class QRActivity extends BaseActivity {
18 |
19 | @BindView(R.id.qr_view)
20 | DecoratedBarcodeView barcodeScannerView;
21 |
22 | private CaptureManager capture;
23 |
24 |
25 | @Override
26 | public int getContentView() {
27 | return R.layout.activity_qr;
28 | }
29 |
30 | @Override
31 | public void initView(Bundle savedInstanceState) {
32 | capture = new CaptureManager(this, barcodeScannerView);
33 | capture.initializeFromIntent(getIntent(), savedInstanceState);
34 | capture.decode();
35 | }
36 |
37 | @Override
38 | public void setListeners() {
39 |
40 | }
41 |
42 | @Override
43 | protected void onResume() {
44 | super.onResume();
45 | capture.onResume();
46 | }
47 |
48 | @Override
49 | protected void onPause() {
50 | super.onPause();
51 | capture.onPause();
52 | }
53 |
54 | @Override
55 | protected void onDestroy() {
56 | super.onDestroy();
57 | capture.onDestroy();
58 | }
59 |
60 | @Override
61 | protected void onSaveInstanceState(Bundle outState) {
62 | super.onSaveInstanceState(outState);
63 | capture.onSaveInstanceState(outState);
64 | }
65 |
66 | @Override
67 | public void bind() {
68 |
69 | }
70 |
71 | @Override
72 | public void unBind() {
73 |
74 | }
75 |
76 | @Override
77 | public boolean onKeyDown(int keyCode, KeyEvent event) {
78 | return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/ui/fragment/HomePageFragment.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.ui.fragment;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.view.View;
6 | import android.widget.ImageButton;
7 |
8 | import com.licrafter.cnode.R;
9 | import com.licrafter.cnode.base.BaseFragment;
10 | import com.licrafter.cnode.cache.UserCache;
11 | import com.licrafter.cnode.ui.activity.LoginActivity;
12 | import com.licrafter.cnode.ui.activity.MarkdownEditActivity;
13 | import com.licrafter.cnode.utils.FragmentUtils;
14 |
15 | import butterknife.BindView;
16 |
17 | /**
18 | * author: shell
19 | * date 2017/2/27 下午3:12
20 | **/
21 | public class HomePageFragment extends BaseFragment {
22 |
23 | @BindView(R.id.btn_create_topic)
24 | ImageButton mBtnCreate;
25 |
26 | public static HomePageFragment newInstance() {
27 | Bundle bundle = new Bundle();
28 | HomePageFragment fragment = new HomePageFragment();
29 | fragment.setArguments(bundle);
30 | return fragment;
31 | }
32 |
33 | @Override
34 | public int setContentView() {
35 | return R.layout.fragment_home_page;
36 | }
37 |
38 | @Override
39 | public void lazyLoad() {
40 |
41 | }
42 |
43 | @Override
44 | public void initView(View root) {
45 | if (getChildFragmentManager().findFragmentByTag(TopicListFragment.class.getName()) == null) {
46 | TopicListFragment fragment = TopicListFragment.instance(null);
47 | fragment.setUserVisibleHint(true);
48 | FragmentUtils.replace(getChildFragmentManager(), R.id.home_page_content, fragment, false, TopicListFragment.class.getName());
49 | }
50 | }
51 |
52 | @Override
53 | public void setListeners() {
54 | mBtnCreate.setOnClickListener(new View.OnClickListener() {
55 | @Override
56 | public void onClick(View v) {
57 | if (UserCache.getUserToken() != null) {
58 | MarkdownEditActivity.createTopic(getBaseActivity());
59 | } else {
60 | startActivity(new Intent(getBaseActivity(), LoginActivity.class));
61 | }
62 | }
63 | });
64 | }
65 |
66 | @Override
67 | public void bind() {
68 |
69 | }
70 |
71 | @Override
72 | public void unBind() {
73 |
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/ui/fragment/MdEditFragment.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.ui.fragment;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.text.TextUtils;
6 | import android.view.View;
7 | import android.widget.EditText;
8 |
9 | import com.licrafter.cnode.R;
10 | import com.licrafter.cnode.base.BaseFragment;
11 | import com.licrafter.cnode.ui.activity.MarkdownEditActivity;
12 | import com.licrafter.cnode.widget.richEditView.engine.PerformEditable;
13 |
14 | import butterknife.BindView;
15 |
16 | /**
17 | * Created by lijx on 2017/4/5.
18 | */
19 |
20 | public class MdEditFragment extends BaseFragment {
21 |
22 | @BindView(R.id.input_content)
23 | EditText mInputContent;
24 | @BindView(R.id.input_title)
25 | EditText mInputTitle;
26 |
27 | private PerformEditable mPerformEditable;
28 | private int mType = MarkdownEditActivity.NEW_TOPIC;
29 | private String mAuthor;
30 |
31 | public static MdEditFragment newInstance(int type, String author) {
32 | MdEditFragment fragment = new MdEditFragment();
33 | Bundle bundle = new Bundle();
34 | bundle.putInt("type", type);
35 | bundle.putString("author", author);
36 | fragment.setArguments(bundle);
37 | return fragment;
38 | }
39 |
40 | @Override
41 | public void onCreate(@Nullable Bundle savedInstanceState) {
42 | super.onCreate(savedInstanceState);
43 | if (getArguments() != null) {
44 | mType = getArguments().getInt("type");
45 | mAuthor = getArguments().getString("author");
46 | }
47 | }
48 |
49 | @Override
50 | public int setContentView() {
51 | return R.layout.fragment_md_edit;
52 | }
53 |
54 | @Override
55 | public void lazyLoad() {
56 |
57 | }
58 |
59 |
60 | @Override
61 | public void initView(View root) {
62 | mPerformEditable = new PerformEditable(mInputContent);
63 | if (mType == MarkdownEditActivity.NEW_TOPIC) {
64 | mInputTitle.setVisibility(View.VISIBLE);
65 | mInputTitle.requestFocus();
66 | } else {
67 | mInputTitle.setVisibility(View.GONE);
68 | mInputContent.requestFocus();
69 | }
70 | if (!TextUtils.isEmpty(mAuthor)) {
71 | mInputContent.setText("@" + mAuthor);
72 | }
73 | }
74 |
75 | @Override
76 | public void setListeners() {
77 |
78 | }
79 |
80 | @Override
81 | public void bind() {
82 |
83 | }
84 |
85 | @Override
86 | public void unBind() {
87 |
88 | }
89 |
90 | public PerformEditable getPerformEditable() {
91 | return mPerformEditable;
92 | }
93 |
94 | public String getContent() {
95 | return mInputContent.getText().toString();
96 | }
97 |
98 | public String getTitle() {
99 | return mInputTitle.getText().toString();
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/ui/fragment/MdPreviewFragment.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.ui.fragment;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.licrafter.cnode.R;
9 | import com.licrafter.cnode.base.BaseFragment;
10 | import com.licrafter.cnode.ui.activity.MarkdownEditActivity;
11 | import com.licrafter.cnode.widget.MarkdownPreview;
12 |
13 | import butterknife.BindView;
14 |
15 | /**
16 | * Created by lijx on 2017/4/5.
17 | */
18 |
19 | public class MdPreviewFragment extends BaseFragment {
20 |
21 | @BindView(R.id.markdownPreview)
22 | MarkdownPreview mMarkdownPreview;
23 | @BindView(R.id.title)
24 | TextView mTitleView;
25 |
26 | private String mContent;
27 | private boolean mPageLoadFinished;
28 | private int mType = MarkdownEditActivity.NEW_TOPIC;
29 |
30 | public static MdPreviewFragment newInstance(int type) {
31 | MdPreviewFragment fragment = new MdPreviewFragment();
32 | Bundle bundle = new Bundle();
33 | bundle.putInt("type", type);
34 | fragment.setArguments(bundle);
35 | return fragment;
36 | }
37 |
38 | @Override
39 | public void onCreate(@Nullable Bundle savedInstanceState) {
40 | super.onCreate(savedInstanceState);
41 | if (getArguments() != null) {
42 | mType = getArguments().getInt("type");
43 | }
44 | }
45 |
46 | @Override
47 | public int setContentView() {
48 | return R.layout.fragment_md_preview;
49 | }
50 |
51 | @Override
52 | public void lazyLoad() {
53 |
54 | }
55 |
56 | @Override
57 | public void initView(View root) {
58 | mTitleView.setVisibility(mType == MarkdownEditActivity.NEW_TOPIC ? View.VISIBLE : View.GONE);
59 | mMarkdownPreview.setOnLoadingFinishListener(new MarkdownPreview.OnLoadingFinishListener() {
60 | @Override
61 | public void onLoadingFinish() {
62 | mPageLoadFinished = true;
63 | }
64 | });
65 | }
66 |
67 | public void refresh(String title, String content) {
68 | mContent = content;
69 | if (mPageLoadFinished && mContent != null) {
70 | mMarkdownPreview.parseMarkdown(mContent, true);
71 | }
72 | if (title != null) {
73 | mTitleView.setText(title);
74 | }
75 | }
76 |
77 | @Override
78 | public void setListeners() {
79 |
80 | }
81 |
82 | @Override
83 | public void bind() {
84 |
85 | }
86 |
87 | @Override
88 | public void unBind() {
89 |
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/utils/DateUtils.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.utils;
2 |
3 | import java.util.Calendar;
4 | import java.util.Date;
5 |
6 | /**
7 | * author: shell
8 | * date 2017/2/27 下午12:37
9 | **/
10 | public class DateUtils {
11 |
12 | private static final long ONE_MINUTE = 60000L;
13 | private static final long ONE_HOUR = 3600000L;
14 | private static final long ONE_DAY = 86400000L;
15 | private static final long ONE_WEEK = 604800000L;
16 |
17 | private static final String ONE_SECOND_AGO = "秒前";
18 | private static final String ONE_MINUTE_AGO = "分钟前";
19 | private static final String ONE_HOUR_AGO = "小时前";
20 | private static final String ONE_DAY_AGO = "天前";
21 | private static final String ONE_MONTH_AGO = "月前";
22 | private static final String ONE_YEAR_AGO = "年前";
23 |
24 |
25 | public static String format(Date date) {
26 | long delta = new Date().getTime() - date.getTime();
27 | if (delta < 1L * ONE_MINUTE) {
28 | long seconds = toSeconds(delta);
29 | return (seconds <= 0 ? 1 : seconds) + ONE_SECOND_AGO;
30 | }
31 | if (delta < 45L * ONE_MINUTE) {
32 | long minutes = toMinutes(delta);
33 | return (minutes <= 0 ? 1 : minutes) + ONE_MINUTE_AGO;
34 | }
35 | if (delta < 24L * ONE_HOUR) {
36 | long hours = toHours(delta);
37 | return (hours <= 0 ? 1 : hours) + ONE_HOUR_AGO;
38 | }
39 | if (delta < 48L * ONE_HOUR) {
40 | return "昨天";
41 | }
42 | if (delta < 30L * ONE_DAY) {
43 | long days = toDays(delta);
44 | return (days <= 0 ? 1 : days) + ONE_DAY_AGO;
45 | }
46 | if (delta < 12L * 4L * ONE_WEEK) {
47 | long months = toMonths(delta);
48 | return (months <= 0 ? 1 : months) + ONE_MONTH_AGO;
49 | } else {
50 | long years = toYears(delta);
51 | return (years <= 0 ? 1 : years) + ONE_YEAR_AGO;
52 | }
53 | }
54 |
55 | private static long toSeconds(long date) {
56 | return date / 1000L;
57 | }
58 |
59 | private static long toMinutes(long date) {
60 | return toSeconds(date) / 60L;
61 | }
62 |
63 | private static long toHours(long date) {
64 | return toMinutes(date) / 60L;
65 | }
66 |
67 | private static long toDays(long date) {
68 | return toHours(date) / 24L;
69 | }
70 |
71 | private static long toMonths(long date) {
72 | return toDays(date) / 30L;
73 | }
74 |
75 | private static long toYears(long date) {
76 | return toMonths(date) / 365L;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/utils/DipConvertUtils.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.utils;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * author: shell
7 | * date 2017/3/3 下午2:16
8 | **/
9 | public class DipConvertUtils {
10 |
11 | /**
12 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
13 | */
14 | public static int dip2px(Context context, float dpValue) {
15 | final float scale = context.getResources().getDisplayMetrics().density;
16 | return (int) (dpValue * scale + 0.5f);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/utils/FragmentUtils.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.utils;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.support.v4.app.FragmentManager;
5 | import android.support.v4.app.FragmentTransaction;
6 |
7 | import com.licrafter.cnode.R;
8 | import com.licrafter.cnode.base.BaseFragment;
9 |
10 | import java.util.List;
11 |
12 | /**
13 | * author: shell
14 | * date 2017/2/24 上午11:18
15 | **/
16 | public class FragmentUtils {
17 |
18 | public static void replace(FragmentManager manager, int containerId, Fragment fragment, boolean addBackStack, String tag) {
19 | FragmentTransaction transaction = manager.beginTransaction();
20 | if (addBackStack) {
21 | transaction.addToBackStack(tag);
22 | }
23 | transaction.replace(containerId, fragment, tag).commitAllowingStateLoss();
24 | }
25 |
26 | public static void add(FragmentManager manager, int containerId, Fragment fragment, boolean addBackStack, String tag) {
27 | FragmentTransaction transaction = manager.beginTransaction();
28 | if (addBackStack) {
29 | transaction.addToBackStack(tag);
30 | }
31 | transaction.add(containerId, fragment, tag).commitAllowingStateLoss();
32 | }
33 |
34 | public static void addMultiple(FragmentManager manager, int containerId, int showPosition, BaseFragment... fragments) {
35 | FragmentTransaction transaction = manager.beginTransaction();
36 | for (int i = 0; i < fragments.length; i++) {
37 | String tag = fragments[i].getClass().getName();
38 | transaction.add(containerId, fragments[i], tag);
39 | if (showPosition != i) {
40 | transaction.hide(fragments[i]);
41 | }
42 | fragments[i].setUserVisibleHint(!fragments[i].isHidden());
43 | }
44 | transaction.commit();
45 | }
46 |
47 | public static void showHideFragment(FragmentManager manager, Fragment show, Fragment hide, boolean animation, boolean backStack) {
48 | FragmentTransaction transaction = manager.beginTransaction();
49 | if (animation) {
50 | transaction.setCustomAnimations(
51 | R.anim.fragment_translate_in, R.anim.fragment_translate_out
52 | ,R.anim.fragment_pop_in,R.anim.fragment_pop_out);
53 | }
54 | transaction.show(show);
55 | if (hide == null) {
56 | List fragments = manager.getFragments();
57 | if (fragments != null) {
58 | for (Fragment fragment : fragments) {
59 | if (fragment != show) {
60 | transaction.hide(fragment);
61 | }
62 | }
63 | }
64 | } else {
65 | transaction.hide(hide);
66 | }
67 | if (backStack) {
68 | transaction.addToBackStack("showHideFragment");
69 | }
70 | transaction.commit();
71 | }
72 |
73 | public static T findFragment(FragmentManager manager, Class tClass) {
74 |
75 | if (manager.getFragments() == null) {
76 | return null;
77 | }
78 | return (T) manager.findFragmentByTag(tClass.getName());
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/utils/ImageLoader.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.utils;
2 |
3 | import android.widget.ImageView;
4 |
5 | import com.squareup.picasso.Picasso;
6 |
7 | /**
8 | * author: shell
9 | * date 2017/2/27 上午11:09
10 | **/
11 | public class ImageLoader {
12 |
13 | public static void loadUrl(ImageView tag, String url) {
14 | Picasso.with(tag.getContext()).load(url).into(tag);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/utils/IntentUtils.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.utils;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 |
7 | /**
8 | * Created by lijx on 2017/4/14.
9 | */
10 |
11 | public class IntentUtils {
12 |
13 | public static void startBrower(Context context, String url) {
14 | Intent intent = new Intent();
15 | intent.setAction(Intent.ACTION_VIEW);
16 | Uri content_url = Uri.parse(url);
17 | intent.setData(content_url);
18 | context.startActivity(intent);
19 | }
20 |
21 | public static void shareTopic(Context context, String title, String id) {
22 | Intent intent = new Intent();
23 | intent.setAction(Intent.ACTION_SEND);
24 | intent.putExtra(Intent.EXTRA_TEXT, title + " 链接>https://cnodejs.org/topic/" + id + "\n\n来自CNode-Android客户端(https://github.com/shellljx/CNode-android)");
25 | intent.setType("text/plain");
26 | context.startActivity(Intent.createChooser(intent, "分享到"));
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/utils/LogUtils.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.utils;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * author: shell
7 | * date 2017/2/24 下午3:35
8 | **/
9 | public class LogUtils {
10 |
11 | public static final boolean DEBUG = false;
12 |
13 | public static void debug(String tag, String message) {
14 | if (DEBUG) {
15 | Log.d(tag, message);
16 | }
17 | }
18 |
19 | public static void error(String tag, String message) {
20 | if (DEBUG) {
21 | Log.e(tag, message);
22 | }
23 | }
24 |
25 | public static void info(String tag, String message) {
26 | if (DEBUG) {
27 | Log.i(tag, message);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/utils/PermissionUtils.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.utils;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.content.Intent;
7 | import android.content.pm.PackageManager;
8 | import android.net.Uri;
9 | import android.provider.Settings;
10 | import android.support.v4.content.ContextCompat;
11 | import android.support.v7.app.AlertDialog;
12 |
13 | /**
14 | * author: shell
15 | * date 2017/3/13 上午11:19
16 | **/
17 | public class PermissionUtils {
18 |
19 | /**
20 | * 权限是否被开启
21 | *
22 | * @param context
23 | * @param permission
24 | * @return
25 | */
26 | public static boolean checkPermission(Context context, String permission) {
27 | return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
28 | }
29 |
30 | /**
31 | * 显示权限dialog
32 | *
33 | * @param activity
34 | * @param message
35 | * @param needFinishActivity
36 | */
37 | public static void showPermissionsDialog(final Activity activity, String message, final boolean needFinishActivity) {
38 | new AlertDialog.Builder(activity)
39 | .setTitle("权限申请")
40 | .setMessage(message)
41 | .setPositiveButton("去设置", new DialogInterface.OnClickListener() {
42 | @Override
43 | public void onClick(DialogInterface dialog, int which) {
44 |
45 | Uri packageURI = Uri.parse("package:" + "com.licrafter.cnode");
46 | Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
47 | activity.startActivity(intent);
48 | activity.finish();
49 | }
50 | })
51 | .setNegativeButton("取消", new DialogInterface.OnClickListener() {
52 | @Override
53 | public void onClick(DialogInterface dialog, int which) {
54 | if (needFinishActivity) {
55 | activity.finish();
56 | }
57 | }
58 | })
59 | .setCancelable(false)
60 | .create()
61 | .show();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/utils/SharedPreferenceUtils.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.utils;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | import java.util.Set;
7 |
8 | /**
9 | * author: shell
10 | * date 2017/2/24 下午3:26
11 | **/
12 | public class SharedPreferenceUtils {
13 |
14 |
15 | public static final String APP_CACHE_NAME = "app_cache_name";
16 | private static SharedPreferences mSharedPreferences = null;
17 |
18 | public static void init(Context context) {
19 | if (mSharedPreferences == null && context != null) {
20 | mSharedPreferences = context.getSharedPreferences(APP_CACHE_NAME, Context.MODE_PRIVATE);
21 | }
22 | }
23 |
24 | private static SharedPreferences getSP() {
25 | return mSharedPreferences;
26 | }
27 |
28 | private static SharedPreferences.Editor getEditor() {
29 | return getSP().edit();
30 | }
31 |
32 | public static void save(String key, boolean value) {
33 | getEditor().putBoolean(key, value).apply();
34 | }
35 |
36 | public static void save(String key, int value) {
37 | getEditor().putInt(key, value).apply();
38 | }
39 |
40 | public static void save(String key, float value) {
41 | getEditor().putFloat(key, value).apply();
42 | }
43 |
44 | public static void save(String key, long value) {
45 | getEditor().putLong(key, value).apply();
46 | }
47 |
48 | public static void save(String key, String value) {
49 | getEditor().putString(key, value).apply();
50 | }
51 |
52 | public static void save(String key, Set value) {
53 | getEditor().putStringSet(key, value).apply();
54 | }
55 |
56 | public static boolean getBoolean(String key, boolean defValue) {
57 | return getSP().getBoolean(key, defValue);
58 | }
59 |
60 | public static String getString(String key, String defValue) {
61 | return getSP().getString(key, defValue);
62 | }
63 |
64 | public static int getInt(String key, int defValue) {
65 | return getSP().getInt(key, defValue);
66 | }
67 |
68 | public static float getFloat(String key, float defValue) {
69 | return getSP().getFloat(key, defValue);
70 | }
71 |
72 | public static long getLong(String key, long defValue) {
73 | return getSP().getLong(key, defValue);
74 | }
75 |
76 | public static Set getStringSet(String key, Set defValue) {
77 | return getSP().getStringSet(key, defValue);
78 | }
79 |
80 | public static boolean contains(String key) {
81 | return getSP().contains(key);
82 | }
83 |
84 | public static void clearAll() {
85 | getEditor().clear().apply();
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/utils/StatusBarUtils.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.utils;
2 |
3 | import android.os.Build;
4 | import android.view.WindowManager;
5 |
6 | import com.licrafter.cnode.base.BaseActivity;
7 |
8 | /**
9 | * author: shell
10 | * date 2017/3/9 上午11:43
11 | **/
12 | public class StatusBarUtils {
13 |
14 | public static void initStatusBar(BaseActivity activity) {
15 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
16 | WindowManager.LayoutParams localLayoutParams = activity.getWindow().getAttributes();
17 | localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/utils/SwipeRefreshUtils.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.utils;
2 |
3 | import android.support.v4.widget.SwipeRefreshLayout;
4 |
5 | import com.licrafter.cnode.R;
6 |
7 | /**
8 | * author: shell
9 | * date 2017/2/27 下午1:47
10 | **/
11 | public class SwipeRefreshUtils {
12 |
13 | public static void initStyle(SwipeRefreshLayout swipeRefreshLayout) {
14 | if (swipeRefreshLayout != null) {
15 | swipeRefreshLayout.setColorSchemeResources(
16 | R.color.loading_color_1,
17 | R.color.loading_color_2,
18 | R.color.loading_color_3,
19 | R.color.loading_color_4,
20 | R.color.loading_color_5);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/utils/TopicDividerDecoration.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.utils;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.Rect;
7 | import android.support.v4.content.ContextCompat;
8 | import android.support.v7.widget.RecyclerView;
9 | import android.view.View;
10 |
11 | import com.licrafter.cnode.R;
12 |
13 | /**
14 | * author: shell
15 | * date 2017/2/27 下午1:15
16 | **/
17 | public class TopicDividerDecoration extends RecyclerView.ItemDecoration {
18 | private int dividerHeight;
19 | private Paint dividerPaint;
20 |
21 | public TopicDividerDecoration(Context context) {
22 | dividerPaint = new Paint();
23 | dividerPaint.setColor(ContextCompat.getColor(context, R.color.gray));
24 | dividerHeight = 2;
25 | }
26 |
27 |
28 | @Override
29 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
30 | super.getItemOffsets(outRect, view, parent, state);
31 | outRect.bottom = dividerHeight;
32 | }
33 |
34 | @Override
35 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
36 | int childCount = parent.getChildCount();
37 | int left = parent.getPaddingLeft();
38 | int right = parent.getWidth() - parent.getPaddingRight();
39 |
40 | for (int i = 0; i < childCount-1; i++) {
41 | View view = parent.getChildAt(i);
42 | float top = view.getBottom();
43 | float bottom = view.getBottom() + dividerHeight;
44 | c.drawRect(left, top, right, bottom, dividerPaint);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/utils/http/HttpClient.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.utils.http;
2 |
3 | import com.licrafter.cnode.BaseApplication;
4 |
5 | import java.net.CookieManager;
6 | import java.net.CookiePolicy;
7 | import java.util.concurrent.TimeUnit;
8 |
9 | import okhttp3.OkHttpClient;
10 | import okhttp3.logging.HttpLoggingInterceptor;
11 |
12 | /**
13 | * author: shell
14 | * date 2017/2/24 下午3:13
15 | **/
16 | public enum HttpClient {
17 |
18 | INSTANCE;
19 | private OkHttpClient client;
20 | private PersistentCookieStore persistentCookieStore;
21 |
22 | HttpClient() {
23 | persistentCookieStore = new PersistentCookieStore(BaseApplication.getContext());
24 | client = new OkHttpClient.Builder()
25 | .connectTimeout(5, TimeUnit.SECONDS)
26 | .writeTimeout(5, TimeUnit.SECONDS)
27 | .readTimeout(10, TimeUnit.SECONDS)
28 | .cookieJar(new CustomCookieJar(new CookieManager(persistentCookieStore, CookiePolicy.ACCEPT_ALL)))
29 | .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
30 | .build();
31 |
32 | }
33 |
34 | public OkHttpClient getOkHttpClient() {
35 | return client;
36 | }
37 |
38 | public void clearCookie() {
39 | persistentCookieStore.removeAll();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/utils/http/SerializableHttpCookie.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.utils.http;
2 |
3 | import java.io.IOException;
4 | import java.io.ObjectInputStream;
5 | import java.io.ObjectOutputStream;
6 | import java.io.Serializable;
7 | import java.net.HttpCookie;
8 |
9 | /**
10 | * author: shell
11 | * date 2017/2/24 下午3:20
12 | **/
13 | public class SerializableHttpCookie implements Serializable {
14 | private static final long serialVersionUID = 6374381323722046732L;
15 |
16 | private transient final HttpCookie cookie;
17 | private transient HttpCookie clientCookie;
18 |
19 | public SerializableHttpCookie(HttpCookie cookie) {
20 | this.cookie = cookie;
21 | }
22 |
23 | public HttpCookie getCookie() {
24 | HttpCookie bestCookie = cookie;
25 | if (clientCookie != null) {
26 | bestCookie = clientCookie;
27 | }
28 | return bestCookie;
29 | }
30 |
31 | private void writeObject(ObjectOutputStream out) throws IOException {
32 | out.writeObject(cookie.getName());
33 | out.writeObject(cookie.getValue());
34 | out.writeObject(cookie.getComment());
35 | out.writeObject(cookie.getCommentURL());
36 | out.writeObject(cookie.getDomain());
37 | out.writeLong(cookie.getMaxAge());
38 | out.writeObject(cookie.getPath());
39 | out.writeObject(cookie.getPortlist());
40 | out.writeInt(cookie.getVersion());
41 | out.writeBoolean(cookie.getSecure());
42 | out.writeBoolean(cookie.getDiscard());
43 | }
44 |
45 | private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
46 | String name = (String) in.readObject();
47 | String value = (String) in.readObject();
48 | clientCookie = new HttpCookie(name, value);
49 | clientCookie.setComment((String) in.readObject());
50 | clientCookie.setCommentURL((String) in.readObject());
51 | clientCookie.setDomain((String) in.readObject());
52 | clientCookie.setMaxAge(in.readLong());
53 | clientCookie.setPath((String) in.readObject());
54 | clientCookie.setPortlist((String) in.readObject());
55 | clientCookie.setVersion(in.readInt());
56 | clientCookie.setSecure(in.readBoolean());
57 | clientCookie.setDiscard(in.readBoolean());
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/widget/BoxingUcrop.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Bilibili
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.licrafter.cnode.widget;
19 |
20 | import android.content.Context;
21 | import android.content.Intent;
22 | import android.graphics.Bitmap;
23 | import android.net.Uri;
24 | import android.support.annotation.NonNull;
25 | import android.support.v4.app.Fragment;
26 |
27 | import com.bilibili.boxing.loader.IBoxingCrop;
28 | import com.bilibili.boxing.model.config.BoxingCropOption;
29 | import com.yalantis.ucrop.UCrop;
30 |
31 | /**
32 | * use Ucrop(https://github.com/Yalantis/uCrop) as the implement for {@link IBoxingCrop}
33 | *
34 | * @author ChenSL
35 | */
36 | public class BoxingUcrop implements IBoxingCrop {
37 |
38 | @Override
39 | public void onStartCrop(Context context, Fragment fragment, @NonNull BoxingCropOption cropConfig,
40 | @NonNull String path, int requestCode) {
41 | Uri uri = new Uri.Builder()
42 | .scheme("file")
43 | .appendPath(path)
44 | .build();
45 | UCrop.Options crop = new UCrop.Options();
46 | // do not copy exif information to crop pictures
47 | // because png do not have exif and png is not Distinguishable
48 | crop.setCompressionFormat(Bitmap.CompressFormat.PNG);
49 | crop.withMaxResultSize(cropConfig.getMaxWidth(), cropConfig.getMaxHeight());
50 | crop.withAspectRatio(cropConfig.getAspectRatioX(), cropConfig.getAspectRatioY());
51 |
52 | UCrop.of(uri, cropConfig.getDestination())
53 | .withOptions(crop)
54 | .start(context, fragment, requestCode);
55 | }
56 |
57 | @Override
58 | public Uri onCropFinish(int resultCode, Intent data) {
59 | if (data == null) {
60 | return null;
61 | }
62 | Throwable throwable = UCrop.getError(data);
63 | if (throwable != null) {
64 | return null;
65 | }
66 | return UCrop.getOutput(data);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/widget/HorizontalOperatorView.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.widget.HorizontalScrollView;
8 | import android.widget.ImageView;
9 |
10 | import com.licrafter.cnode.R;
11 |
12 | import butterknife.BindView;
13 | import butterknife.ButterKnife;
14 |
15 | /**
16 | * Created by lijx on 2017/4/10.
17 | */
18 |
19 | public class HorizontalOperatorView extends HorizontalScrollView implements View.OnClickListener {
20 |
21 | @BindView(R.id.font_blod)
22 | ImageView mFontBlod;
23 | @BindView(R.id.font_italic)
24 | ImageView mFontItalic;
25 | @BindView(R.id.quote)
26 | ImageView mOpQuote;
27 | @BindView(R.id.list_ul)
28 | ImageView mOpUl;
29 | @BindView(R.id.list_ol)
30 | ImageView mOpOl;
31 | @BindView(R.id.op_link)
32 | ImageView mOpLink;
33 | @BindView(R.id.op_image)
34 | ImageView mOpImage;
35 | @BindView(R.id.op_code)
36 | ImageView mOpCode;
37 | @BindView(R.id.op_preview)
38 | ImageView mOpPreview;
39 |
40 | private OperationListener mListener;
41 |
42 | public HorizontalOperatorView(Context context) {
43 | this(context, null);
44 | }
45 |
46 | public HorizontalOperatorView(Context context, AttributeSet attrs) {
47 | this(context, attrs, 0);
48 | }
49 |
50 | public HorizontalOperatorView(Context context, AttributeSet attrs, int defStyleAttr) {
51 | super(context, attrs, defStyleAttr);
52 | View rootView = LayoutInflater.from(context).inflate(R.layout.layout_operator, this, true);
53 | ButterKnife.bind(this, rootView);
54 |
55 | mFontBlod.setOnClickListener(this);
56 | mFontItalic.setOnClickListener(this);
57 | mOpQuote.setOnClickListener(this);
58 | mOpUl.setOnClickListener(this);
59 | mOpOl.setOnClickListener(this);
60 | mOpLink.setOnClickListener(this);
61 | mOpImage.setOnClickListener(this);
62 | mOpCode.setOnClickListener(this);
63 | mOpPreview.setOnClickListener(this);
64 | }
65 |
66 | @Override
67 | public void onClick(View v) {
68 | if (mListener != null) {
69 | mListener.operation(v);
70 | }
71 | }
72 |
73 | public void setOperationListener(OperationListener listener) {
74 | mListener = listener;
75 | }
76 |
77 | public interface OperationListener {
78 | public void operation(View view);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/widget/NotScrollViewPager.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.widget;
2 |
3 | import android.content.Context;
4 | import android.support.v4.view.ViewPager;
5 | import android.util.AttributeSet;
6 | import android.view.MotionEvent;
7 |
8 | /**
9 | * author: shell
10 | * date 2017/2/24 上午11:42
11 | **/
12 | public class NotScrollViewPager extends ViewPager {
13 |
14 | public NotScrollViewPager(Context context) {
15 | super(context);
16 | }
17 |
18 | public NotScrollViewPager(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | }
21 |
22 | @Override
23 | public boolean onTouchEvent(MotionEvent ev) {
24 | return false;
25 | }
26 |
27 | @Override
28 | public boolean onInterceptTouchEvent(MotionEvent ev) {
29 | return false;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/widget/dialog/InsertLinkDialog.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.widget.dialog;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.support.design.widget.TextInputEditText;
6 | import android.support.design.widget.TextInputLayout;
7 | import android.support.v7.app.AlertDialog;
8 | import android.text.TextUtils;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.widget.TextView;
12 |
13 | import com.licrafter.cnode.R;
14 |
15 | import butterknife.BindView;
16 | import butterknife.ButterKnife;
17 |
18 | /**
19 | * Created by lijx on 2017/4/10.
20 | */
21 |
22 | public class InsertLinkDialog extends AlertDialog implements View.OnClickListener {
23 |
24 | @BindView(R.id.inputNameHint)
25 | TextInputLayout mTitleHint;
26 | @BindView(R.id.inputHint)
27 | TextInputLayout mLinkHint;
28 | @BindView(R.id.titleInput)
29 | TextInputEditText mTitle;
30 | @BindView(R.id.linkInput)
31 | TextInputEditText mLink;
32 | @BindView(R.id.confirm)
33 | TextView mConfirm;
34 | @BindView(R.id.cancel)
35 | TextView mCancel;
36 |
37 | private OnClickListener mListener;
38 |
39 | public InsertLinkDialog(@NonNull Context context, OnClickListener listener) {
40 | super(context);
41 | mListener = listener;
42 | View rootView = LayoutInflater.from(context).inflate(R.layout.layout_insert_link_dialog, null);
43 | ButterKnife.bind(this, rootView);
44 | setTitle("插入链接");
45 | setView(rootView);
46 | mConfirm.setOnClickListener(this);
47 | mCancel.setOnClickListener(this);
48 | }
49 |
50 | @Override
51 | public void onClick(View v) {
52 | switch (v.getId()) {
53 | case R.id.confirm:
54 | String titleStr = mTitle.getText().toString().trim();
55 | String linkStr = mLink.getText().toString().trim();
56 |
57 | if (TextUtils.isEmpty(titleStr)) {
58 | mTitleHint.setError("不能为空");
59 | return;
60 | }
61 | if (TextUtils.isEmpty(linkStr)) {
62 | mLinkHint.setError("不能为空");
63 | return;
64 | }
65 |
66 | if (mTitleHint.isErrorEnabled())
67 | mTitleHint.setErrorEnabled(false);
68 | if (mLinkHint.isErrorEnabled())
69 | mLinkHint.setErrorEnabled(false);
70 |
71 | if (mListener != null) {
72 | mListener.confirm(titleStr, linkStr);
73 | }
74 | dismiss();
75 | break;
76 | case R.id.cancel:
77 | if (mListener!=null){
78 | mListener.cancel();
79 | }
80 | dismiss();
81 | break;
82 | }
83 | }
84 |
85 |
86 | public interface OnClickListener {
87 | void confirm(String title, String url);
88 |
89 | void cancel();
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/widget/dialog/UploadingDialog.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.widget.dialog;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.support.v7.app.AlertDialog;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.widget.TextView;
9 |
10 | import com.licrafter.cnode.R;
11 |
12 | /**
13 | * Created by lijx on 2017/4/10.
14 | */
15 |
16 | public class UploadingDialog extends AlertDialog {
17 |
18 | private TextView mProgressVeiw;
19 |
20 | public UploadingDialog(@NonNull Context context) {
21 | super(context);
22 | View dialogView = LayoutInflater.from(context).inflate(R.layout.layout_uploading_dialog, null);
23 | mProgressVeiw = (TextView) dialogView.findViewById(R.id.tv_message);
24 | mProgressVeiw.setText(String.format(getContext().getString(R.string.uploading_progress), 0));
25 | setView(dialogView);
26 | }
27 |
28 | public void updateProgress(int progress) {
29 | mProgressVeiw.setText(String.format(getContext().getString(R.string.uploading_progress), progress));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licrafter/cnode/widget/richTextView/RichTextView.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode.widget.richTextView;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.AppCompatTextView;
5 | import android.text.Html;
6 | import android.text.SpannableStringBuilder;
7 | import android.text.Spanned;
8 | import android.text.method.LinkMovementMethod;
9 | import android.util.AttributeSet;
10 |
11 | /**
12 | * author: shell
13 | * date 2017/3/2 上午10:30
14 | **/
15 | public class RichTextView extends AppCompatTextView {
16 |
17 | public RichTextView(Context context) {
18 | super(context);
19 | }
20 |
21 | public RichTextView(Context context, AttributeSet attrs) {
22 | super(context, attrs);
23 | }
24 |
25 | public RichTextView(Context context, AttributeSet attrs, int defStyleAttr) {
26 | super(context, attrs, defStyleAttr);
27 | }
28 |
29 | public void setRichText(String text) {
30 | Spanned spanned = Html.fromHtml(text, new AsyncImageGetter(getContext(), this), null);
31 | SpannableStringBuilder htmlSpannable;
32 | if (spanned instanceof SpannableStringBuilder) {
33 | htmlSpannable = (SpannableStringBuilder) spanned;
34 | } else {
35 | htmlSpannable = new SpannableStringBuilder(spanned);
36 | }
37 | handleP(htmlSpannable);
38 | setText(htmlSpannable);
39 | setMovementMethod(LinkMovementMethod.getInstance());
40 | }
41 |
42 | private static void handleP(SpannableStringBuilder text) {
43 | int len = text.length();
44 | if (len >= 2 && text.charAt(len - 1) == '\n' && text.charAt(len - 2) == '\n') {
45 | text.replace(len - 2, len - 1, "");
46 | }
47 |
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/app/src/main/res/anim/fragment_pop_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fragment_pop_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fragment_translate_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fragment_translate_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/color/color_bottom_navigation_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_gray_rect.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_operator_clicked.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_red_dot.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_topic_tag.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_topic_tag_normal.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
16 |
17 |
22 |
23 |
29 |
30 |
33 |
34 |
40 |
41 |
42 |
51 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
18 |
19 |
25 |
26 |
30 |
31 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_markdown_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
10 |
11 |
17 |
18 |
22 |
23 |
30 |
31 |
39 |
40 |
45 |
46 |
47 |
48 |
52 |
53 |
57 |
58 |
59 |
60 |
61 |
62 |
69 |
70 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_notification_center.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
16 |
17 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_qr.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_topic_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_category.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
20 |
27 |
28 |
34 |
35 |
42 |
43 |
51 |
52 |
53 |
54 |
60 |
61 |
62 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_home_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_md_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
23 |
24 |
29 |
30 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_md_preview.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
25 |
26 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_mine_topic_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_topic_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/include_comments_bottom_sheet.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_footer.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
21 |
22 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_notification.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
29 |
30 |
39 |
40 |
48 |
49 |
56 |
57 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_reply.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
27 |
28 |
36 |
37 |
44 |
45 |
52 |
53 |
59 |
60 |
68 |
69 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_reply_count.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_uploading_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/toolbar_common.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/toolbar_create_topic.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
19 |
20 |
26 |
27 |
34 |
35 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_bottom_navigation.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_send_post.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_topic_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_code.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_collected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_collected.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_comment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_comment.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_comment_gray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_comment_gray.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_create_topic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_create_topic.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_empty.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_font_blod.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_font_blod.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_font_italic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_font_italic.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_gray_dot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_gray_dot.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_image.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_link.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_more_infomation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_more_infomation.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_notification.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_ol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_ol.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_preview.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_profit_bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_profit_bg.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_quote.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_quote.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_reply.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_reply.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_send.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_send.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_settings.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_share.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_tab_category.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_tab_category.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_tab_home_page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_tab_home_page.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_tab_mine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_tab_mine.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_tab_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_tab_notification.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_ul.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_ul.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_uncollected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_uncollected.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_unup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_unup.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_up.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_visit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxhdpi/ic_visit.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-v19/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 25dp
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - 分享
5 | - 问答
6 | - 招聘
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #F44336
4 | #D32F2F
5 | #F44336
6 |
7 | #FFFFFF
8 | #000000
9 | #80BD01
10 | #E5E5E5
11 | #999999
12 |
13 | #FE8C1D
14 | #37BC9B
15 | #3BB7E9
16 | #FD6018
17 | #FF6766
18 | #ffffff
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 0dp
6 | 52dp
7 |
8 | 16dp
9 | 14dp
10 | 12dp
11 |
12 | 25dp
13 | 10dp
14 | 5dp
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | CNode社区
3 |
4 |
5 | 首页
6 | 分类
7 | 我的
8 | 消息
9 | 文章详情
10 | 创建帖子
11 | 创建评论
12 | 消息中心
13 | 登录
14 | 用户登录后,在设置页面可以看到自己的 accessToken。 建议各移动端应用使用手机扫码的形式获得 Token 登录
15 | 扫码
16 |
17 | 新建话题
18 | 最后回复: %s
19 | 收藏
20 | 取消收藏
21 | 浏览次数: %d
22 | 来自版块: %s
23 | 回复 %d 条 上拉查看更多
24 | 积分: %d
25 | 社龄: %s
26 | GitHub: %s
27 |
28 | 还没有文章呢!
29 | 文章还没有评论呢,板凳是你的!
30 | 所有图片
31 | 确定
32 | 预览
33 | 正在上传: %d/100
34 |
35 | 创建帖子成功!
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/licrafter/cnode/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.licrafter.cnode;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/art/01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/art/01.jpg
--------------------------------------------------------------------------------
/art/02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/art/02.jpg
--------------------------------------------------------------------------------
/art/03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/art/03.jpg
--------------------------------------------------------------------------------
/art/04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/art/04.jpg
--------------------------------------------------------------------------------
/art/05.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/art/05.jpg
--------------------------------------------------------------------------------
/art/06.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/art/06.jpg
--------------------------------------------------------------------------------
/art/07.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/art/07.jpg
--------------------------------------------------------------------------------
/art/08.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/art/08.jpg
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | //这里是 LeanCloud 的包仓库
7 | maven {
8 | url "http://mvn.leancloud.cn/nexus/content/repositories/public"
9 | }
10 | }
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:3.0.0'
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | jcenter()
21 | mavenCentral()
22 | maven { url 'https://jitpack.io' }
23 | maven {
24 | url "http://mvn.leancloud.cn/nexus/content/repositories/public"
25 | }
26 | }
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shellljx/CNode-android/69d4911a5176c5ff4db1dd9136b4bf0a394e04e6/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Nov 01 16:36:36 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':album'
2 |
--------------------------------------------------------------------------------