├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml ├── render.experimental.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── apikey.example.properties ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── engineer │ │ └── kaobei │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── fonts.json │ │ ├── open_source.json │ │ └── themes.json │ ├── ic_launcher-playstore.png │ ├── java │ │ └── engineer │ │ │ └── kaobei │ │ │ ├── DepthPageTransformer.kt │ │ │ ├── KaobeiEngineerService.kt │ │ │ ├── NestedScrollviewLoadMoreController.kt │ │ │ ├── OnLoadMoreListener.kt │ │ │ ├── RecyclerViewAdapterListener.kt │ │ │ ├── RecyclerViewLoadMoreScroll.kt │ │ │ ├── activity │ │ │ ├── ArticleActivity.kt │ │ │ ├── ArticleActivityV2.kt │ │ │ ├── CreateArticleActivity.kt │ │ │ ├── LoginActivity.kt │ │ │ ├── MainActivity.kt │ │ │ └── settings │ │ │ │ ├── OpenSourceAdapter.kt │ │ │ │ └── SettingsActivity.kt │ │ │ ├── database │ │ │ ├── AuthStateManager.kt │ │ │ ├── FontManager.kt │ │ │ ├── OpenSourceManager.kt │ │ │ └── ThemeManager.kt │ │ │ ├── fragment │ │ │ ├── ArticleListFragment.kt │ │ │ ├── CreateArticleFragment.kt │ │ │ ├── DashBoardFragment.kt │ │ │ ├── IndexFragment.kt │ │ │ └── ReviewFragment.kt │ │ │ ├── model │ │ │ ├── ReviewArticle │ │ │ │ ├── Data.kt │ │ │ │ └── SingleReviewArticle.kt │ │ │ ├── ReviewArticles │ │ │ │ ├── Links.kt │ │ │ │ ├── Meta.kt │ │ │ │ ├── Pagination.kt │ │ │ │ ├── ReviewArticle.kt │ │ │ │ └── ReviewArticles.kt │ │ │ ├── articleInfo │ │ │ │ ├── ArticleInfo.kt │ │ │ │ └── KaobeiArticleInfo.kt │ │ │ ├── articles │ │ │ │ ├── Article.kt │ │ │ │ ├── KaobeiArticleList.kt │ │ │ │ ├── Links.kt │ │ │ │ ├── Meta.kt │ │ │ │ └── Pagination.kt │ │ │ ├── comments │ │ │ │ ├── Comment.kt │ │ │ │ ├── KaobeiComments.kt │ │ │ │ ├── Links.kt │ │ │ │ ├── Media.kt │ │ │ │ ├── Meta.kt │ │ │ │ └── Pagination.kt │ │ │ ├── fonts │ │ │ │ ├── Font.kt │ │ │ │ └── KaobeiFonts.kt │ │ │ ├── kaobeluser │ │ │ │ ├── BeanKaobeiUser.kt │ │ │ │ └── KaobeiUser.kt │ │ │ ├── link │ │ │ │ ├── Data.kt │ │ │ │ └── KaobeiLink.kt │ │ │ ├── opensources │ │ │ │ ├── OpenSource.kt │ │ │ │ └── OpenSources.kt │ │ │ ├── themes │ │ │ │ ├── Additional.kt │ │ │ │ ├── KaobeiThemes.kt │ │ │ │ └── Theme.kt │ │ │ └── userarticles │ │ │ │ ├── Links.kt │ │ │ │ ├── Meta.kt │ │ │ │ ├── Pagination.kt │ │ │ │ ├── UserArticle.kt │ │ │ │ └── UserArticles.kt │ │ │ ├── util │ │ │ ├── ClipBoardUtil.kt │ │ │ ├── CustomTabUtil.kt │ │ │ ├── SnackbarUtil.kt │ │ │ ├── ViewUtil.kt │ │ │ └── ext │ │ │ │ └── ImageViewExt.kt │ │ │ ├── view │ │ │ ├── AnimatedGap.kt │ │ │ ├── KaobeiArticleViewer.kt │ │ │ └── UserViewer.kt │ │ │ └── viewmodel │ │ │ ├── ArticleInfoViewModel.kt │ │ │ ├── ArticleListViewModel.kt │ │ │ ├── CommentsViewModel.kt │ │ │ ├── IndexViewModel.kt │ │ │ ├── LinkViewModel.kt │ │ │ ├── ListViewModel.kt │ │ │ ├── ObjectViewModel.kt │ │ │ ├── ProfileViewModel.kt │ │ │ ├── ReviewViewModel.kt │ │ │ └── UserArticleListViewModel.kt │ └── res │ │ ├── anim │ │ ├── slide_in_right.xml │ │ └── slide_out_left.xml │ │ ├── color │ │ └── bnv_color.xml │ │ ├── drawable-v24 │ │ ├── ic_launcher_foreground.xml │ │ └── img_mallet.jpg │ │ ├── drawable │ │ ├── bg_sheet.xml │ │ ├── bottomsheet_line.xml │ │ ├── cursor.xml │ │ ├── custom_ripple.xml │ │ ├── ic_dashboard_white_24dp.xml │ │ ├── ic_favorite_white_24dp.xml │ │ ├── ic_home_white_24dp.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_menu_white_24dp.xml │ │ ├── ic_mode_edit_white_24dp.xml │ │ ├── ic_people_outline_white_24dp.xml │ │ ├── ic_person_white_24dp.xml │ │ ├── ic_reply_white_24dp.xml │ │ ├── ic_share_white_24dp.xml │ │ ├── ic_thumb_up_white_24dp.xml │ │ ├── ic_warning_red_24dp.xml │ │ ├── img_animated_rainbow.gif │ │ ├── img_background_star.gif │ │ ├── img_bat.xml │ │ ├── img_bird.xml │ │ ├── img_bull.xml │ │ ├── img_camel.xml │ │ ├── img_cat.xml │ │ ├── img_devotion_bg.png │ │ ├── img_flag1.jpg │ │ ├── img_kb.png │ │ ├── img_mallet.jpg │ │ ├── img_nopic_192.gif │ │ ├── img_plurk.png │ │ ├── img_twitter.jpg │ │ ├── kohlrabi.png │ │ ├── test1.png │ │ ├── test2.png │ │ └── text_bottom_line.xml │ │ ├── font │ │ └── dos.ttf │ │ ├── layout │ │ ├── activity_article.xml │ │ ├── activity_article_v2.xml │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ ├── ads_style1.xml │ │ ├── ads_style2.xml │ │ ├── bottom_sheet_authorized.xml │ │ ├── bottom_sheet_edit_content.xml │ │ ├── bottom_sheet_id_selecter.xml │ │ ├── bottom_sheet_links.xml │ │ ├── bottom_sheet_not_authorized.xml │ │ ├── bottom_sheet_open_source.xml │ │ ├── bottom_sheet_review_article.xml │ │ ├── bottom_sheet_select_font.xml │ │ ├── bottom_sheet_select_theme.xml │ │ ├── bottom_sheet_share.xml │ │ ├── bottom_sheet_submit_article.xml │ │ ├── cardview_header1.xml │ │ ├── cardview_header2.xml │ │ ├── cardview_open_source.xml │ │ ├── cardview_style1.xml │ │ ├── cardview_style2.xml │ │ ├── cardview_style3.xml │ │ ├── cardview_style_comment.xml │ │ ├── cardview_style_font.xml │ │ ├── cardview_style_link.xml │ │ ├── cardview_style_theme.xml │ │ ├── content_article_activity_v2.xml │ │ ├── create_article_activity.xml │ │ ├── fragment_article_list.xml │ │ ├── fragment_create_article.xml │ │ ├── fragment_dashboard.xml │ │ ├── fragment_index.xml │ │ ├── fragment_review.xml │ │ ├── progress_loading.xml │ │ ├── settings_activity.xml │ │ ├── view_last_one.xml │ │ ├── view_not_authorized.xml │ │ ├── widget_kaobei_article_viewer.xml │ │ └── widget_user_viewer.xml │ │ ├── menu │ │ └── navigation.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── navigation │ │ └── mobile_navigation.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── root_preferences.xml │ └── test │ └── java │ └── engineer │ └── kaobei │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /apikey.properties 4 | /local.properties 5 | /.idea/caches 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | /.idea/navEditor.xml 10 | /.idea/assetWizardSettings.xml 11 | .DS_Store 12 | /build 13 | /captures 14 | .externalNativeBuild 15 | .cxx 16 | /app/release/* 17 | /keystore -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | xmlns:android 17 | 18 | ^$ 19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | 27 | xmlns:.* 28 | 29 | ^$ 30 | 31 | 32 | BY_NAME 33 | 34 |
35 |
36 | 37 | 38 | 39 | .*:id 40 | 41 | http://schemas.android.com/apk/res/android 42 | 43 | 44 | 45 |
46 |
47 | 48 | 49 | 50 | .*:name 51 | 52 | http://schemas.android.com/apk/res/android 53 | 54 | 55 | 56 |
57 |
58 | 59 | 60 | 61 | name 62 | 63 | ^$ 64 | 65 | 66 | 67 |
68 |
69 | 70 | 71 | 72 | style 73 | 74 | ^$ 75 | 76 | 77 | 78 |
79 |
80 | 81 | 82 | 83 | .* 84 | 85 | ^$ 86 | 87 | 88 | BY_NAME 89 | 90 |
91 |
92 | 93 | 94 | 95 | .* 96 | 97 | http://schemas.android.com/apk/res/android 98 | 99 | 100 | ANDROID_ATTRIBUTE_ORDER 101 | 102 |
103 |
104 | 105 | 106 | 107 | .* 108 | 109 | .* 110 | 111 | 112 | BY_NAME 113 | 114 |
115 |
116 |
117 |
118 | 119 | 121 |
122 |
-------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 1.8 19 | 20 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea/render.experimental.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Google Play 3 |

4 |

喜歡純靠北工程師?我們也是。

5 |

這是一份純靠北工程師的 Android APP 專案,請好好愛護它,謝謝。

6 |

7 | facebook init 8 | facebook testing 9 | twitter 10 | plurk 11 |

12 | 13 | --- 14 | # 預覽 15 |
16 | Google Play 17 | Google Play 18 | Google Play 19 | Google Play 20 |
21 | 22 | --- 23 | # 貢獻 24 | 如果有問題麻煩在 GitHub 當中發 issues 給我們,感謝! 25 | 26 | ## 步驟一 27 | - 🍴 Fork 這個專案,或者不要 Fork 也可以,因為 Fork 發出去,不一定會有 PR 發回來。 28 | - 👯 Clone 這個專案到你的電腦當中,或者你根本不會用,你只會到 [Google Play](https://play.google.com/store/apps/details?id=engineer.kaobei) 按下載也行。 29 | 30 | ## 步驟二 31 | - 弄壞它,並且修好它 🔨🔨🔨 32 | 33 | ## 步驟三 34 | - 🔃 發 PR 回來。 35 | 36 | --- 37 | # 聯絡資訊 38 | - 信箱 `kantai.developer@gmail.com` 39 | - 網站 `https://init.engineer` 40 | -------------------------------------------------------------------------------- /apikey.example.properties: -------------------------------------------------------------------------------- 1 | # OAuth Client 2 | oath2clientsecret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 3 | oath2clientid=00000 4 | 5 | oath2scheme=YOUR_SCHEME 6 | oath2host=engineer.kaobei 7 | oath2path=YOUR_CALLBACK_PATH 8 | 9 | # Google AdMob 10 | admobID=ca-app-pub-0000000000000000~0000000000 11 | admob1=ca-app-pub-0000000000000000/0000000000 -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | def apiKeyPropertiesFile = rootProject.file("apikey.properties") 6 | def apiKeyProperties = new Properties() 7 | apiKeyProperties.load(new FileInputStream(apiKeyPropertiesFile)) 8 | 9 | android { 10 | compileSdkVersion 29 11 | 12 | // Facebook Audience0 13 | packagingOptions { 14 | exclude 'AndroidManifest.xml' 15 | } 16 | 17 | defaultConfig { 18 | applicationId "engineer.kaobei" 19 | minSdkVersion 21 20 | targetSdkVersion 29 21 | versionCode 112 22 | versionName "1.1.2" 23 | 24 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 25 | 26 | buildConfigField("String", "OATH2CLIENTID", "\"" + apiKeyProperties['oath2clientid'] + "\"") 27 | buildConfigField("String", "OATH2CLIENTSECRET", "\"" + apiKeyProperties['oath2clientsecret'] + "\"") 28 | buildConfigField("String", "OATH2REDIRECTURL", "\"" + apiKeyProperties['oath2scheme'] + "://" + apiKeyProperties['oath2host'] + apiKeyProperties['oath2path'] + "\"") 29 | buildConfigField("String", "ADMOB_1", "\"" + apiKeyProperties['admob1'] + "\"") 30 | 31 | manifestPlaceholders = [ 32 | schemeName: apiKeyProperties['oath2scheme'], 33 | hostName : apiKeyProperties['oath2host'], 34 | pathName : apiKeyProperties['oath2path'], 35 | admobID : apiKeyProperties['admobID'], 36 | ] 37 | } 38 | 39 | buildTypes { 40 | release { 41 | minifyEnabled false 42 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 43 | } 44 | debug { 45 | 46 | } 47 | } 48 | 49 | // To inline the bytecode built with JVM target 1.8 into 50 | // bytecode that is being built with JVM target 1.6. (e.g. navArgs) 51 | 52 | compileOptions { 53 | sourceCompatibility JavaVersion.VERSION_1_8 54 | targetCompatibility JavaVersion.VERSION_1_8 55 | } 56 | kotlinOptions { 57 | jvmTarget = "1.8" 58 | } 59 | 60 | } 61 | 62 | dependencies { 63 | 64 | implementation fileTree(dir: 'libs', include: ['*.jar']) 65 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 66 | implementation 'androidx.appcompat:appcompat:1.1.0' 67 | implementation 'androidx.core:core-ktx:1.2.0' 68 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 69 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 70 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' 71 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' 72 | implementation 'androidx.annotation:annotation:1.1.0' 73 | implementation 'androidx.preference:preference:1.1.1' 74 | testImplementation 'junit:junit:4.13' 75 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 76 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 77 | 78 | // ui 79 | implementation 'com.google.android.material:material:1.2.0-alpha06' 80 | implementation 'androidx.browser:browser:1.2.0' 81 | implementation "androidx.paging:paging-runtime:2.1.2" 82 | implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2' 83 | implementation 'androidx.navigation:navigation-ui-ktx:2.2.2' 84 | 85 | // ads 86 | implementation 'com.google.android.gms:play-services-ads:19.1.0' 87 | 88 | // shimmer 89 | implementation 'com.facebook.shimmer:shimmer:0.5.0' 90 | 91 | // gson 92 | implementation 'com.google.code.gson:gson:2.8.6' 93 | 94 | // glide 95 | implementation 'com.github.bumptech.glide:glide:4.11.0' 96 | annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' 97 | 98 | // okHttp 99 | implementation 'com.squareup.okhttp3:okhttp:4.7.1' 100 | implementation 'com.squareup.retrofit2:retrofit:2.8.2' 101 | implementation 'com.squareup.retrofit2:converter-gson:2.8.2' 102 | 103 | // oath2 104 | implementation 'net.openid:appauth:0.7.1' 105 | 106 | // optional - Kotlin Extensions and Coroutines support for Room 107 | implementation "androidx.room:room-ktx:2.2.5" 108 | 109 | implementation "androidx.viewpager2:viewpager2:1.0.0" 110 | 111 | } 112 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/engineer/kaobei/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("engineer.kaobei", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 16 | 20 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 47 | 48 | 49 | 50 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts.json: -------------------------------------------------------------------------------- 1 | { 2 | "options": [ 3 | { 4 | "name": "AURAKA 點陣宋字型", 5 | "font": "Auraka", 6 | "value": "ea98dde8987df3cd8aef75479019b688" 7 | }, 8 | { 9 | "name": "國喬點陣字型", 10 | "font": "KC24M", 11 | "value": "813ca6cbbd95d7e08fa2af59bc12072d" 12 | }, 13 | { 14 | "name": "ZPIX 點陣字型", 15 | "font": "Zfull", 16 | "value": "1b23b3cd9223930ac694b7f29f38ff21" 17 | }, 18 | { 19 | "name": "張海山銳諧體", 20 | "font": "Harmonic", 21 | "value": "68068fcf50e7cae709cb8ed0b7b9b0f3" 22 | }, 23 | { 24 | "name": "蒙納繁圓點陣", 25 | "font": "MBitmapRoundHK", 26 | "value": "f762e3a99692b40e5929ab3668606a4a" 27 | }, 28 | { 29 | "name": "微軟正黑體", 30 | "font": "Microsoft JhengHei", 31 | "value": "13f5333afe00f8c7e8da7e0b13ec2c94" 32 | }, 33 | { 34 | "name": "新細明體", 35 | "font": "Mingliu", 36 | "value": "c0b5dd764ede0ca105be22cf13ebadff" 37 | }, 38 | { 39 | "name": "標楷體", 40 | "font": "Kaiu", 41 | "value": "21881fc6a87aca0dd1afc685cb6ee891" 42 | }, 43 | { 44 | "name": "極粗明朝體", 45 | "font": "MatissePro EB", 46 | "value": "ozke4ri3gkpy7e9c312u5l0w5vr9jdqq" 47 | }, 48 | { 49 | "name": "台北黑體", 50 | "font": "Taipei Sans TC Beta", 51 | "value": "yc45sgsfbss490dqgs2g23a7z24slhoj" 52 | } 53 | ] 54 | } -------------------------------------------------------------------------------- /app/src/main/assets/open_source.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "name": "Shimmer for Android", 5 | "author": "Facebook", 6 | "url": "https://github.com/facebook/shimmer-android" 7 | }, 8 | { 9 | "name": "Gson", 10 | "author": "Google", 11 | "url": "https://github.com/google/gson" 12 | }, 13 | { 14 | "name": "Glide", 15 | "author": "Bumptech", 16 | "url": "https://github.com/bumptech/glide" 17 | }, 18 | { 19 | "name": "OkHttp", 20 | "author": "Square", 21 | "url": "https://github.com/square/okhttp" 22 | }, 23 | { 24 | "name": "AppAuth-Android", 25 | "author": "OpenID", 26 | "url": "https://github.com/openid/AppAuth-Android" 27 | }, 28 | { 29 | "name": "Retrofit", 30 | "author": "Square", 31 | "url": "https://github.com/square/retrofit" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/DepthPageTransformer.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei 2 | 3 | import android.view.View 4 | import androidx.annotation.RequiresApi 5 | import androidx.viewpager2.widget.ViewPager2 6 | 7 | private const val MIN_SCALE = 0.75f 8 | 9 | @RequiresApi(21) 10 | class DepthPageTransformer : ViewPager2.PageTransformer { 11 | 12 | override fun transformPage(view: View, position: Float) { 13 | view.apply { 14 | val pageWidth = width 15 | when { 16 | position < -1 -> { // [-Infinity,-1) 17 | // This page is way off-screen to the left. 18 | alpha = 0f 19 | } 20 | position <= 0 -> { // [-1,0] 21 | // Use the default slide transition when moving to the left page 22 | alpha = 1f 23 | translationX = 0f 24 | translationZ = 0f 25 | scaleX = 1f 26 | scaleY = 1f 27 | } 28 | position <= 1 -> { // (0,1] 29 | // Fade the page out. 30 | alpha = 1 - position 31 | 32 | // Counteract the default slide transition 33 | translationX = pageWidth * -position 34 | // Move it behind the left page 35 | translationZ = -1f 36 | 37 | // Scale the page down (between MIN_SCALE and 1) 38 | val scaleFactor = (MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position))) 39 | scaleX = scaleFactor 40 | scaleY = scaleFactor 41 | } 42 | else -> { // (1,+Infinity] 43 | // This page is way off-screen to the right. 44 | alpha = 0f 45 | } 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/KaobeiEngineerService.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei 2 | 3 | import engineer.kaobei.model.ReviewArticle.SingleReviewArticle 4 | import engineer.kaobei.model.ReviewArticles.ReviewArticles 5 | import engineer.kaobei.model.articleInfo.KaobeiArticleInfo 6 | import engineer.kaobei.model.articles.KaobeiArticleList 7 | import engineer.kaobei.model.comments.KaobeiComments 8 | import engineer.kaobei.model.kaobeluser.BeanKaobeiUser 9 | import engineer.kaobei.model.link.KaobeiLink 10 | import engineer.kaobei.model.userarticles.UserArticles 11 | import okhttp3.MultipartBody 12 | import okhttp3.ResponseBody 13 | import retrofit2.Call 14 | import retrofit2.http.* 15 | 16 | const val BASE_URL = "https://init.engineer" 17 | 18 | interface KaobeiEngineerService { 19 | 20 | @GET("api/frontend/user/profile") 21 | fun profile( 22 | @Header("Authorization") accessToken: String 23 | ): Call 24 | 25 | @GET("api/frontend/social/cards/api/dashboard") 26 | fun userArticleList( 27 | @Header("Authorization") accessToken: String, 28 | @Query("page") page: String? = null 29 | ): Call 30 | 31 | @GET("api/frontend/social/cards") 32 | fun articleList( 33 | @Query("page") page: String? = null 34 | ): Call 35 | 36 | @GET("api/frontend/social/cards/{id}/show") 37 | fun show( 38 | @Path("id") id: String 39 | ): Call 40 | 41 | @GET("api/frontend/social/cards/{id}/links") 42 | fun links( 43 | @Path("id") id: String 44 | ): Call 45 | 46 | @GET("api/frontend/social/cards/{id}/comments") 47 | fun comments( 48 | @Path("id") id: String, 49 | @Query("page") page: String? = null 50 | ): Call 51 | 52 | @Headers("Accept: application/json") 53 | @Multipart 54 | @POST("api/frontend/social/cards/api/publish") 55 | fun publishArticle( 56 | @Header("Authorization") accessToken: String, 57 | @Part content: MultipartBody.Part, 58 | @Part themeStyle: MultipartBody.Part, 59 | @Part fontStyle: MultipartBody.Part, 60 | @Part avatar: MultipartBody.Part 61 | ): Call 62 | 63 | @Headers("Accept: application/json") 64 | @Multipart 65 | @POST("api/frontend/social/cards/api/publish") 66 | fun publishArticleNoImg( 67 | @Header("Authorization") accessToken: String, 68 | @Part content: MultipartBody.Part, 69 | @Part themeStyle: MultipartBody.Part, 70 | @Part fontStyle: MultipartBody.Part 71 | ): Call 72 | 73 | @GET("api/frontend/social/cards/api/review") 74 | fun reviewArticleList( 75 | @Header("Authorization") accessToken: String, 76 | @Query("page") page: String? = null 77 | ): Call 78 | 79 | @GET("api/frontend/social/cards/api/review/{id}/succeeded") 80 | fun approveArticle( 81 | @Header("Authorization") accessToken: String, 82 | @Path("id") id: String 83 | ): Call 84 | 85 | @GET("api/frontend/social/cards/api/review/{id}/failed") 86 | fun denyArticle( 87 | @Header("Authorization") accessToken: String, 88 | @Path("id") id: String 89 | ): Call 90 | 91 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/NestedScrollviewLoadMoreController.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei 2 | 3 | class NestedScrollviewLoadMoreController { 4 | 5 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/OnLoadMoreListener.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei 2 | 3 | interface OnLoadMoreListener { 4 | fun onLoadMore() 5 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/RecyclerViewAdapterListener.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei 2 | 3 | interface RecyclerViewAdapterListener { 4 | fun onTheFirstInit(list : List) 5 | fun onReceiveData() 6 | fun onFailedToReceiveData() 7 | fun onNoMoreData() 8 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/RecyclerViewLoadMoreScroll.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei 2 | 3 | import androidx.recyclerview.widget.GridLayoutManager 4 | import androidx.recyclerview.widget.LinearLayoutManager 5 | import androidx.recyclerview.widget.RecyclerView 6 | import androidx.recyclerview.widget.StaggeredGridLayoutManager 7 | 8 | /** 9 | * Base class for the function to implement load more on recyclerview 10 | */ 11 | class RecyclerViewLoadMoreScroll : RecyclerView.OnScrollListener { 12 | 13 | private var isScrolledToEnd = false 14 | private var visibleThreshold = 10 15 | private lateinit var mOnLoadMoreListener: OnLoadMoreListener 16 | private var isLoading: Boolean = false 17 | private var lastVisibleItem: Int = 0 18 | private var totalItemCount:Int = 0 19 | private var mLayoutManager: RecyclerView.LayoutManager 20 | 21 | fun setIsScrolledToEnd() { 22 | isScrolledToEnd = true 23 | } 24 | 25 | fun setLoaded() { 26 | isLoading = false 27 | } 28 | 29 | fun getLoaded(): Boolean { 30 | return isLoading 31 | } 32 | 33 | fun setOnLoadMoreListener(mOnLoadMoreListener: OnLoadMoreListener) { 34 | this.mOnLoadMoreListener = mOnLoadMoreListener 35 | } 36 | 37 | constructor(layoutManager: LinearLayoutManager,visibleThreshold:Int) { 38 | this.mLayoutManager = layoutManager 39 | this.visibleThreshold= visibleThreshold 40 | } 41 | 42 | constructor(layoutManager: GridLayoutManager) { 43 | this.mLayoutManager = layoutManager 44 | visibleThreshold *= layoutManager.spanCount 45 | } 46 | 47 | constructor(layoutManager: StaggeredGridLayoutManager) { 48 | this.mLayoutManager = layoutManager 49 | visibleThreshold *= layoutManager.spanCount 50 | } 51 | 52 | 53 | override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { 54 | super.onScrolled(recyclerView, dx, dy) 55 | 56 | if (dy <= 0) return 57 | 58 | totalItemCount = mLayoutManager.itemCount 59 | 60 | if (mLayoutManager is StaggeredGridLayoutManager) { 61 | val lastVisibleItemPositions = 62 | (mLayoutManager as StaggeredGridLayoutManager).findLastVisibleItemPositions(null) 63 | // get maximum element within the list 64 | lastVisibleItem = getLastVisibleItem(lastVisibleItemPositions) 65 | } else if (mLayoutManager is GridLayoutManager) { 66 | lastVisibleItem = (mLayoutManager as GridLayoutManager).findLastVisibleItemPosition() 67 | } else if (mLayoutManager is LinearLayoutManager) { 68 | lastVisibleItem = (mLayoutManager as LinearLayoutManager).findLastVisibleItemPosition() 69 | } 70 | 71 | if (!isLoading && totalItemCount <= lastVisibleItem + visibleThreshold && !isScrolledToEnd) { 72 | mOnLoadMoreListener.onLoadMore() 73 | isLoading = true 74 | } 75 | 76 | } 77 | 78 | private fun getLastVisibleItem(lastVisibleItemPositions: IntArray): Int { 79 | var maxSize = 0 80 | for (i in lastVisibleItemPositions.indices) { 81 | if (i == 0) { 82 | maxSize = lastVisibleItemPositions[i] 83 | } else if (lastVisibleItemPositions[i] > maxSize) { 84 | maxSize = lastVisibleItemPositions[i] 85 | } 86 | } 87 | return maxSize 88 | } 89 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/activity/ArticleActivityV2.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.activity 2 | 3 | import android.os.Bundle 4 | import com.google.android.material.snackbar.Snackbar 5 | import androidx.appcompat.app.AppCompatActivity 6 | import engineer.kaobei.R 7 | 8 | import kotlinx.android.synthetic.main.activity_article_v2.* 9 | 10 | class ArticleActivityV2 : AppCompatActivity() { 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | setContentView(R.layout.activity_article_v2) 15 | setSupportActionBar(toolbar) 16 | 17 | fab.setOnClickListener { view -> 18 | Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) 19 | .setAction("Action", null).show() 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/activity/CreateArticleActivity.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.activity 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import engineer.kaobei.R 6 | import engineer.kaobei.fragment.CreateArticleFragment 7 | 8 | class CreateArticleActivity : AppCompatActivity() { 9 | 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | setContentView(R.layout.create_article_activity) 13 | if (savedInstanceState == null) { 14 | supportFragmentManager.beginTransaction() 15 | .replace(R.id.container, CreateArticleFragment.newInstance()) 16 | .commitNow() 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/activity/settings/OpenSourceAdapter.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.activity.settings 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.TextView 7 | import androidx.recyclerview.widget.RecyclerView 8 | import engineer.kaobei.R 9 | import engineer.kaobei.model.opensources.OpenSource 10 | 11 | /** 12 | * Created by Kimi.Peng on 2020/6/9. 13 | * Refactoring [OpenSourceRecyclerViewAdapter] class, also rename to OpenSourceAdapter, remove interface. 14 | */ 15 | class OpenSourceAdapter(private val openSource: List, val clickListener: (OpenSource) -> Unit) : 16 | RecyclerView.Adapter() { 17 | 18 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { 19 | val view = LayoutInflater.from(parent.context).inflate(R.layout.cardview_open_source, parent, false) 20 | return ItemViewHolder(view) 21 | } 22 | 23 | override fun getItemCount(): Int { 24 | return openSource.size 25 | } 26 | 27 | override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { 28 | holder.bind(openSource[position]) 29 | } 30 | 31 | inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 32 | 33 | val tv_name: TextView = itemView.findViewById(R.id.tv_name) 34 | val tv_author: TextView = itemView.findViewById(R.id.tv_author) 35 | val tv_url: TextView = itemView.findViewById(R.id.tv_url) 36 | 37 | fun bind(openSource: OpenSource) { 38 | tv_name.text = openSource.name 39 | tv_author.text = openSource.author 40 | tv_url.text = openSource.url 41 | itemView.setOnClickListener { 42 | clickListener(openSource) 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/database/FontManager.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.database 2 | 3 | import android.content.Context 4 | import com.google.gson.Gson 5 | import engineer.kaobei.model.fonts.Font 6 | import engineer.kaobei.model.fonts.KaobeiFonts 7 | 8 | class FontManager(context: Context) { 9 | 10 | private var fonts: List 11 | 12 | init { 13 | val fileInString: String = 14 | context.assets.open("fonts.json").bufferedReader().use { it.readText() } 15 | val bean = Gson().fromJson(fileInString, KaobeiFonts::class.javaObjectType) 16 | fonts = bean.options 17 | } 18 | 19 | companion object { 20 | @Volatile 21 | private var INSTANCE: FontManager? = null 22 | 23 | fun getInstance(context: Context): FontManager { 24 | return INSTANCE ?: synchronized(this) { 25 | INSTANCE ?: FontManager(context).also { INSTANCE = it } 26 | } 27 | 28 | } 29 | } 30 | 31 | fun getFonts(): List { 32 | return fonts 33 | } 34 | 35 | 36 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/database/OpenSourceManager.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.database 2 | 3 | import android.content.Context 4 | import com.google.gson.Gson 5 | import engineer.kaobei.model.opensources.OpenSource 6 | import engineer.kaobei.model.opensources.OpenSources 7 | 8 | class OpenSourceManager(context: Context) { 9 | 10 | private var openSourceList: List 11 | 12 | init { 13 | val fileInString: String = 14 | context.assets.open("open_source.json").bufferedReader().use { it.readText() } 15 | val bean = Gson().fromJson(fileInString, OpenSources::class.javaObjectType) 16 | openSourceList = bean.data 17 | } 18 | 19 | companion object { 20 | 21 | @Volatile 22 | private var INSTANCE: OpenSourceManager? = null 23 | 24 | fun getInstance(context: Context): OpenSourceManager { 25 | return INSTANCE ?: synchronized(this) { 26 | INSTANCE ?: OpenSourceManager(context).also { INSTANCE = it } 27 | } 28 | } 29 | } 30 | 31 | fun getOpenSource(): List { 32 | return openSourceList 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/database/ThemeManager.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.database 2 | 3 | import android.content.Context 4 | import com.google.gson.Gson 5 | import engineer.kaobei.model.themes.KaobeiThemes 6 | import engineer.kaobei.model.themes.Theme 7 | 8 | class ThemeManager(context: Context) { 9 | 10 | private var themes: List 11 | 12 | init { 13 | val fileInString: String = 14 | context.assets.open("themes.json").bufferedReader().use { it.readText() } 15 | val bean = Gson().fromJson(fileInString, KaobeiThemes::class.javaObjectType) 16 | themes = bean.themes 17 | } 18 | 19 | companion object { 20 | 21 | private var INSTANCE: ThemeManager? = null 22 | 23 | fun getInstance(context: Context): ThemeManager { 24 | return INSTANCE ?: synchronized(this) { 25 | INSTANCE ?: ThemeManager(context).also { INSTANCE = it } 26 | } 27 | } 28 | } 29 | 30 | fun getThemes(): List { 31 | return themes 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/ReviewArticle/Data.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.ReviewArticle 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class Data( 7 | @SerializedName("content") 8 | val content: String = "", 9 | @SerializedName("created_at") 10 | val createdAt: String = "", 11 | @SerializedName("created_diff") 12 | val createdDiff: String = "", 13 | @SerializedName("failed") 14 | val failed: Int = 0, 15 | @SerializedName("id") 16 | val id: Int = 0, 17 | @SerializedName("image") 18 | val image: String = "", 19 | @SerializedName("succeeded") 20 | val succeeded: Int = 0, 21 | @SerializedName("updated_at") 22 | val updatedAt: String = "", 23 | @SerializedName("updated_diff") 24 | val updatedDiff: String = "" 25 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/ReviewArticle/SingleReviewArticle.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.ReviewArticle 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class SingleReviewArticle( 7 | @SerializedName("data") 8 | val `data`: Data = Data() 9 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/ReviewArticles/Links.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.ReviewArticles 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class Links( 7 | @SerializedName("next") 8 | val next: String = "" 9 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/ReviewArticles/Meta.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.ReviewArticles 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class Meta( 7 | @SerializedName("pagination") 8 | val pagination: Pagination = Pagination() 9 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/ReviewArticles/Pagination.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.ReviewArticles 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class Pagination( 7 | @SerializedName("count") 8 | val count: Int = 0, 9 | @SerializedName("current_page") 10 | val currentPage: Int = 0, 11 | @SerializedName("links") 12 | val links: Links = Links(), 13 | @SerializedName("per_page") 14 | val perPage: Int = 0, 15 | @SerializedName("total") 16 | val total: Int = 0, 17 | @SerializedName("total_pages") 18 | val totalPages: Int = 0 19 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/ReviewArticles/ReviewArticle.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.ReviewArticles 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class ReviewArticle( 7 | @SerializedName("content") 8 | val content: String = "", 9 | @SerializedName("created_at") 10 | val createdAt: String = "", 11 | @SerializedName("created_diff") 12 | val createdDiff: String = "", 13 | @SerializedName("failed") 14 | val failed: Int = 0, 15 | @SerializedName("id") 16 | val id: Int = 0, 17 | @SerializedName("image") 18 | val image: String = "", 19 | @SerializedName("review") 20 | val review: Int = 0, 21 | @SerializedName("succeeded") 22 | val succeeded: Int = 0, 23 | @SerializedName("updated_at") 24 | val updatedAt: String = "", 25 | @SerializedName("updated_diff") 26 | val updatedDiff: String = "" 27 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/ReviewArticles/ReviewArticles.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.ReviewArticles 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class ReviewArticles( 7 | @SerializedName("data") 8 | val `data`: List = listOf(), 9 | @SerializedName("meta") 10 | val meta: Meta = Meta() 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/articleInfo/ArticleInfo.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.articleInfo 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class ArticleInfo( 7 | @SerializedName("content") 8 | val content: String = "", 9 | @SerializedName("created_at") 10 | val createdAt: String = "", 11 | @SerializedName("created_diff") 12 | val createdDiff: String = "", 13 | @SerializedName("id") 14 | val id: Int = 0, 15 | @SerializedName("image") 16 | val image: String = "", 17 | @SerializedName("updated_at") 18 | val updatedAt: String = "", 19 | @SerializedName("updated_diff") 20 | val updatedDiff: String = "" 21 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/articleInfo/KaobeiArticleInfo.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.articleInfo 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class KaobeiArticleInfo( 7 | @SerializedName("data") 8 | val articleInfo: ArticleInfo = ArticleInfo() 9 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/articles/Article.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.articles 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.android.parcel.Parcelize 6 | 7 | @Parcelize 8 | data class Article( 9 | @SerializedName("content") 10 | val content: String = "", 11 | @SerializedName("created_at") 12 | val createdAt: String = "", 13 | @SerializedName("created_diff") 14 | val createdDiff: String = "", 15 | @SerializedName("id") 16 | val id: Int = 0, 17 | @SerializedName("image") 18 | val image: String = "", 19 | @SerializedName("updated_at") 20 | val updatedAt: String = "", 21 | @SerializedName("updated_diff") 22 | val updatedDiff: String = "" 23 | ) : Parcelable -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/articles/KaobeiArticleList.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.articles 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class KaobeiArticleList( 6 | @SerializedName("data") 7 | val `data`: List
= listOf(), 8 | @SerializedName("meta") 9 | val meta: Meta = Meta() 10 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/articles/Links.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.articles 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Links( 6 | @SerializedName("next") 7 | val next: String = "" 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/articles/Meta.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.articles 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Meta( 6 | @SerializedName("pagination") 7 | val pagination: Pagination = Pagination() 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/articles/Pagination.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.articles 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Pagination( 6 | @SerializedName("count") 7 | val count: Int = 0, 8 | @SerializedName("current_page") 9 | val currentPage: Int = 0, 10 | @SerializedName("links") 11 | val links: Links = Links(), 12 | @SerializedName("per_page") 13 | val perPage: Int = 0, 14 | @SerializedName("total") 15 | val total: Int = 0, 16 | @SerializedName("total_pages") 17 | val totalPages: Int = 0 18 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/comments/Comment.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.comments 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Comment( 6 | @SerializedName("avatar") 7 | val avatar: String = "", 8 | @SerializedName("content") 9 | val content: String = "", 10 | @SerializedName("created") 11 | val created: String = "", 12 | @SerializedName("media") 13 | val media: Media = Media(), 14 | @SerializedName("name") 15 | val name: String = "" 16 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/comments/KaobeiComments.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.comments 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class KaobeiComments( 6 | @SerializedName("data") 7 | val `data`: List = listOf(), 8 | @SerializedName("meta") 9 | val meta: Meta = Meta() 10 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/comments/Links.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.comments 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Links( 6 | @SerializedName("next") 7 | val next: String = "" 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/comments/Media.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.comments 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Media( 6 | @SerializedName("connections") 7 | val connections: String = "", 8 | @SerializedName("type") 9 | val type: String = "" 10 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/comments/Meta.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.comments 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Meta( 6 | @SerializedName("pagination") 7 | val pagination: Pagination = Pagination() 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/comments/Pagination.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.comments 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Pagination( 6 | @SerializedName("count") 7 | val count: Int = 0, 8 | @SerializedName("current_page") 9 | val currentPage: Int = 0, 10 | @SerializedName("links") 11 | val links: Links = Links(), 12 | @SerializedName("per_page") 13 | val perPage: Int = 0, 14 | @SerializedName("total") 15 | val total: Int = 0, 16 | @SerializedName("total_pages") 17 | val totalPages: Int = 0 18 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/fonts/Font.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.fonts 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Font( 6 | @SerializedName("font") 7 | val font: String = "", 8 | @SerializedName("name") 9 | val name: String = "", 10 | @SerializedName("value") 11 | val value: String = "" 12 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/fonts/KaobeiFonts.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.fonts 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class KaobeiFonts( 6 | @SerializedName("options") 7 | val options: List = listOf() 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/kaobeluser/BeanKaobeiUser.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.kaobeluser 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class BeanKaobeiUser( 6 | @SerializedName("data") 7 | val `data`: KaobeiUser = KaobeiUser() 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/kaobeluser/KaobeiUser.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.kaobeluser 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class KaobeiUser( 6 | @SerializedName("active") 7 | val active: Boolean = false, 8 | @SerializedName("avatar") 9 | val avatar: String = "", 10 | @SerializedName("confirmed") 11 | val confirmed: Boolean = false, 12 | @SerializedName("email") 13 | val email: String = "", 14 | @SerializedName("first_name") 15 | val firstName: String = "", 16 | @SerializedName("full_name") 17 | val fullName: String = "", 18 | @SerializedName("id") 19 | val id: Int = 0, 20 | @SerializedName("last_name") 21 | val lastName: String = "", 22 | @SerializedName("timezone") 23 | val timezone: String = "", 24 | @SerializedName("uuid") 25 | val uuid: String = "" 26 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/link/Data.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.link 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Data( 6 | @SerializedName("connections") 7 | val connections: String = "", 8 | @SerializedName("like") 9 | val like: Int = 0, 10 | @SerializedName("share") 11 | val share: Int = 0, 12 | @SerializedName("type") 13 | val type: String = "", 14 | @SerializedName("url") 15 | val url: String = "" 16 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/link/KaobeiLink.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.link 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class KaobeiLink( 6 | @SerializedName("data") 7 | val `data`: List = listOf(Data(), Data(), Data(), Data()) 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/opensources/OpenSource.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.opensources 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class OpenSource( 6 | @SerializedName("author") 7 | val author: String = "", 8 | @SerializedName("name") 9 | val name: String = "", 10 | @SerializedName("url") 11 | val url: String = "" 12 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/opensources/OpenSources.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.opensources 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class OpenSources( 6 | @SerializedName("data") 7 | val `data`: List = listOf() 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/themes/Additional.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.themes 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Additional( 6 | @SerializedName("background_image") 7 | val backgroundImage: String = "", 8 | @SerializedName("footer") 9 | val footer: String = "", 10 | @SerializedName("header") 11 | val header: String = "" 12 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/themes/KaobeiThemes.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.themes 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class KaobeiThemes( 6 | @SerializedName("themes") 7 | val themes: List = listOf() 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/themes/Theme.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.themes 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Theme( 6 | @SerializedName("additional") 7 | val additional: Additional = Additional(), 8 | @SerializedName("background_color") 9 | val backgroundColor: String = "#121212", 10 | @SerializedName("name") 11 | val name: String = "", 12 | @SerializedName("text_color") 13 | val textColor: String = "#FAFAFA", 14 | @SerializedName("value") 15 | val value: String = "" 16 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/userarticles/Links.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.userarticles 2 | 3 | class Links( 4 | // Do something ... 5 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/userarticles/Meta.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.userarticles 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Meta( 6 | @SerializedName("pagination") 7 | val pagination: Pagination = Pagination() 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/userarticles/Pagination.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.userarticles 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Pagination( 6 | @SerializedName("count") 7 | val count: Int = 0, 8 | @SerializedName("current_page") 9 | val currentPage: Int = 0, 10 | @SerializedName("links") 11 | val links: Links = Links(), 12 | @SerializedName("per_page") 13 | val perPage: Int = 0, 14 | @SerializedName("total") 15 | val total: Int = 0, 16 | @SerializedName("total_pages") 17 | val totalPages: Int = 0 18 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/userarticles/UserArticle.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.userarticles 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class UserArticle( 6 | @SerializedName("banned_remarks") 7 | val bannedRemarks: String = "", 8 | @SerializedName("content") 9 | val content: String = "", 10 | @SerializedName("created_at") 11 | val createdAt: String = "", 12 | @SerializedName("created_diff") 13 | val createdDiff: String = "", 14 | @SerializedName("id") 15 | val id: Int = 0, 16 | @SerializedName("image") 17 | val image: String = "", 18 | @SerializedName("is_banned") 19 | val isBanned: Int = 0, 20 | @SerializedName("updated_at") 21 | val updatedAt: String = "", 22 | @SerializedName("updated_diff") 23 | val updatedDiff: String = "" 24 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/model/userarticles/UserArticles.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.model.userarticles 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class UserArticles( 6 | @SerializedName("data") 7 | val `data`: List = listOf(), 8 | @SerializedName("meta") 9 | val meta: Meta = Meta() 10 | ) -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/util/ClipBoardUtil.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.util 2 | 3 | import android.content.ClipData 4 | import android.content.ClipboardManager 5 | import android.content.Context 6 | import android.content.Context.CLIPBOARD_SERVICE 7 | 8 | object ClipBoardUtil { 9 | 10 | fun copy(context: Context, string: String) { 11 | val myClipboard: ClipboardManager? = 12 | context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager? 13 | val myClip: ClipData? = ClipData.newPlainText("text", string) 14 | myClipboard?.primaryClip = myClip; 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/util/CustomTabUtil.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.util 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import androidx.browser.customtabs.CustomTabsIntent 6 | import androidx.core.content.ContextCompat 7 | import engineer.kaobei.R 8 | 9 | object CustomTabUtil { 10 | fun createCustomTab(context: Context, url: String) { 11 | val builder: CustomTabsIntent.Builder = CustomTabsIntent.Builder() 12 | builder.setToolbarColor(ContextCompat.getColor(context, R.color.colorPrimary)) 13 | builder.setShowTitle(true) 14 | val customTabsIntent: CustomTabsIntent = builder.build() 15 | customTabsIntent.launchUrl( 16 | context, 17 | Uri.parse(url) 18 | ) 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/util/SnackbarUtil.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.util 2 | 3 | import android.view.Gravity 4 | import androidx.coordinatorlayout.widget.CoordinatorLayout 5 | import com.google.android.material.snackbar.Snackbar 6 | 7 | object SnackbarUtil { 8 | @JvmStatic 9 | fun makeAnchorSnackbar(coorView: CoordinatorLayout, text: String, anchorID: Int) { 10 | val snackbar = Snackbar.make(coorView, text, Snackbar.LENGTH_SHORT) 11 | val layoutParams = snackbar.view.layoutParams as CoordinatorLayout.LayoutParams 12 | layoutParams.anchorId = anchorID 13 | layoutParams.gravity = Gravity.TOP 14 | snackbar.view.layoutParams = layoutParams 15 | snackbar.show() 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/util/ViewUtil.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.util 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import engineer.kaobei.view.AnimatedGap 5 | import kotlin.jvm.internal.Ref 6 | 7 | object ViewUtil { 8 | fun addGapController(recyclerView: RecyclerView, gap: AnimatedGap) { 9 | val reInTop: Ref.BooleanRef = Ref.BooleanRef() 10 | recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { 11 | override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { 12 | super.onScrolled(recyclerView, dx, dy) 13 | if (!recyclerView.canScrollVertically(-1)) { 14 | reInTop.element = true 15 | gap.hide() 16 | } else { 17 | if (reInTop.element) { 18 | reInTop.element = false 19 | gap.show() 20 | } 21 | } 22 | } 23 | }) 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/util/ext/ImageViewExt.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.util.ext 2 | 3 | import android.graphics.drawable.Drawable 4 | import android.widget.ImageView 5 | import com.bumptech.glide.Glide 6 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions 7 | 8 | /** 9 | * Created by Kimi.Peng on 2020/6/3. 10 | */ 11 | fun ImageView.viewLoading(resource: String) { 12 | Glide.with(this) 13 | .load(resource) 14 | .into(this) 15 | } 16 | 17 | fun ImageView.viewLoading(drawable: Drawable?) { 18 | Glide.with(this) 19 | .load(drawable) 20 | .into(this) 21 | } 22 | 23 | fun ImageView.viewLoadingWithTransition( 24 | resource: String, drawableTransitionOptions: DrawableTransitionOptions = DrawableTransitionOptions.withCrossFade() 25 | ) { 26 | Glide.with(this) 27 | .load(resource) 28 | .transition(drawableTransitionOptions) 29 | .into(this) 30 | } 31 | 32 | fun ImageView.viewLoadingWithTransition( 33 | drawable: Drawable?, drawableTransitionOptions: DrawableTransitionOptions = DrawableTransitionOptions.withCrossFade() 34 | ) { 35 | Glide.with(this) 36 | .load(drawable) 37 | .transition(drawableTransitionOptions) 38 | .into(this) 39 | } 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/view/AnimatedGap.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.view 2 | 3 | import android.animation.ValueAnimator 4 | import android.content.Context 5 | import android.graphics.Canvas 6 | import android.graphics.Paint 7 | import android.util.AttributeSet 8 | import android.util.Log 9 | import android.view.animation.DecelerateInterpolator 10 | import android.widget.LinearLayout 11 | import androidx.core.content.ContextCompat 12 | import engineer.kaobei.R 13 | 14 | /** 15 | * Class AnimatedGap. 16 | */ 17 | class AnimatedGap : LinearLayout { 18 | 19 | private var paint: Paint = Paint() 20 | 21 | private var animator: ValueAnimator? = null 22 | private var animator2: ValueAnimator? = null 23 | private var currentIndexLeft = 0 24 | private var currentIndexRight = 0 25 | 26 | private var w = 0 27 | private var h = 0 28 | private var init = false 29 | 30 | constructor(context: Context) : super(context) { 31 | initial(context, null) 32 | } 33 | 34 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { 35 | initial(context, attrs) 36 | } 37 | 38 | constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super( 39 | context, 40 | attrs, 41 | defStyle 42 | ) { 43 | initial(context, attrs) 44 | } 45 | 46 | private fun initial(context: Context, attrs: AttributeSet?) { 47 | setWillNotDraw(false) 48 | this.paint.color = ContextCompat.getColor(context, R.color.FxWhite) 49 | this.paint.strokeWidth = 15F 50 | } 51 | 52 | override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { 53 | super.onSizeChanged(w, h, oldw, oldh) 54 | this.w = w 55 | this.h = h 56 | } 57 | 58 | override fun onDraw(canvas: Canvas?) { 59 | super.onDraw(canvas) 60 | if (this.init) { 61 | canvas?.drawLine((this.w / 2).toFloat(), 0F, this.currentIndexLeft.toFloat(), 0F, this.paint) 62 | canvas?.drawLine((this.w / 2).toFloat(), 0F, this.currentIndexRight.toFloat(), 0F, this.paint) 63 | } 64 | } 65 | 66 | fun hide() { 67 | this.animator?.cancel() 68 | this.animator2?.cancel() 69 | this.animator = ValueAnimator.ofInt(0, w / 2).apply { 70 | this.duration = 200 71 | this.interpolator = DecelerateInterpolator() 72 | addUpdateListener { valueAnimator -> 73 | currentIndexLeft = valueAnimator.animatedValue as Int 74 | invalidate() 75 | } 76 | } 77 | this.animator2 = ValueAnimator.ofInt(w, w / 2).apply { 78 | this.duration = 200 79 | this.interpolator = DecelerateInterpolator() 80 | addUpdateListener { valueAnimator -> 81 | currentIndexRight = valueAnimator.animatedValue as Int 82 | Log.d("qijian", "curValue:" + valueAnimator.animatedValue); 83 | invalidate() 84 | } 85 | } 86 | this.animator?.start() 87 | this.animator2?.start() 88 | } 89 | 90 | fun show() { 91 | this.animator?.cancel() 92 | this.animator2?.cancel() 93 | this.animator = ValueAnimator.ofInt(this.w / 2, 0).apply { 94 | this.duration = 200 95 | this.interpolator = DecelerateInterpolator() 96 | addUpdateListener { valueAnimator -> 97 | if (!init) { 98 | init = true 99 | } else { 100 | currentIndexLeft = valueAnimator.animatedValue as Int 101 | invalidate() 102 | } 103 | } 104 | } 105 | this.animator2 = ValueAnimator.ofInt(this.w / 2, this.w).apply { 106 | this.duration = 200 107 | this.interpolator = DecelerateInterpolator() 108 | addUpdateListener { valueAnimator -> 109 | currentIndexRight = valueAnimator.animatedValue as Int 110 | Log.d("qijian", "curValue:" + valueAnimator.animatedValue); 111 | invalidate() 112 | } 113 | } 114 | this.animator?.start() 115 | this.animator2?.start() 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/view/UserViewer.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.View 6 | import android.widget.ImageView 7 | import android.widget.LinearLayout 8 | import android.widget.TextView 9 | import androidx.cardview.widget.CardView 10 | import androidx.core.content.ContextCompat 11 | import engineer.kaobei.model.kaobeluser.KaobeiUser 12 | import engineer.kaobei.R 13 | import engineer.kaobei.util.ext.viewLoading 14 | 15 | /** 16 | * Class UserViewer. 17 | */ 18 | class UserViewer : LinearLayout { 19 | 20 | private lateinit var imageViewAvatar: ImageView 21 | private lateinit var imageViewAvatarBackground: ImageView 22 | private lateinit var textViewName: TextView 23 | private lateinit var textViewEmail: TextView 24 | private lateinit var cardViewAvatar: CardView 25 | private var authorized: Boolean = false 26 | 27 | constructor(context: Context) : super(context) { 28 | initWidget(context, null) 29 | } 30 | 31 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { 32 | initWidget(context, attrs) 33 | } 34 | 35 | constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super( 36 | context, 37 | attrs, 38 | defStyle 39 | ) { 40 | initWidget(context, attrs) 41 | } 42 | 43 | fun setProfile(kaobeiUser: KaobeiUser) { 44 | this.imageViewAvatar.viewLoading(kaobeiUser.avatar) 45 | this.imageViewAvatarBackground.viewLoading(context.getDrawable(R.drawable.img_animated_rainbow)) 46 | this.textViewName.text = kaobeiUser.fullName 47 | this.textViewEmail.text = kaobeiUser.email 48 | } 49 | 50 | fun initView(authorized: Boolean) { 51 | if (authorized) { 52 | this.authorized = true 53 | this.textViewName.text = "Loading" 54 | this.textViewEmail.text = "" 55 | } else { 56 | this.authorized = false 57 | this.textViewName.text = "點擊登入" 58 | this.textViewEmail.visibility = View.GONE 59 | imageViewAvatar.viewLoading(ContextCompat.getDrawable(imageViewAvatar.context, R.drawable.img_kb)) 60 | } 61 | } 62 | 63 | private fun initWidget(context: Context, attrs: AttributeSet?) { 64 | View.inflate(context, R.layout.widget_user_viewer, this) 65 | this.imageViewAvatar = findViewById(R.id.img_avatar) 66 | this.imageViewAvatarBackground = findViewById(R.id.content_background) 67 | this.textViewName = findViewById(R.id.tv_name) 68 | this.textViewEmail = findViewById(R.id.tv_email) 69 | this.cardViewAvatar = findViewById(R.id.cardview_avatar) 70 | } 71 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/viewmodel/ArticleInfoViewModel.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.viewmodel 2 | 3 | import engineer.kaobei.BASE_URL 4 | import engineer.kaobei.KaobeiEngineerService 5 | import engineer.kaobei.model.articleInfo.KaobeiArticleInfo 6 | import retrofit2.Retrofit 7 | import retrofit2.converter.gson.GsonConverterFactory 8 | 9 | class ArticleInfoViewModel : ObjectViewModel() { 10 | 11 | fun loadArticleInfo(id: Int) { 12 | val retrofit = Retrofit.Builder() 13 | .baseUrl(BASE_URL) 14 | .addConverterFactory(GsonConverterFactory.create()) 15 | .build() 16 | val service = retrofit.create(KaobeiEngineerService::class.java) 17 | service.show(id.toString()) 18 | .enqueue(object : retrofit2.Callback { 19 | override fun onFailure(call: retrofit2.Call, t: Throwable) { 20 | mOnReceiveDataListener?.onFailureReceiveData() 21 | } 22 | 23 | override fun onResponse( 24 | call: retrofit2.Call, 25 | response: retrofit2.Response 26 | ) { 27 | if (response.isSuccessful) { 28 | response.body()?.let { change(it) } 29 | mOnReceiveDataListener?.onReceiveData() 30 | } else { 31 | mOnReceiveDataListener?.onFailureReceiveData() 32 | } 33 | } 34 | }) 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/viewmodel/ArticleListViewModel.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import engineer.kaobei.BASE_URL 6 | import engineer.kaobei.KaobeiEngineerService 7 | import engineer.kaobei.model.articles.Article 8 | import engineer.kaobei.model.articles.KaobeiArticleList 9 | import retrofit2.Retrofit 10 | import retrofit2.converter.gson.GsonConverterFactory 11 | 12 | /** 13 | * Class ArticleListViewModel. 14 | * 15 | * A ViewModel used for the {@link ArticleListFragment}. 16 | */ 17 | class ArticleListViewModel : ListViewModel
() { 18 | 19 | protected var mPage : MutableLiveData = MutableLiveData(1) 20 | protected var mInit : MutableLiveData = MutableLiveData(0) 21 | 22 | init { 23 | mList = arrayListOf(Article()) 24 | mLiveData = MutableLiveData>(arrayListOf(Article())).also { 25 | 26 | } 27 | } 28 | 29 | fun refreshData(){ 30 | mInit.value = 0 31 | mPage.value = 1 32 | mList = arrayListOf(Article()) 33 | mLiveData.value = arrayListOf(Article()) 34 | } 35 | 36 | fun setInit(value : Boolean){ 37 | if(value){ 38 | mInit.value = 1 39 | }else{ 40 | mInit.value = 0 41 | } 42 | } 43 | 44 | fun isInit() : Boolean{ 45 | val value:Int = mInit.value!! 46 | return value != 0 47 | } 48 | 49 | fun getPage(): LiveData { 50 | return mPage 51 | } 52 | 53 | fun addPage(){ 54 | if(mPage.value!=null){ 55 | var index :Int= mPage.value!! 56 | index++ 57 | mPage.value = index 58 | } 59 | } 60 | 61 | fun minusPage(){ 62 | if(mPage.value!=null){ 63 | var index :Int= mPage.value!! 64 | index-- 65 | mPage.value = index 66 | } 67 | } 68 | 69 | fun loadMoreArticles(page: Int) { 70 | val retrofit = Retrofit.Builder() 71 | .baseUrl(BASE_URL) 72 | .addConverterFactory(GsonConverterFactory.create()) 73 | .build() 74 | val service = retrofit.create(KaobeiEngineerService::class.java) 75 | service.articleList(page.toString()) 76 | .enqueue(object : retrofit2.Callback { 77 | override fun onFailure(call: retrofit2.Call, t: Throwable) { 78 | mOnReceiveDataListener?.onFailureToReceiveData() 79 | } 80 | 81 | override fun onResponse( 82 | call: retrofit2.Call, 83 | response: retrofit2.Response 84 | ) { 85 | if (response.isSuccessful) { 86 | mOnReceiveDataListener?.onReceiveData(response.body()?.data!!) 87 | add(response.body()?.data!!) 88 | } else { 89 | mOnReceiveDataListener?.onFailureToReceiveData() 90 | } 91 | } 92 | }) 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/viewmodel/CommentsViewModel.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import engineer.kaobei.BASE_URL 6 | import engineer.kaobei.KaobeiEngineerService 7 | import engineer.kaobei.model.comments.Comment 8 | import engineer.kaobei.model.comments.KaobeiComments 9 | import retrofit2.Retrofit 10 | import retrofit2.converter.gson.GsonConverterFactory 11 | 12 | /** 13 | * Class CommentsViewModel. 14 | * 15 | * A ViewModel used for the {@link ArticleListFragment}. 16 | */ 17 | class CommentsViewModel : ListViewModel() { 18 | 19 | protected var mPage : MutableLiveData = MutableLiveData(1) 20 | protected var id :Int = 0 21 | protected var mInit : MutableLiveData = MutableLiveData(0) 22 | 23 | init { 24 | mList = arrayListOf(Comment(),Comment()) 25 | mLiveData = MutableLiveData>(arrayListOf(Comment(),Comment())).also { 26 | } 27 | } 28 | 29 | fun getPage(): LiveData { 30 | return mPage 31 | } 32 | 33 | fun addPage(){ 34 | if(mPage.value!=null){ 35 | var index :Int= mPage.value!! 36 | index++ 37 | mPage.postValue(index) 38 | mPage.value = index 39 | } 40 | } 41 | 42 | fun changePage(page:Int){ 43 | mPage.postValue(page) 44 | } 45 | 46 | fun getLivaDataValue(id:Int):LiveData>{ 47 | this.id = id 48 | val value:Int = mInit.value!! 49 | if(value==0){ 50 | mInit.postValue(1) 51 | mPage.value?.let { loadComments(id, it) } 52 | } 53 | return mLiveData 54 | } 55 | 56 | fun loadComments(id: Int, page: Int) { 57 | val retrofit = Retrofit.Builder() 58 | .baseUrl(BASE_URL) 59 | .addConverterFactory(GsonConverterFactory.create()) 60 | .build() 61 | val service = retrofit.create(KaobeiEngineerService::class.java) 62 | service.comments(id.toString(), page.toString()) 63 | .enqueue(object : retrofit2.Callback { 64 | override fun onFailure(call: retrofit2.Call, t: Throwable) { 65 | mOnReceiveDataListener?.onFailureToReceiveData() 66 | } 67 | 68 | override fun onResponse( 69 | call: retrofit2.Call, 70 | response: retrofit2.Response 71 | ) { 72 | if (response.isSuccessful) { 73 | mOnReceiveDataListener?.onReceiveData(response.body()?.data!!) 74 | add(response.body()?.data!!) 75 | if (response.body()?.meta?.pagination?.currentPage == response.body()?.meta?.pagination?.totalPages) { 76 | mOnReceiveDataListener?.onNoMoreData() 77 | } 78 | } else { 79 | mOnReceiveDataListener?.onFailureToReceiveData() 80 | } 81 | } 82 | }) 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/viewmodel/IndexViewModel.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.viewmodel 2 | 3 | import androidx.lifecycle.ViewModel 4 | 5 | /** 6 | * Class IndexViewModel. 7 | */ 8 | class IndexViewModel : ViewModel() { 9 | 10 | // TODO: Implement the ViewModel 11 | 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/viewmodel/LinkViewModel.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.viewmodel 2 | 3 | import engineer.kaobei.BASE_URL 4 | import engineer.kaobei.KaobeiEngineerService 5 | import engineer.kaobei.model.link.KaobeiLink 6 | import retrofit2.Retrofit 7 | import retrofit2.converter.gson.GsonConverterFactory 8 | 9 | /** 10 | * Class LinkViewModel. 11 | */ 12 | class LinkViewModel : ObjectViewModel() { 13 | 14 | fun loadLink(id: Int) { 15 | val retrofit = Retrofit.Builder() 16 | .baseUrl(BASE_URL) 17 | .addConverterFactory(GsonConverterFactory.create()) 18 | .build() 19 | val service = retrofit.create(KaobeiEngineerService::class.java) 20 | service.links(id.toString()).enqueue(object : retrofit2.Callback { 21 | override fun onFailure(call: retrofit2.Call, t: Throwable) { 22 | mOnReceiveDataListener?.onFailureReceiveData() 23 | } 24 | 25 | override fun onResponse( 26 | call: retrofit2.Call, 27 | response: retrofit2.Response 28 | ) { 29 | if (response.isSuccessful) { 30 | response.body()?.let { change(it) } 31 | mOnReceiveDataListener?.onReceiveData() 32 | } else { 33 | mOnReceiveDataListener?.onFailureReceiveData() 34 | } 35 | } 36 | }) 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/viewmodel/ListViewModel.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.viewmodel 2 | 3 | import android.util.Log 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.ViewModel 7 | 8 | /** 9 | * Class ListViewModel. 10 | */ 11 | open class ListViewModel : ViewModel() { 12 | 13 | protected var mListViewModelController: ListViewModelController? = null 14 | protected var mOnReceiveDataListener: OnReceiveDataListener? = null 15 | protected var mReviewOnReceiveDataListener: OnReviewOnReceiveDataListener? = null 16 | protected var mList = ArrayList() 17 | protected var mLiveData: MutableLiveData> = MutableLiveData>() 18 | 19 | open fun getLiveData(): LiveData> { 20 | return mLiveData 21 | } 22 | 23 | fun add(list: List) { 24 | mList.addAll(list) 25 | mLiveData.value = mList 26 | } 27 | 28 | fun add(t: T) { 29 | mList.add(t) 30 | mLiveData.value = mList 31 | } 32 | 33 | fun add(index: Int, t: T) { 34 | mList.add(index, t) 35 | mLiveData.value = mList 36 | } 37 | 38 | fun change(index: Int, t: T){ 39 | mList[index] = t 40 | mLiveData.value = mList 41 | } 42 | 43 | fun remove(index: Int) { 44 | mList.removeAt(index) 45 | mLiveData.value = mList 46 | } 47 | 48 | fun addOnReceiveDataListener(mOnReceiveDataListener: OnReceiveDataListener) { 49 | this.mOnReceiveDataListener = mOnReceiveDataListener 50 | } 51 | 52 | fun addOnReviewReceiveDataListener(mOnReceiveDataListener: OnReviewOnReceiveDataListener) { 53 | this.mReviewOnReceiveDataListener = mOnReceiveDataListener 54 | } 55 | 56 | fun addListViewModelController(mListViewModelController:ListViewModelController){ 57 | this.mListViewModelController = mListViewModelController 58 | } 59 | 60 | interface OnReceiveDataListener { 61 | fun onReceiveData(list: List) 62 | fun onFailureToReceiveData() 63 | fun onNoMoreData() 64 | } 65 | 66 | interface OnReviewOnReceiveDataListener { 67 | fun onReceiveData(list: List,totalPage : Int) 68 | fun onFailureToReceiveData() 69 | fun onNoMoreData() 70 | } 71 | 72 | interface ListViewModelController { 73 | fun initLiveData() 74 | } 75 | 76 | 77 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/viewmodel/ObjectViewModel.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | 7 | /** 8 | * Class ObjectViewModel. 9 | */ 10 | open class ObjectViewModel : ViewModel() { 11 | 12 | protected var mOnReceiveDataListener: OnReceiveDataListener? = null 13 | private val mLiveData: MutableLiveData by lazy { 14 | MutableLiveData().also { 15 | // Do something ... 16 | } 17 | } 18 | 19 | fun getLiveData(): LiveData { 20 | return mLiveData 21 | } 22 | 23 | fun change(t: T) { 24 | mLiveData.postValue(t) 25 | } 26 | 27 | fun addOnReceiveDataListener(mOnReceiveDataListener: OnReceiveDataListener) { 28 | this.mOnReceiveDataListener = mOnReceiveDataListener 29 | } 30 | 31 | interface OnReceiveDataListener { 32 | fun onReceiveData() 33 | fun onFailureReceiveData() 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/viewmodel/ProfileViewModel.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.viewmodel 2 | 3 | import engineer.kaobei.BASE_URL 4 | import engineer.kaobei.KaobeiEngineerService 5 | import engineer.kaobei.model.kaobeluser.BeanKaobeiUser 6 | import engineer.kaobei.model.kaobeluser.KaobeiUser 7 | import retrofit2.Call 8 | import retrofit2.Response 9 | import retrofit2.Retrofit 10 | import retrofit2.converter.gson.GsonConverterFactory 11 | 12 | /** 13 | * Class ProfileViewModel. 14 | */ 15 | class ProfileViewModel : ObjectViewModel() { 16 | 17 | fun loadProfile(accessToken: String) { 18 | val retrofit = Retrofit.Builder() 19 | .baseUrl(BASE_URL) 20 | .addConverterFactory(GsonConverterFactory.create()) 21 | .build() 22 | val service = retrofit.create(KaobeiEngineerService::class.java) 23 | service.profile("Bearer $accessToken") 24 | .enqueue(object : retrofit2.Callback { 25 | override fun onFailure(call: Call, t: Throwable) { 26 | mOnReceiveDataListener?.onFailureReceiveData() 27 | } 28 | 29 | override fun onResponse( 30 | call: Call, 31 | response: Response 32 | ) { 33 | if (response.isSuccessful) { 34 | response.body()?.data?.let { change(it) } 35 | mOnReceiveDataListener?.onReceiveData() 36 | } else { 37 | mOnReceiveDataListener?.onFailureReceiveData() 38 | } 39 | } 40 | 41 | }) 42 | } 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/viewmodel/ReviewViewModel.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import engineer.kaobei.BASE_URL 7 | import engineer.kaobei.KaobeiEngineerService 8 | import engineer.kaobei.model.ReviewArticles.ReviewArticle 9 | import engineer.kaobei.model.ReviewArticles.ReviewArticles 10 | import retrofit2.Retrofit 11 | import retrofit2.converter.gson.GsonConverterFactory 12 | 13 | class ReviewViewModel : ListViewModel() { 14 | protected var mPage : MutableLiveData = MutableLiveData(1) 15 | protected var mInit : MutableLiveData = MutableLiveData(0) 16 | 17 | init { 18 | mList = arrayListOf() 19 | mLiveData = MutableLiveData>(arrayListOf()).also { 20 | 21 | } 22 | } 23 | 24 | fun refreshData(){ 25 | mInit.value = 0 26 | mPage.value = 1 27 | mList = arrayListOf() 28 | mLiveData.value = arrayListOf() 29 | } 30 | 31 | fun setInit(value : Boolean){ 32 | if(value){ 33 | mInit.value = 1 34 | }else{ 35 | mInit.value = 0 36 | } 37 | } 38 | 39 | fun isInit() : Boolean{ 40 | val value:Int = mInit.value!! 41 | return value != 0 42 | } 43 | 44 | fun getPage(): LiveData { 45 | return mPage 46 | } 47 | 48 | fun addPage(){ 49 | if(mPage.value!=null){ 50 | var index :Int= mPage.value!! 51 | index++ 52 | mPage.value = index 53 | } 54 | } 55 | 56 | fun minusPage(){ 57 | if(mPage.value!=null){ 58 | var index :Int= mPage.value!! 59 | index-- 60 | mPage.value = index 61 | } 62 | } 63 | 64 | fun loadMoreArticles(accessToken: String, page: Int) { 65 | val retrofit = Retrofit.Builder() 66 | .baseUrl(BASE_URL) 67 | .addConverterFactory(GsonConverterFactory.create()) 68 | .build() 69 | val service = retrofit.create(KaobeiEngineerService::class.java) 70 | 71 | service.reviewArticleList("Bearer $accessToken", page.toString()) 72 | .enqueue(object : retrofit2.Callback { 73 | override fun onFailure(call: retrofit2.Call, t: Throwable) { 74 | mReviewOnReceiveDataListener?.onFailureToReceiveData() 75 | } 76 | 77 | override fun onResponse( 78 | call: retrofit2.Call, 79 | response: retrofit2.Response 80 | ) { 81 | if (response.isSuccessful) { 82 | response.body()?.meta?.pagination?.total?.let { 83 | mReviewOnReceiveDataListener?.onReceiveData(response.body()?.data!!, 84 | it 85 | ) 86 | } 87 | response.body()?.data 88 | add(response.body()?.data!!) 89 | } else { 90 | mReviewOnReceiveDataListener?.onFailureToReceiveData() 91 | } 92 | } 93 | }) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/engineer/kaobei/viewmodel/UserArticleListViewModel.kt: -------------------------------------------------------------------------------- 1 | package engineer.kaobei.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import engineer.kaobei.BASE_URL 6 | import engineer.kaobei.KaobeiEngineerService 7 | import engineer.kaobei.model.articles.Article 8 | import engineer.kaobei.model.userarticles.UserArticle 9 | import engineer.kaobei.model.userarticles.UserArticles 10 | import retrofit2.Retrofit 11 | import retrofit2.converter.gson.GsonConverterFactory 12 | 13 | /** 14 | * Class UserArticleListViewModel. 15 | */ 16 | class UserArticleListViewModel : ListViewModel() { 17 | 18 | protected var mPage : MutableLiveData = MutableLiveData(1) 19 | protected var mInit : MutableLiveData = MutableLiveData(0) 20 | 21 | init { 22 | mList = arrayListOf() 23 | mLiveData = MutableLiveData>(arrayListOf()).also { 24 | 25 | } 26 | } 27 | 28 | fun refreshData(){ 29 | mInit.value = 0 30 | mPage.value = 1 31 | mList = arrayListOf() 32 | mLiveData.value = arrayListOf() 33 | } 34 | 35 | fun setInit(value : Boolean){ 36 | if(value){ 37 | mInit.value = 1 38 | }else{ 39 | mInit.value = 0 40 | } 41 | } 42 | 43 | fun isInit() : Boolean{ 44 | val value:Int = mInit.value!! 45 | return value != 0 46 | } 47 | 48 | fun getPage(): LiveData { 49 | return mPage 50 | } 51 | 52 | fun addPage(){ 53 | if(mPage.value!=null){ 54 | var index :Int= mPage.value!! 55 | index++ 56 | mPage.value = index 57 | } 58 | } 59 | 60 | fun minusPage(){ 61 | if(mPage.value!=null){ 62 | var index :Int= mPage.value!! 63 | index-- 64 | mPage.value = index 65 | } 66 | } 67 | 68 | fun loadMoreArticles(accessToken: String, page: Int) { 69 | val retrofit = Retrofit.Builder() 70 | .baseUrl(BASE_URL) 71 | .addConverterFactory(GsonConverterFactory.create()) 72 | .build() 73 | val service = retrofit.create(KaobeiEngineerService::class.java) 74 | 75 | service.userArticleList("Bearer $accessToken", page.toString()) 76 | .enqueue(object : retrofit2.Callback { 77 | override fun onFailure(call: retrofit2.Call, t: Throwable) { 78 | mOnReceiveDataListener?.onFailureToReceiveData() 79 | } 80 | 81 | override fun onResponse( 82 | call: retrofit2.Call, 83 | response: retrofit2.Response 84 | ) { 85 | if (response.isSuccessful) { 86 | mOnReceiveDataListener?.onReceiveData(response.body()?.data!!) 87 | add(response.body()?.data!!) 88 | } else { 89 | mOnReceiveDataListener?.onFailureToReceiveData() 90 | } 91 | } 92 | }) 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/color/bnv_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/img_mallet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/drawable-v24/img_mallet.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_sheet.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 9 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bottomsheet_line.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cursor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/custom_ripple.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_dashboard_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_favorite_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_mode_edit_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_people_outline_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_person_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_reply_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_share_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_thumb_up_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_warning_red_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_animated_rainbow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/drawable/img_animated_rainbow.gif -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_background_star.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/drawable/img_background_star.gif -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_bat.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_bird.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 42 | 45 | 48 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_bull.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_camel.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_cat.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_devotion_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/drawable/img_devotion_bg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_flag1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/drawable/img_flag1.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_kb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/drawable/img_kb.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_mallet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/drawable/img_mallet.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_nopic_192.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/drawable/img_nopic_192.gif -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_plurk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/drawable/img_plurk.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_twitter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/drawable/img_twitter.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/kohlrabi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/drawable/kohlrabi.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/test1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/drawable/test1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/test2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/drawable/test2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/text_bottom_line.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/font/dos.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init-engineer/init.engineer-Android-App/9e7f32108f3a7713ca67d1a6c6c06f54d427542e/app/src/main/res/font/dos.ttf -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_article.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | 31 | 32 | 42 | 43 | 44 | 45 | 48 | 49 | 55 | 56 | 57 | 58 | 62 | 63 | 64 | 65 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_article_v2.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 21 | 22 | 27 | 28 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ads_style1.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 22 | 23 | 27 | 28 | 33 | 34 | 44 | 45 | 48 | 49 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 67 | 75 | 76 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ads_style2.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 22 | 23 | 29 | 30 | 38 | 39 | 47 | 48 | 49 | 50 | 56 | 57 | 58 | 59 | 64 | 65 | 70 | 71 | 76 | 77 | 85 | 86 | 95 | 96 | 97 | 98 |