From 84aaba725d2b136b9deb2e74f4065531855ee0d8 Mon Sep 17 00:00:00 2001 From: nekocode Date: Fri, 5 Feb 2021 21:54:04 +0800 Subject: [PATCH] Upgraded stuff & renamed packages --- app/build.gradle.kts | 75 +++++++-------- .../{gank => caka}/di/module/FlipperModule.kt | 4 +- app/src/main/AndroidManifest.xml | 6 +- .../MyApplication.kt} | 14 +-- .../{gank => caka}/base/BaseActivity.kt | 48 ++-------- .../cn/nekocode/caka/base/BaseFragment.kt | 65 +++++++++++++ .../{gank => caka}/base/BaseViewModel.kt | 63 ++++-------- .../{gank => caka}/di/ActivityScope.kt | 2 +- .../cn/nekocode/{gank => caka}/di/AppScope.kt | 2 +- .../{gank => caka}/di/FragmentScope.kt | 2 +- .../di/ViewModelDaggerHelper.kt | 2 +- .../di/component/ActivityComponent.kt | 8 +- .../di/component/AppComponent.kt | 16 ++-- .../di/component/FragmentComponent.kt | 14 +-- .../di/module/ActivityModule.kt | 4 +- .../{gank => caka}/di/module/AppModule.kt | 8 +- .../di/module/FragmentModule.kt | 4 +- .../di/module/ViewModelModule.kt | 12 +-- .../{gank => caka}/ui/MainActivity.kt | 6 +- .../ui/home/HomeFragment.kt} | 37 ++++---- .../cn/nekocode/caka/ui/home/HomeViewModel.kt | 60 ++++++++++++ .../cn/nekocode/caka/ui/home/ReposAdapter.kt | 82 ++++++++++++++++ .../ui/repo/RepoFragment.kt} | 24 ++--- .../cn/nekocode/gank/base/BaseFragment.kt | 89 ----------------- .../cn/nekocode/gank/ui/pic/PicViewModel.kt | 48 ---------- app/src/main/res/layout/fragment_home.xml | 31 +++--- .../{fragment_pic.xml => fragment_repo.xml} | 13 +-- app/src/main/res/layout/item_repo.xml | 43 +++++++++ app/src/main/res/navigation/nav_main.xml | 30 +++--- app/src/main/res/values/strings.xml | 7 +- .../{gank => caka}/di/module/FlipperModule.kt | 4 +- backend/build.gradle.kts | 34 +++---- backend/src/main/AndroidManifest.xml | 2 +- .../PicApi.kt => caka/backend/api/RepoApi.kt} | 20 ++-- .../backend/di/module/ApiModule.kt | 17 ++-- .../backend/model/Repository.kt} | 18 ++-- .../cn/nekocode/gank/backend/model/WithId.kt | 24 ----- .../backend/model/wrapper/GankIoResponse.kt | 27 ------ .../ApisTest.kt => caka/backend/ApiTest.kt} | 31 +++--- .../{gank => caka}/backend/TestComponent.kt | 6 +- build.gradle.kts | 6 +- create-android-kotlin-app.py | 95 ++++++++++--------- gradle/wrapper/gradle-wrapper.properties | 4 +- 43 files changed, 557 insertions(+), 550 deletions(-) rename app/src/debug/java/cn/nekocode/{gank => caka}/di/module/FlipperModule.kt (97%) rename app/src/main/java/cn/nekocode/{gank/GankApplication.kt => caka/MyApplication.kt} (84%) rename app/src/main/java/cn/nekocode/{gank => caka}/base/BaseActivity.kt (50%) create mode 100644 app/src/main/java/cn/nekocode/caka/base/BaseFragment.kt rename app/src/main/java/cn/nekocode/{gank => caka}/base/BaseViewModel.kt (55%) rename app/src/main/java/cn/nekocode/{gank => caka}/di/ActivityScope.kt (96%) rename app/src/main/java/cn/nekocode/{gank => caka}/di/AppScope.kt (96%) rename app/src/main/java/cn/nekocode/{gank => caka}/di/FragmentScope.kt (96%) rename app/src/main/java/cn/nekocode/{gank => caka}/di/ViewModelDaggerHelper.kt (98%) rename app/src/main/java/cn/nekocode/{gank => caka}/di/component/ActivityComponent.kt (88%) rename app/src/main/java/cn/nekocode/{gank => caka}/di/component/AppComponent.kt (75%) rename app/src/main/java/cn/nekocode/{gank => caka}/di/component/FragmentComponent.kt (80%) rename app/src/main/java/cn/nekocode/{gank => caka}/di/module/ActivityModule.kt (92%) rename app/src/main/java/cn/nekocode/{gank => caka}/di/module/AppModule.kt (84%) rename app/src/main/java/cn/nekocode/{gank => caka}/di/module/FragmentModule.kt (92%) rename app/src/main/java/cn/nekocode/{gank => caka}/di/module/ViewModelModule.kt (78%) rename app/src/main/java/cn/nekocode/{gank => caka}/ui/MainActivity.kt (92%) rename app/src/main/java/cn/nekocode/{gank/ui/pic/PicFragment.kt => caka/ui/home/HomeFragment.kt} (51%) create mode 100644 app/src/main/java/cn/nekocode/caka/ui/home/HomeViewModel.kt create mode 100644 app/src/main/java/cn/nekocode/caka/ui/home/ReposAdapter.kt rename app/src/main/java/cn/nekocode/{gank/ui/home/HomeFragment.kt => caka/ui/repo/RepoFragment.kt} (66%) delete mode 100644 app/src/main/java/cn/nekocode/gank/base/BaseFragment.kt delete mode 100644 app/src/main/java/cn/nekocode/gank/ui/pic/PicViewModel.kt rename app/src/main/res/layout/{fragment_pic.xml => fragment_repo.xml} (65%) create mode 100644 app/src/main/res/layout/item_repo.xml rename app/src/release/java/cn/nekocode/{gank => caka}/di/module/FlipperModule.kt (87%) rename backend/src/main/java/cn/nekocode/{gank/backend/api/PicApi.kt => caka/backend/api/RepoApi.kt} (66%) rename backend/src/main/java/cn/nekocode/{gank => caka}/backend/di/module/ApiModule.kt (80%) rename backend/src/main/java/cn/nekocode/{gank/backend/model/MeiziPic.kt => caka/backend/model/Repository.kt} (64%) delete mode 100644 backend/src/main/java/cn/nekocode/gank/backend/model/WithId.kt delete mode 100644 backend/src/main/java/cn/nekocode/gank/backend/model/wrapper/GankIoResponse.kt rename backend/src/test/java/cn/nekocode/{gank/backend/ApisTest.kt => caka/backend/ApiTest.kt} (65%) rename backend/src/test/java/cn/nekocode/{gank => caka}/backend/TestComponent.kt (87%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4e5b206..6ee3f4e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -3,34 +3,28 @@ import org.jetbrains.kotlin.config.KotlinCompilerVersion plugins { id("com.android.application") kotlin("android") - kotlin("android.extensions") kotlin("kapt") + id("kotlin-parcelize") id("androidx.navigation.safeargs") id("com.akaita.android.easylauncher") } -androidExtensions { - isExperimental = true - features = setOf("views", "parcelize") -} - android { - compileSdkVersion(28) + compileSdkVersion(29) defaultConfig { - targetSdkVersion(28) minSdkVersion(21) - applicationId = "cn.nekocode.gank" + applicationId = "cn.nekocode.caka" versionCode = 1 versionName = "1.0" - val scheme = "gank" + val scheme = "caka" buildConfigField("String", "SCHEME", "\"$scheme\"") - manifestPlaceholders = mapOf( - "APPLICATION_ID" to applicationId, + addManifestPlaceholders(mapOf( + "APPLICATION_ID" to applicationId!!, "SCHEME" to scheme - ) + )) } buildTypes { getByName("release") { @@ -38,6 +32,9 @@ android { proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") } } + buildFeatures { + viewBinding = true + } compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 @@ -52,46 +49,46 @@ dependencies { implementation(kotlin("stdlib-jdk8", KotlinCompilerVersion.VERSION)) // Androidx - implementation("androidx.appcompat:appcompat:1.1.0") - implementation("androidx.core:core-ktx:1.1.0") - implementation("androidx.recyclerview:recyclerview:1.1.0-beta05") - implementation("androidx.constraintlayout:constraintlayout:2.0.0-beta2") - implementation("androidx.lifecycle:lifecycle-extensions:2.1.0") + implementation("androidx.appcompat:appcompat:1.2.0") + implementation("androidx.core:core-ktx:1.3.2") + implementation("androidx.recyclerview:recyclerview:1.2.0-beta01") + implementation("androidx.constraintlayout:constraintlayout:2.1.0-alpha2") + implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") // Navigation - implementation("android.arch.navigation:navigation-fragment-ktx:1.0.0") - implementation("android.arch.navigation:navigation-ui-ktx:1.0.0") + implementation("androidx.navigation:navigation-fragment-ktx:2.3.3") + implementation("androidx.navigation:navigation-ui-ktx:2.3.3") // ReactiveX - implementation("com.uber.autodispose:autodispose:1.4.0") - implementation("com.uber.autodispose:autodispose-android:1.4.0") - implementation("com.uber.autodispose:autodispose-android-archcomponents:1.4.0") + implementation("com.uber.autodispose2:autodispose:2.0.0") + implementation("com.uber.autodispose2:autodispose-android:2.0.0") + implementation("com.uber.autodispose2:autodispose-androidx-lifecycle:2.0.0") // Dependency injection - implementation("com.google.dagger:dagger:2.24") - kapt("com.google.dagger:dagger-compiler:2.24") + implementation("com.google.dagger:dagger:2.31.2") + kapt("com.google.dagger:dagger-compiler:2.31.2") // Others - implementation("com.squareup.picasso:picasso:2.71828") + implementation("com.jakewharton.timber:timber:4.7.1") implementation("com.evernote:android-state:1.4.1") kapt("com.evernote:android-state-processor:1.4.1") // For debugging debugImplementation("com.facebook.flipper:flipper:0.23.2") - debugImplementation("com.facebook.soloader:soloader:0.8.0") + debugImplementation("com.facebook.soloader:soloader:0.10.1") releaseImplementation("com.facebook.flipper:flipper-noop:0.23.2") - debugImplementation("com.willowtreeapps.hyperion:hyperion-core:0.9.27") - debugImplementation("com.willowtreeapps.hyperion:hyperion-attr:0.9.27") - debugImplementation("com.willowtreeapps.hyperion:hyperion-build-config:0.9.27") - debugImplementation("com.willowtreeapps.hyperion:hyperion-crash:0.9.27") - debugImplementation("com.willowtreeapps.hyperion:hyperion-disk:0.9.27") - debugImplementation("com.willowtreeapps.hyperion:hyperion-geiger-counter:0.9.27") - debugImplementation("com.willowtreeapps.hyperion:hyperion-measurement:0.9.27") - debugImplementation("com.willowtreeapps.hyperion:hyperion-phoenix:0.9.27") - debugImplementation("com.willowtreeapps.hyperion:hyperion-recorder:0.9.27") - debugImplementation("com.willowtreeapps.hyperion:hyperion-shared-preferences:0.9.27") - debugImplementation("com.willowtreeapps.hyperion:hyperion-timber:0.9.27") - releaseImplementation("com.willowtreeapps.hyperion:hyperion-core-no-op:0.9.27") + debugImplementation("com.willowtreeapps.hyperion:hyperion-core:0.9.31") + debugImplementation("com.willowtreeapps.hyperion:hyperion-attr:0.9.31") + debugImplementation("com.willowtreeapps.hyperion:hyperion-build-config:0.9.31") + debugImplementation("com.willowtreeapps.hyperion:hyperion-crash:0.9.31") + debugImplementation("com.willowtreeapps.hyperion:hyperion-disk:0.9.31") + debugImplementation("com.willowtreeapps.hyperion:hyperion-geiger-counter:0.9.31") + debugImplementation("com.willowtreeapps.hyperion:hyperion-measurement:0.9.31") + debugImplementation("com.willowtreeapps.hyperion:hyperion-phoenix:0.9.31") + debugImplementation("com.willowtreeapps.hyperion:hyperion-recorder:0.9.31") + debugImplementation("com.willowtreeapps.hyperion:hyperion-shared-preferences:0.9.31") + debugImplementation("com.willowtreeapps.hyperion:hyperion-timber:0.9.31") + releaseImplementation("com.willowtreeapps.hyperion:hyperion-core-no-op:0.9.31") } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class.java).all { diff --git a/app/src/debug/java/cn/nekocode/gank/di/module/FlipperModule.kt b/app/src/debug/java/cn/nekocode/caka/di/module/FlipperModule.kt similarity index 97% rename from app/src/debug/java/cn/nekocode/gank/di/module/FlipperModule.kt rename to app/src/debug/java/cn/nekocode/caka/di/module/FlipperModule.kt index 4af860f..1ca3a8e 100644 --- a/app/src/debug/java/cn/nekocode/gank/di/module/FlipperModule.kt +++ b/app/src/debug/java/cn/nekocode/caka/di/module/FlipperModule.kt @@ -1,7 +1,7 @@ -package cn.nekocode.gank.di.module +package cn.nekocode.caka.di.module import android.app.Application -import cn.nekocode.gank.di.AppScope +import cn.nekocode.caka.di.AppScope import com.facebook.flipper.android.AndroidFlipperClient import com.facebook.flipper.android.utils.FlipperUtils import com.facebook.flipper.core.FlipperClient diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b1c98c7..7dddd2a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,14 +1,14 @@ + package="cn.nekocode.caka"> diff --git a/app/src/main/java/cn/nekocode/gank/GankApplication.kt b/app/src/main/java/cn/nekocode/caka/MyApplication.kt similarity index 84% rename from app/src/main/java/cn/nekocode/gank/GankApplication.kt rename to app/src/main/java/cn/nekocode/caka/MyApplication.kt index 160cad3..e98922c 100644 --- a/app/src/main/java/cn/nekocode/gank/GankApplication.kt +++ b/app/src/main/java/cn/nekocode/caka/MyApplication.kt @@ -14,15 +14,15 @@ * limitations under the License. */ -package cn.nekocode.gank +package cn.nekocode.caka import android.app.Application import androidx.lifecycle.ViewModelProvider -import cn.nekocode.gank.backend.di.module.ApiModule -import cn.nekocode.gank.di.component.AppComponent -import cn.nekocode.gank.di.component.DaggerAppComponent -import cn.nekocode.gank.di.module.AppModule -import cn.nekocode.gank.di.module.FlipperModule +import cn.nekocode.caka.backend.di.module.ApiModule +import cn.nekocode.caka.di.component.AppComponent +import cn.nekocode.caka.di.component.DaggerAppComponent +import cn.nekocode.caka.di.module.AppModule +import cn.nekocode.caka.di.module.FlipperModule import com.facebook.flipper.core.FlipperClient import com.google.gson.GsonBuilder import okhttp3.OkHttpClient @@ -32,7 +32,7 @@ import javax.inject.Inject /** * @author nekocode (nekocode.cn@gmail.com) */ -open class GankApplication : Application() { +open class MyApplication : Application() { lateinit var component: AppComponent @Inject lateinit var flipperClient: FlipperClient diff --git a/app/src/main/java/cn/nekocode/gank/base/BaseActivity.kt b/app/src/main/java/cn/nekocode/caka/base/BaseActivity.kt similarity index 50% rename from app/src/main/java/cn/nekocode/gank/base/BaseActivity.kt rename to app/src/main/java/cn/nekocode/caka/base/BaseActivity.kt index 35555b2..fd0a9cd 100644 --- a/app/src/main/java/cn/nekocode/gank/base/BaseActivity.kt +++ b/app/src/main/java/cn/nekocode/caka/base/BaseActivity.kt @@ -14,31 +14,24 @@ * limitations under the License. */ -package cn.nekocode.gank.base +package cn.nekocode.caka.base import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProviders -import cn.nekocode.gank.GankApplication -import cn.nekocode.gank.di.component.buildAndInject +import androidx.lifecycle.ViewModelProvider +import cn.nekocode.caka.MyApplication +import cn.nekocode.caka.di.component.buildAndInject import com.evernote.android.state.StateSaver -import com.uber.autodispose.* -import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider -import io.reactivex.* -import io.reactivex.annotations.CheckReturnValue -import io.reactivex.parallel.ParallelFlowable /** * @author nekocode (nekocode.cn@gmail.com) */ abstract class BaseActivity : AppCompatActivity() { - private val scopeProvider by lazy { AndroidLifecycleScopeProvider.from(this) } - override fun onCreate(savedInstanceState: Bundle?) { - (application as GankApplication).component + (application as MyApplication).component .newActivityComponentBuilder().buildAndInject(this) super.onCreate(savedInstanceState) StateSaver.restoreInstanceState(this, savedInstanceState) @@ -56,35 +49,8 @@ abstract class BaseActivity : AppCompatActivity() { getViewModelProvider(this).get(modelClass) fun getViewModelProvider(fragment: Fragment) = - ViewModelProviders.of(fragment, (application as GankApplication).viewModelFactory) + ViewModelProvider(fragment, (application as MyApplication).viewModelFactory) fun getViewModelProvider(activity: FragmentActivity) = - ViewModelProviders.of(activity, (application as GankApplication).viewModelFactory) - - /** - * Modified from https://github.com/uber/AutoDispose - */ - @CheckReturnValue - fun Flowable.autoDisposable(): FlowableSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(scopeProvider)) - - @CheckReturnValue - fun Observable.autoDisposable(): ObservableSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(scopeProvider)) - - @CheckReturnValue - fun Single.autoDisposable(): SingleSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(scopeProvider)) - - @CheckReturnValue - fun Maybe.autoDisposable(): MaybeSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(scopeProvider)) - - @CheckReturnValue - fun Completable.autoDisposable(): CompletableSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(scopeProvider)) - - @CheckReturnValue - fun ParallelFlowable.autoDisposable(): ParallelFlowableSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(scopeProvider)) + ViewModelProvider(activity, (application as MyApplication).viewModelFactory) } diff --git a/app/src/main/java/cn/nekocode/caka/base/BaseFragment.kt b/app/src/main/java/cn/nekocode/caka/base/BaseFragment.kt new file mode 100644 index 0000000..cfbf5e7 --- /dev/null +++ b/app/src/main/java/cn/nekocode/caka/base/BaseFragment.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2019. nekocode (nekocode.cn@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.nekocode.caka.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import cn.nekocode.caka.MyApplication +import cn.nekocode.caka.di.component.buildAndInject +import com.evernote.android.state.StateSaver + +/** + * @author nekocode (nekocode.cn@gmail.com) + */ +abstract class BaseFragment : Fragment { + constructor() : super() + constructor(contentLayoutId: Int) : super(contentLayoutId) + + override fun onCreate(savedInstanceState: Bundle?) { + application?.component?.newFragmentComponentBuilder()?.buildAndInject(this) + super.onCreate(savedInstanceState) + StateSaver.restoreInstanceState(this, savedInstanceState) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + StateSaver.saveInstanceState(this, outState) + } + + val application: MyApplication? + get() = context?.applicationContext as MyApplication? + + fun setTitle(title: String) { + (activity as AppCompatActivity?)?.supportActionBar?.title = title + } + + fun getViewModel(key: String, modelClass: Class) = + getViewModelProvider(this).get(key, modelClass) + + fun getViewModel(modelClass: Class) = + getViewModelProvider(this).get(modelClass) + + fun getViewModelProvider(fragment: Fragment) = + ViewModelProvider(fragment, application!!.viewModelFactory) + + fun getViewModelProvider(activity: FragmentActivity) = + ViewModelProvider(activity, application!!.viewModelFactory) +} \ No newline at end of file diff --git a/app/src/main/java/cn/nekocode/gank/base/BaseViewModel.kt b/app/src/main/java/cn/nekocode/caka/base/BaseViewModel.kt similarity index 55% rename from app/src/main/java/cn/nekocode/gank/base/BaseViewModel.kt rename to app/src/main/java/cn/nekocode/caka/base/BaseViewModel.kt index 55e421e..abe2302 100644 --- a/app/src/main/java/cn/nekocode/gank/base/BaseViewModel.kt +++ b/app/src/main/java/cn/nekocode/caka/base/BaseViewModel.kt @@ -14,17 +14,14 @@ * limitations under the License. */ -package cn.nekocode.gank.base +package cn.nekocode.caka.base import androidx.lifecycle.ViewModel -import com.uber.autodispose.* -import com.uber.autodispose.lifecycle.CorrespondingEventsFunction -import com.uber.autodispose.lifecycle.LifecycleEndedException -import com.uber.autodispose.lifecycle.LifecycleScopeProvider -import io.reactivex.* -import io.reactivex.annotations.CheckReturnValue -import io.reactivex.parallel.ParallelFlowable -import io.reactivex.subjects.BehaviorSubject +import autodispose2.lifecycle.CorrespondingEventsFunction +import autodispose2.lifecycle.LifecycleEndedException +import autodispose2.lifecycle.LifecycleScopeProvider +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.subjects.BehaviorSubject /** * Copied and modified from: https://github.com/uber/AutoDispose @@ -32,24 +29,14 @@ import io.reactivex.subjects.BehaviorSubject * @author nekocode (nekocode.cn@gmail.com) */ abstract class BaseViewModel : ViewModel(), LifecycleScopeProvider { - private val lifecycleEventsDelegate = lazy { BehaviorSubject.createDefault(ViewModelEvent.CREATED) } + private val lifecycleEventsDelegate = + lazy { BehaviorSubject.createDefault(ViewModelEvent.CREATED) } private val lifecycleEvents by lifecycleEventsDelegate enum class ViewModelEvent { CREATED, CLEARED } - companion object { - private val CORRESPONDING_EVENTS = CorrespondingEventsFunction { event -> - when (event) { - ViewModelEvent.CREATED -> ViewModelEvent.CLEARED - else -> throw LifecycleEndedException( - "Cannot bind to ViewModel lifecycle after onCleared." - ) - } - } - } - override fun lifecycle(): Observable { return lifecycleEvents.hide() } @@ -63,7 +50,6 @@ abstract class BaseViewModel : ViewModel(), LifecycleScopeProvider Flowable.autoDisposable(): FlowableSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(this@BaseViewModel)) - - @CheckReturnValue - fun Observable.autoDisposable(): ObservableSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(this@BaseViewModel)) - - @CheckReturnValue - fun Single.autoDisposable(): SingleSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(this@BaseViewModel)) - - @CheckReturnValue - fun Maybe.autoDisposable(): MaybeSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(this@BaseViewModel)) - - @CheckReturnValue - fun Completable.autoDisposable(): CompletableSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(this@BaseViewModel)) - - @CheckReturnValue - fun ParallelFlowable.autoDisposable(): ParallelFlowableSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(this@BaseViewModel)) + companion object { + private val CORRESPONDING_EVENTS = CorrespondingEventsFunction { event -> + when (event) { + ViewModelEvent.CREATED -> ViewModelEvent.CLEARED + else -> throw LifecycleEndedException( + "Cannot bind to ViewModel lifecycle after onCleared." + ) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/cn/nekocode/gank/di/ActivityScope.kt b/app/src/main/java/cn/nekocode/caka/di/ActivityScope.kt similarity index 96% rename from app/src/main/java/cn/nekocode/gank/di/ActivityScope.kt rename to app/src/main/java/cn/nekocode/caka/di/ActivityScope.kt index 52212c6..8990afa 100644 --- a/app/src/main/java/cn/nekocode/gank/di/ActivityScope.kt +++ b/app/src/main/java/cn/nekocode/caka/di/ActivityScope.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package cn.nekocode.gank.di +package cn.nekocode.caka.di import javax.inject.Scope diff --git a/app/src/main/java/cn/nekocode/gank/di/AppScope.kt b/app/src/main/java/cn/nekocode/caka/di/AppScope.kt similarity index 96% rename from app/src/main/java/cn/nekocode/gank/di/AppScope.kt rename to app/src/main/java/cn/nekocode/caka/di/AppScope.kt index ca1238a..29f1869 100644 --- a/app/src/main/java/cn/nekocode/gank/di/AppScope.kt +++ b/app/src/main/java/cn/nekocode/caka/di/AppScope.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package cn.nekocode.gank.di +package cn.nekocode.caka.di import javax.inject.Scope diff --git a/app/src/main/java/cn/nekocode/gank/di/FragmentScope.kt b/app/src/main/java/cn/nekocode/caka/di/FragmentScope.kt similarity index 96% rename from app/src/main/java/cn/nekocode/gank/di/FragmentScope.kt rename to app/src/main/java/cn/nekocode/caka/di/FragmentScope.kt index 87feb94..01cebd4 100644 --- a/app/src/main/java/cn/nekocode/gank/di/FragmentScope.kt +++ b/app/src/main/java/cn/nekocode/caka/di/FragmentScope.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package cn.nekocode.gank.di +package cn.nekocode.caka.di import javax.inject.Scope diff --git a/app/src/main/java/cn/nekocode/gank/di/ViewModelDaggerHelper.kt b/app/src/main/java/cn/nekocode/caka/di/ViewModelDaggerHelper.kt similarity index 98% rename from app/src/main/java/cn/nekocode/gank/di/ViewModelDaggerHelper.kt rename to app/src/main/java/cn/nekocode/caka/di/ViewModelDaggerHelper.kt index c41d53a..72f2506 100644 --- a/app/src/main/java/cn/nekocode/gank/di/ViewModelDaggerHelper.kt +++ b/app/src/main/java/cn/nekocode/caka/di/ViewModelDaggerHelper.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package cn.nekocode.gank.di +package cn.nekocode.caka.di import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider diff --git a/app/src/main/java/cn/nekocode/gank/di/component/ActivityComponent.kt b/app/src/main/java/cn/nekocode/caka/di/component/ActivityComponent.kt similarity index 88% rename from app/src/main/java/cn/nekocode/gank/di/component/ActivityComponent.kt rename to app/src/main/java/cn/nekocode/caka/di/component/ActivityComponent.kt index 52047a4..a980fff 100644 --- a/app/src/main/java/cn/nekocode/gank/di/component/ActivityComponent.kt +++ b/app/src/main/java/cn/nekocode/caka/di/component/ActivityComponent.kt @@ -14,12 +14,12 @@ * limitations under the License. */ -package cn.nekocode.gank.di.component +package cn.nekocode.caka.di.component import android.app.Activity -import cn.nekocode.gank.di.ActivityScope -import cn.nekocode.gank.di.module.ActivityModule -import cn.nekocode.gank.ui.MainActivity +import cn.nekocode.caka.di.ActivityScope +import cn.nekocode.caka.di.module.ActivityModule +import cn.nekocode.caka.ui.MainActivity import dagger.Subcomponent /** diff --git a/app/src/main/java/cn/nekocode/gank/di/component/AppComponent.kt b/app/src/main/java/cn/nekocode/caka/di/component/AppComponent.kt similarity index 75% rename from app/src/main/java/cn/nekocode/gank/di/component/AppComponent.kt rename to app/src/main/java/cn/nekocode/caka/di/component/AppComponent.kt index 84f7b58..6ed13d5 100644 --- a/app/src/main/java/cn/nekocode/gank/di/component/AppComponent.kt +++ b/app/src/main/java/cn/nekocode/caka/di/component/AppComponent.kt @@ -14,14 +14,14 @@ * limitations under the License. */ -package cn.nekocode.gank.di.component +package cn.nekocode.caka.di.component -import cn.nekocode.gank.GankApplication -import cn.nekocode.gank.backend.di.module.ApiModule -import cn.nekocode.gank.di.AppScope -import cn.nekocode.gank.di.module.AppModule -import cn.nekocode.gank.di.module.FlipperModule -import cn.nekocode.gank.di.module.ViewModelModule +import cn.nekocode.caka.MyApplication +import cn.nekocode.caka.backend.di.module.ApiModule +import cn.nekocode.caka.di.AppScope +import cn.nekocode.caka.di.module.AppModule +import cn.nekocode.caka.di.module.FlipperModule +import cn.nekocode.caka.di.module.ViewModelModule import dagger.Component import javax.inject.Singleton @@ -40,7 +40,7 @@ import javax.inject.Singleton ) interface AppComponent { - fun inject(app: GankApplication) + fun inject(app: MyApplication) fun newActivityComponentBuilder(): ActivityComponent.Builder diff --git a/app/src/main/java/cn/nekocode/gank/di/component/FragmentComponent.kt b/app/src/main/java/cn/nekocode/caka/di/component/FragmentComponent.kt similarity index 80% rename from app/src/main/java/cn/nekocode/gank/di/component/FragmentComponent.kt rename to app/src/main/java/cn/nekocode/caka/di/component/FragmentComponent.kt index 9a0a402..ced6074 100644 --- a/app/src/main/java/cn/nekocode/gank/di/component/FragmentComponent.kt +++ b/app/src/main/java/cn/nekocode/caka/di/component/FragmentComponent.kt @@ -14,13 +14,13 @@ * limitations under the License. */ -package cn.nekocode.gank.di.component +package cn.nekocode.caka.di.component import androidx.fragment.app.Fragment -import cn.nekocode.gank.di.FragmentScope -import cn.nekocode.gank.di.module.FragmentModule -import cn.nekocode.gank.ui.home.HomeFragment -import cn.nekocode.gank.ui.pic.PicFragment +import cn.nekocode.caka.di.FragmentScope +import cn.nekocode.caka.di.module.FragmentModule +import cn.nekocode.caka.ui.home.HomeFragment +import cn.nekocode.caka.ui.repo.RepoFragment import dagger.Subcomponent /** @@ -41,7 +41,7 @@ interface FragmentComponent { } fun inject(fragment: HomeFragment) - fun inject(fragment: PicFragment) + fun inject(fragment: RepoFragment) } fun FragmentComponent.Builder.buildAndInject(fragment: Fragment) { @@ -49,6 +49,6 @@ fun FragmentComponent.Builder.buildAndInject(fragment: Fragment) { when (fragment) { is HomeFragment -> component.inject(fragment) - is PicFragment -> component.inject(fragment) + is RepoFragment -> component.inject(fragment) } } \ No newline at end of file diff --git a/app/src/main/java/cn/nekocode/gank/di/module/ActivityModule.kt b/app/src/main/java/cn/nekocode/caka/di/module/ActivityModule.kt similarity index 92% rename from app/src/main/java/cn/nekocode/gank/di/module/ActivityModule.kt rename to app/src/main/java/cn/nekocode/caka/di/module/ActivityModule.kt index c437a3e..00a85fe 100644 --- a/app/src/main/java/cn/nekocode/gank/di/module/ActivityModule.kt +++ b/app/src/main/java/cn/nekocode/caka/di/module/ActivityModule.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package cn.nekocode.gank.di.module +package cn.nekocode.caka.di.module import android.app.Activity -import cn.nekocode.gank.di.ActivityScope +import cn.nekocode.caka.di.ActivityScope import dagger.Module import dagger.Provides diff --git a/app/src/main/java/cn/nekocode/gank/di/module/AppModule.kt b/app/src/main/java/cn/nekocode/caka/di/module/AppModule.kt similarity index 84% rename from app/src/main/java/cn/nekocode/gank/di/module/AppModule.kt rename to app/src/main/java/cn/nekocode/caka/di/module/AppModule.kt index 70709cc..09136f2 100644 --- a/app/src/main/java/cn/nekocode/gank/di/module/AppModule.kt +++ b/app/src/main/java/cn/nekocode/caka/di/module/AppModule.kt @@ -14,12 +14,12 @@ * limitations under the License. */ -package cn.nekocode.gank.di.module +package cn.nekocode.caka.di.module import android.app.Application -import cn.nekocode.gank.di.AppScope -import cn.nekocode.gank.di.component.ActivityComponent -import cn.nekocode.gank.di.component.FragmentComponent +import cn.nekocode.caka.di.AppScope +import cn.nekocode.caka.di.component.ActivityComponent +import cn.nekocode.caka.di.component.FragmentComponent import dagger.Module import dagger.Provides diff --git a/app/src/main/java/cn/nekocode/gank/di/module/FragmentModule.kt b/app/src/main/java/cn/nekocode/caka/di/module/FragmentModule.kt similarity index 92% rename from app/src/main/java/cn/nekocode/gank/di/module/FragmentModule.kt rename to app/src/main/java/cn/nekocode/caka/di/module/FragmentModule.kt index bddeb53..f79b272 100644 --- a/app/src/main/java/cn/nekocode/gank/di/module/FragmentModule.kt +++ b/app/src/main/java/cn/nekocode/caka/di/module/FragmentModule.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package cn.nekocode.gank.di.module +package cn.nekocode.caka.di.module import androidx.fragment.app.Fragment -import cn.nekocode.gank.di.FragmentScope +import cn.nekocode.caka.di.FragmentScope import dagger.Module import dagger.Provides diff --git a/app/src/main/java/cn/nekocode/gank/di/module/ViewModelModule.kt b/app/src/main/java/cn/nekocode/caka/di/module/ViewModelModule.kt similarity index 78% rename from app/src/main/java/cn/nekocode/gank/di/module/ViewModelModule.kt rename to app/src/main/java/cn/nekocode/caka/di/module/ViewModelModule.kt index eb60330..fbc5c1c 100644 --- a/app/src/main/java/cn/nekocode/gank/di/module/ViewModelModule.kt +++ b/app/src/main/java/cn/nekocode/caka/di/module/ViewModelModule.kt @@ -14,13 +14,13 @@ * limitations under the License. */ -package cn.nekocode.gank.di.module +package cn.nekocode.caka.di.module import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider -import cn.nekocode.gank.di.DaggerViewModelFactory -import cn.nekocode.gank.di.ViewModelKey -import cn.nekocode.gank.ui.pic.PicViewModel +import cn.nekocode.caka.di.DaggerViewModelFactory +import cn.nekocode.caka.di.ViewModelKey +import cn.nekocode.caka.ui.home.HomeViewModel import dagger.Binds import dagger.Module import dagger.multibindings.IntoMap @@ -36,6 +36,6 @@ abstract class ViewModelModule { @Binds @IntoMap - @ViewModelKey(PicViewModel::class) - abstract fun bindPicViewModel(viewModel: PicViewModel): ViewModel + @ViewModelKey(HomeViewModel::class) + abstract fun bindHomeViewModel(viewModel: HomeViewModel): ViewModel } \ No newline at end of file diff --git a/app/src/main/java/cn/nekocode/gank/ui/MainActivity.kt b/app/src/main/java/cn/nekocode/caka/ui/MainActivity.kt similarity index 92% rename from app/src/main/java/cn/nekocode/gank/ui/MainActivity.kt rename to app/src/main/java/cn/nekocode/caka/ui/MainActivity.kt index 6dfbfb2..4a38239 100644 --- a/app/src/main/java/cn/nekocode/gank/ui/MainActivity.kt +++ b/app/src/main/java/cn/nekocode/caka/ui/MainActivity.kt @@ -14,13 +14,13 @@ * limitations under the License. */ -package cn.nekocode.gank.ui +package cn.nekocode.caka.ui import android.os.Bundle import androidx.navigation.findNavController import androidx.navigation.ui.setupActionBarWithNavController -import cn.nekocode.gank.R -import cn.nekocode.gank.base.BaseActivity +import cn.nekocode.caka.R +import cn.nekocode.caka.base.BaseActivity /** * @author nekocode (nekocode.cn@gmail.com) diff --git a/app/src/main/java/cn/nekocode/gank/ui/pic/PicFragment.kt b/app/src/main/java/cn/nekocode/caka/ui/home/HomeFragment.kt similarity index 51% rename from app/src/main/java/cn/nekocode/gank/ui/pic/PicFragment.kt rename to app/src/main/java/cn/nekocode/caka/ui/home/HomeFragment.kt index 12865f9..31e69a5 100644 --- a/app/src/main/java/cn/nekocode/gank/ui/pic/PicFragment.kt +++ b/app/src/main/java/cn/nekocode/caka/ui/home/HomeFragment.kt @@ -14,38 +14,39 @@ * limitations under the License. */ -package cn.nekocode.gank.ui.pic +package cn.nekocode.caka.ui.home import android.os.Bundle -import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import androidx.lifecycle.Observer -import cn.nekocode.gank.R -import cn.nekocode.gank.base.BaseFragment -import com.squareup.picasso.Picasso -import kotlinx.android.synthetic.main.fragment_pic.* +import androidx.navigation.fragment.findNavController +import cn.nekocode.caka.R +import cn.nekocode.caka.base.BaseFragment +import cn.nekocode.caka.databinding.FragmentHomeBinding /** * @author nekocode (nekocode.cn@gmail.com) */ -class PicFragment : BaseFragment() { +class HomeFragment : BaseFragment(R.layout.fragment_home) { private val vm by lazy { - getViewModel(PicViewModel::class.java) - } - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_pic, container, false) + getViewModel(HomeViewModel::class.java) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + val binding = FragmentHomeBinding.bind(view) + + val adapter = ReposAdapter { repo -> + findNavController().navigate(HomeFragmentDirections.actionHomeToRepo(repo)) + } + binding.recyclerView.adapter = adapter + binding.recyclerView.visibility = View.INVISIBLE + binding.progressBar.visibility = View.VISIBLE - vm.liveDataPic.observe(this, Observer { - Picasso.get().load(it.url).centerCrop().fit().into(imageView) + vm.reposLiveData.observe(viewLifecycleOwner, Observer { repos -> + adapter.submitList(repos) + binding.recyclerView.visibility = View.VISIBLE + binding.progressBar.visibility = View.INVISIBLE }) } } diff --git a/app/src/main/java/cn/nekocode/caka/ui/home/HomeViewModel.kt b/app/src/main/java/cn/nekocode/caka/ui/home/HomeViewModel.kt new file mode 100644 index 0000000..05b432b --- /dev/null +++ b/app/src/main/java/cn/nekocode/caka/ui/home/HomeViewModel.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2021. nekocode (nekocode.cn@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.nekocode.caka.ui.home + +import android.app.Application +import android.widget.Toast +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import autodispose2.autoDispose +import cn.nekocode.caka.backend.api.RepoApi +import cn.nekocode.caka.backend.model.Repository +import cn.nekocode.caka.base.BaseViewModel +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.schedulers.Schedulers +import javax.inject.Inject + +/** + * @author nekocode (nekocode.cn@gmail.com) + */ +class HomeViewModel @Inject constructor( + private val app: Application, + private val repoApi: RepoApi +) : BaseViewModel() { + val reposLiveData: LiveData> = MutableLiveData() + + init { + fetchRepos() + } + + private fun fetchRepos() { + reposLiveData as MutableLiveData + repoApi.listRepos("nekocode") + .retry() + .map { repos -> + repos.sortedByDescending { it.stargazersCount } + } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .autoDispose(this) + .subscribe({ repos -> + reposLiveData.postValue(repos) + }, { err -> + Toast.makeText(app, err.message, Toast.LENGTH_SHORT).show() + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/nekocode/caka/ui/home/ReposAdapter.kt b/app/src/main/java/cn/nekocode/caka/ui/home/ReposAdapter.kt new file mode 100644 index 0000000..fafa38e --- /dev/null +++ b/app/src/main/java/cn/nekocode/caka/ui/home/ReposAdapter.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2021. nekocode (nekocode.cn@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.nekocode.caka.ui.home + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import cn.nekocode.caka.R +import cn.nekocode.caka.backend.model.Repository +import cn.nekocode.caka.databinding.ItemRepoBinding + +/** + * @author nekocode (nekocode.cn@gmail.com) + */ +class ReposAdapter( + private val onClick: (Repository) -> Unit +) : ListAdapter(RepositoryDiffCallback) { + + inner class ViewHolder( + private val binding: ItemRepoBinding + ) : RecyclerView.ViewHolder(binding.root) { + private var currentRepo: Repository? = null + + init { + itemView.setOnClickListener { + currentRepo?.let { + onClick(it) + } + } + } + + fun bind(repo: Repository) { + currentRepo = repo + + val context = itemView.context + binding.tvTitle.text = repo.name + binding.tvBody.text = repo.description + binding.tvBottom.text = context.getString( + R.string.repo_bottom_text, + repo.stargazersCount, + repo.forksCount + ) + } + } + + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(viewGroup.context) + .inflate(R.layout.item_repo, viewGroup, false) + return ViewHolder(ItemRepoBinding.bind(view)) + } + + override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { + val repo = getItem(position) + viewHolder.bind(repo) + } +} + +object RepositoryDiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Repository, newItem: Repository): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame(oldItem: Repository, newItem: Repository): Boolean { + return oldItem.id == newItem.id + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/nekocode/gank/ui/home/HomeFragment.kt b/app/src/main/java/cn/nekocode/caka/ui/repo/RepoFragment.kt similarity index 66% rename from app/src/main/java/cn/nekocode/gank/ui/home/HomeFragment.kt rename to app/src/main/java/cn/nekocode/caka/ui/repo/RepoFragment.kt index a436a54..6c99b38 100644 --- a/app/src/main/java/cn/nekocode/gank/ui/home/HomeFragment.kt +++ b/app/src/main/java/cn/nekocode/caka/ui/repo/RepoFragment.kt @@ -14,35 +14,35 @@ * limitations under the License. */ -package cn.nekocode.gank.ui.home +package cn.nekocode.caka.ui.repo import android.os.Bundle -import android.text.Html import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.navigation.fragment.findNavController -import cn.nekocode.gank.R -import cn.nekocode.gank.base.BaseFragment -import kotlinx.android.synthetic.main.fragment_home.* +import androidx.navigation.fragment.navArgs +import cn.nekocode.caka.R +import cn.nekocode.caka.base.BaseFragment +import cn.nekocode.caka.databinding.FragmentRepoBinding /** * @author nekocode (nekocode.cn@gmail.com) */ -class HomeFragment : BaseFragment() { +class RepoFragment : BaseFragment() { + private val args: RepoFragmentArgs by navArgs() + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_home, container, false) + setTitle(args.repo.name) + return inflater.inflate(R.layout.fragment_repo, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + val binding = FragmentRepoBinding.bind(view) - fetchBtn.text = Html.fromHtml(getString(R.string.fetch_pic)) - fetchBtn.setOnClickListener { - findNavController().navigate(HomeFragmentDirections.actionHomeToPic()) - } + binding.webView.loadUrl(args.repo.url) } } diff --git a/app/src/main/java/cn/nekocode/gank/base/BaseFragment.kt b/app/src/main/java/cn/nekocode/gank/base/BaseFragment.kt deleted file mode 100644 index cf7a942..0000000 --- a/app/src/main/java/cn/nekocode/gank/base/BaseFragment.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2019. nekocode (nekocode.cn@gmail.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package cn.nekocode.gank.base - -import android.os.Bundle -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProviders -import cn.nekocode.gank.GankApplication -import cn.nekocode.gank.di.component.buildAndInject -import com.evernote.android.state.StateSaver -import com.uber.autodispose.* -import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider -import io.reactivex.* -import io.reactivex.annotations.CheckReturnValue -import io.reactivex.parallel.ParallelFlowable - -/** - * @author nekocode (nekocode.cn@gmail.com) - */ -abstract class BaseFragment : Fragment() { - private val scopeProvider by lazy { AndroidLifecycleScopeProvider.from(this) } - - override fun onCreate(savedInstanceState: Bundle?) { - (context?.applicationContext as GankApplication?)?.component - ?.newFragmentComponentBuilder()?.buildAndInject(this) - super.onCreate(savedInstanceState) - StateSaver.restoreInstanceState(this, savedInstanceState) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - StateSaver.saveInstanceState(this, outState) - } - - fun getViewModel(key: String, modelClass: Class) = - getViewModelProvider(this).get(key, modelClass) - - fun getViewModel(modelClass: Class) = - getViewModelProvider(this).get(modelClass) - - fun getViewModelProvider(fragment: Fragment) = - ViewModelProviders.of(fragment, (requireActivity().application as GankApplication).viewModelFactory) - - fun getViewModelProvider(activity: FragmentActivity) = - ViewModelProviders.of(activity, (requireActivity().application as GankApplication).viewModelFactory) - - /** - * Modified from https://github.com/uber/AutoDispose - */ - @CheckReturnValue - fun Flowable.autoDisposable(): FlowableSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(scopeProvider)) - - @CheckReturnValue - fun Observable.autoDisposable(): ObservableSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(scopeProvider)) - - @CheckReturnValue - fun Single.autoDisposable(): SingleSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(scopeProvider)) - - @CheckReturnValue - fun Maybe.autoDisposable(): MaybeSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(scopeProvider)) - - @CheckReturnValue - fun Completable.autoDisposable(): CompletableSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(scopeProvider)) - - @CheckReturnValue - fun ParallelFlowable.autoDisposable(): ParallelFlowableSubscribeProxy = - this.`as`(AutoDispose.autoDisposable(scopeProvider)) -} \ No newline at end of file diff --git a/app/src/main/java/cn/nekocode/gank/ui/pic/PicViewModel.kt b/app/src/main/java/cn/nekocode/gank/ui/pic/PicViewModel.kt deleted file mode 100644 index dc4c224..0000000 --- a/app/src/main/java/cn/nekocode/gank/ui/pic/PicViewModel.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2019. nekocode (nekocode.cn@gmail.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package cn.nekocode.gank.ui.pic - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import cn.nekocode.gank.backend.api.PicApi -import cn.nekocode.gank.backend.model.MeiziPic -import cn.nekocode.gank.base.BaseViewModel -import io.reactivex.schedulers.Schedulers -import javax.inject.Inject - -/** - * @author nekocode (nekocode.cn@gmail.com) - */ -class PicViewModel @Inject constructor( - private val picApi: PicApi -) : BaseViewModel() { - val liveDataPic: LiveData = MutableLiveData() - - init { - fetchOnePic() - } - - fun fetchOnePic() { - liveDataPic as MutableLiveData - picApi.getMeiziPics(1, (1..10).random()) - .subscribeOn(Schedulers.io()) - .autoDisposable() - .subscribe { response -> - liveDataPic.postValue(response.results[0]) - } - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index cfb56de..68d5872 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -1,23 +1,32 @@ - + tools:context=".ui.home.HomeFragment"> -