├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── release
│ ├── app-release.apk
│ └── output.json
└── src
│ ├── androidTest
│ └── java
│ │ └── me
│ │ └── luowl
│ │ └── wan
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── about_app.html
│ ├── ic_launcher-web.png
│ ├── java
│ │ └── me
│ │ │ └── luowl
│ │ │ └── wan
│ │ │ ├── AppConfig.kt
│ │ │ ├── WanApplication.kt
│ │ │ ├── base
│ │ │ ├── AppManager.kt
│ │ │ ├── ArticlePageListViewModel.kt
│ │ │ ├── BaseActivity.kt
│ │ │ ├── BaseFragment.kt
│ │ │ ├── BaseViewModel.kt
│ │ │ ├── ContainerActivity.kt
│ │ │ ├── SingleLiveEvent.kt
│ │ │ ├── StateModel.kt
│ │ │ └── adapter
│ │ │ │ ├── CommonFragmentStatePagerAdapter.kt
│ │ │ │ ├── LoadMoreState.kt
│ │ │ │ └── RVAdapter.kt
│ │ │ ├── data
│ │ │ ├── WanRepository.kt
│ │ │ ├── local
│ │ │ │ ├── SearchRecordDao.kt
│ │ │ │ ├── SearchRecordDataSource.kt
│ │ │ │ └── WanDatabase.kt
│ │ │ ├── model
│ │ │ │ ├── Architecture.kt
│ │ │ │ ├── ArticleData.kt
│ │ │ │ ├── ArticleTag.kt
│ │ │ │ ├── BannerData.kt
│ │ │ │ ├── BaseResp.kt
│ │ │ │ ├── CoinData.kt
│ │ │ │ ├── CoinPage.kt
│ │ │ │ ├── LoginData.kt
│ │ │ │ ├── Navigation.kt
│ │ │ │ ├── PageData.kt
│ │ │ │ ├── SearchHotKey.kt
│ │ │ │ └── SearchRecord.kt
│ │ │ └── network
│ │ │ │ ├── ApiService.kt
│ │ │ │ └── WanNetwork.kt
│ │ │ ├── event
│ │ │ ├── CollectEvent.kt
│ │ │ └── LoginEvent.kt
│ │ │ ├── http
│ │ │ ├── ApiException.kt
│ │ │ ├── CookieInterceptor.kt
│ │ │ ├── HttpLoggingInterceptor.kt
│ │ │ └── ServiceCreator.kt
│ │ │ ├── ui
│ │ │ ├── FindFragment.kt
│ │ │ ├── FindViewModel.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── MainViewModel.kt
│ │ │ ├── account
│ │ │ │ ├── CoinListActivity.kt
│ │ │ │ ├── CoinViewModel.kt
│ │ │ │ ├── CollectionActivity.kt
│ │ │ │ ├── CollectionViewModel.kt
│ │ │ │ ├── LoginActivity.kt
│ │ │ │ ├── LoginViewModel.kt
│ │ │ │ ├── MineFragment.kt
│ │ │ │ ├── MineViewModel.kt
│ │ │ │ ├── RegisterActivity.kt
│ │ │ │ └── RegisterViewModel.kt
│ │ │ ├── architecture
│ │ │ │ ├── ArchitectureArticleListFragment.kt
│ │ │ │ ├── ArchitectureArticleListViewModel.kt
│ │ │ │ ├── ArchitectureCategoryActivity.kt
│ │ │ │ ├── ArchitectureCategoryViewModel.kt
│ │ │ │ ├── ArchitectureFragment.kt
│ │ │ │ └── ArchitectureViewModel.kt
│ │ │ ├── home
│ │ │ │ ├── HomeFragment.kt
│ │ │ │ └── HomeViewModel.kt
│ │ │ ├── navigation
│ │ │ │ ├── NavigationFragment.kt
│ │ │ │ └── NavigationViewModel.kt
│ │ │ ├── project
│ │ │ │ ├── ProjectFragment.kt
│ │ │ │ ├── ProjectListFragment.kt
│ │ │ │ ├── ProjectListViewModel.kt
│ │ │ │ └── ProjectViewModel.kt
│ │ │ ├── search
│ │ │ │ ├── SearchActivity.kt
│ │ │ │ └── SearchViewModel.kt
│ │ │ ├── setting
│ │ │ │ └── AboutFragment.kt
│ │ │ ├── webview
│ │ │ │ ├── SonicJavaScriptInterface.java
│ │ │ │ ├── SonicRuntimeImpl.java
│ │ │ │ ├── SonicSessionClientImpl.java
│ │ │ │ ├── WebViewActivity.kt
│ │ │ │ └── WebViewModel.kt
│ │ │ └── wxarticle
│ │ │ │ ├── ArticleListActivity.kt
│ │ │ │ ├── ArticleListViewModel.kt
│ │ │ │ ├── WXArticleChaptersFragment.kt
│ │ │ │ └── WXArticleChaptersViewModel.kt
│ │ │ ├── util
│ │ │ ├── BindingAdapters.kt
│ │ │ ├── BindingConverters.kt
│ │ │ ├── GlobalUtil.kt
│ │ │ ├── Log.kt
│ │ │ ├── Preference.kt
│ │ │ ├── ViewModelFactory.kt
│ │ │ └── WanGlideModel.java
│ │ │ └── widget
│ │ │ ├── BannerView.java
│ │ │ ├── RVDividerItemDecoration.java
│ │ │ └── SimpleDividerItemDecoration.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ ├── progress_bar.xml
│ │ ├── selector_list_item_bg.xml
│ │ ├── shape_knowledge_sub_child.xml
│ │ ├── shape_login_edit_bg.xml
│ │ ├── shape_search.xml
│ │ ├── shape_top_article.xml
│ │ └── shaper_fliter.xml
│ │ ├── layout
│ │ ├── activity_architecture_category.xml
│ │ ├── activity_coin_list.xml
│ │ ├── activity_collection.xml
│ │ ├── activity_container.xml
│ │ ├── activity_login.xml
│ │ ├── activity_main.xml
│ │ ├── activity_register.xml
│ │ ├── activity_search.xml
│ │ ├── activity_web_view.xml
│ │ ├── activity_wx_article_list.xml
│ │ ├── dialog_confirm_cannel_collection.xml
│ │ ├── fragment_architecture.xml
│ │ ├── fragment_architecture_article_list.xml
│ │ ├── fragment_find.xml
│ │ ├── fragment_home.xml
│ │ ├── fragment_mine.xml
│ │ ├── fragment_navigation.xml
│ │ ├── fragment_project.xml
│ │ ├── fragment_project_list.xml
│ │ ├── fragment_wx_article_chapters.xml
│ │ ├── nav_tab_item.xml
│ │ ├── page_state_view.xml
│ │ ├── recycler_item_architecture.xml
│ │ ├── recycler_item_article.xml
│ │ ├── recycler_item_banner.xml
│ │ ├── recycler_item_coin.xml
│ │ ├── recycler_item_collection_article.xml
│ │ ├── recycler_item_entry.xml
│ │ ├── recycler_item_filter.xml
│ │ ├── recycler_item_knowledge_article.xml
│ │ ├── recycler_item_load_more.xml
│ │ ├── recycler_item_navigation_left_tab.xml
│ │ ├── recycler_item_navigation_sub_child.xml
│ │ ├── recycler_item_project.xml
│ │ ├── recycler_item_search_record.xml
│ │ ├── recycler_item_wx_article.xml
│ │ ├── recycler_item_wx_chapter.xml
│ │ └── recycler_view_header_view.xml
│ │ ├── menu
│ │ └── menu_search_view.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
│ │ ├── bad_network_image.png
│ │ ├── bg_mine_top.png
│ │ ├── ic_about.png
│ │ ├── ic_close.png
│ │ ├── ic_coin.png
│ │ ├── ic_collect.png
│ │ ├── ic_collection.png
│ │ ├── ic_feedback.png
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ ├── ic_launcher_round.png
│ │ ├── ic_nav_home_normal.png
│ │ ├── ic_nav_home_selected.png
│ │ ├── ic_nav_mine_noraml.png
│ │ ├── ic_nav_mine_selected.png
│ │ ├── ic_nav_project_normal.png
│ │ ├── ic_nav_project_selected.png
│ │ ├── ic_nav_wx_chapters_normal.png
│ │ ├── ic_nav_wx_chapters_selected.png
│ │ ├── ic_record.png
│ │ ├── ic_uncollect.png
│ │ ├── ic_user_default_poster.png
│ │ ├── icon_author.png
│ │ ├── icon_search.png
│ │ ├── icon_time.png
│ │ ├── image_server_return_false.png
│ │ ├── image_service_exception.png
│ │ ├── no_content_image.png
│ │ ├── user_default_img.png
│ │ └── wan_android_logo.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── attr.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── ids.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── me
│ └── luowl
│ └── wan
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── myapp.jks
├── screenshots
├── Screenshot_01.jpg
├── Screenshot_02.jpg
├── Screenshot_03.jpg
├── Screenshot_04.jpg
├── Screenshot_05.jpg
├── Screenshot_06.jpg
├── Screenshot_07.jpg
├── Screenshot_08.jpg
├── Screenshot_09.jpg
└── Screenshot_10.jpg
├── settings.gradle
└── test
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | /app/build
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### 玩Android 练手app
2 |
3 | 使用Kotlin编写,利用玩Android开发[API](http://www.wanandroid.com/blog/show/2),开发的一个练手APP
4 |
5 | #### 主要完成的功能
6 |
7 | - 首页最新文章
8 | - 项目
9 | - 公众号
10 | - 体系
11 | - 网站导航
12 | - 登录、注册
13 | - 收藏
14 | - 文章搜索
15 | - 我的积分
16 |
17 | #### 相关技术
18 |
19 | - Kotlin
20 | - MVVM
21 | - DataBinding
22 | - Retrofit
23 | - Glide
24 | - Room
25 |
26 |
27 | #### 截图
28 |
29 | | - | - | - |
30 | |-------|:---:|-----------:|
31 | |
|
|
|
32 | |
|
|
|
33 | |
|
|
|
34 |
|
35 |
36 |
37 | #### 扫码下载
38 |
39 | 
40 |
41 | [点击下载1](https://www.pgyer.com/wanandroid4luowl)
42 |
43 | [点击下载2](https://github.com/luowl123/WanAndroid/blob/master/app/release/app-release.apk)
44 |
45 | #### 感谢
46 |
47 |
58 |
59 | #### 项目地址
60 |
61 | >https://github.com/luowl123/WanAndroid
62 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/release/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/release/app-release.apk
--------------------------------------------------------------------------------
/app/release/output.json:
--------------------------------------------------------------------------------
1 | [{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":3,"versionName":"1.0.2","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
--------------------------------------------------------------------------------
/app/src/androidTest/java/me/luowl/wan/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan
2 |
3 | import androidx.test.InstrumentationRegistry
4 | import androidx.test.runner.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.getTargetContext()
22 | assertEquals("com.open.wanandroid", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
42 |
43 |
44 |
49 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/assets/about_app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 | 关于APP
8 |
58 |
59 |
60 | 关于APP
61 |
62 |
63 |
64 | 只是一个练手app,使用玩Android开放API
65 |
66 |
67 | 1.功能概述
68 |
69 |
70 | - 最新文章列表
71 | - 项目
72 | - 公众号
73 | - 体系
74 | - 网站导航
75 | - 登录、注册
76 | - 收藏
77 | - 文章搜索
78 |
79 |
80 |
81 | 2.开源框架
82 |
83 |
94 |
95 |
96 | 3.关于作者
97 |
98 | https://github.com/luowl123/WanAndroid
99 |
100 |
101 | 4.未完待续
102 |
103 | loading...
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/AppConfig.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan
2 |
3 | import me.luowl.wan.data.model.LoginData
4 | import me.luowl.wan.util.Preference
5 |
6 |
7 | /*
8 | *
9 | * Created by luowl
10 | * Date: 2019/8/5
11 | * Desc:
12 | */
13 |
14 | object AppConfig {
15 |
16 | const val SAVE_WAN_ANDROID_COOKIE = "save_wan_android_cookie"
17 | const val HEADER_SAVE_WAN_ANDROID_COOKIE = "save_wan_android_cookie:true"
18 |
19 | const val ADD_WAN_ANDROID_COOKIE = "add_wan_android_cookie"
20 | const val HEADER_AND_WAN_ANDROID_COOKIE = "add_wan_android_cookie:true"
21 |
22 | const val SET_COOKIE_KEY = "set-cookie"
23 | const val COOKIE_NAME = "Cookie"
24 |
25 | const val PROJECT_ISSUES_URL="https://github.com/luowl123/WanAndroid/issues"
26 | const val ABOUT_APP_URL="file:///android_asset/about_app.html"
27 | const val BUGLY_APP_ID="5448210acd"//自己申请
28 |
29 | fun encodeCookie(cookies: List): String {
30 | val sb = StringBuilder()
31 | val set = HashSet()
32 | cookies
33 | .map { cookie ->
34 | cookie.split(";".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
35 | }
36 | .forEach {
37 | it.filterNot { set.contains(it) }.forEach { set.add(it) }
38 | }
39 | val ite = set.iterator()
40 | while (ite.hasNext()) {
41 | val cookie = ite.next()
42 | sb.append(cookie).append(";")
43 | }
44 | val last = sb.lastIndexOf(";")
45 | if (sb.length - 1 == last) {
46 | sb.deleteCharAt(last)
47 | }
48 | return sb.toString()
49 | }
50 |
51 | fun saveCookie(url: String?, domain: String?, cookies: String) {
52 | url ?: return
53 | var spUrl: String by Preference(url, cookies)
54 | @Suppress("UNUSED_VALUE")
55 | spUrl = cookies
56 | domain ?: return
57 | var spDomain: String by Preference(domain, cookies)
58 | @Suppress("UNUSED_VALUE")
59 | spDomain = cookies
60 | }
61 |
62 | const val LOGIN_KEY = "login"
63 | const val USERNAME_KEY = "username"
64 | const val PASSWORD_KEY = "password"
65 | const val TOKEN_KEY = "token"
66 |
67 | /**
68 | * local username
69 | */
70 | var user: String by Preference(USERNAME_KEY, "")
71 |
72 | /**
73 | * local password
74 | */
75 | var pwd: String by Preference(PASSWORD_KEY, "")
76 |
77 | /**
78 | * token
79 | */
80 | var token: String by Preference(TOKEN_KEY, "")
81 |
82 | fun saveLoginInfo(info:LoginData){
83 | user=info.username
84 | pwd=info.password
85 | token=info.token
86 | }
87 |
88 | fun clearLoginInfo(){
89 | Preference.clearPreference()
90 | }
91 |
92 | fun isLogin(): Boolean {
93 | return user.isNotEmpty()
94 | }
95 |
96 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/WanApplication.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.Activity
5 | import android.app.Application
6 | import android.content.Context
7 | import android.os.Bundle
8 | import android.util.Log
9 | import androidx.multidex.MultiDex
10 | import com.jeremyliao.liveeventbus.LiveEventBus
11 | import com.tencent.bugly.Bugly
12 | import com.tencent.sonic.sdk.SonicConfig
13 | import com.tencent.sonic.sdk.SonicEngine
14 | import me.luowl.wan.base.AppManager
15 | import me.luowl.wan.ui.webview.SonicRuntimeImpl
16 | import me.luowl.wan.util.logDebug
17 | import timber.log.Timber
18 |
19 |
20 | /**
21 | *
22 | * Created by luowl
23 | * Date: 2019/7/23
24 | * Desc:
25 | */
26 | class WanApplication : Application() {
27 |
28 | override fun attachBaseContext(base: Context?) {
29 | super.attachBaseContext(base)
30 | MultiDex.install(this)
31 | }
32 |
33 | override fun onCreate() {
34 | super.onCreate()
35 | context = this
36 |
37 | val debug = BuildConfig.DEBUG
38 | if (debug) {
39 | Timber.plant(Timber.DebugTree())
40 | } else {
41 | Timber.plant(CrashReportingTree())
42 | }
43 |
44 | // CrashReport.initCrashReport(applicationContext, AppConfig.BUGLY_APP_ID, debug)
45 | Bugly.init(applicationContext, AppConfig.BUGLY_APP_ID, debug)
46 |
47 | LiveEventBus.get()
48 | .config()
49 | .supportBroadcast(this)
50 | .lifecycleObserverAlwaysActive(true)
51 | .autoClear(false)
52 |
53 | setApplication(this)
54 |
55 | // init sonic engine if necessary, or maybe u can do this when application created
56 | if (!SonicEngine.isGetInstanceAllowed()) {
57 | SonicEngine.createInstance(SonicRuntimeImpl(this), SonicConfig.Builder().build())
58 | }
59 |
60 | }
61 |
62 | private fun setApplication(app: Application) {
63 | app.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
64 |
65 | override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
66 | logDebug(TAG, "${activity.javaClass.simpleName} onActivityCreated")
67 | AppManager.addActivity(activity)
68 | }
69 |
70 | override fun onActivityStarted(activity: Activity) {
71 | logDebug(TAG, "${activity.javaClass.simpleName} onActivityStarted")
72 | }
73 |
74 | override fun onActivityResumed(activity: Activity) {
75 | logDebug(TAG, "${activity.javaClass.simpleName} onActivityResumed")
76 | }
77 |
78 | override fun onActivityPaused(activity: Activity) {
79 | logDebug(TAG, "${activity.javaClass.simpleName} onActivityPaused")
80 | }
81 |
82 | override fun onActivityStopped(activity: Activity) {
83 | logDebug(TAG, "${activity.javaClass.simpleName} onActivityStopped")
84 | }
85 |
86 | override fun onActivityDestroyed(activity: Activity) {
87 | logDebug(TAG, "${activity.javaClass.simpleName} onActivityDestroyed")
88 | AppManager.finishActivity(activity)
89 | }
90 |
91 | override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
92 | logDebug(TAG, "${activity.javaClass.simpleName} onActivitySaveInstanceState")
93 | }
94 |
95 | })
96 | }
97 |
98 | class CrashReportingTree : Timber.Tree() {
99 | override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
100 | if (priority == Log.VERBOSE || priority == Log.DEBUG) {
101 | return
102 | }
103 | }
104 | }
105 |
106 | companion object {
107 | const val TAG = "WanApplication"
108 | @SuppressLint("StaticFieldLeak")
109 | lateinit var context: Context
110 | }
111 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/base/ArticlePageListViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.base
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import me.luowl.wan.base.adapter.LoadMoreState
5 | import me.luowl.wan.data.WanRepository
6 | import me.luowl.wan.data.model.ArticleData
7 | import me.luowl.wan.data.model.BaseResp
8 | import me.luowl.wan.data.model.PageData
9 | import me.luowl.wan.util.GlobalUtil
10 | import java.util.*
11 |
12 | /*
13 | *
14 | * Created by luowl
15 | * Date: 2019/8/3
16 | * Desc:
17 | */
18 |
19 | abstract class ArticlePageListViewModel constructor(val repository: WanRepository) : BaseViewModel() {
20 | private val _items = MutableLiveData>().apply { value = mutableListOf() }
21 | val dataList: MutableLiveData> = _items
22 | var pageIndex = 0
23 | var requestLoadDataState = MutableLiveData()
24 |
25 | abstract suspend fun request(): BaseResp
26 |
27 | override fun retry() {
28 | super.retry()
29 | pageIndex = 0
30 | getData()
31 | }
32 |
33 | fun getData() {
34 | launch({
35 | if (pageIndex == 0)
36 | startLoading()
37 | val resp = request()
38 | checkResponseCode(resp)
39 | val pageData = resp.data ?: throw Throwable("page data is null")
40 | val pageItems = ArrayList(pageData.datas ?: listOf())
41 | if (pageIndex == 0 || _items.value == null) {
42 | _items.value = pageItems
43 | } else {
44 | _items.value?.addAll(pageItems)
45 | }
46 | if (pageIndex == 0 && pageItems.size == 0) {
47 | loadDataEmpty()
48 | } else {
49 | loadDataFinish()
50 | }
51 | if (pageIndex < pageData.pageCount - 1) {
52 | pageIndex++
53 | requestLoadDataState.value = LoadMoreState.STATE_LOAD_NONE
54 | } else {
55 | requestLoadDataState.value = LoadMoreState.STATE_LOAD_END
56 | }
57 | }, {
58 | if (pageIndex == 0) {
59 | loadDataError()
60 | } else {
61 | requestLoadDataState.value = LoadMoreState.STATE_LOAD_FAIL
62 | loadDataFinish()
63 | }
64 | })
65 | }
66 |
67 | fun collectArticle(id: Long) {
68 | launch({
69 | val resp = repository.addCollect(id)
70 | checkResponseCode(resp)
71 | // GlobalUtil.showToastShort("收藏成功")
72 | }, {
73 | // GlobalUtil.showToastShort("收藏失败")
74 | })
75 | }
76 |
77 | fun cancelCollectArticle(id: Long) {
78 | launch({
79 | val resp = repository.cancelCollectArticleByOriginId(id)
80 | checkResponseCode(resp)
81 | // GlobalUtil.showToastShort("取消收藏")
82 | }, {
83 | // GlobalUtil.showToastShort("操作失败")
84 | })
85 | }
86 |
87 | fun cancelCollectArticle(id: Long, originId: Long) {
88 | launch({
89 | val resp = repository.cancelCollectArticle(id, originId)
90 | checkResponseCode(resp)
91 | // GlobalUtil.showToastShort("取消收藏")
92 | retry()
93 | }, {
94 | // GlobalUtil.showToastShort("操作失败")
95 | })
96 | }
97 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/base/ContainerActivity.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.base
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import android.view.WindowManager
6 | import androidx.appcompat.app.AppCompatActivity
7 | import androidx.fragment.app.Fragment
8 | import me.luowl.wan.R
9 | import java.lang.ref.WeakReference
10 |
11 |
12 | /*
13 | *
14 | * Created by luowl
15 | * Date: 2019/8/6
16 | * Desc:
17 | */
18 |
19 | class ContainerActivity : AppCompatActivity() {
20 |
21 | protected lateinit var mFragment: WeakReference
22 |
23 | override fun onCreate(savedInstanceState: Bundle?) {
24 | window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
25 | super.onCreate(savedInstanceState)
26 | setContentView(R.layout.activity_container)
27 | val fm = supportFragmentManager
28 | var fragment: Fragment? = null
29 | if (savedInstanceState != null) {
30 | fragment = fm.getFragment(savedInstanceState, FRAGMENT_TAG)
31 | }
32 | if (fragment == null) {
33 | fragment = initFromIntent(intent)
34 | }
35 | val trans = supportFragmentManager
36 | .beginTransaction()
37 | trans.replace(R.id.content, fragment)
38 | trans.commitAllowingStateLoss()
39 | mFragment = WeakReference(fragment)
40 | }
41 |
42 | override fun onSaveInstanceState(outState: Bundle) {
43 | super.onSaveInstanceState(outState)
44 | mFragment.get()?.let { supportFragmentManager.putFragment(outState, FRAGMENT_TAG, it) }
45 | }
46 |
47 | protected fun initFromIntent(data: Intent?): Fragment {
48 | if (data == null) {
49 | throw RuntimeException("you must provide a page info to display")
50 | }
51 | try {
52 | val fragmentName = data.getStringExtra(FRAGMENT)
53 | if (fragmentName == null || "" == fragmentName) {
54 | throw IllegalArgumentException("can not find page fragmentName")
55 | }
56 | val fragmentClass = Class.forName(fragmentName)
57 | val fragment = fragmentClass.newInstance() as Fragment
58 | val args = data.getBundleExtra(BUNDLE)
59 | if (args != null) {
60 | fragment.arguments = args
61 | }
62 | return fragment
63 | } catch (e: ClassNotFoundException) {
64 | e.printStackTrace()
65 | } catch (e: InstantiationException) {
66 | e.printStackTrace()
67 | } catch (e: IllegalAccessException) {
68 | e.printStackTrace()
69 | }
70 |
71 | throw RuntimeException("fragment initialization failed!")
72 | }
73 |
74 | override fun onBackPressed() {
75 | val fragment = supportFragmentManager.findFragmentById(R.id.content)
76 | if (fragment is BaseFragment<*, *>) {
77 | if (!fragment.isBackPressed()) {
78 | super.onBackPressed()
79 | }
80 | } else {
81 | super.onBackPressed()
82 | }
83 | }
84 |
85 | companion object {
86 | const val FRAGMENT_TAG = "content_fragment_tag"
87 | const val FRAGMENT = "fragment"
88 | const val BUNDLE = "bundle"
89 | }
90 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/base/SingleLiveEvent.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.base
2 |
3 | import androidx.annotation.MainThread
4 | import androidx.lifecycle.LifecycleOwner
5 | import androidx.lifecycle.MutableLiveData
6 | import androidx.lifecycle.Observer
7 | import java.util.concurrent.atomic.AtomicBoolean
8 |
9 | /*
10 | *
11 | * Created by luowl
12 | * Date: 2019/8/5
13 | * Desc:
14 | */
15 |
16 | open class SingleLiveEvent : MutableLiveData() {
17 |
18 | private val pending = AtomicBoolean(false)
19 |
20 | @MainThread
21 | override fun setValue(value: T?) {
22 | pending.set(true)
23 | super.setValue(value)
24 | }
25 |
26 | @MainThread
27 | override fun observe(owner: LifecycleOwner, observer: Observer) {
28 |
29 | super.observe(owner, Observer {
30 | if (pending.compareAndSet(true, false)) {
31 | observer.onChanged(it)
32 | }
33 | })
34 | }
35 |
36 | @MainThread
37 | fun call() {
38 | value = null
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/base/StateModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.base
2 |
3 | import androidx.databinding.BaseObservable
4 |
5 | /*
6 | *
7 | * Created by luowl
8 | * Date: 2019/8/2
9 | * Desc:
10 | */
11 |
12 | class StateModel : BaseObservable() {
13 |
14 | private var state = STATE_NORMAL
15 | set(value) {
16 | field = value
17 | notifyChange()
18 | }
19 |
20 | val empty
21 | get() = state == STATE_EMPTY
22 |
23 | val loading
24 | get() = state == STATE_LOADING
25 |
26 | val error
27 | get() = state == STATE_ERROR
28 |
29 | val badNetwork
30 | get() = state == STATE_BAD_NETWORK
31 |
32 | val success
33 | get() = state == STATE_NORMAL
34 |
35 | fun showEmpty() {
36 | state = STATE_EMPTY
37 | }
38 |
39 | fun startLoading() {
40 | state = STATE_LOADING
41 | }
42 |
43 | fun loadDataError() {
44 | state = STATE_ERROR
45 | }
46 |
47 | fun loadDataFinish() {
48 | state = STATE_NORMAL
49 | }
50 |
51 | companion object {
52 | const val STATE_NORMAL = 0
53 | const val STATE_EMPTY = 1
54 | const val STATE_LOADING = 2
55 | const val STATE_ERROR = 3
56 | const val STATE_BAD_NETWORK = 4
57 | }
58 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/base/adapter/CommonFragmentStatePagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.base.adapter
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentManager
5 | import androidx.fragment.app.FragmentStatePagerAdapter
6 |
7 | /*
8 | *
9 | * Created by luowl
10 | * Date: 2019/7/25
11 | * Desc:
12 | */
13 |
14 | class CommonFragmentStatePagerAdapter(
15 | fm: FragmentManager,
16 | private val titles: List,
17 | private val fragments: List
18 | ) : FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
19 |
20 | override fun getPageTitle(position: Int): CharSequence? {
21 | return titles[position]
22 | }
23 |
24 | override fun getCount(): Int {
25 | return titles.size
26 | }
27 |
28 | override fun getItem(position: Int): Fragment {
29 | return fragments[position]
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/base/adapter/LoadMoreState.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.base.adapter
2 |
3 | import androidx.databinding.BaseObservable
4 |
5 | /*
6 | *
7 | * Created by luowl
8 | * Date: 2019/7/24
9 | * Desc:
10 | */
11 |
12 | class LoadMoreState : BaseObservable() {
13 |
14 | private var state = STATE_LOAD_NONE
15 | set(value) {
16 | field = value
17 | notifyChange()
18 | }
19 |
20 | val loading
21 | get() = state == STATE_LOADING
22 |
23 | val error
24 | get() = state == STATE_LOAD_FAIL
25 |
26 | val end
27 | get() = state == STATE_LOAD_END
28 |
29 | val none
30 | get() = state == STATE_LOAD_NONE
31 |
32 | fun loadMoreComplete() {
33 | state = STATE_LOAD_NONE
34 | }
35 |
36 | fun startLoading() {
37 | state = STATE_LOADING
38 | }
39 |
40 | fun loadMoreFail() {
41 | state = STATE_LOAD_FAIL
42 | }
43 |
44 | fun loadMoreEnd() {
45 | state = STATE_LOAD_END
46 | }
47 |
48 | companion object {
49 | public const val STATE_LOADING = 1
50 | public const val STATE_LOAD_FAIL = 2
51 | public const val STATE_LOAD_END = 3
52 | public const val STATE_LOAD_NONE = 4
53 | }
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/local/SearchRecordDao.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.local
2 |
3 | import androidx.room.*
4 | import me.luowl.wan.data.model.SearchRecord
5 |
6 | /*
7 | *
8 | * Created by luowl
9 | * Date: 2019/7/30
10 | * Desc:
11 | */
12 | @Dao
13 | interface SearchRecordDao {
14 | @Query("SELECT * FROM search_records order by record_id desc")
15 | suspend fun getRecords(): List
16 |
17 | @Insert
18 | suspend fun insertRecord(record: SearchRecord)
19 |
20 | @Delete
21 | suspend fun deleteRecord(record: SearchRecord)
22 |
23 | @Query("SELECT * FROM search_records WHERE keyword=:keyword")
24 | suspend fun findRecord(keyword: String): List
25 |
26 | @Query("DELETE FROM search_records WHERE keyword=:keyword")
27 | suspend fun deleteRecords(keyword: String)
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/local/SearchRecordDataSource.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.local
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.Dispatchers
5 | import kotlinx.coroutines.withContext
6 | import me.luowl.wan.data.model.SearchRecord
7 |
8 | /*
9 | *
10 | * Created by luowl
11 | * Date: 2019/7/30
12 | * Desc:
13 | */
14 |
15 | class SearchRecordDataSource internal constructor(
16 | private val recordDao: SearchRecordDao,
17 | private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
18 | ) {
19 | suspend fun getRecords(): List = withContext(ioDispatcher) {
20 | recordDao.getRecords()
21 | }
22 |
23 | suspend fun insertRecord(record: SearchRecord) = withContext(ioDispatcher) {
24 | recordDao.insertRecord(record)
25 | }
26 |
27 | suspend fun deleteRecord(record: SearchRecord) = withContext(ioDispatcher) {
28 | recordDao.deleteRecord(record)
29 | }
30 |
31 | suspend fun findRecord(keyword: String): List = withContext(ioDispatcher) {
32 | recordDao.findRecord(keyword)
33 | }
34 |
35 | suspend fun deleteRecords(keyword: String)= withContext(ioDispatcher){
36 | recordDao.deleteRecords(keyword)
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/local/WanDatabase.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.local
2 |
3 | import androidx.room.Database
4 | import androidx.room.Room
5 | import androidx.room.RoomDatabase
6 | import me.luowl.wan.WanApplication
7 | import me.luowl.wan.data.model.SearchRecord
8 |
9 | /*
10 | *
11 | * Created by luowl
12 | * Date: 2019/7/30
13 | * Desc:
14 | */
15 | @Database(entities = [SearchRecord::class], version = 2, exportSchema = false)
16 | abstract
17 | class WanDatabase : RoomDatabase() {
18 | abstract fun searchRecordDao(): SearchRecordDao
19 |
20 | companion object {
21 |
22 | val instance = Single.sin
23 |
24 | }
25 |
26 | private object Single {
27 |
28 | val sin: WanDatabase = Room.databaseBuilder(
29 | WanApplication.context,
30 | WanDatabase::class.java,
31 | "Wan.db"
32 | )
33 | .allowMainThreadQueries()
34 | .build()
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/model/Architecture.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.model
2 |
3 | import java.io.Serializable
4 |
5 | /**
6 | *
7 | * Created by luowl
8 | * Date: 2019/7/23
9 | * Desc:
10 | */
11 | class Architecture :Serializable{
12 | /**
13 | "courseId": 13,
14 | "id": 150,
15 | "name": "开发环境",
16 | "order": 1,
17 | "parentChapterId": 0,
18 | "userControlSetTop": false,
19 | "visible": 1,
20 | "children":[]
21 | * */
22 |
23 | var courseId = 0
24 | var id = 0L
25 | var name = ""
26 | var order = 0
27 | var parentChapterId = 0
28 | var userControlSetTop = false
29 | var visible = 1
30 | var children: List? = null
31 |
32 | fun getChildrenNames(): String {
33 | if (children == null) return ""
34 | return children!!.joinToString(separator = "\t",transform = {it.name})
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/model/ArticleData.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.model
2 |
3 | /**
4 | *
5 | * Created by luowl
6 | * Date: 2019/7/23
7 | * Desc:
8 | */
9 | class ArticleData {
10 | /**
11 | {
12 | "apkLink": "",
13 | "author": " 振之",
14 | "chapterId": 26,
15 | "chapterName": "基础UI控件",
16 | "collect": false,
17 | "courseId": 13,
18 | "desc": "",
19 | "envelopePic": "",
20 | "fresh": false,
21 | "id": 8575,
22 | "link": "https://juejin.im/post/597d88f75188257fc2177c36",
23 | "niceDate": "2019-06-03",
24 | "origin": "",
25 | "prefix": "",
26 | "projectLink": "",
27 | "publishTime": 1559534523000,
28 | "superChapterId": 26,
29 | "superChapterName": "常用控件",
30 | "tags": [],
31 | "title": "如何实现 “中间这几个字要加粗,但是不要太粗,比较纤细的那种粗” ?",
32 | "type": 0,
33 | "userId": -1,
34 | "visible": 1,
35 | "zan": 0
36 | }
37 | */
38 | var apkLink = ""
39 | var author = ""
40 | var chapterId = 0
41 | var chapterName = ""
42 | var collect = false
43 | var courseId = 0
44 | var desc = ""
45 | var envelopePic = ""
46 | var fresh = false
47 | var id = 0L
48 | var link = ""
49 | var niceDate = ""
50 | get() {
51 | return "时间:$field"
52 | }
53 | var origin = ""
54 | var originId=0L
55 | var prefix = ""
56 | var projectLink = ""
57 | var publishTime = 0L
58 | var superChapterId = 0
59 | var superChapterName = ""
60 | var title = ""
61 | var type = 0
62 | var userId = -1
63 | var visible = 1
64 | var zan = 0L
65 |
66 | var tags: MutableList? = null
67 |
68 | fun getChapter(): String {
69 | if (superChapterName.isNotEmpty())
70 | return "$superChapterName/$chapterName"
71 | return chapterName
72 | }
73 |
74 | fun hasDesc():Boolean{
75 | return desc.isNotEmpty()
76 | }
77 |
78 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/model/ArticleTag.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.model
2 |
3 | /**
4 | *
5 | * Created by luowl
6 | * Date: 2019/7/23
7 | * Desc:
8 | */
9 | class ArticleTag {
10 | var name = ""
11 | var url = ""
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/model/BannerData.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.model
2 |
3 | /**
4 | *
5 | * Created by luowl
6 | * Date: 2019/7/23
7 | * Desc:
8 | */
9 | class BannerData {
10 |
11 | /**
12 | {
13 | "desc": "Android高级进阶直播课免费学习",
14 | "id": 22,
15 | "imagePath": "https://wanandroid.com/blogimgs/fbed8f14-1043-4a43-a7ee-0651996f7c49.jpeg",
16 | "isVisible": 1,
17 | "order": 0,
18 | "title": "Android高级进阶直播课免费学习",
19 | "type": 0,
20 | "url": "https://url.163.com/4bj"
21 | }
22 | */
23 |
24 | var desc = ""
25 | var id = 0L
26 | var imagePath = ""
27 | var isVibile = 1
28 | var order = 0
29 | var title = ""
30 | var type = 0
31 | var url = ""
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/model/BaseResp.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.model
2 |
3 | /**
4 | *
5 | * Created by luowl
6 | * Date: 2019/7/23
7 | * Desc:
8 | */
9 | open class BaseResp {
10 | var errorCode = 0
11 |
12 | var errorMsg = ""
13 |
14 | var data: T? = null
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/model/CoinData.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.model
2 |
3 | import java.io.Serializable
4 |
5 | /*
6 | *
7 | * Created by luowl
8 | * Date: 2019/8/31
9 | * Desc:
10 | */
11 |
12 | class CoinData :Serializable{
13 | var coinCount = 0L
14 | var date = 0L
15 | var desc = ""
16 | var id = 0L
17 | var type = 0
18 | var userId = 0L
19 | var userName = ""
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/model/CoinPage.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.model
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import java.io.Serializable
5 |
6 | /*
7 | *
8 | * Created by luowl
9 | * Date: 2019/8/31
10 | * Desc:
11 | */
12 |
13 | class CoinPage :Serializable{
14 | @SerializedName("curPage")
15 | var currentPage = 0
16 | var datas: MutableList? = null
17 | var offset = 0
18 | var over = false
19 | var pageCount = 0
20 | var size = 0
21 | var total = 0L
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/model/LoginData.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.model
2 |
3 | import java.io.Serializable
4 |
5 | /*
6 | *
7 | * Created by luowl
8 | * Date: 2019/8/5
9 | * Desc:
10 | */
11 |
12 | class LoginData : Serializable {
13 | var admin = false
14 | var email = ""
15 | var icon = ""
16 | var id = 0L
17 | var nickname = ""
18 | var password = ""
19 | var token = ""
20 | var type = 0
21 | var username = ""
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/model/Navigation.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.model
2 |
3 | /**
4 | *
5 | * Created by luowl
6 | * Date: 2019/7/23
7 | * Desc:
8 | */
9 | class Navigation {
10 | var articles: List? = null
11 | var cid = 0
12 | var name = ""
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/model/PageData.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.model
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import java.io.Serializable
5 |
6 | /**
7 | *
8 | * Created by luowl
9 | * Date: 2019/7/23
10 | * Desc:
11 | */
12 | class PageData :Serializable{
13 | @SerializedName("curPage")
14 | var currentPage = 0
15 | var datas: MutableList? = null
16 | var offset = 0
17 | var over = false
18 | var pageCount = 0
19 | var size = 0
20 | var total = 0L
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/model/SearchHotKey.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.model
2 |
3 | /**
4 | *
5 | * Created by luowl
6 | * Date: 2019/7/23
7 | * Desc:
8 | */
9 | class SearchHotKey {
10 | /**
11 | {
12 | "id": 6,
13 | "link": "",
14 | "name": "面试",
15 | "order": 1,
16 | "visible": 1
17 | }
18 | */
19 |
20 | var id = 0L
21 | var link = ""
22 | var name = ""
23 | var order = 0
24 | var visible = 1
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/model/SearchRecord.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.model
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.Ignore
6 | import androidx.room.PrimaryKey
7 |
8 | /*
9 | *
10 | * Created by luowl
11 | * Date: 2019/7/23
12 | * Desc:
13 | */
14 | @Entity(tableName = "search_records")
15 | class SearchRecord {
16 |
17 | @PrimaryKey(autoGenerate = true)
18 | @ColumnInfo(name = "record_id")
19 | var id: Long = 0L
20 |
21 | @ColumnInfo(name = "keyword")
22 | var keyword: String = ""
23 |
24 | @Ignore
25 | constructor()
26 |
27 | constructor(
28 | keyword: String = "",
29 | id: Long = 0L
30 | ) {
31 | this.id = id
32 | this.keyword = keyword
33 | }
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/data/network/WanNetwork.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.data.network
2 |
3 | import me.luowl.wan.http.ServiceCreator
4 | import retrofit2.Call
5 | import retrofit2.Callback
6 | import retrofit2.Response
7 | import kotlin.coroutines.resume
8 | import kotlin.coroutines.resumeWithException
9 | import kotlin.coroutines.suspendCoroutine
10 |
11 | /**
12 | *
13 | * Created by luowl
14 | * Date: 2019/7/23
15 | * Desc:
16 | */
17 | class WanNetwork {
18 |
19 | private val apiService = ServiceCreator.create(ApiService::class.java)
20 |
21 | suspend fun getHomeArticleList(pageIndex: Int) = apiService.getHomeArticleList(pageIndex).await()
22 |
23 | suspend fun getBanner() = apiService.getBanner().await()
24 |
25 | suspend fun getTopArticleList() = apiService.getTopArticleList().await()
26 |
27 | suspend fun getHotSearchKey() = apiService.getHotSearchKey().await()
28 |
29 | suspend fun getArchitectureTree() = apiService.getArchitectureTree().await()
30 |
31 | suspend fun getArticleListByTreeId(pageIndex: Int, cid: Long) =
32 | apiService.getArticleListByTreeId(pageIndex, cid).await()
33 |
34 | suspend fun getNavigationData() = apiService.getNavigationData().await()
35 |
36 | suspend fun getProjectClassification() = apiService.getProjectClassification().await()
37 |
38 | suspend fun getProjectList(pageIndex: Int, cid: Long) = apiService.getProjectList(pageIndex, cid).await()
39 |
40 | suspend fun getNewestProjectList(pageIndex: Int) = apiService.getNewestProjectList(pageIndex).await()
41 |
42 | suspend fun getWXArticleChapters() = apiService.getWXArticleChapters().await()
43 |
44 | suspend fun getWXArticleList(chapterId: Long, pageIndex: Int) =
45 | apiService.getWXArticleList(chapterId, pageIndex).await()
46 |
47 | suspend fun getWXArticleListByKey(chapterId: Long, pageIndex: Int, key: String) =
48 | apiService.getWXArticleListByKey(chapterId, pageIndex, key).await()
49 |
50 | suspend fun searchArticleByKey(pageIndex: Int, key: String) = apiService.searchArticleByKey(pageIndex, key).await()
51 |
52 | suspend fun login(username:String,password:String)=apiService.login(username,password).await()
53 |
54 | suspend fun register(username:String,password:String,repassword:String)=apiService.register(username,password,repassword).await()
55 |
56 | suspend fun logout()=apiService.logout().await()
57 |
58 | suspend fun getCollectList(pageIndex: Int)=apiService.getCollectList(pageIndex).await()
59 |
60 | suspend fun addCollect(articleId:Long)=apiService.addCollectArticle(articleId).await()
61 |
62 | suspend fun addCollectOutsideArticle(title: String,author: String,link: String)=apiService.addCollectOutsideArticle(title,author,link).await()
63 |
64 | suspend fun cancelCollectArticleByOriginId(articleId:Long)=apiService.cancelCollectArticleByOriginId(articleId).await()
65 |
66 | suspend fun cancelCollectArticle(articleId:Long,originId: Long=-1)=apiService.cancelCollectArticle(articleId,originId).await()
67 |
68 | suspend fun getMyCoinCount() = apiService.getMyCoinCount().await()
69 |
70 | suspend fun getCoinList(pageIndex: Int) = apiService.getCoinList(pageIndex).await()
71 |
72 | private suspend fun Call.await(): T {
73 | return suspendCoroutine { continuation ->
74 | enqueue(object : Callback {
75 | override fun onFailure(call: Call, t: Throwable) {
76 | continuation.resumeWithException(t)
77 | }
78 |
79 | override fun onResponse(call: Call, response: Response) {
80 | val body = response.body()
81 | if (body != null) continuation.resume(body)
82 | else continuation.resumeWithException(RuntimeException("response body is null"))
83 | }
84 | })
85 | }
86 | }
87 |
88 | companion object {
89 |
90 | private var network: WanNetwork? = null
91 |
92 | fun getInstance(): WanNetwork {
93 | if (network == null) {
94 | synchronized(WanNetwork::class.java) {
95 | if (network == null) {
96 | network = WanNetwork()
97 | }
98 | }
99 | }
100 | return network!!
101 | }
102 | }
103 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/event/CollectEvent.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.event
2 |
3 | /*
4 | *
5 | * Created by luowl
6 | * Date: 2019/8/8
7 | * Desc:
8 | */
9 |
10 | class CollectEvent()
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/event/LoginEvent.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.event
2 |
3 | /*
4 | *
5 | * Created by luowl
6 | * Date: 2019/8/5
7 | * Desc:
8 | */
9 |
10 | class LoginEvent(val success: Boolean)
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/http/ApiException.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.http
2 |
3 | /*
4 | *
5 | * Created by luowl
6 | * Date: 2019/8/6
7 | * Desc:
8 | */
9 |
10 | class ApiException(val errorCode:Int,val errorMsg:String) : Throwable(errorMsg) {
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/http/CookieInterceptor.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.http
2 |
3 | import me.luowl.wan.AppConfig
4 | import me.luowl.wan.util.Preference
5 | import me.luowl.wan.util.logDebug
6 | import okhttp3.Interceptor
7 | import okhttp3.Response
8 |
9 | /**
10 | * @desc CookieInterceptor: 添加Cookie,保存Cookie
11 | */
12 | class CookieInterceptor : Interceptor {
13 |
14 | override fun intercept(chain: Interceptor.Chain): Response {
15 |
16 | val request = chain.request()
17 | val builder = request.newBuilder()
18 |
19 | val addCookie = request.header(AppConfig.ADD_WAN_ANDROID_COOKIE)?.equals("true") ?: false
20 | logDebug("addCookie:$addCookie")
21 |
22 | builder.addHeader("Content-type", "application/json; charset=utf-8")
23 |
24 | val domain = request.url().host()
25 | val requestUrl = request.url().toString()
26 | if (domain.isNotEmpty() && addCookie) {
27 | val spDomain: String by Preference(domain, "")
28 | val cookie: String = if (spDomain.isNotEmpty()) spDomain else ""
29 | if (cookie.isNotEmpty()) {
30 | // 将 Cookie 添加到请求头
31 | builder.addHeader(AppConfig.COOKIE_NAME, cookie)
32 | }
33 | }
34 |
35 | val response = chain.proceed(builder.build())
36 | val saveCookie = request.header(AppConfig.SAVE_WAN_ANDROID_COOKIE)?.equals("true") ?: false
37 | logDebug("saveCookie:$saveCookie")
38 | // set-cookie maybe has multi, login to save cookie
39 | if (saveCookie && response.headers(AppConfig.SET_COOKIE_KEY).isNotEmpty()) {
40 | val cookies = response.headers(AppConfig.SET_COOKIE_KEY)
41 | val cookie = AppConfig.encodeCookie(cookies)
42 | AppConfig.saveCookie(requestUrl, domain, cookie)
43 | }
44 |
45 | return response
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/http/ServiceCreator.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.http
2 |
3 | import me.luowl.wan.BuildConfig
4 | import okhttp3.OkHttpClient
5 | import retrofit2.Retrofit
6 | import retrofit2.converter.gson.GsonConverterFactory
7 | import retrofit2.converter.scalars.ScalarsConverterFactory
8 | import java.util.logging.Level
9 |
10 | object ServiceCreator {
11 |
12 | private const val BASE_URL = "https://www.wanandroid.com/"
13 |
14 | private val httpClient = OkHttpClient.Builder()
15 |
16 | init {
17 | val loggingInterceptor = HttpLoggingInterceptor("WanHttp")
18 | loggingInterceptor.setPrintLevel(if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE)
19 | loggingInterceptor.setColorLevel(Level.INFO)
20 | httpClient.addInterceptor(CookieInterceptor())
21 | httpClient.addInterceptor(loggingInterceptor)
22 | }
23 |
24 | private val builder = Retrofit.Builder()
25 | .baseUrl(BASE_URL)
26 | .client(httpClient.build())
27 | .addConverterFactory(ScalarsConverterFactory.create())
28 | .addConverterFactory(GsonConverterFactory.create())
29 |
30 |
31 | private val retrofit = builder.build()
32 |
33 | fun create(serviceClass: Class): T = retrofit.create(serviceClass)
34 |
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/FindFragment.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui
2 |
3 | import android.os.Bundle
4 | import android.widget.TextView
5 | import androidx.fragment.app.Fragment
6 | import androidx.viewpager.widget.ViewPager
7 | import kotlinx.android.synthetic.main.fragment_find.*
8 | import me.luowl.wan.BR
9 | import me.luowl.wan.R
10 | import me.luowl.wan.base.BaseFragment
11 | import me.luowl.wan.base.adapter.CommonFragmentStatePagerAdapter
12 | import me.luowl.wan.databinding.FragmentFindBinding
13 | import me.luowl.wan.ui.architecture.ArchitectureFragment
14 | import me.luowl.wan.ui.navigation.NavigationFragment
15 | import me.luowl.wan.ui.wxarticle.WXArticleChaptersFragment
16 | import me.luowl.wan.util.GlobalUtil
17 | import me.luowl.wan.util.logDebug
18 |
19 | /*
20 | *
21 | * Created by luowl
22 | * Date: 2019/7/26
23 | * Desc:
24 | */
25 |
26 | class FindFragment : BaseFragment() {
27 |
28 | override fun getViewModelClass(): Class = FindViewModel::class.java
29 |
30 | override fun getLayoutId() = R.layout.fragment_find
31 |
32 | override fun initVariableId() = BR.viewModel
33 |
34 | var currentIndex = 0
35 | val tabs: ArrayList = ArrayList()
36 |
37 | override fun setupViews(savedInstanceState: Bundle?) {
38 | super.setupViews(savedInstanceState)
39 | val titles = listOf(
40 | GlobalUtil.getString(R.string.chapter),
41 | GlobalUtil.getString(R.string.system),
42 | GlobalUtil.getString(R.string.navigation)
43 | )
44 | tabs.addAll(listOf(tv_item_wx_chapters, tv_item_system, tv_item_guide))
45 | val fragments = ArrayList()
46 | fragments.add(WXArticleChaptersFragment())
47 | fragments.add(ArchitectureFragment())
48 | fragments.add(NavigationFragment())
49 | setItemSelected(tabs[currentIndex])
50 | view_pager.adapter = CommonFragmentStatePagerAdapter(childFragmentManager, titles, fragments)
51 | view_pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
52 | override fun onPageScrollStateChanged(state: Int) {
53 | }
54 |
55 | override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
56 | }
57 |
58 | override fun onPageSelected(position: Int) {
59 | logDebug("select $position")
60 | setItemUnSelected(tabs[currentIndex])
61 | setItemSelected(tabs[position])
62 | currentIndex = position
63 | }
64 | })
65 | setItemClickEvent()
66 | }
67 |
68 | private fun setItemClickEvent() {
69 | tv_item_wx_chapters.setOnClickListener {
70 | view_pager.currentItem = 0
71 | }
72 | tv_item_system.setOnClickListener {
73 | view_pager.currentItem = 1
74 | }
75 | tv_item_guide.setOnClickListener {
76 | view_pager.currentItem = 2
77 | }
78 | }
79 |
80 | fun setItemSelected(textView: TextView) {
81 | textView.setTextColor(GlobalUtil.getColor(R.color.white))
82 | }
83 |
84 | fun setItemUnSelected(textView: TextView) {
85 | textView.setTextColor(GlobalUtil.getColor(R.color.half_white))
86 | }
87 |
88 | fun setCurrentPage(index:Int){
89 | view_pager.currentItem = index
90 | }
91 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/FindViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui
2 |
3 | import me.luowl.wan.base.BaseViewModel
4 |
5 | /*
6 | *
7 | * Created by luowl
8 | * Date: 2019/8/3
9 | * Desc:
10 | */
11 |
12 | class FindViewModel :BaseViewModel(){
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/MainViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui
2 |
3 | import androidx.lifecycle.ViewModel
4 |
5 | /**
6 | *
7 | * Created by luowl
8 | * Date: 2019/7/23
9 | * Desc:
10 | */
11 | class MainViewModel : ViewModel() {
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/account/CoinListActivity.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.account
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import androidx.lifecycle.Observer
7 | import androidx.recyclerview.widget.LinearLayoutManager
8 | import me.luowl.wan.BR
9 | import me.luowl.wan.R
10 | import me.luowl.wan.base.BaseActivity
11 | import me.luowl.wan.base.adapter.LoadMoreState
12 | import me.luowl.wan.base.adapter.RVAdapter
13 | import me.luowl.wan.data.model.CoinData
14 | import me.luowl.wan.databinding.ActivityCoinListBinding
15 | import me.luowl.wan.util.logDebug
16 | import me.luowl.wan.widget.SimpleDividerItemDecoration
17 |
18 | /*
19 | *
20 | * Created by luowl
21 | * Date: 2019/8/31
22 | * Desc:
23 | */
24 |
25 | class CoinListActivity : BaseActivity() {
26 |
27 | private lateinit var adapter: RVAdapter
28 |
29 | override fun getViewModelClass(): Class = CoinViewModel::class.java
30 |
31 | override fun getLayoutId(): Int = R.layout.activity_coin_list
32 |
33 | override fun initVariableId(): Int = BR.viewModel
34 |
35 | override fun setupViews(savedInstanceState: Bundle?) {
36 | super.setupViews(savedInstanceState)
37 | setupToolbar()
38 | binding.recyclerView.layoutManager = LinearLayoutManager(this)
39 | binding.recyclerView.addItemDecoration(SimpleDividerItemDecoration(this, 1f))
40 | adapter =
41 | object : RVAdapter(mutableListOf(), R.layout.recycler_item_coin, BR.data) {
42 | }
43 | adapter.setRequestLoadMoreListener(object : RVAdapter.LoadMoreListener {
44 | override fun loadMore() {
45 | viewModel.getData()
46 | }
47 | }, binding.recyclerView)
48 | binding.recyclerView.adapter = adapter
49 | }
50 |
51 | override fun initViewObservable() {
52 | super.initViewObservable()
53 | viewModel.dataList.observe(this, Observer {
54 | logDebug("dataChange")
55 | adapter.setData(viewModel.dataList.value)
56 | })
57 | viewModel.requestLoadDataState.observe(this, Observer {
58 | when (it) {
59 | LoadMoreState.STATE_LOAD_FAIL -> adapter.loadMoreFail()
60 | LoadMoreState.STATE_LOAD_END -> adapter.loadMoreEnd()
61 | LoadMoreState.STATE_LOAD_NONE -> adapter.loadMoreComplete()
62 | }
63 | })
64 | }
65 |
66 | override fun startLoadData() {
67 | super.startLoadData()
68 | viewModel.dataList.value?.run {
69 | if (isEmpty()) {
70 | viewModel.getData()
71 | }
72 | }
73 | }
74 |
75 | companion object {
76 | fun startActivity(context: Context) {
77 | context.startActivity(Intent(context, CoinListActivity::class.java))
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/account/CoinViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.account
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import me.luowl.wan.base.BaseViewModel
5 | import me.luowl.wan.base.adapter.LoadMoreState
6 | import me.luowl.wan.data.WanRepository
7 | import me.luowl.wan.data.model.*
8 | import java.util.ArrayList
9 |
10 | /*
11 | *
12 | * Created by luowl
13 | * Date: 2019/8/31
14 | * Desc:
15 | */
16 |
17 | class CoinViewModel constructor(val repository: WanRepository) :BaseViewModel(){
18 | private val _items = MutableLiveData>().apply { value = mutableListOf() }
19 | val dataList: MutableLiveData> = _items
20 | var pageIndex = 0
21 | var requestLoadDataState = MutableLiveData()
22 |
23 | override fun retry() {
24 | super.retry()
25 | pageIndex = 0
26 | getData()
27 | }
28 |
29 | fun getData() {
30 | launch({
31 | if (pageIndex == 0)
32 | startLoading()
33 | val resp = repository.getCoinList(pageIndex)
34 | checkResponseCode(resp)
35 | val pageData = resp.data ?: throw Throwable("page data is null")
36 | val pageItems = ArrayList(pageData.datas ?: listOf())
37 | if (pageIndex == 0 || _items.value == null) {
38 | _items.value = pageItems
39 | } else {
40 | _items.value?.addAll(pageItems)
41 | }
42 | if (pageIndex == 0 && pageItems.size == 0) {
43 | loadDataEmpty()
44 | } else {
45 | loadDataFinish()
46 | }
47 | if (pageIndex < pageData.pageCount - 1) {
48 | pageIndex++
49 | requestLoadDataState.value = LoadMoreState.STATE_LOAD_NONE
50 | } else {
51 | requestLoadDataState.value = LoadMoreState.STATE_LOAD_END
52 | }
53 | }, {
54 | if (pageIndex == 0) {
55 | loadDataError()
56 | } else {
57 | requestLoadDataState.value = LoadMoreState.STATE_LOAD_FAIL
58 | loadDataFinish()
59 | }
60 | })
61 | }
62 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/account/CollectionViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.account
2 |
3 | import me.luowl.wan.base.ArticlePageListViewModel
4 | import me.luowl.wan.data.WanRepository
5 | import me.luowl.wan.data.model.BaseResp
6 | import me.luowl.wan.data.model.PageData
7 |
8 | /*
9 | *
10 | * Created by luowl
11 | * Date: 2019/8/6
12 | * Desc:
13 | */
14 |
15 | class CollectionViewModel constructor(repository: WanRepository) : ArticlePageListViewModel(repository) {
16 |
17 | override suspend fun request(): BaseResp = repository.getCollectionList(pageIndex)
18 |
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/account/LoginActivity.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.account
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import androidx.lifecycle.Observer
7 | import com.jeremyliao.liveeventbus.LiveEventBus
8 | import me.luowl.wan.AppConfig
9 | import me.luowl.wan.R
10 | import me.luowl.wan.BR
11 | import me.luowl.wan.base.BaseActivity
12 | import me.luowl.wan.databinding.ActivityLoginBinding
13 | import me.luowl.wan.event.LoginEvent
14 |
15 | /*
16 | *
17 | * Created by luowl
18 | * Date: 2019/8/5
19 | * Desc:
20 | */
21 |
22 | class LoginActivity : BaseActivity() {
23 | override fun getViewModelClass(): Class = LoginViewModel::class.java
24 |
25 | override fun getLayoutId() = R.layout.activity_login
26 |
27 | override fun initVariableId() = BR.viewModel
28 |
29 | override fun setupViews(savedInstanceState: Bundle?) {
30 | super.setupViews(savedInstanceState)
31 | setupToolbar()
32 | binding.registerBtn.setOnClickListener {
33 | RegisterActivity.startActivity(this@LoginActivity)
34 | }
35 | }
36 |
37 | override fun initViewObservable() {
38 | super.initViewObservable()
39 | LiveEventBus.get().with(AppConfig.LOGIN_KEY, LoginEvent::class.java)
40 | .observe(this, Observer {
41 | finish()
42 | })
43 | }
44 |
45 | companion object {
46 |
47 | fun startActivity(context: Context) {
48 | val intent = Intent(context, LoginActivity::class.java)
49 | context.startActivity(intent)
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/account/LoginViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.account
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import com.jeremyliao.liveeventbus.LiveEventBus
5 | import me.luowl.wan.AppConfig
6 | import me.luowl.wan.base.BaseViewModel
7 | import me.luowl.wan.data.WanRepository
8 | import me.luowl.wan.event.LoginEvent
9 | import me.luowl.wan.util.GlobalUtil
10 | import me.luowl.wan.util.logDebug
11 |
12 | /*
13 | *
14 | * Created by luowl
15 | * Date: 2019/8/5
16 | * Desc:
17 | */
18 |
19 | class LoginViewModel constructor(private val repository: WanRepository) : BaseViewModel() {
20 |
21 | val username: MutableLiveData = MutableLiveData()
22 | val password: MutableLiveData = MutableLiveData()
23 |
24 | fun login() {
25 | logDebug("username=${username.value} password=${password.value}")
26 | val usernameValue = username.value
27 | val pwdValue = password.value
28 | if (usernameValue.isNullOrEmpty()) {
29 | GlobalUtil.showToastShort("请输入用户名")
30 | return
31 | }
32 | if (pwdValue.isNullOrEmpty()) {
33 | GlobalUtil.showToastShort("请输入密码")
34 | return
35 | }
36 | launch({
37 | val resp = repository.login(usernameValue, pwdValue)
38 | when (resp.errorCode) {
39 | 0 -> {
40 | showDialog("登录成功")
41 | AppConfig.saveLoginInfo(resp.data!!)
42 | postLoginSuccessEvent()
43 | // onBackPressed()
44 | }
45 | else -> {
46 | showDialog("登录失败【${resp.errorMsg}】")
47 | }
48 | }
49 | }, {})
50 | }
51 |
52 | private fun postLoginSuccessEvent(){
53 | LiveEventBus.get().with(AppConfig.LOGIN_KEY).post(LoginEvent(true))
54 | }
55 |
56 |
57 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/account/MineFragment.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.account
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AlertDialog
5 | import androidx.lifecycle.Observer
6 | import com.jeremyliao.liveeventbus.LiveEventBus
7 | import me.luowl.wan.AppConfig
8 | import me.luowl.wan.BR
9 | import me.luowl.wan.R
10 | import me.luowl.wan.base.BaseFragment
11 | import me.luowl.wan.databinding.FragmentMineBinding
12 | import me.luowl.wan.event.LoginEvent
13 | import me.luowl.wan.ui.webview.WebViewActivity
14 | import me.luowl.wan.util.GlobalUtil
15 |
16 | /*
17 | *
18 | * Created by luowl
19 | * Date: 2019/8/4
20 | * Desc:
21 | */
22 |
23 | class MineFragment : BaseFragment() {
24 | override fun getViewModelClass(): Class = MineViewModel::class.java
25 |
26 | override fun getLayoutId() = R.layout.fragment_mine
27 |
28 | override fun initVariableId() = BR.viewModel
29 |
30 | override fun setupViews(savedInstanceState: Bundle?) {
31 | super.setupViews(savedInstanceState)
32 | updateInfo()
33 | binding.collectionView.setOnClickListener {
34 | if (viewModel.isLogin()) {
35 | context?.let { ctx -> CollectionActivity.startActivity(ctx) }
36 | } else {
37 | showLoginDialog()
38 | }
39 | }
40 |
41 | binding.feedbackView.setOnClickListener {
42 | context?.let {
43 | WebViewActivity.startActivity(
44 | it,
45 | GlobalUtil.getString(R.string.text_feedback),
46 | AppConfig.PROJECT_ISSUES_URL
47 | )
48 | }
49 | }
50 |
51 | binding.aboutView.setOnClickListener {
52 | context?.let {
53 | WebViewActivity.startActivity(
54 | it,
55 | GlobalUtil.getString(R.string.text_about),
56 | AppConfig.ABOUT_APP_URL
57 | )
58 | }
59 | }
60 |
61 | binding.coinView.setOnClickListener {
62 | if (viewModel.isLogin()) {
63 | context?.let { ctx -> CoinListActivity.startActivity(ctx) }
64 | } else {
65 | showLoginDialog()
66 | }
67 | }
68 |
69 | binding.logoutBtn.setOnClickListener {
70 | showConfirmLogoutDialog()
71 | }
72 | }
73 |
74 | private fun updateInfo() {
75 | if (AppConfig.user.isNotEmpty()) {
76 | binding.usernameTv.text = AppConfig.user
77 | binding.userImg.setOnClickListener {
78 | }
79 | viewModel.getMyCoin()
80 | } else {
81 | binding.usernameTv.text = "未登录"
82 | binding.userImg.setOnClickListener {
83 | context?.let {
84 | LoginActivity.startActivity(it)
85 | }
86 | }
87 | }
88 | }
89 |
90 | override fun initLoginChangeObservable() {
91 | LiveEventBus.get().with(AppConfig.LOGIN_KEY, LoginEvent::class.java)
92 | .observe(this, Observer {
93 | updateInfo()
94 | refreshLayout()
95 | })
96 | }
97 |
98 | private fun showConfirmLogoutDialog() {
99 | context?.let {
100 | val dialog = AlertDialog.Builder(it).setTitle(GlobalUtil.getString(R.string.logout_tip)).setPositiveButton(
101 | GlobalUtil.getString(R.string.logout_yes)
102 | ) { dialog, _ ->
103 | dialog.dismiss()
104 | viewModel.logout()
105 | }.setNegativeButton(GlobalUtil.getString(R.string.logout_no)) { dialog, _ ->
106 | dialog.dismiss()
107 | }
108 | dialog.show()
109 | }
110 | }
111 |
112 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/account/MineViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.account
2 |
3 | import com.jeremyliao.liveeventbus.LiveEventBus
4 | import me.luowl.wan.AppConfig
5 | import me.luowl.wan.base.BaseViewModel
6 | import me.luowl.wan.base.SingleLiveEvent
7 | import me.luowl.wan.data.WanRepository
8 | import me.luowl.wan.event.LoginEvent
9 |
10 | /*
11 | *
12 | * Created by luowl
13 | * Date: 2019/8/4
14 | * Desc:
15 | */
16 |
17 | class MineViewModel constructor(private val repository: WanRepository) : BaseViewModel() {
18 |
19 | val myCoinCount = SingleLiveEvent().apply { value = "我的积分" }
20 | val showMyCoin = SingleLiveEvent().apply { value = false }
21 |
22 | fun getMyCoin() {
23 | launch({
24 | val resp = repository.getMyCoinCount()
25 | checkResponseCode(resp)
26 | myCoinCount.value="我的积分:${resp.data}"
27 | showMyCoin.setValue(true)
28 | }, {
29 | showMyCoin.setValue(false)
30 | })
31 | }
32 |
33 | fun isLogin(): Boolean {
34 | return AppConfig.isLogin()
35 | }
36 |
37 | fun logout() {
38 | launch({
39 | repository.logout()
40 | clearData()
41 | }, {
42 | clearData()
43 | })
44 | }
45 |
46 | private fun clearData() {
47 | showMyCoin.value = false
48 | AppConfig.clearLoginInfo()
49 | LiveEventBus.get().with(AppConfig.LOGIN_KEY).post(LoginEvent(false))
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/account/RegisterActivity.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.account
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import me.luowl.wan.BR
7 | import me.luowl.wan.R
8 | import me.luowl.wan.base.BaseActivity
9 | import me.luowl.wan.databinding.ActivityRegisterBinding
10 |
11 | /*
12 | *
13 | * Created by luowl
14 | * Date: 2019/8/6
15 | * Desc:
16 | */
17 |
18 | class RegisterActivity : BaseActivity() {
19 | override fun getViewModelClass(): Class = RegisterViewModel::class.java
20 |
21 | override fun getLayoutId(): Int = R.layout.activity_register
22 |
23 | override fun initVariableId(): Int = BR.viewModel
24 |
25 | override fun setupViews(savedInstanceState: Bundle?) {
26 | super.setupViews(savedInstanceState)
27 | setupToolbar()
28 | }
29 |
30 | companion object {
31 |
32 | fun startActivity(context: Context) {
33 | val intent = Intent(context, RegisterActivity::class.java)
34 | context.startActivity(intent)
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/account/RegisterViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.account
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import com.jeremyliao.liveeventbus.LiveEventBus
5 | import me.luowl.wan.AppConfig
6 | import me.luowl.wan.base.BaseViewModel
7 | import me.luowl.wan.data.WanRepository
8 | import me.luowl.wan.event.LoginEvent
9 | import me.luowl.wan.util.GlobalUtil
10 | import me.luowl.wan.util.logDebug
11 | import java.util.regex.Pattern
12 |
13 | /*
14 | *
15 | * Created by luowl
16 | * Date: 2019/8/6
17 | * Desc:
18 | */
19 |
20 | class RegisterViewModel constructor(private val repository: WanRepository) : BaseViewModel() {
21 |
22 | val username: MutableLiveData = MutableLiveData()
23 | val password: MutableLiveData = MutableLiveData()
24 | val repassword: MutableLiveData = MutableLiveData()
25 |
26 |
27 | fun register() {
28 | // logDebug("username=${username.value} password=${password.value}")
29 | val usernameValue = username.value
30 | val pwdValue = password.value
31 | val rePwdValue = repassword.value
32 | if (usernameValue.isNullOrEmpty()) {
33 | GlobalUtil.showToastShort("请输入用户名")
34 | return
35 | }
36 | if (usernameValue.length < 6) {
37 | GlobalUtil.showToastShort("用户名最少6位")
38 | return
39 | }
40 | if (pwdValue.isNullOrEmpty()) {
41 | GlobalUtil.showToastShort("请输入密码")
42 | return
43 | }
44 | if (!regexPwd(pwdValue)) {
45 | GlobalUtil.showToastShort("密码6~50位且为数字、字母、-、_")
46 | return
47 | }
48 | if (rePwdValue.isNullOrEmpty()) {
49 | GlobalUtil.showToastShort("请输入确认密码")
50 | return
51 | }
52 | if (!pwdValue.equals(rePwdValue, false)) {
53 | GlobalUtil.showToastShort("确认密码和密码不符")
54 | return
55 | }
56 | launch({
57 | val resp = repository.register(usernameValue, pwdValue, rePwdValue)
58 | when (resp.errorCode) {
59 | 0 -> {
60 | showDialog("注册成功")
61 | AppConfig.saveLoginInfo(resp.data!!)
62 | postLoginSuccessEvent()
63 | finish()
64 | }
65 | else -> {
66 | showDialog("注册失败【${resp.errorMsg}】")
67 | }
68 | }
69 | }, {
70 |
71 | })
72 | }
73 |
74 | private fun postLoginSuccessEvent() {
75 | LiveEventBus.get().with(AppConfig.LOGIN_KEY).post(LoginEvent(true))
76 | }
77 |
78 | private fun regexPwd(pwd: String): Boolean {
79 | //密码6~50位且为数字、字母、-、_
80 | val regex = "^[A-Za-z0-9_-]{6,50}\$"
81 | return Pattern.matches(regex, pwd)
82 | }
83 |
84 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/architecture/ArchitectureArticleListViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.architecture
2 |
3 | import me.luowl.wan.base.ArticlePageListViewModel
4 | import me.luowl.wan.data.WanRepository
5 | import me.luowl.wan.data.model.BaseResp
6 | import me.luowl.wan.data.model.PageData
7 |
8 | /*
9 | *
10 | * Created by luowl
11 | * Date: 2019/7/29
12 | * Desc:
13 | */
14 |
15 | class ArchitectureArticleListViewModel constructor(repository: WanRepository) : ArticlePageListViewModel(repository) {
16 |
17 | var treeId: Long = 0L
18 |
19 | override suspend fun request(): BaseResp = repository.getArticleListByTreeId(pageIndex, treeId)
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/architecture/ArchitectureCategoryActivity.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.architecture
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import androidx.fragment.app.Fragment
7 | import kotlinx.android.synthetic.main.activity_architecture_category.*
8 | import me.luowl.wan.BR
9 | import me.luowl.wan.R
10 | import me.luowl.wan.base.BaseActivity
11 | import me.luowl.wan.base.adapter.CommonFragmentStatePagerAdapter
12 | import me.luowl.wan.data.model.Architecture
13 | import me.luowl.wan.databinding.ActivityArchitectureCategoryBinding
14 |
15 | /*
16 | *
17 | * Created by luowl
18 | * Date: 2019/7/29
19 | * Desc:
20 | */
21 |
22 | class ArchitectureCategoryActivity :
23 | BaseActivity() {
24 |
25 | override fun getViewModelClass(): Class = ArchitectureCategoryViewModel::class.java
26 |
27 | override fun getLayoutId() = R.layout.activity_architecture_category
28 |
29 | override fun initVariableId() = BR.viewModel
30 |
31 | override fun setupViews(savedInstanceState: Bundle?) {
32 | super.setupViews(savedInstanceState)
33 | setupToolbar()
34 | val data = intent.getBundleExtra("data")
35 | data?.let {
36 | setUpTabLayout(data.getSerializable("architecture") as Architecture)
37 | }
38 | }
39 |
40 | private fun setUpTabLayout(data: Architecture) {
41 | title = data.name
42 | val fragments = ArrayList()
43 | val titles = ArrayList()
44 | for (item in data.children!!) {
45 | titles.add(item.name)
46 | fragments.add(ArchitectureArticleListFragment.newInstance(item.id))
47 | }
48 | view_pager.adapter = CommonFragmentStatePagerAdapter(supportFragmentManager, titles, fragments)
49 | tab_layout.setupWithViewPager(view_pager)
50 | }
51 |
52 | companion object {
53 | fun startActivity(context: Context, data: Architecture) {
54 | val intent = Intent(context, ArchitectureCategoryActivity::class.java).apply {
55 | val bundle = Bundle()
56 | bundle.putSerializable("architecture", data)
57 | putExtra("data", bundle)
58 | }
59 | context.startActivity(intent)
60 | }
61 | }
62 |
63 |
64 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/architecture/ArchitectureCategoryViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.architecture
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import me.luowl.wan.base.BaseViewModel
5 | import me.luowl.wan.data.model.Architecture
6 |
7 | /*
8 | *
9 | * Created by luowl
10 | * Date: 2019/7/29
11 | * Desc:
12 | */
13 |
14 | class ArchitectureCategoryViewModel :BaseViewModel(){
15 |
16 | var architecture: MutableLiveData? = null
17 |
18 |
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/architecture/ArchitectureFragment.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.architecture
2 |
3 | import android.os.Bundle
4 | import androidx.databinding.ViewDataBinding
5 | import androidx.lifecycle.Observer
6 | import androidx.recyclerview.widget.LinearLayoutManager
7 | import kotlinx.android.synthetic.main.fragment_architecture.*
8 | import me.luowl.wan.BR
9 | import me.luowl.wan.R
10 | import me.luowl.wan.base.BaseFragment
11 | import me.luowl.wan.base.adapter.RVAdapter
12 | import me.luowl.wan.data.model.Architecture
13 | import me.luowl.wan.databinding.FragmentArchitectureBinding
14 | import me.luowl.wan.widget.SimpleDividerItemDecoration
15 |
16 | /*
17 | *
18 | * Created by luowl
19 | * Date: 2019/7/27
20 | * Desc:
21 | */
22 |
23 | class ArchitectureFragment : BaseFragment() {
24 |
25 |
26 | private lateinit var adapter: RVAdapter
27 |
28 | override fun getViewModelClass(): Class = ArchitectureViewModel::class.java
29 |
30 | override fun getLayoutId() = R.layout.fragment_architecture
31 |
32 | override fun initVariableId() = BR.viewModel
33 |
34 | override fun setupViews(savedInstanceState: Bundle?) {
35 | super.setupViews(savedInstanceState)
36 | val layoutManager = LinearLayoutManager(context)
37 | recycler_view.layoutManager = layoutManager
38 | recycler_view.addItemDecoration(SimpleDividerItemDecoration(context, 10f))
39 |
40 | adapter = object :
41 | RVAdapter(mutableListOf(), R.layout.recycler_item_architecture, BR.data) {
42 | override fun addListener(binding: ViewDataBinding, itemData: Architecture, position: Int) {
43 | binding.root.setOnClickListener {
44 | context?.run {
45 | ArchitectureCategoryActivity.startActivity(this, itemData)
46 | }
47 | }
48 | }
49 | }
50 | recycler_view.adapter = adapter
51 | }
52 |
53 | override fun initViewObservable() {
54 | super.initViewObservable()
55 | viewModel.dataList.observe(this, Observer {
56 | adapter.setData(it)
57 | })
58 | }
59 |
60 | override fun startLoadData() {
61 | super.startLoadData()
62 | viewModel.getData()
63 | }
64 |
65 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/architecture/ArchitectureViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.architecture
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import me.luowl.wan.base.BaseViewModel
5 | import me.luowl.wan.data.WanRepository
6 | import me.luowl.wan.data.model.Architecture
7 |
8 | /*
9 | *
10 | * Created by luowl
11 | * Date: 2019/7/27
12 | * Desc:
13 | */
14 |
15 | class ArchitectureViewModel constructor(private val repository: WanRepository) : BaseViewModel() {
16 |
17 |
18 | private val _items = MutableLiveData>().apply { value = mutableListOf() }
19 | val dataList: MutableLiveData> = _items
20 |
21 | var leftTabIndex = MutableLiveData()
22 |
23 | fun getData() {
24 | launch({
25 | stateModel.startLoading()
26 | val resp = repository.getArchitectureTree()
27 | val data = resp.data ?: listOf()
28 | if (data.isNotEmpty()) {
29 | _items.value = ArrayList(data)
30 | leftTabIndex.value = 0
31 | stateModel.loadDataFinish()
32 | } else {
33 | stateModel.showEmpty()
34 | }
35 | }, {
36 | stateModel.loadDataError()
37 | })
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/navigation/NavigationViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.navigation
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import me.luowl.wan.base.BaseViewModel
5 | import me.luowl.wan.data.WanRepository
6 | import me.luowl.wan.data.model.Navigation
7 |
8 | /*
9 | *
10 | * Created by luowl
11 | * Date: 2019/7/27
12 | * Desc:
13 | */
14 |
15 | class NavigationViewModel constructor(private val repository: WanRepository) : BaseViewModel() {
16 |
17 | private val _items = MutableLiveData>().apply { value = listOf() }
18 | val dataList: MutableLiveData> = _items
19 | val leftTabIndex = MutableLiveData()
20 |
21 | fun getData() {
22 | launch({
23 | stateModel.startLoading()
24 | val resp = repository.getNavigationData()
25 | val data = resp.data ?: listOf()
26 | if (data.isNotEmpty()) {
27 | _items.value = data
28 | leftTabIndex.value = 0
29 | stateModel.loadDataFinish()
30 | } else {
31 | stateModel.showEmpty()
32 | }
33 | }, {
34 | stateModel.loadDataError()
35 | })
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/project/ProjectFragment.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.project
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.lifecycle.Observer
5 | import kotlinx.android.synthetic.main.fragment_project.*
6 | import me.luowl.wan.BR
7 | import me.luowl.wan.R
8 | import me.luowl.wan.base.BaseFragment
9 | import me.luowl.wan.base.adapter.CommonFragmentStatePagerAdapter
10 | import me.luowl.wan.data.model.Architecture
11 | import me.luowl.wan.databinding.FragmentProjectBinding
12 |
13 | /*
14 | *
15 | * Created by luowl
16 | * Date: 2019/7/25
17 | * Desc:
18 | */
19 |
20 | class ProjectFragment : BaseFragment() {
21 |
22 | override fun getViewModelClass(): Class = ProjectViewModel::class.java
23 |
24 | override fun getLayoutId() = R.layout.fragment_project
25 |
26 | override fun initVariableId() = BR.viewModel
27 |
28 | override fun initViewObservable() {
29 | super.initViewObservable()
30 | viewModel.dataList.observe(this, Observer {
31 | setUpTabLayout(it)
32 | })
33 | }
34 |
35 | override fun startLoadData() {
36 | super.startLoadData()
37 | viewModel.dataList.value?.run {
38 | if (isEmpty()) {
39 | viewModel.getData()
40 | }
41 | }
42 | }
43 |
44 | private fun setUpTabLayout(items: List) {
45 | val titles = ArrayList()
46 | val fragments = ArrayList()
47 | for (item in items) {
48 | titles.add(item.name)
49 | fragments.add(ProjectListFragment.newInstance(item.id))
50 | }
51 | view_pager.adapter =
52 | CommonFragmentStatePagerAdapter(childFragmentManager, titles, fragments)
53 | tab_layout.setupWithViewPager(view_pager)
54 | }
55 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/project/ProjectListFragment.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.project
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.databinding.ViewDataBinding
6 | import androidx.lifecycle.Observer
7 | import androidx.recyclerview.widget.LinearLayoutManager
8 | import kotlinx.android.synthetic.main.fragment_project_list.*
9 | import me.luowl.wan.BR
10 | import me.luowl.wan.R
11 | import me.luowl.wan.base.BaseFragment
12 | import me.luowl.wan.base.adapter.LoadMoreState
13 | import me.luowl.wan.base.adapter.RVAdapter
14 | import me.luowl.wan.data.model.ArticleData
15 | import me.luowl.wan.databinding.FragmentProjectListBinding
16 | import me.luowl.wan.ui.webview.WebViewActivity
17 | import me.luowl.wan.util.logDebug
18 | import me.luowl.wan.widget.SimpleDividerItemDecoration
19 |
20 | /*
21 | *
22 | * Created by luowl
23 | * Date: 2019/7/25
24 | * Desc:
25 | */
26 |
27 | class ProjectListFragment : BaseFragment() {
28 |
29 | private lateinit var adapter: RVAdapter
30 |
31 | override fun getViewModelClass(): Class = ProjectListViewModel::class.java
32 |
33 | override fun getLayoutId() = R.layout.fragment_project_list
34 |
35 | override fun initVariableId() = BR.viewModel
36 |
37 | override fun initParams() {
38 | super.initParams()
39 | arguments?.let {
40 | viewModel.treeId = it.getLong(EXTRA_PROJECT_TREE_ID)
41 | }
42 | }
43 |
44 | override fun setupViews(savedInstanceState: Bundle?) {
45 | super.setupViews(savedInstanceState)
46 | swipe_refresh_layout.setColorSchemeColors(resources.getColor(R.color.appThemeColor))
47 | recycler_view.layoutManager = LinearLayoutManager(context)
48 | recycler_view.addItemDecoration(SimpleDividerItemDecoration(context, 10f))
49 | adapter = object : RVAdapter(mutableListOf(), R.layout.recycler_item_project, BR.article) {
50 | override fun addListener(binding: ViewDataBinding, itemData: ArticleData, position: Int) {
51 | binding.root.setOnClickListener {
52 | context?.let {
53 | WebViewActivity.startActivity(it, itemData.title, itemData.link)
54 | }
55 | }
56 | }
57 | }
58 | adapter.setRequestLoadMoreListener(object : RVAdapter.LoadMoreListener {
59 | override fun loadMore() {
60 | viewModel.getData()
61 | }
62 | }, recycler_view)
63 | recycler_view.adapter = adapter
64 | }
65 |
66 | override fun initViewObservable() {
67 | super.initViewObservable()
68 | viewModel.dataList.observe(this, Observer {
69 | logDebug("dataChange")
70 | adapter.setData(viewModel.dataList.value)
71 | })
72 | viewModel.requestLoadDataState.observe(this, Observer {
73 | when (it) {
74 | LoadMoreState.STATE_LOAD_FAIL -> adapter.loadMoreFail()
75 | LoadMoreState.STATE_LOAD_END -> adapter.loadMoreEnd()
76 | LoadMoreState.STATE_LOAD_NONE -> adapter.loadMoreComplete()
77 | }
78 | })
79 | }
80 |
81 | override fun startLoadData() {
82 | super.startLoadData()
83 | viewModel.dataList.value?.run {
84 | if (isEmpty()) {
85 | viewModel.getData()
86 | }
87 | }
88 | }
89 |
90 | companion object {
91 | const val EXTRA_PROJECT_TREE_ID = "treeId"
92 | fun newInstance(treeId: Long): ProjectListFragment {
93 | val fragment = ProjectListFragment()
94 | val data = Bundle()
95 | data.putLong(EXTRA_PROJECT_TREE_ID, treeId)
96 | fragment.arguments = data
97 | return fragment
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/project/ProjectListViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.project
2 |
3 | import me.luowl.wan.base.ArticlePageListViewModel
4 | import me.luowl.wan.data.WanRepository
5 | import me.luowl.wan.data.model.BaseResp
6 | import me.luowl.wan.data.model.PageData
7 |
8 | /*
9 | *
10 | * Created by luowl
11 | * Date: 2019/7/25
12 | * Desc:
13 | */
14 |
15 | class ProjectListViewModel(repository: WanRepository) : ArticlePageListViewModel(repository) {
16 |
17 | var treeId: Long = 0L
18 |
19 | override suspend fun request(): BaseResp {
20 | return if (treeId == -1L) repository.getNewestProjectList(pageIndex) else repository.getProjectList(
21 | pageIndex,
22 | treeId
23 | )
24 | }
25 |
26 | // fun getData() {
27 | // launch({
28 | // if (pageIndex == 0)
29 | // stateModel.startLoading()
30 | // val resp = if (treeId == -1L) repository.getNewestProjectList(pageIndex) else repository.getProjectList(
31 | // pageIndex,
32 | // treeId
33 | // )
34 | // when (resp.errorCode) {
35 | // 0 -> {
36 | // val pageData = resp.data ?: throw Throwable("page data is null")
37 | // val pageItems = ArrayList(pageData.datas ?: listOf())
38 | // if (_items.value == null) {
39 | // _items.value = pageItems
40 | // } else {
41 | // _items.value?.addAll(pageItems)
42 | // }
43 | // if (pageIndex == 0 && pageItems.size == 0) {
44 | // stateModel.showEmpty()
45 | // } else {
46 | // stateModel.loadDataFinish()
47 | // }
48 | // if (pageIndex < pageData.pageCount - 1) {
49 | // pageIndex++
50 | // requestLoadDataState.value = LoadMoreState.STATE_LOAD_NONE
51 | // } else {
52 | // requestLoadDataState.value = LoadMoreState.STATE_LOAD_END
53 | // }
54 | // }
55 | // else -> {
56 | // if (pageIndex == 0) {
57 | // stateModel.loadDataError()
58 | // } else {
59 | // requestLoadDataState.value = LoadMoreState.STATE_LOAD_FAIL
60 | // stateModel.loadDataFinish()
61 | // }
62 | // }
63 | // }
64 | // }, {
65 | // if (pageIndex == 0) {
66 | // stateModel.loadDataError()
67 | // } else {
68 | // requestLoadDataState.value = LoadMoreState.STATE_LOAD_FAIL
69 | // stateModel.loadDataFinish()
70 | // }
71 | // })
72 | // }
73 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/project/ProjectViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.project
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import me.luowl.wan.base.BaseViewModel
5 | import me.luowl.wan.data.WanRepository
6 | import me.luowl.wan.data.model.Architecture
7 |
8 | /*
9 | *
10 | * Created by luowl
11 | * Date: 2019/7/25
12 | * Desc:
13 | */
14 |
15 | class ProjectViewModel constructor(private val repository: WanRepository) : BaseViewModel() {
16 |
17 | private val _items = MutableLiveData>().apply { value = mutableListOf() }
18 | val dataList: MutableLiveData> = _items
19 |
20 | fun getData() {
21 | launch({
22 | stateModel.startLoading()
23 | val resp = repository.getProjectClassification()
24 | val tempItems = ArrayList()
25 | val newProjectItem = Architecture()
26 | newProjectItem.id = -1
27 | newProjectItem.name = "最新"
28 | tempItems.add(newProjectItem)
29 | resp.data?.let {
30 | if (it.isNotEmpty()) {
31 | tempItems.addAll(it)
32 | }
33 | }
34 | _items.value = tempItems
35 | stateModel.loadDataFinish()
36 | }, {
37 | stateModel.loadDataError()
38 | })
39 | }
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/search/SearchViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.search
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import me.luowl.wan.base.ArticlePageListViewModel
6 | import me.luowl.wan.data.WanRepository
7 | import me.luowl.wan.data.local.SearchRecordDataSource
8 | import me.luowl.wan.data.model.BaseResp
9 | import me.luowl.wan.data.model.PageData
10 | import me.luowl.wan.data.model.SearchRecord
11 |
12 | /*
13 | *
14 | * Created by luowl
15 | * Date: 2019/7/29
16 | * Desc:
17 | */
18 |
19 | class SearchViewModel constructor(
20 | repository: WanRepository,
21 | private val localDataSource: SearchRecordDataSource
22 | ) : ArticlePageListViewModel(repository) {
23 |
24 | private val _records = MutableLiveData>().apply { value = mutableListOf() }
25 | val records: LiveData> = _records
26 |
27 | var keyword: String? = null
28 |
29 | fun queryRecord() {
30 | launch {
31 | _records.value = localDataSource.getRecords()
32 | }
33 | }
34 |
35 | fun deleteRecord(keyword: String) {
36 | launch {
37 | localDataSource.deleteRecords(keyword)
38 | queryRecord()
39 | }
40 | }
41 |
42 | private fun insertRecord(keyword: String) {
43 | launch({
44 | val oldRecords = localDataSource.findRecord(keyword)
45 | if (oldRecords.isNotEmpty()) {
46 | localDataSource.deleteRecords(keyword)
47 | }
48 | val newRecord = SearchRecord(keyword)
49 | localDataSource.insertRecord(newRecord)
50 | }, {})
51 | }
52 |
53 | fun doSearch(key: String) {
54 | insertRecord(key)
55 | keyword = key
56 | retry()
57 | }
58 |
59 | override suspend fun request(): BaseResp = repository.searchArticleByKey(pageIndex, keyword!!)
60 |
61 | fun searchArticleByKey() {
62 | getData()
63 | }
64 |
65 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/setting/AboutFragment.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.setting
2 |
3 | import androidx.fragment.app.Fragment
4 |
5 | /*
6 | *
7 | * Created by luowl
8 | * Date: 2019/8/26
9 | * Desc:
10 | */
11 |
12 | class AboutFragment :Fragment(){
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/webview/SonicJavaScriptInterface.java:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.webview;
2 |
3 | import android.content.Intent;
4 | import android.os.Handler;
5 | import android.os.Looper;
6 | import android.webkit.JavascriptInterface;
7 | import com.tencent.sonic.sdk.SonicDiffDataCallback;
8 | import org.json.JSONObject;
9 |
10 | /**
11 | * Sonic javaScript Interface (Android API Level >= 17)
12 | */
13 |
14 | public class SonicJavaScriptInterface {
15 |
16 | private final SonicSessionClientImpl sessionClient;
17 |
18 | private final Intent intent;
19 |
20 | public static final String PARAM_CLICK_TIME = "clickTime";
21 |
22 | public static final String PARAM_LOAD_URL_TIME = "loadUrlTime";
23 |
24 | public SonicJavaScriptInterface(SonicSessionClientImpl sessionClient, Intent intent) {
25 | this.sessionClient = sessionClient;
26 | this.intent = intent;
27 | }
28 |
29 | @JavascriptInterface
30 | public void getDiffData() {
31 | // the callback function of demo page is hardcode as 'getDiffDataCallback'
32 | getDiffData2("getDiffDataCallback");
33 | }
34 |
35 | @JavascriptInterface
36 | public void getDiffData2(final String jsCallbackFunc) {
37 | if (null != sessionClient) {
38 | sessionClient.getDiffData(new SonicDiffDataCallback() {
39 | @Override
40 | public void callback(final String resultData) {
41 | Runnable callbackRunnable = new Runnable() {
42 | @Override
43 | public void run() {
44 | String jsCode = "javascript:" + jsCallbackFunc + "('"+ toJsString(resultData) + "')";
45 | sessionClient.getWebView().loadUrl(jsCode);
46 | }
47 | };
48 | if (Looper.getMainLooper() == Looper.myLooper()) {
49 | callbackRunnable.run();
50 | } else {
51 | new Handler(Looper.getMainLooper()).post(callbackRunnable);
52 | }
53 | }
54 | });
55 | }
56 | }
57 |
58 | @JavascriptInterface
59 | public String getPerformance() {
60 | long clickTime = intent.getLongExtra(PARAM_CLICK_TIME, -1);
61 | long loadUrlTime = intent.getLongExtra(PARAM_LOAD_URL_TIME, -1);
62 | try {
63 | JSONObject result = new JSONObject();
64 | result.put(PARAM_CLICK_TIME, clickTime);
65 | result.put(PARAM_LOAD_URL_TIME, loadUrlTime);
66 | return result.toString();
67 | } catch (Exception e) {
68 |
69 | }
70 |
71 | return "";
72 | }
73 |
74 | /*
75 | * * From RFC 4627, "All Unicode characters may be placed within the quotation marks except
76 | * for the characters that must be escaped: quotation mark,
77 | * reverse solidus, and the control characters (U+0000 through U+001F)."
78 | */
79 | private static String toJsString(String value) {
80 | if (value == null) {
81 | return "null";
82 | }
83 | StringBuilder out = new StringBuilder(1024);
84 | for (int i = 0, length = value.length(); i < length; i++) {
85 | char c = value.charAt(i);
86 |
87 |
88 | switch (c) {
89 | case '"':
90 | case '\\':
91 | case '/':
92 | out.append('\\').append(c);
93 | break;
94 |
95 | case '\t':
96 | out.append("\\t");
97 | break;
98 |
99 | case '\b':
100 | out.append("\\b");
101 | break;
102 |
103 | case '\n':
104 | out.append("\\n");
105 | break;
106 |
107 | case '\r':
108 | out.append("\\r");
109 | break;
110 |
111 | case '\f':
112 | out.append("\\f");
113 | break;
114 |
115 | default:
116 | if (c <= 0x1F) {
117 | out.append(String.format("\\u%04x", (int) c));
118 | } else {
119 | out.append(c);
120 | }
121 | break;
122 | }
123 |
124 | }
125 | return out.toString();
126 | }
127 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/webview/SonicRuntimeImpl.java:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.webview;
2 |
3 | import android.content.Context;
4 | import android.os.Build;
5 | import android.text.TextUtils;
6 | import android.util.Log;
7 | import android.webkit.CookieManager;
8 | import android.webkit.WebResourceResponse;
9 | import com.tencent.sonic.sdk.SonicRuntime;
10 | import com.tencent.sonic.sdk.SonicSessionClient;
11 | import me.luowl.wan.BuildConfig;
12 | import me.luowl.wan.WanApplication;
13 |
14 | import java.io.File;
15 | import java.io.InputStream;
16 | import java.util.List;
17 | import java.util.Map;
18 |
19 | /**
20 | * the sonic host application must implement SonicRuntime to do right things.
21 | */
22 |
23 | public class SonicRuntimeImpl extends SonicRuntime {
24 |
25 | public SonicRuntimeImpl(Context context) {
26 | super(context);
27 | }
28 |
29 | /**
30 | * 获取用户UA信息
31 | *
32 | * @return
33 | */
34 | @Override
35 | public String getUserAgent() {
36 | return "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Mobile Safari/537.36";
37 | }
38 |
39 | /**
40 | * 获取用户ID信息
41 | *
42 | * @return
43 | */
44 | @Override
45 | public String getCurrentUserAccount() {
46 | return "sonic-demo-master";
47 | }
48 |
49 | @Override
50 | public String getCookie(String url) {
51 | CookieManager cookieManager = CookieManager.getInstance();
52 | return cookieManager.getCookie(url);
53 | }
54 |
55 | @Override
56 | public void log(String tag, int level, String message) {
57 | switch (level) {
58 | case Log.ERROR:
59 | Log.e(tag, message);
60 | break;
61 | case Log.INFO:
62 | Log.i(tag, message);
63 | break;
64 | default:
65 | Log.d(tag, message);
66 | }
67 | }
68 |
69 | @Override
70 | public Object createWebResourceResponse(String mimeType, String encoding, InputStream data, Map headers) {
71 | WebResourceResponse resourceResponse = new WebResourceResponse(mimeType, encoding, data);
72 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
73 | resourceResponse.setResponseHeaders(headers);
74 | }
75 | return resourceResponse;
76 | }
77 |
78 | @Override
79 | public void showToast(CharSequence text, int duration) {
80 |
81 | }
82 |
83 | @Override
84 | public void notifyError(SonicSessionClient client, String url, int errorCode) {
85 |
86 | }
87 |
88 | @Override
89 | public boolean isSonicUrl(String url) {
90 | return true;
91 | }
92 |
93 | @Override
94 | public boolean setCookie(String url, List cookies) {
95 | if (!TextUtils.isEmpty(url) && cookies != null && cookies.size() > 0) {
96 | CookieManager cookieManager = CookieManager.getInstance();
97 | for (String cookie : cookies) {
98 | cookieManager.setCookie(url, cookie);
99 | }
100 | return true;
101 | }
102 | return false;
103 | }
104 |
105 | @Override
106 | public boolean isNetworkValid() {
107 | return true;
108 | }
109 |
110 | @Override
111 | public void postTaskToThread(Runnable task, long delayMillis) {
112 | Thread thread = new Thread(task, "SonicThread");
113 | thread.start();
114 | }
115 |
116 | @Override
117 | public File getSonicCacheDir() {
118 | if (BuildConfig.DEBUG) {
119 | String path = WanApplication.context.getExternalCacheDir().getAbsolutePath() + File.separator + "sonic/";
120 | File file = new File(path.trim());
121 | if (!file.exists()) {
122 | file.mkdir();
123 | }
124 | return file;
125 | }
126 | return super.getSonicCacheDir();
127 | }
128 |
129 | @Override
130 | public String getHostDirectAddress(String url) {
131 | return null;
132 | }
133 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/webview/SonicSessionClientImpl.java:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.webview;
2 |
3 | import android.os.Bundle;
4 | import android.webkit.WebView;
5 | import com.tencent.sonic.sdk.SonicSessionClient;
6 |
7 | import java.util.HashMap;
8 |
9 | /**
10 | * a implement of SonicSessionClient which need to connect webview and content data.
11 | */
12 |
13 | public class SonicSessionClientImpl extends SonicSessionClient {
14 |
15 | private WebView webView;
16 |
17 | public void bindWebView(WebView webView) {
18 | this.webView = webView;
19 | }
20 |
21 | public WebView getWebView() {
22 | return webView;
23 | }
24 |
25 | @Override
26 | public void loadUrl(String url, Bundle extraData) {
27 | webView.loadUrl(url);
28 | }
29 |
30 | @Override
31 | public void loadDataWithBaseUrl(String baseUrl, String data, String mimeType, String encoding, String historyUrl) {
32 | webView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
33 | }
34 |
35 |
36 | @Override
37 | public void loadDataWithBaseUrlAndHeader(String baseUrl, String data, String mimeType, String encoding, String historyUrl, HashMap headers) {
38 | loadDataWithBaseUrl(baseUrl, data, mimeType, encoding, historyUrl);
39 | }
40 |
41 | public void destroy() {
42 | if (null != webView) {
43 | webView.destroy();
44 | webView = null;
45 | }
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/webview/WebViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.webview
2 |
3 | import me.luowl.wan.base.BaseViewModel
4 |
5 | /*
6 | *
7 | * Created by luowl
8 | * Date: 2019/8/8
9 | * Desc:
10 | */
11 |
12 | class WebViewModel : BaseViewModel() {
13 |
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/wxarticle/ArticleListViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.wxarticle
2 |
3 | import me.luowl.wan.base.ArticlePageListViewModel
4 | import me.luowl.wan.data.WanRepository
5 | import me.luowl.wan.data.model.BaseResp
6 | import me.luowl.wan.data.model.PageData
7 |
8 | /*
9 | *
10 | * Created by luowl
11 | * Date: 2019/7/25
12 | * Desc:
13 | */
14 |
15 | class ArticleListViewModel(repository: WanRepository) : ArticlePageListViewModel(repository) {
16 |
17 | var chapterId: Long = 0L
18 | var keyword: String? = null
19 |
20 | override suspend fun request(): BaseResp {
21 | return if (hasKeyWord()) {
22 | repository.getWXArticleListByKey(chapterId, pageIndex, keyword!!)
23 | } else {
24 | repository.getWXArticleList(chapterId, pageIndex)
25 | }
26 | }
27 |
28 | private fun hasKeyWord(): Boolean {
29 | return keyword != null && keyword!!.isNotEmpty()
30 | }
31 |
32 | fun reset() {
33 | pageIndex = 0
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/wxarticle/WXArticleChaptersFragment.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.wxarticle
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.databinding.ViewDataBinding
6 | import androidx.lifecycle.Observer
7 | import androidx.recyclerview.widget.GridLayoutManager
8 | import kotlinx.android.synthetic.main.fragment_home.*
9 | import me.luowl.wan.BR
10 | import me.luowl.wan.R
11 | import me.luowl.wan.base.BaseFragment
12 | import me.luowl.wan.base.adapter.RVAdapter
13 | import me.luowl.wan.data.model.Architecture
14 | import me.luowl.wan.databinding.FragmentWxArticleChaptersBinding
15 | import me.luowl.wan.util.GlobalUtil
16 | import me.luowl.wan.util.logDebug
17 | import me.luowl.wan.widget.SimpleDividerItemDecoration
18 |
19 | /*
20 | *
21 | * Created by luowl
22 | * Date: 2019/7/24
23 | * Desc:
24 | */
25 |
26 | class WXArticleChaptersFragment : BaseFragment() {
27 |
28 | private lateinit var adapter: RVAdapter
29 |
30 | override fun getViewModelClass(): Class = WXArticleChaptersViewModel::class.java
31 |
32 | override fun getLayoutId() = R.layout.fragment_wx_article_chapters
33 |
34 | override fun initVariableId() = BR.viewModel
35 |
36 | override fun setupViews(savedInstanceState: Bundle?) {
37 | super.setupViews(savedInstanceState)
38 | recycler_view.layoutManager = GridLayoutManager(context, 2)
39 | recycler_view.addItemDecoration(
40 | SimpleDividerItemDecoration(
41 | context,
42 | 20f,
43 | GlobalUtil.getColor(R.color.white),
44 | 20f, 15f, 15f
45 | )
46 | )
47 | adapter = object : RVAdapter(mutableListOf(), R.layout.recycler_item_wx_chapter, BR.data) {
48 | override fun addListener(binding: ViewDataBinding, itemData: Architecture, position: Int) {
49 | binding.root.setOnClickListener {
50 | context?.let {
51 | ArticleListActivity.startActivity(it, itemData.id, itemData.name)
52 | }
53 | }
54 | }
55 | }
56 | recycler_view.adapter = adapter
57 | }
58 |
59 | override fun initViewObservable() {
60 | super.initViewObservable()
61 | viewModel.dataList.observe(this, Observer {
62 | logDebug("dataChange")
63 | adapter.setData(viewModel.dataList.value)
64 | })
65 | }
66 |
67 | override fun startLoadData() {
68 | super.startLoadData()
69 | viewModel.dataList.value?.run {
70 | if (isEmpty()) {
71 | viewModel.getData()
72 | }
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/ui/wxarticle/WXArticleChaptersViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.ui.wxarticle
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import me.luowl.wan.base.BaseViewModel
5 | import me.luowl.wan.data.WanRepository
6 | import me.luowl.wan.data.model.Architecture
7 |
8 | /*
9 | *
10 | * Created by luowl
11 | * Date: 2019/7/24
12 | * Desc:
13 | */
14 |
15 | class WXArticleChaptersViewModel constructor(private val repository: WanRepository) : BaseViewModel() {
16 |
17 | private val _items = MutableLiveData>().apply { value = mutableListOf() }
18 | val dataList: MutableLiveData> = _items
19 |
20 | fun getData() {
21 | launch {
22 | stateModel.startLoading()
23 | val resp = repository.getWXArticleChapters()
24 | checkResponseCode(resp)
25 | val data = resp.data ?: listOf()
26 | if (data.isNotEmpty()) {
27 | dataList.value = ArrayList(data)
28 | stateModel.loadDataFinish()
29 | } else {
30 | stateModel.showEmpty()
31 | }
32 | }
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/util/BindingAdapters.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.util
2 |
3 | import android.widget.ImageView
4 | import androidx.databinding.BindingAdapter
5 | import androidx.databinding.InverseBindingAdapter
6 | import androidx.databinding.InverseBindingListener
7 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
8 | import com.bumptech.glide.Glide
9 | import me.luowl.wan.R
10 |
11 | /*
12 | *
13 | * Created by luowl
14 | * Date: 2019/7/25
15 | * Desc:
16 | */
17 |
18 | object BindingAdapters {
19 | @BindingAdapter(value = ["app:imageUrl"], requireAll = true)
20 | @JvmStatic
21 | fun loadImage(view: ImageView, url: String) {
22 | Glide.with(view.context).load(url).into(view)
23 | }
24 |
25 | @BindingAdapter(value = ["app:collect"], requireAll = true)
26 | @JvmStatic
27 | fun setCollectionImageSrc(view: ImageView, collect: Boolean) {
28 | view.setImageResource(if (collect) R.mipmap.ic_collect else R.mipmap.ic_uncollect)
29 | }
30 |
31 | //是否刷新中
32 | @JvmStatic
33 | @BindingAdapter("refreshing")
34 | fun setRefreshing(swipeRefreshLayout: SwipeRefreshLayout, refreshing: Boolean) {
35 | if (swipeRefreshLayout.isRefreshing != refreshing)
36 | swipeRefreshLayout.isRefreshing = refreshing
37 | }
38 |
39 | @JvmStatic
40 | @InverseBindingAdapter(attribute = "refreshing", event = "onRefresh")
41 | fun isRefreshing(swipeRefreshLayout: SwipeRefreshLayout): Boolean {
42 | logDebug("refreshing:$swipeRefreshLayout.isRefreshing")
43 | return swipeRefreshLayout.isRefreshing
44 | }
45 |
46 | @JvmStatic
47 | @BindingAdapter("onRefresh", requireAll = false)
48 | fun setOnRefreshListener(swipeRefreshLayout: SwipeRefreshLayout, bindingListener: InverseBindingListener?) {
49 | if (bindingListener != null)
50 | swipeRefreshLayout.setOnRefreshListener {
51 | bindingListener.onChange()
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/util/BindingConverters.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.util
2 |
3 | import android.graphics.drawable.Drawable
4 | import android.graphics.drawable.GradientDrawable
5 | import androidx.databinding.BindingConversion
6 | import me.luowl.wan.R
7 | import me.luowl.wan.WanApplication
8 |
9 | /*
10 | *
11 | * Created by luowl
12 | * Date: 2019/7/25
13 | * Desc:
14 | */
15 |
16 | object BindingConverters {
17 | @BindingConversion
18 | @JvmStatic
19 | fun convertStringToDrawable(value: Long): Drawable {
20 | val colors = WanApplication.context.resources.obtainTypedArray(R.array.chapter_colors)
21 | val color = colors.getColor((value % colors.length()).toInt(), 0)
22 | val gradientDrawable = GradientDrawable()
23 | gradientDrawable.setColor(color)
24 | gradientDrawable.cornerRadius = 30f
25 | return gradientDrawable
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/util/GlobalUtil.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.util
2 |
3 | import android.widget.Toast
4 | import androidx.core.content.ContextCompat
5 | import me.luowl.wan.WanApplication
6 |
7 |
8 | /**
9 | * 应用程序全局的通用工具类,功能比较单一,经常被复用的功能,应该封装到此工具类当中,从而给全局代码提供方面的操作。
10 | *
11 | */
12 | object GlobalUtil {
13 |
14 | private var TAG = "GlobalUtil"
15 |
16 | private val toast: Toast by lazy {
17 | Toast.makeText(WanApplication.context,null,Toast.LENGTH_SHORT)
18 | }
19 |
20 | fun showToastShort(msg: String) {
21 | toast.setText(msg)
22 | toast.duration = Toast.LENGTH_SHORT
23 | toast.show()
24 | }
25 |
26 | /**
27 | * 获取当前应用程序的包名。
28 | *
29 | * @return 当前应用程序的包名。
30 | */
31 | val appPackage: String
32 | get() = WanApplication.context.packageName
33 |
34 | /**
35 | * 获取资源文件中定义的字符串。
36 | *
37 | * @param resId
38 | * 字符串资源id
39 | * @return 字符串资源id对应的字符串内容。
40 | */
41 | fun getString(resId: Int): String {
42 | return WanApplication.context.resources.getString(resId)
43 | }
44 |
45 | /**
46 | * 获取指定资源名的资源id。
47 | *
48 | * @param name
49 | * 资源名
50 | * @param type
51 | * 资源类型
52 | * @return 指定资源名的资源id。
53 | */
54 | fun getResourceId(name: String, type: String): Int {
55 | return WanApplication.context.resources.getIdentifier(name, type, appPackage)
56 | }
57 |
58 | fun getColor(resId: Int): Int {
59 | return ContextCompat.getColor(WanApplication.context, resId)
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/util/Log.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.util
2 |
3 | import android.os.Build
4 | import timber.log.Timber
5 |
6 | /**
7 | * 日志操作的扩展工具类。
8 | *
9 | */
10 |
11 | fun Any.logVerbose(msg: String?) {
12 | Timber.v(msg)
13 | }
14 |
15 | fun Any.logDebug(msg: String?) {
16 | Timber.v(msg)
17 | }
18 |
19 | fun Any.logInfo(msg: String?) {
20 | Timber.i(msg)
21 | }
22 |
23 | fun Any.logWarn(msg: String?, tr: Throwable? = null) {
24 | Timber.w(msg)
25 | }
26 |
27 | fun Any.logError(msg: String?, tr: Throwable) {
28 | Timber.e(tr, msg)
29 | }
30 |
31 | fun logVerbose(tag: String, msg: String?) {
32 | Timber.tag(tag).v(tag, msg)
33 | }
34 |
35 | fun logDebug(tag: String, msg: String?) {
36 | Timber.tag(tag).d(msg)
37 | }
38 |
39 | fun logInfo(tag: String, msg: String?) {
40 | Timber.tag(tag).i(msg)
41 | }
42 |
43 | fun logWarn(tag: String, msg: String?, tr: Throwable? = null) {
44 | Timber.tag(tag).w(tag, msg)
45 | }
46 |
47 | fun logError(tag: String, msg: String?, tr: Throwable) {
48 | Timber.tag(tag).e(tag, msg)
49 | }
50 |
51 | fun printlnDeviceInfo(){
52 | val head = "************* Log Head ****************" +
53 | "\nDevice Manufacturer 制造商 : " + Build.MANUFACTURER +
54 | "\nDevice Model 型号 : " + Build.MODEL +
55 | "\nIdentifier_Brand 品牌 : " + Build.BRAND +
56 | "\nIdentifier_Device 设备名 : " + Build.DEVICE +
57 | "\nAndroid Version : " + Build.VERSION.RELEASE +
58 | "\nAndroid SDK : " + Build.VERSION.SDK_INT +
59 | "\n************* Log Head ****************\n\n"
60 | logDebug("Device",head)
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/util/Preference.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.util
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.content.SharedPreferences
6 | import me.luowl.wan.WanApplication
7 | import java.io.*
8 | import kotlin.reflect.KProperty
9 |
10 | /**
11 | * kotlin委托属性+SharedPreference实例
12 | */
13 | class Preference(val name: String, private val default: T) {
14 |
15 | companion object {
16 | private val file_name = "wan_android_file"
17 |
18 | private val prefs: SharedPreferences by lazy {
19 | WanApplication.context.getSharedPreferences(file_name, Context.MODE_PRIVATE)
20 | }
21 |
22 | /**
23 | * 删除全部数据
24 | */
25 | fun clearPreference() {
26 | prefs.edit().clear().apply()
27 | }
28 |
29 | /**
30 | * 根据key删除存储数据
31 | */
32 | fun clearPreference(key: String) {
33 | prefs.edit().remove(key).apply()
34 | }
35 |
36 | /**
37 | * 查询某个key是否已经存在
38 | *
39 | * @param key
40 | * @return
41 | */
42 | fun contains(key: String): Boolean {
43 | return prefs.contains(key)
44 | }
45 |
46 | /**
47 | * 返回所有的键值对
48 | *
49 | * @param context
50 | * @return
51 | */
52 | fun getAll(): Map {
53 | return prefs.all
54 | }
55 | }
56 |
57 | operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
58 | return getSharedPreferences(name, default)
59 | }
60 |
61 | operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
62 | putSharedPreferences(name, value)
63 | }
64 |
65 | @SuppressLint("CommitPrefEdits")
66 | private fun putSharedPreferences(name: String, value: T) = with(prefs.edit()) {
67 | when (value) {
68 | is Long -> putLong(name, value)
69 | is String -> putString(name, value)
70 | is Int -> putInt(name, value)
71 | is Boolean -> putBoolean(name, value)
72 | is Float -> putFloat(name, value)
73 | else -> putString(name, serialize(value))
74 | }.apply()
75 | }
76 |
77 | @Suppress("UNCHECKED_CAST")
78 | private fun getSharedPreferences(name: String, default: T): T = with(prefs) {
79 | val res: Any = when (default) {
80 | is Long -> getLong(name, default)
81 | is String -> getString(name, default)!!
82 | is Int -> getInt(name, default)
83 | is Boolean -> getBoolean(name, default)
84 | is Float -> getFloat(name, default)
85 | else -> deSerialization(getString(name, serialize(default))!!)
86 | }
87 | return res as T
88 | }
89 |
90 | /**
91 | * 序列化对象
92 | * @param person
93 | * *
94 | * @return
95 | * *
96 | * @throws IOException
97 | */
98 | @Throws(IOException::class)
99 | private fun serialize(obj: A): String {
100 | val byteArrayOutputStream = ByteArrayOutputStream()
101 | val objectOutputStream = ObjectOutputStream(
102 | byteArrayOutputStream
103 | )
104 | objectOutputStream.writeObject(obj)
105 | var serStr = byteArrayOutputStream.toString("ISO-8859-1")
106 | serStr = java.net.URLEncoder.encode(serStr, "UTF-8")
107 | objectOutputStream.close()
108 | byteArrayOutputStream.close()
109 | return serStr
110 | }
111 |
112 | /**
113 | * 反序列化对象
114 | * @param str
115 | * *
116 | * @return
117 | * *
118 | * @throws IOException
119 | * *
120 | * @throws ClassNotFoundException
121 | */
122 | @Suppress("UNCHECKED_CAST")
123 | @Throws(IOException::class, ClassNotFoundException::class)
124 | private fun deSerialization(str: String): A {
125 | val redStr = java.net.URLDecoder.decode(str, "UTF-8")
126 | val byteArrayInputStream = ByteArrayInputStream(
127 | redStr.toByteArray(charset("ISO-8859-1"))
128 | )
129 | val objectInputStream = ObjectInputStream(
130 | byteArrayInputStream
131 | )
132 | val obj = objectInputStream.readObject() as A
133 | objectInputStream.close()
134 | byteArrayInputStream.close()
135 | return obj
136 | }
137 |
138 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/util/WanGlideModel.java:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.util;
2 | /*
3 | *
4 | * Created by luowl
5 | * Date: 2019/8/26
6 | * Desc:
7 | */
8 |
9 | import android.content.Context;
10 |
11 | import androidx.annotation.NonNull;
12 |
13 | import com.bumptech.glide.Glide;
14 | import com.bumptech.glide.Registry;
15 | import com.bumptech.glide.annotation.GlideModule;
16 | import com.bumptech.glide.module.AppGlideModule;
17 |
18 | @GlideModule(glideName = "WanGlide")
19 | public class WanGlideModel extends AppGlideModule {
20 | @Override
21 | public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
22 | super.registerComponents(context, glide, registry);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/me/luowl/wan/widget/SimpleDividerItemDecoration.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan.widget
2 |
3 | import android.content.Context
4 | import androidx.recyclerview.widget.GridLayoutManager
5 | import androidx.recyclerview.widget.LinearLayoutManager
6 | import androidx.recyclerview.widget.RecyclerView
7 | import me.luowl.wan.R
8 | import me.luowl.wan.base.adapter.RVAdapter
9 | import me.luowl.wan.util.GlobalUtil
10 |
11 | /**
12 | *
13 | * Created by luowl@iPanel.cn
14 | * Date: 2019/7/23
15 | * Desc:
16 | */
17 | class SimpleDividerItemDecoration(
18 | context: Context?, private val dividerWidth: Float = 0.5f,
19 | val color: Int = GlobalUtil.getColor(R.color.app_divider),
20 | private val firstTop: Float = 0f, private val firstLeft: Float = 0f, private val lastRight: Float = 0f
21 | ) : RVDividerItemDecoration(context) {
22 | override fun getDivider(parent: RecyclerView, itemPosition: Int): RVDivider {
23 | when (parent.adapter?.getItemViewType(itemPosition)) {
24 | RVAdapter.TYPE_LOAD_MORE -> return RVDividerBuilder().create()
25 | }
26 | if (parent.layoutManager is GridLayoutManager) {
27 | val spanCount = getSpanCount(parent)
28 | val rvDividerBuilder = RVDividerBuilder()
29 | if (itemPosition < spanCount) {
30 | rvDividerBuilder.setTopSideLine(true, color, firstTop, 0f, 0f)
31 | }
32 | return when (itemPosition % spanCount) {
33 | 0 ->
34 | //每一行第一个填充右边
35 | rvDividerBuilder
36 | .setLeftSideLine(true, color, firstLeft, 0f, 0f)
37 | .setRightSideLine(true, color, dividerWidth / 2, 0f, 0f)
38 | .setBottomSideLine(true, color, dividerWidth, 0f, 0f)
39 | spanCount - 1 ->
40 | //第行最后一个填充左边
41 | rvDividerBuilder
42 | .setLeftSideLine(true, color, dividerWidth / 2, 0f, 0f)
43 | .setRightSideLine(true, color, lastRight, 0f, 0f)
44 | .setBottomSideLine(true, color, dividerWidth, 0f, 0f)
45 | else -> rvDividerBuilder
46 | .setLeftSideLine(true, color, dividerWidth / 2, 0f, 0f)
47 | .setRightSideLine(true, color, dividerWidth / 2, 0f, 0f)
48 | .setBottomSideLine(true, color, dividerWidth, 0f, 0f)
49 | }.create()
50 | }
51 | if (parent.layoutManager is LinearLayoutManager) {
52 | val rvDividerBuilder = RVDividerBuilder()
53 | .setBottomSideLine(true, color, dividerWidth, 0f, 0f)
54 | if (itemPosition == 0)
55 | rvDividerBuilder.setTopSideLine(true, color, firstTop, 0f, 0f)
56 | rvDividerBuilder.setLeftSideLine(true, color, firstLeft, 0f, 0f)
57 | rvDividerBuilder.setRightSideLine(true, color, lastRight, 0f, 0f)
58 | return rvDividerBuilder.create()
59 | }
60 | return RVDividerBuilder().create()
61 | }
62 |
63 | private fun getSpanCount(parent: RecyclerView): Int {
64 | val layoutManager = parent.layoutManager
65 | return (layoutManager as? GridLayoutManager)?.spanCount ?: 1
66 | }
67 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/progress_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 |
13 |
14 |
15 | -
16 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/selector_list_item_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 | -
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_knowledge_sub_child.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_login_edit_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_top_article.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shaper_fliter.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_architecture_category.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 |
14 |
21 |
22 |
37 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_coin_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
15 |
16 |
24 |
25 |
28 |
29 |
33 |
34 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_collection.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
17 |
18 |
26 |
27 |
30 |
31 |
35 |
36 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_container.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
15 |
16 |
24 |
25 |
38 |
39 |
43 |
44 |
57 |
58 |
70 |
71 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
24 |
25 |
33 |
34 |
50 |
51 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_register.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
19 |
20 |
33 |
36 |
37 |
50 |
51 |
54 |
55 |
68 |
69 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 |
14 |
21 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_web_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
14 |
21 |
24 |
28 |
29 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_wx_article_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 |
14 |
21 |
22 |
28 |
30 |
34 |
35 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_confirm_cannel_collection.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_architecture.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_architecture_article_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
14 |
18 |
19 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_find.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
15 |
25 |
35 |
45 |
46 |
47 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
21 |
33 |
34 |
35 |
41 |
43 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_navigation.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
19 |
27 |
28 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
17 |
18 |
33 |
38 |
39 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_project_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
14 |
18 |
19 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_wx_article_chapters.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/nav_tab_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_item_architecture.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 |
23 |
24 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_item_banner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_item_coin.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_item_entry.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
17 |
27 |
37 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_item_filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_item_load_more.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
19 |
20 |
26 |
27 |
35 |
36 |
37 |
42 |
43 |
44 |
50 |
51 |
52 |
53 |
58 |
59 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_item_navigation_left_tab.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_item_navigation_sub_child.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_item_project.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
13 |
14 |
27 |
28 |
41 |
42 |
49 |
50 |
58 |
59 |
64 |
65 |
73 |
74 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_item_search_record.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
20 |
21 |
36 |
37 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_item_wx_chapter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_view_header_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_search_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/bad_network_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/bad_network_image.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/bg_mine_top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/bg_mine_top.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_about.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_about.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_close.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_coin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_coin.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_collect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_collect.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_collection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_collection.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_feedback.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_feedback.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_nav_home_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_nav_home_normal.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_nav_home_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_nav_home_selected.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_nav_mine_noraml.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_nav_mine_noraml.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_nav_mine_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_nav_mine_selected.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_nav_project_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_nav_project_normal.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_nav_project_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_nav_project_selected.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_nav_wx_chapters_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_nav_wx_chapters_normal.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_nav_wx_chapters_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_nav_wx_chapters_selected.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_record.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_record.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_uncollect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_uncollect.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_user_default_poster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/ic_user_default_poster.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/icon_author.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/icon_author.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/icon_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/icon_search.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/icon_time.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/icon_time.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/image_server_return_false.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/image_server_return_false.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/image_service_exception.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/image_service_exception.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/no_content_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/no_content_image.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/user_default_img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/user_default_img.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/wan_android_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxhdpi/wan_android_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/attr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #0172e5
4 | #0172e5
5 | #0172e5
6 |
7 | #007fff
8 | #FFFFFF
9 | #000000
10 | #FD544F
11 | #666666
12 | #333333
13 | #F4F6F9
14 | #40000000
15 | #f5f5f5
16 | #80FFFFFF
17 | #757575
18 | #fff5f5f5
19 |
20 |
21 | - #F08080
22 | - #FFB6C1
23 | - #FFC125
24 | - #F28300
25 | - #90EE90
26 | - #7FFFD4
27 | - #7EC0EE
28 | - #3CB371
29 | - #FFE4C4
30 | - #E6E6FA
31 | - #B4EEB4
32 | - #FF5E8B
33 | - #FF7C9E
34 | - #FE76C8
35 | - #9171ED
36 | - #7380F9
37 | - #4790EA
38 | - #39AEEA
39 | - #1EC392
40 | - #2BB568
41 | - #69CA19
42 | - #E3AB13
43 | - #FD716D
44 | - #FD544F
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 52dp
4 | 50dp
5 | 10dp
6 | 46dp
7 | 14dp
8 | 16sp
9 | 16sp
10 | 10dp
11 | 8dp
12 | 14sp
13 | 12dp
14 | 12sp
15 | 5dp
16 | 20dp
17 | 200dp
18 | 90dp
19 | 80dp
20 | 20sp
21 | 54dp
22 | 15dp
23 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 玩Android哈
3 |
4 | 正在加载中...
5 | 加载失败,请点我重试
6 | 没有更多数据
7 | 项目
8 | 置顶
9 | 新
10 | 每日一问
11 | 面试指南
12 | 知识体系
13 | 站点导航
14 | 公众号
15 | 体系
16 | 导航
17 | 搜索文章、项目
18 | menu_search
19 | menu_setting
20 |
21 | 网络连接异常
22 | 网络连接超时
23 | 无法连接到服务器
24 | 服务器状态码异常:
25 | 您的网络不给力,点击屏幕可重新加载
26 | 加载失败,点击可重新加载
27 | 未登录
28 | 登录
29 | 账号注册
30 | 请输入用户名
31 | 请输入密码
32 | 请输入确认密码
33 | 我的收藏
34 | 我的积分
35 | 退出当前账号
36 | 意见反馈
37 | 关于app
38 | 注册
39 | 宁还没有登录呢
40 | 取消
41 | 确定要退出当前账号吗
42 | 是的
43 | 点错了
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
14 |
15 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/test/java/me/luowl/wan/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package me.luowl.wan
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | ext.kotlin_version = '1.5.10'
4 | repositories {
5 | gradlePluginPortal()
6 | google()
7 | mavenCentral()
8 | jcenter()
9 |
10 | }
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:7.1.3'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.21'
17 | }
18 | }
19 |
20 | allprojects {
21 | repositories {
22 | gradlePluginPortal()
23 | google()
24 | mavenCentral()
25 | jcenter()
26 |
27 | }
28 | }
29 |
30 | task clean(type: Delete) {
31 | delete rootProject.buildDir
32 | }
33 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Oct 18 15:41:05 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
7 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/myapp.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/myapp.jks
--------------------------------------------------------------------------------
/screenshots/Screenshot_01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/screenshots/Screenshot_01.jpg
--------------------------------------------------------------------------------
/screenshots/Screenshot_02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/screenshots/Screenshot_02.jpg
--------------------------------------------------------------------------------
/screenshots/Screenshot_03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/screenshots/Screenshot_03.jpg
--------------------------------------------------------------------------------
/screenshots/Screenshot_04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/screenshots/Screenshot_04.jpg
--------------------------------------------------------------------------------
/screenshots/Screenshot_05.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/screenshots/Screenshot_05.jpg
--------------------------------------------------------------------------------
/screenshots/Screenshot_06.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/screenshots/Screenshot_06.jpg
--------------------------------------------------------------------------------
/screenshots/Screenshot_07.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/screenshots/Screenshot_07.jpg
--------------------------------------------------------------------------------
/screenshots/Screenshot_08.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/screenshots/Screenshot_08.jpg
--------------------------------------------------------------------------------
/screenshots/Screenshot_09.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/screenshots/Screenshot_09.jpg
--------------------------------------------------------------------------------
/screenshots/Screenshot_10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowl123/WanAndroid/283d2cbb19786614163104c70df8ab3947e35ee8/screenshots/Screenshot_10.jpg
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/test:
--------------------------------------------------------------------------------
1 |
2 | 我觉得选择C,还是一个不错的选择。
3 | 1、你和男二都有了分手的觉悟,分了对谁都好,
--------------------------------------------------------------------------------