├── base ├── consumer-rules.pro ├── .gitignore ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── samiu │ │ └── base │ │ ├── ui │ │ ├── BaseActivity.kt │ │ ├── BaseFragment.kt │ │ ├── DataBindingDelegate.kt │ │ └── ViewBindingDelegate.kt │ │ ├── http │ │ ├── NetWorkUtils.kt │ │ ├── CacheInterceptor.kt │ │ └── RetrofitClient.kt │ │ ├── adapter │ │ └── BaseSingleRecyclerAdapter.kt │ │ └── interactive │ │ └── ZoomOutPageTransformer.kt ├── proguard-rules.pro └── build.gradle.kts ├── res ├── consumer-rules.pro ├── .gitignore ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── res │ │ ├── drawable │ │ ├── ic_menu_white.xml │ │ ├── ic_more_white.xml │ │ ├── ic_search_white.xml │ │ ├── avatar_none.xml │ │ ├── ic_arrow_drop_up.xml │ │ ├── ic_forward.xml │ │ ├── ic_done.xml │ │ ├── ic_reply.xml │ │ ├── ic_arrow_down.xml │ │ ├── ic_delete.xml │ │ ├── ic_reply_all.xml │ │ ├── ic_close.xml │ │ ├── ic_edit.xml │ │ ├── ic_twotone_add_circle_outline.xml │ │ ├── bottom_app_bar_title_foreground.xml │ │ ├── ic_search.xml │ │ ├── ic_archive.xml │ │ ├── ic_twotone_forward.xml │ │ ├── ic_twotone_delete.xml │ │ ├── ic_twotone_folder.xml │ │ ├── ic_twotone_send.xml │ │ ├── ic_twotone_drafts.xml │ │ ├── ic_twotone_star.xml │ │ ├── asl_edit_reply.xml │ │ ├── ic_twotone_error.xml │ │ ├── ic_twotone_star_on_background.xml │ │ ├── ic_twotone_inbox.xml │ │ ├── ic_settings.xml │ │ └── ic_twotone_stars.xml │ │ ├── values │ │ ├── ids.xml │ │ ├── motion.xml │ │ ├── elevation.xml │ │ ├── preloaded_fonts.xml │ │ ├── layout.xml │ │ ├── dimens.xml │ │ ├── attrs.xml │ │ └── shape.xml │ │ ├── color │ │ ├── color_on_surface_divider.xml │ │ ├── color_on_primary_surface_divider.xml │ │ ├── color_on_surface_emphasis_high.xml │ │ ├── color_on_surface_emphasis_medium.xml │ │ ├── color_on_surface_emphasis_disabled.xml │ │ ├── color_on_primary_surface_emphasis_high.xml │ │ ├── color_on_primary_surface_emphasis_medium.xml │ │ ├── color_on_primary_surface_emphasis_disabled.xml │ │ └── color_navigation_drawer_menu_item.xml │ │ ├── animator │ │ ├── fade_in.xml │ │ ├── fade_out.xml │ │ ├── slide_in_left.xml │ │ ├── slide_out_right.xml │ │ ├── slide_in_right.xml │ │ ├── slide_out_left.xml │ │ ├── fab_hide.xml │ │ └── fab_show.xml │ │ └── font │ │ ├── work_sans.xml │ │ ├── work_sans_bold.xml │ │ ├── work_sans_extrabold.xml │ │ ├── work_sans_medium.xml │ │ └── work_sans_semibold.xml ├── build.gradle.kts └── proguard-rules.pro ├── app ├── .gitignore ├── buildkey │ └── mykey.jks ├── src │ └── main │ │ ├── res │ │ ├── menu │ │ │ ├── bottom_app_bar_empty_menu.xml │ │ │ ├── bottom_app_bar_home_menu.xml │ │ │ ├── bottom_app_bar_settings_menu.xml │ │ │ └── bottom_app_bar_email_menu.xml │ │ ├── drawable-xxxhdpi │ │ │ ├── my_qr.png │ │ │ ├── avatar.png │ │ │ ├── ic_time.png │ │ │ ├── splash.png │ │ │ ├── ic_author.png │ │ │ ├── logo_main.png │ │ │ ├── back_icon_black.png │ │ │ └── login_btn_back_white.png │ │ ├── anim │ │ │ ├── in_bottom.xml │ │ │ └── out_bottom.xml │ │ ├── drawable │ │ │ ├── arrow_back.xml │ │ │ ├── ic_wan_project_24dp.xml │ │ │ ├── ic_wan_home_24dp.xml │ │ │ ├── ic_mine_24dp.xml │ │ │ ├── ic_search_24dp.xml │ │ │ ├── ic_wan_search_24dp.xml │ │ │ ├── ic_gank_home_24dp.xml │ │ │ ├── ic_twotone_star_on_background.xml │ │ │ └── color_progressbar.xml │ │ ├── xml │ │ │ └── network_security_config.xml │ │ ├── layout │ │ │ ├── layout_image_dialog.xml │ │ │ ├── fragment_recycler.xml │ │ │ ├── fragment_wan_square.xml │ │ │ ├── item_wan_verti_title.xml │ │ │ ├── fragment_wan_system.xml │ │ │ ├── item_wan_type.xml │ │ │ ├── item_wan_system.xml │ │ │ ├── fragment_wan_home.xml │ │ │ ├── activity_browser.xml │ │ │ ├── fragment_wan_wx_article.xml │ │ │ ├── fragment_wan_project.xml │ │ │ ├── nav_menu_item_layout.xml │ │ │ ├── activity_wan_personal.xml │ │ │ └── activity_system_display.xml │ │ ├── values │ │ │ ├── dimens.xml │ │ │ ├── styles.xml │ │ │ └── colors.xml │ │ └── navigation │ │ │ └── main_navigation.xml │ │ ├── java │ │ └── com │ │ │ └── samiu │ │ │ └── wangank │ │ │ ├── bean │ │ │ ├── base │ │ │ │ ├── WanResponse.kt │ │ │ │ └── WanResult.kt │ │ │ ├── Navigation.kt │ │ │ ├── Account.kt │ │ │ ├── Hot.kt │ │ │ ├── Banner.kt │ │ │ ├── SystemParent.kt │ │ │ ├── ArticleList.kt │ │ │ ├── SystemChild.kt │ │ │ ├── ShareArticle.kt │ │ │ ├── User.kt │ │ │ └── Article.kt │ │ │ ├── ui │ │ │ ├── home │ │ │ │ ├── HomeScrollListener.kt │ │ │ │ ├── WanHomeViewModel.kt │ │ │ │ ├── WanHomeRepository.kt │ │ │ │ ├── adapter │ │ │ │ │ ├── WanArticleAdapter.kt │ │ │ │ │ ├── WanBannerAdapter.kt │ │ │ │ │ └── ArticleListenerImpl.kt │ │ │ │ └── HomeArticlePagingSource.kt │ │ │ ├── square │ │ │ │ ├── WanSquareViewModel.kt │ │ │ │ ├── WanSquareRepository.kt │ │ │ │ ├── SquareArticlePagingSource.kt │ │ │ │ └── WanSquareFragment.kt │ │ │ ├── view │ │ │ │ └── MarqueeTextView.kt │ │ │ ├── mine │ │ │ │ ├── collect │ │ │ │ │ ├── WanCollectViewModel.kt │ │ │ │ │ ├── adapter │ │ │ │ │ │ ├── CollectArticleAdapter.kt │ │ │ │ │ │ └── CollectArticleListenerImpl.kt │ │ │ │ │ └── WanCollectFragment.kt │ │ │ │ ├── personal │ │ │ │ │ ├── WanPersonalViewModel.kt │ │ │ │ │ ├── CollectArticlePagingSource.kt │ │ │ │ │ └── MyArticlePagingSource.kt │ │ │ │ ├── share │ │ │ │ │ ├── WanShareViewModel.kt │ │ │ │ │ └── WanShareActivity.kt │ │ │ │ ├── login │ │ │ │ │ ├── WanLoginViewModel.kt │ │ │ │ │ └── WanLoginActivity.kt │ │ │ │ └── WanMineRepository.kt │ │ │ ├── search │ │ │ │ ├── WanSearchRepository.kt │ │ │ │ └── WanSearchViewModel.kt │ │ │ ├── wxpub │ │ │ │ ├── WanWxRepository.kt │ │ │ │ ├── WanWxViewModel.kt │ │ │ │ └── adapter │ │ │ │ │ ├── WxArticleAdapter.kt │ │ │ │ │ ├── WxArticleListenerImpl.kt │ │ │ │ │ └── WanTitleAdapter.kt │ │ │ ├── base │ │ │ │ ├── SplashActivity.kt │ │ │ │ └── MineActivity.kt │ │ │ ├── system │ │ │ │ ├── WanSystemRepository.kt │ │ │ │ ├── SystemDisplayViewModel.kt │ │ │ │ ├── WanSystemFragment.kt │ │ │ │ └── WanSystemViewModel.kt │ │ │ ├── nav │ │ │ │ ├── OnSandwichSlideAction.kt │ │ │ │ └── adapter │ │ │ │ │ ├── NavigationViewHolder.kt │ │ │ │ │ ├── NavigationAdapter.kt │ │ │ │ │ └── NavigationModelItem.kt │ │ │ └── project │ │ │ │ ├── WanProjectRepository.kt │ │ │ │ ├── adapter │ │ │ │ ├── WanProjectAdapter.kt │ │ │ │ └── WanTypeAdapter.kt │ │ │ │ └── WanProjectViewModel.kt │ │ │ ├── http │ │ │ ├── WanClient.kt │ │ │ └── BaseWanRepository.kt │ │ │ ├── global │ │ │ └── Constants.kt │ │ │ ├── base │ │ │ ├── AppApplication.kt │ │ │ └── module.kt │ │ │ └── util │ │ │ ├── JumpHelper.kt │ │ │ ├── ContextExtensions.kt │ │ │ └── ShapeDrawableHelper.kt │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── settings.gradle.kts ├── LICENSE └── gradlew.bat /base/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /base/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /res/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/buildkey/mykey.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamiuZhong/WanAndroidGank/HEAD/app/buildkey/mykey.jks -------------------------------------------------------------------------------- /app/src/main/res/menu/bottom_app_bar_empty_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamiuZhong/WanAndroidGank/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /res/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/my_qr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamiuZhong/WanAndroidGank/HEAD/app/src/main/res/drawable-xxxhdpi/my_qr.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamiuZhong/WanAndroidGank/HEAD/app/src/main/res/drawable-xxxhdpi/avatar.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamiuZhong/WanAndroidGank/HEAD/app/src/main/res/drawable-xxxhdpi/ic_time.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamiuZhong/WanAndroidGank/HEAD/app/src/main/res/drawable-xxxhdpi/splash.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_author.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamiuZhong/WanAndroidGank/HEAD/app/src/main/res/drawable-xxxhdpi/ic_author.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/logo_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamiuZhong/WanAndroidGank/HEAD/app/src/main/res/drawable-xxxhdpi/logo_main.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | release 9 | .externalNativeBuild 10 | .cxx 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/back_icon_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamiuZhong/WanAndroidGank/HEAD/app/src/main/res/drawable-xxxhdpi/back_icon_black.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/login_btn_back_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamiuZhong/WanAndroidGank/HEAD/app/src/main/res/drawable-xxxhdpi/login_btn_back_white.png -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/bean/base/WanResponse.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.bean.base 2 | 3 | /** 4 | * @author Samiu 2020/3/3 5 | * @email samiuzhong@outlook.com 6 | */ 7 | data class WanResponse(val errorCode: Int, val errorMsg: String, val data: T?) -------------------------------------------------------------------------------- /app/src/main/res/anim/in_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/out_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Mar 04 14:27:58 CST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/bean/Navigation.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.bean 2 | 3 | /** 4 | * @author Samiu 2020/3/3 5 | * @email samiuzhong@outlook.com 6 | */ 7 | data class Navigation(val articles: List
, 8 | val cid: Int, 9 | val name: String) -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/bean/Account.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.bean 2 | 3 | import androidx.annotation.DrawableRes 4 | 5 | /** 6 | * @author Samiu 2020/4/14 7 | * @email samiuzhong@outlook.com 8 | */ 9 | data class Account( 10 | val id:Long, 11 | @DrawableRes val avatar:Int 12 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/home/HomeScrollListener.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.home 2 | 3 | /** 4 | * @author Samiu 2022/3/8 5 | * @email samiuzhong@outlook.com 6 | */ 7 | const val SCROLL_UP = 0x0001 8 | const val SCROLL_DOWN = 0x0002 9 | 10 | interface HomeScrollListener { 11 | 12 | fun onHomeScrolled(scrollType: Int) 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/bean/Hot.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.bean 2 | 3 | /** 4 | * @author Samiu 2020/3/3 5 | * @email samiuzhong@outlook.com 6 | */ 7 | data class Hot(val id: Int, 8 | val link: String, 9 | val name: String, 10 | val order: Int, 11 | val visible: Int, 12 | val icon: String) -------------------------------------------------------------------------------- /app/src/main/res/drawable/arrow_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_menu_white.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/menu/bottom_app_bar_home_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_wan_project_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_image_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_wan_home_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/bean/Banner.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.bean 2 | 3 | /** 4 | * @author Samiu 2020/3/3 5 | * @email samiuzhong@outlook.com 6 | */ 7 | data class Banner(val desc: String, 8 | val id: Int, 9 | val imagePath: String, 10 | val isVisible: Int, 11 | val order: Int, 12 | val title: String, 13 | val type: Int, 14 | val url: String) -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_mine_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/bean/SystemParent.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.bean 2 | 3 | /** 4 | * @author Samiu 2020/3/3 5 | * @email samiuzhong@outlook.com 6 | */ 7 | data class SystemParent( 8 | val children: List, 9 | val courseId: Int, 10 | val id: Int, 11 | val name: String, 12 | val order: Int, 13 | val parentChapterId: Int, 14 | val visible: Int, 15 | var isSelected: Boolean = false, 16 | val userControlSetTop: Boolean 17 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/bean/ArticleList.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.bean 2 | 3 | /** 4 | * @author Samiu 2020/3/3 5 | * @email samiuzhong@outlook.com 6 | */ 7 | data class ArticleList( val offset: Int, 8 | val size: Int, 9 | val total: Int, 10 | val pageCount: Int, 11 | val curPage: Int, 12 | val over: Boolean, 13 | val datas: List
) -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/bean/SystemChild.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.bean 2 | 3 | /** 4 | * @author Samiu 2020/3/3 5 | * @email samiuzhong@outlook.com 6 | */ 7 | data class SystemChild(val child: List, 8 | val courseId: Int, 9 | val id: Int, 10 | val name: String, 11 | val order: Int, 12 | val parentChapterId: Int, 13 | val visible: Int) -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/http/WanClient.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.http 2 | 3 | import com.samiu.base.http.BaseRetrofitClient 4 | import com.samiu.wangank.base.AppApplication 5 | 6 | /** 7 | * @author Samiu 2020/3/3 8 | * @email samiuzhong@outlook.com 9 | */ 10 | const val WAN_BASE_URL = "https://www.wanandroid.com" 11 | 12 | object WanClient : BaseRetrofitClient(AppApplication.context, 13 | WAN_BASE_URL 14 | ) { 15 | val service by lazy { getService(WanApiService::class.java) } 16 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_recycler.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_wan_square.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/square/WanSquareViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.square 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import androidx.paging.cachedIn 6 | 7 | /** 8 | * @author Samiu 2020/3/5 9 | * @email samiuzhong@outlook.com 10 | */ 11 | class WanSquareViewModel( 12 | wanSquareRepository: WanSquareRepository 13 | ) : ViewModel() { 14 | 15 | val articlePagingList = wanSquareRepository.getArticlePaging().cachedIn(viewModelScope) 16 | } -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_more_white.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /res/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.samiu.buildsrc.BuildConfig 2 | import com.samiu.buildsrc.Deps 3 | 4 | plugins { 5 | id("com.android.library") 6 | kotlin("android") 7 | } 8 | 9 | android { 10 | compileSdk = BuildConfig.compileSdk 11 | 12 | defaultConfig { 13 | minSdk = BuildConfig.minSdk 14 | targetSdk = BuildConfig.targetSdk 15 | } 16 | } 17 | 18 | dependencies { 19 | implementation(Deps.AndroidX.coreKtx) 20 | implementation(Deps.AndroidX.appcompat) 21 | 22 | api(Deps.Google.material) 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/view/MarqueeTextView.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import androidx.appcompat.widget.AppCompatTextView 6 | 7 | /** 8 | * @author Samiu 2020/3/4 9 | * @email samiuzhong@outlook.com 10 | */ 11 | class MarqueeTextView @JvmOverloads constructor( 12 | context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 13 | ) : AppCompatTextView(context, attrs, defStyleAttr) { 14 | 15 | override fun isFocused() = true 16 | } -------------------------------------------------------------------------------- /base/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/bean/base/WanResult.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.bean.base 2 | 3 | /** 4 | * @author Samiu 2020/3/3 5 | * @email samiuzhong@outlook.com 6 | */ 7 | sealed class WanResult { 8 | 9 | data class Success(val data: T?) : WanResult() 10 | data class Error(val msg: String) : WanResult() 11 | 12 | override fun toString(): String { 13 | return when (this) { 14 | is Success<*> -> "Success[data=$data]" 15 | is Error -> "Error[msg=$msg]" 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/mine/collect/WanCollectViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.mine.collect 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import androidx.paging.cachedIn 6 | import com.samiu.wangank.ui.mine.WanMineRepository 7 | 8 | /** 9 | * @author Samiu 2022/3/12 10 | * @email samiuzhong@outlook.com 11 | */ 12 | class WanCollectViewModel( 13 | mineRepository: WanMineRepository 14 | ) : ViewModel() { 15 | 16 | val articlePagingList = mineRepository.getCollectPaging().cachedIn(viewModelScope) 17 | } -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_search_white.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/search/WanSearchRepository.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.search 2 | 3 | import com.samiu.wangank.http.BaseWanRepository 4 | import com.samiu.wangank.http.WanClient 5 | 6 | /** 7 | * @author Samiu 2020/3/7 8 | * @email samiuzhong@outlook.com 9 | */ 10 | class WanSearchRepository : BaseWanRepository() { 11 | 12 | suspend fun getSearchArticle(page: Int, key: String) = readyCall{ 13 | call(WanClient.service.searchHot(page, key)) 14 | } 15 | 16 | suspend fun getHotKey() = readyCall{ 17 | call(WanClient.service.getHot()) 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_wan_search_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_gank_home_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_wan_verti_title.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/menu/bottom_app_bar_settings_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_wan_system.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/global/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.global 2 | 3 | /** 4 | * @author Samiu 2020/3/4 5 | * @email samiuzhong@outlook.com 6 | */ 7 | const val URL = "url" 8 | const val CID = "cid" 9 | const val TITLE = "title" 10 | const val NETWORK_ERROR = "请检查网络" 11 | const val WAN_ANDROID = "wanAndroid" 12 | const val IS_LOGIN = "isLogin" 13 | const val USER_NAME = "userName" 14 | const val WAN_ERROR = "wanError" 15 | 16 | const val REFRESH = -1 17 | const val LOAD_MORE = 1 18 | const val DEFAULT_PAGE_SIZE = 20 19 | 20 | const val GITHUB_PAGE = "https://github.com/SamiuZhong/WanAndroidGank" 21 | const val MY_BLOG = "https://samiu.top/" -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 10dp 3 | 10dp 4 | 20dp 5 | 30dp 6 | 40dp 7 | 12sp 8 | 14sp 9 | 16sp 10 | 18sp 11 | 20sp 12 | 5dp 13 | 20dp 14 | 24dp 15 | 0dp 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/bean/ShareArticle.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.bean 2 | 3 | /** 4 | * @author Samiu 2022/3/12 5 | * @email samiuzhong@outlook.com 6 | */ 7 | data class ShareArticle( 8 | val coinInfo: CoinInfo, 9 | val shareArticles: ShareArticles 10 | ) 11 | 12 | data class CoinInfo( 13 | val coinCount: Int, 14 | val level: Int, 15 | val nickname: String, 16 | val rank: String, 17 | val userId: Int, 18 | val username: String 19 | ) 20 | 21 | data class ShareArticles( 22 | val curPage: Int, 23 | val datas: List
, 24 | val offset: Int, 25 | val over: Boolean, 26 | val pageCount: Int, 27 | val size: Int, 28 | val total: Int 29 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/bean/User.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.bean 2 | 3 | /** 4 | * @author Samiu 2020/5/11 5 | * @email samiuzhong@outlook.com 6 | */ 7 | data class User( 8 | val admin: Boolean, 9 | val chapterTops: List, 10 | val collectIds: List, 11 | val email: String, 12 | val icon: String, 13 | val id: Int, 14 | val nickname: String, 15 | val password: String, 16 | val publicName: String, 17 | val token: String, 18 | val type: Int, 19 | val username: String 20 | ) { 21 | override fun equals(other: Any?): Boolean { 22 | return if (other is User) { 23 | this.id == other.id 24 | } else false 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/item_wan_type.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/base/AppApplication.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.base 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import org.koin.android.ext.koin.androidContext 6 | import org.koin.core.context.startKoin 7 | import kotlin.properties.Delegates 8 | 9 | /** 10 | * @author Samiu 2020/3/3 11 | * @email samiuzhong@outlook.com 12 | */ 13 | class AppApplication : Application() { 14 | 15 | companion object { 16 | var context: Context by Delegates.notNull() 17 | } 18 | 19 | override fun onCreate() { 20 | super.onCreate() 21 | context = applicationContext 22 | 23 | startKoin { 24 | androidContext(this@AppApplication) 25 | modules(module) 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/wxpub/WanWxRepository.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.wxpub 2 | 3 | import com.samiu.wangank.bean.ArticleList 4 | import com.samiu.wangank.bean.SystemParent 5 | import com.samiu.wangank.http.BaseWanRepository 6 | import com.samiu.wangank.http.WanClient 7 | import com.samiu.wangank.bean.base.WanResult 8 | 9 | /** 10 | * @author Samiu 2020/3/5 11 | * @email samiuzhong@outlook.com 12 | */ 13 | class WanWxRepository : BaseWanRepository() { 14 | 15 | suspend fun getWxAccount(): WanResult> = readyCall { 16 | call(WanClient.service.getBlogType()) 17 | } 18 | 19 | suspend fun getWxArticle(id: Int, page: Int): WanResult = readyCall { 20 | call(WanClient.service.getBlogArticle(id, page)) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/base/SplashActivity.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.base 2 | 3 | import android.content.Intent 4 | import androidx.appcompat.app.AppCompatActivity 5 | import android.os.Bundle 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.runBlocking 8 | 9 | /** 10 | * 启动页 11 | * 12 | * @author Samiu 2020/5/21 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class SplashActivity : AppCompatActivity() { 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | jump() 20 | } 21 | 22 | private fun jump() { 23 | runBlocking { 24 | delay(1000) 25 | } 26 | startActivity(Intent(this, MainActivity::class.java)) 27 | finish() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/system/WanSystemRepository.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.system 2 | 3 | import com.samiu.wangank.bean.ArticleList 4 | import com.samiu.wangank.bean.SystemParent 5 | import com.samiu.wangank.http.BaseWanRepository 6 | import com.samiu.wangank.http.WanClient 7 | import com.samiu.wangank.bean.base.WanResult 8 | 9 | /** 10 | * @author Samiu 2020/3/6 11 | * @email samiuzhong@outlook.com 12 | */ 13 | class WanSystemRepository : BaseWanRepository() { 14 | 15 | suspend fun getSystem(): WanResult> = readyCall { 16 | call(WanClient.service.getSystemType()) 17 | } 18 | 19 | suspend fun getSystemDetail(page: Int, cid: Int): WanResult = readyCall { 20 | call(WanClient.service.getSystemTypeDetail(page, cid)) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_twotone_star_on_background.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /res/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/square/WanSquareRepository.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.square 2 | 3 | import androidx.paging.Pager 4 | import androidx.paging.PagingConfig 5 | import com.samiu.wangank.global.DEFAULT_PAGE_SIZE 6 | import com.samiu.wangank.http.BaseWanRepository 7 | import com.samiu.wangank.http.WanClient 8 | import com.samiu.wangank.ui.home.HomeArticlePagingSource 9 | import com.samiu.wangank.ui.home.WanHomeRepository 10 | 11 | /** 12 | * @author Samiu 2020/3/5 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class WanSquareRepository : BaseWanRepository() { 16 | 17 | fun getArticlePaging() = Pager( 18 | PagingConfig( 19 | pageSize = DEFAULT_PAGE_SIZE, 20 | prefetchDistance = DEFAULT_PAGE_SIZE, 21 | ) 22 | ) { 23 | SquareArticlePagingSource() 24 | }.flow 25 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle.kts. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /res/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle.kts. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /base/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle.kts. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/res/menu/bottom_app_bar_email_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | 17 | 22 | -------------------------------------------------------------------------------- /res/src/main/res/color/color_on_surface_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /res/src/main/res/color/color_on_primary_surface_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/avatar_none.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /res/src/main/res/color/color_on_surface_emphasis_high.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /res/src/main/res/color/color_on_surface_emphasis_medium.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /res/src/main/res/color/color_on_surface_emphasis_disabled.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/mine/personal/WanPersonalViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.mine.personal 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import androidx.paging.cachedIn 6 | import com.samiu.wangank.global.IS_LOGIN 7 | import com.samiu.wangank.ui.mine.WanMineRepository 8 | import com.samiu.wangank.util.Preference 9 | import kotlinx.coroutines.launch 10 | 11 | /** 12 | * @author Samiu 2020/5/20 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class WanPersonalViewModel( 16 | private val mineRepository: WanMineRepository 17 | ) : ViewModel() { 18 | 19 | private var isLogin by Preference(IS_LOGIN, false) 20 | 21 | val articlePagingList = mineRepository.getArticlePaging().cachedIn(viewModelScope) 22 | 23 | fun logout() = viewModelScope.launch { 24 | mineRepository.logout() 25 | isLogin = false 26 | } 27 | } -------------------------------------------------------------------------------- /res/src/main/res/color/color_on_primary_surface_emphasis_high.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/system/SystemDisplayViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.system 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.samiu.wangank.bean.Article 7 | import com.samiu.wangank.bean.base.WanResult 8 | import kotlinx.coroutines.launch 9 | 10 | /** 11 | * @author Samiu 2020/3/6 12 | * @email samiuzhong@outlook.com 13 | */ 14 | class SystemDisplayViewModel( 15 | private val systemRepository: WanSystemRepository 16 | ) : ViewModel() { 17 | 18 | val mSystemArticles = MutableLiveData>() 19 | 20 | fun getSystemArticles(page: Int, cid: Int) = viewModelScope.launch { 21 | val data = systemRepository.getSystemDetail(page, cid) 22 | if (data is WanResult.Success) { 23 | mSystemArticles.value = data.data?.datas 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /res/src/main/res/color/color_on_primary_surface_emphasis_medium.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /res/src/main/res/color/color_on_primary_surface_emphasis_disabled.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## For more details on how to configure your build environment visit 2 | # http://www.gradle.org/docs/current/userguide/build_environment.html 3 | # 4 | # Specifies the JVM arguments used for the daemon process. 5 | # The setting is particularly useful for tweaking memory settings. 6 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 7 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 8 | # 9 | # When configured, Gradle will run in incubating parallel mode. 10 | # This option should only be used with decoupled projects. More details, visit 11 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 12 | # org.gradle.parallel=true 13 | #Sun Mar 06 15:25:33 CST 2022 14 | kotlin.code.style=official 15 | org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M" 16 | android.useAndroidX=true 17 | android.enableJetifier=true 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/home/WanHomeViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.home 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import androidx.paging.cachedIn 7 | import com.samiu.wangank.bean.Banner 8 | import com.samiu.wangank.bean.base.WanResult 9 | import kotlinx.coroutines.launch 10 | 11 | /** 12 | * @author Samiu 2020/3/3 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class WanHomeViewModel( 16 | private val wanHomeRepository: WanHomeRepository 17 | ) : ViewModel() { 18 | 19 | val mBanners = MutableLiveData>() 20 | val articlePagingList = wanHomeRepository.getArticlePaging().cachedIn(viewModelScope) 21 | 22 | fun getBanners() = viewModelScope.launch { 23 | val data = wanHomeRepository.getBanners() 24 | if (data is WanResult.Success) 25 | mBanners.value = data.data 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/nav/OnSandwichSlideAction.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.nav 2 | 3 | import android.view.View 4 | import androidx.annotation.FloatRange 5 | import com.samiu.wangank.util.normalize 6 | 7 | /** 8 | * @author Samiu 2020/3/31 9 | * @email samiuzhong@outlook.com 10 | */ 11 | interface OnSandwichSlideAction { 12 | 13 | fun onSlide( 14 | @FloatRange( 15 | from = 0.0, 16 | fromInclusive = true, 17 | to = 1.0, 18 | toInclusive = true 19 | ) slideOffset: Float 20 | ) 21 | } 22 | 23 | /** 24 | * 将给过来的view逆时针旋转180度 25 | */ 26 | class HalfCounterClockwiseRotateSlideAction( 27 | private val view: View 28 | ) : OnSandwichSlideAction { 29 | override fun onSlide(slideOffset: Float) { 30 | view.rotation = slideOffset.normalize( 31 | 0F, 32 | 1F, 33 | 180F, 34 | 0F 35 | ) 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/mine/share/WanShareViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.mine.share 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.samiu.wangank.bean.base.WanResult 7 | import com.samiu.wangank.ui.mine.WanMineRepository 8 | import kotlinx.coroutines.launch 9 | 10 | class WanShareViewModel( 11 | private val shareRepository: WanMineRepository 12 | ) : ViewModel() { 13 | 14 | val shareSuccess = MutableLiveData() 15 | val shareMsg = MutableLiveData() 16 | 17 | 18 | fun shareArticle(title: String, link: String) = viewModelScope.launch { 19 | val share = shareRepository.share(title, link) 20 | if (share is WanResult.Success) { 21 | shareSuccess.value = true 22 | } else if (share is WanResult.Error) { 23 | shareMsg.value = share.msg 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /res/src/main/res/values/motion.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | 19 | 300 20 | 225 21 | 175 22 | 23 | -------------------------------------------------------------------------------- /res/src/main/res/color/color_navigation_drawer_menu_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_progressbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/project/WanProjectRepository.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.project 2 | 3 | import com.samiu.wangank.bean.ArticleList 4 | import com.samiu.wangank.bean.SystemParent 5 | import com.samiu.wangank.http.BaseWanRepository 6 | import com.samiu.wangank.http.WanClient 7 | import com.samiu.wangank.bean.base.WanResult 8 | 9 | /** 10 | * @author Samiu 2020/3/5 11 | * @email samiuzhong@outlook.com 12 | */ 13 | class WanProjectRepository : BaseWanRepository() { 14 | 15 | suspend fun getRecentProjects(page: Int): WanResult = readyCall { 16 | call(WanClient.service.getLastedProject(page)) 17 | } 18 | 19 | suspend fun getProjectType(): WanResult> = readyCall { 20 | call(WanClient.service.getProjectType()) 21 | } 22 | 23 | suspend fun getAllProjects(page: Int, cid: Int): WanResult = readyCall { 24 | call(WanClient.service.getProjectTypeDetail(page, cid)) 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/home/WanHomeRepository.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.home 2 | 3 | import androidx.paging.Pager 4 | import androidx.paging.PagingConfig 5 | import com.samiu.wangank.bean.ArticleList 6 | import com.samiu.wangank.bean.Banner 7 | import com.samiu.wangank.http.BaseWanRepository 8 | import com.samiu.wangank.http.WanClient 9 | import com.samiu.wangank.bean.base.WanResult 10 | import com.samiu.wangank.global.DEFAULT_PAGE_SIZE 11 | 12 | /** 13 | * @author Samiu 2020/3/3 14 | * @email samiuzhong@outlook.com 15 | */ 16 | class WanHomeRepository : BaseWanRepository() { 17 | 18 | suspend fun getBanners(): WanResult> = readyCall { 19 | call(WanClient.service.getBanner()) 20 | } 21 | 22 | fun getArticlePaging() = Pager( 23 | PagingConfig( 24 | pageSize = DEFAULT_PAGE_SIZE, 25 | prefetchDistance = DEFAULT_PAGE_SIZE, 26 | ) 27 | ) { 28 | HomeArticlePagingSource() 29 | }.flow 30 | } -------------------------------------------------------------------------------- /base/src/main/java/com/samiu/base/ui/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.base.ui 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.appcompat.app.AppCompatActivity 6 | import kotlinx.coroutines.CoroutineScope 7 | import kotlinx.coroutines.MainScope 8 | import kotlinx.coroutines.cancel 9 | 10 | /** 11 | * @author Samiu 2020/3/2 12 | * @email samiuzhong@outlook.com 13 | */ 14 | abstract class BaseActivity : AppCompatActivity(), 15 | CoroutineScope by MainScope() { 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | setContentView(getBindingRoot()) 20 | initView() 21 | initData() 22 | startObserve() 23 | } 24 | 25 | abstract fun getBindingRoot():View 26 | abstract fun initView() 27 | abstract fun initData() 28 | open fun startObserve()=Unit 29 | 30 | override fun onDestroy() { 31 | super.onDestroy() 32 | cancel() 33 | } 34 | } -------------------------------------------------------------------------------- /res/src/main/res/values/elevation.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 0dp 18 | 1dp 19 | 2dp 20 | 6dp 21 | 8dp 22 | 16dp 23 | -------------------------------------------------------------------------------- /base/src/main/java/com/samiu/base/ui/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.base.ui 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.annotation.LayoutRes 6 | import androidx.fragment.app.Fragment 7 | import kotlinx.coroutines.CoroutineScope 8 | import kotlinx.coroutines.MainScope 9 | import kotlinx.coroutines.cancel 10 | 11 | /** 12 | * @author Samiu 2020/3/2 13 | * @email samiuzhong@outlook.com 14 | */ 15 | abstract class BaseFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayoutId), 16 | CoroutineScope by MainScope() { 17 | 18 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 19 | initView() 20 | initData() 21 | startObserve() 22 | super.onViewCreated(view, savedInstanceState) 23 | } 24 | 25 | abstract fun initView() 26 | abstract fun initData() 27 | open fun startObserve()=Unit 28 | 29 | override fun onDestroy() { 30 | super.onDestroy() 31 | cancel() 32 | } 33 | } -------------------------------------------------------------------------------- /res/src/main/res/animator/fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 23 | -------------------------------------------------------------------------------- /res/src/main/res/animator/fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 23 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "WanAndroidGank" 2 | include(":app") 3 | include(":base") 4 | include(":res") 5 | 6 | pluginManagement { 7 | repositories { 8 | gradlePluginPortal() 9 | google() 10 | jcenter() 11 | mavenCentral() 12 | } 13 | 14 | plugins { 15 | id("com.android.application") version "7.1.0" apply false 16 | id("com.android.library") version "7.1.0" apply false 17 | id("org.jetbrains.kotlin.android") version "1.6.0" apply false 18 | id("org.jetbrains.kotlin.kapt") version "1.6.0" apply false 19 | } 20 | } 21 | 22 | dependencyResolutionManagement { 23 | repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) 24 | repositories { 25 | google() 26 | jcenter() 27 | mavenCentral() 28 | maven { 29 | setUrl("https://jitpack.io") 30 | } 31 | maven { 32 | setUrl("https://dl.bintray.com/thelasterstar/maven/") 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /res/src/main/res/animator/slide_in_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /res/src/main/res/animator/slide_out_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /res/src/main/res/font/work_sans.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /res/src/main/res/animator/slide_in_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /res/src/main/res/animator/slide_out_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_arrow_drop_up.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_forward.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /res/src/main/res/values/preloaded_fonts.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | @font/work_sans 18 | @font/work_sans_bold 19 | @font/work_sans_extrabold 20 | @font/work_sans_medium 21 | @font/work_sans_semibold 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_wan_system.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 25 | -------------------------------------------------------------------------------- /res/src/main/res/font/work_sans_bold.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /res/src/main/res/font/work_sans_extrabold.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /res/src/main/res/font/work_sans_medium.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /res/src/main/res/font/work_sans_semibold.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_done.xml: -------------------------------------------------------------------------------- 1 | 14 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_reply.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_arrow_down.xml: -------------------------------------------------------------------------------- 1 | 14 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/nav/adapter/NavigationViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.nav.adapter 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.samiu.wangank.databinding.NavMenuItemLayoutBinding 6 | 7 | /** 8 | * 这个密封类可以定义NavigationViewHolder的多个子类,方便拓展 9 | * 10 | * @author Samiu 2020/3/31 11 | * @email samiuzhong@outlook.com 12 | */ 13 | sealed class NavigationViewHolder( 14 | view: View 15 | ) : RecyclerView.ViewHolder(view) { 16 | 17 | abstract fun bind(navItem:T) 18 | 19 | class NavMenuItemViewHolder( 20 | private val binding:NavMenuItemLayoutBinding, 21 | private val listener: NavigationAdapter.NavigationAdapterListener 22 | ): NavigationViewHolder(binding.root){ 23 | 24 | override fun bind(navItem: NavigationModelItem.NavMenuItem) { 25 | binding.run { 26 | navMenuItem = navItem 27 | navListener = listener 28 | executePendingBindings() 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_reply_all.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Samiu Zhong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /base/src/main/java/com/samiu/base/ui/DataBindingDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.base.ui 2 | 3 | import androidx.annotation.LayoutRes 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.databinding.DataBindingUtil 6 | import androidx.databinding.ViewDataBinding 7 | import kotlin.reflect.KProperty 8 | 9 | /** 10 | * @author Samiu 2020/4/2 11 | * @email samiuzhong@outlook.com 12 | */ 13 | 14 | /** 15 | * Activity DataBinding Delegate 16 | */ 17 | fun dataBinding( 18 | @LayoutRes layoutRes: Int 19 | ): ContentViewBindingDelegate = ContentViewBindingDelegate(layoutRes) 20 | 21 | class ContentViewBindingDelegate( 22 | @LayoutRes private val layoutRes: Int 23 | ) { 24 | 25 | private var binding: T? = null 26 | 27 | operator fun getValue(activity: R, property: KProperty<*>): T { 28 | if (binding == null) { 29 | binding = DataBindingUtil.setContentView(activity, layoutRes).apply { 30 | lifecycleOwner = activity 31 | } 32 | } 33 | return binding!! 34 | } 35 | } -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 22 | 25 | 26 | -------------------------------------------------------------------------------- /res/src/main/res/values/layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 2dp 22 | 4dp 23 | 24 | 25 | 8dp 26 | 16dp 27 | 24dp 28 | 32dp 29 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_edit.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_twotone_add_circle_outline.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/bottom_app_bar_title_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #252F3B 4 | #9496A4 5 | #87918F 6 | #3F51B5 7 | #303F9F 8 | #FF4081 9 | #ffffff 10 | #000000 11 | #8e8e8e 12 | #e0e0e0 13 | #ff0000 14 | #2f2f2f 15 | #99323232 16 | #99000000 17 | #B9CDDA 18 | #A6D8D4 19 | #6B7D7D 20 | #3066BE 21 | #119DA4 22 | #80DED9 23 | #29E7CD 24 | #F7F7F7 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/http/BaseWanRepository.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.http 2 | 3 | import com.samiu.wangank.bean.base.WanResponse 4 | import com.samiu.wangank.bean.base.WanResult 5 | import kotlinx.coroutines.CoroutineScope 6 | import kotlinx.coroutines.coroutineScope 7 | 8 | /** 9 | * @author Samiu 2020/3/3 10 | * @email samiuzhong@outlook.com 11 | */ 12 | open class BaseWanRepository { 13 | 14 | suspend fun readyCall( 15 | call: suspend () -> WanResult, 16 | ): WanResult = try { 17 | call() 18 | } catch (e: Exception) { 19 | WanResult.Error(e.toString()) 20 | } 21 | 22 | suspend fun call( 23 | response: WanResponse, 24 | successBlock: (suspend CoroutineScope.() -> Unit)? = null, 25 | errorBlock: (suspend CoroutineScope.() -> Unit)? = null 26 | ): WanResult { 27 | return coroutineScope { 28 | if (response.errorCode != 0) { 29 | errorBlock?.let { it() } 30 | WanResult.Error(response.errorMsg) 31 | } else { 32 | successBlock?.let { it() } 33 | WanResult.Success(response.data) 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/project/adapter/WanProjectAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.project.adapter 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.samiu.base.adapter.BaseSingleRecyclerAdapter 7 | import com.samiu.wangank.databinding.ItemWanProjectBinding 8 | import com.samiu.wangank.bean.Article 9 | import com.samiu.wangank.ui.home.adapter.WanArticleAdapter 10 | 11 | /** 12 | * @author Samiu 2020/3/5 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class WanProjectAdapter( 16 | private val listener: WanArticleAdapter.ArticleAdapterListener 17 | ) : BaseSingleRecyclerAdapter
() { 18 | 19 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { 20 | return ProjectViewHolder( 21 | ItemWanProjectBinding.inflate( 22 | LayoutInflater.from(parent.context), 23 | parent, 24 | false 25 | ), listener 26 | ) 27 | } 28 | 29 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { 30 | if (holder is ProjectViewHolder) holder.bind(mList[position]) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/search/WanSearchViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.search 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.samiu.wangank.bean.Article 7 | import com.samiu.wangank.bean.Hot 8 | import com.samiu.wangank.bean.base.WanResult 9 | import kotlinx.coroutines.launch 10 | 11 | /** 12 | * @author Samiu 2020/3/7 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class WanSearchViewModel( 16 | private val searchRepository: WanSearchRepository 17 | ) : ViewModel() { 18 | 19 | val mArticles = MutableLiveData>() 20 | val mkeys = MutableLiveData>() 21 | 22 | fun getArticles(page: Int, key: String) = viewModelScope.launch { 23 | val articleList = searchRepository.getSearchArticle(page, key) 24 | if (articleList is WanResult.Success) 25 | mArticles.value = articleList.data?.datas 26 | } 27 | 28 | fun getHotKeys() = viewModelScope.launch { 29 | val key = searchRepository.getHotKey() 30 | if (key is WanResult.Success) { 31 | key.data?.let { 32 | mkeys.value = it 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/wxpub/WanWxViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.wxpub 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.samiu.wangank.bean.Article 7 | import com.samiu.wangank.bean.SystemParent 8 | import com.samiu.wangank.bean.base.WanResult 9 | import kotlinx.coroutines.launch 10 | 11 | /** 12 | * @author Samiu 2020/3/5 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class WanWxViewModel( 16 | private val wanWxRepository: WanWxRepository 17 | ) : ViewModel() { 18 | 19 | val mAccounts = MutableLiveData>() 20 | val mArticles = MutableLiveData>() 21 | 22 | fun getAccounts() = viewModelScope.launch { 23 | val accounts = wanWxRepository.getWxAccount() 24 | if (accounts is WanResult.Success) { 25 | accounts.data?.let { 26 | mAccounts.value = it 27 | } 28 | } 29 | } 30 | 31 | fun getArticles(id: Int, page: Int) = viewModelScope.launch { 32 | val articles = wanWxRepository.getWxArticle(id, page) 33 | if (articles is WanResult.Success) 34 | mArticles.value = articles.data?.datas 35 | } 36 | } -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_search.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/mine/login/WanLoginViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.mine.login 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.samiu.wangank.global.IS_LOGIN 7 | import com.samiu.wangank.global.USER_NAME 8 | import com.samiu.wangank.bean.base.WanResult 9 | import com.samiu.wangank.ui.mine.WanMineRepository 10 | import com.samiu.wangank.util.Preference 11 | import kotlinx.coroutines.launch 12 | 13 | /** 14 | * @author Samiu 2020/4/17 15 | * @email samiuzhong@outlook.com 16 | */ 17 | class WanLoginViewModel( 18 | private val loginRepository: WanMineRepository 19 | ) : ViewModel() { 20 | 21 | private var isLogin by Preference(IS_LOGIN, false) 22 | private var nickname by Preference(USER_NAME, "") 23 | val loginSuccess = MutableLiveData() 24 | 25 | fun login(userName: String, passWord: String) = viewModelScope.launch { 26 | val user = loginRepository.login(userName, passWord) 27 | if (user is WanResult.Success) { 28 | loginSuccess.value = true 29 | isLogin = true 30 | nickname = user.data!!.nickname 31 | } else loginSuccess.value = false 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_archive.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_twotone_forward.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_twotone_delete.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/system/WanSystemFragment.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.system 2 | 3 | import androidx.lifecycle.Observer 4 | import com.samiu.base.ui.BaseFragment 5 | import com.samiu.base.ui.viewBinding 6 | import com.samiu.wangank.R 7 | import com.samiu.wangank.databinding.FragmentWanSystemBinding 8 | import com.samiu.wangank.ui.system.adapter.WanSystemAdapter 9 | import org.koin.androidx.viewmodel.ext.android.viewModel 10 | 11 | /** 12 | * 体系页 13 | * 14 | * @author Samiu 2020/3/2 15 | * @email samiuzhong@outlook.com 16 | */ 17 | class WanSystemFragment : BaseFragment(R.layout.fragment_wan_system) { 18 | private val viewModel: WanSystemViewModel by viewModel() 19 | private val binding by viewBinding(FragmentWanSystemBinding::bind) 20 | 21 | override fun initView() = initRecyclerView() 22 | override fun initData() = viewModel.getSystem() 23 | 24 | private lateinit var adapter: WanSystemAdapter 25 | 26 | override fun startObserve() = viewModel.run { 27 | mSystems.observe(this@WanSystemFragment, Observer { 28 | adapter.addAll(it) 29 | }) 30 | } 31 | 32 | private fun initRecyclerView() { 33 | adapter = WanSystemAdapter(requireContext()) 34 | binding.systemRv.adapter = adapter 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/system/WanSystemViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.system 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.samiu.wangank.bean.ArticleList 7 | import com.samiu.wangank.bean.SystemParent 8 | import com.samiu.wangank.bean.base.WanResult 9 | import kotlinx.coroutines.launch 10 | 11 | /** 12 | * @author Samiu 2020/3/6 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class WanSystemViewModel( 16 | private val wanSystemRepository: WanSystemRepository 17 | ):ViewModel() { 18 | 19 | val mSystems = MutableLiveData>() 20 | val mSystemDetails = MutableLiveData() 21 | 22 | fun getSystem(){ 23 | viewModelScope.launch { 24 | val system = wanSystemRepository.getSystem() 25 | if (system is WanResult.Success) 26 | mSystems.value = system.data 27 | } 28 | } 29 | 30 | fun getSystemDetail(page: Int, cid: Int){ 31 | viewModelScope.launch { 32 | val articles = wanSystemRepository.getSystemDetail(page, cid) 33 | if (articles is WanResult.Success) 34 | mSystemDetails.value = articles.data 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/project/WanProjectViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.project 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.samiu.wangank.bean.Article 7 | import com.samiu.wangank.bean.SystemParent 8 | import com.samiu.wangank.bean.base.WanResult 9 | import kotlinx.coroutines.launch 10 | 11 | /** 12 | * @author Samiu 2020/3/5 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class WanProjectViewModel( 16 | private val wanProjectRepository: WanProjectRepository 17 | ) : ViewModel() { 18 | 19 | val mProjectType = MutableLiveData>() 20 | val mAllProjects = MutableLiveData>() 21 | 22 | fun getProjectType() = viewModelScope.launch { 23 | val type = wanProjectRepository.getProjectType() 24 | if (type is WanResult.Success) { 25 | type.data?.let { 26 | mProjectType.value = it 27 | } 28 | } 29 | } 30 | 31 | fun getAllProjects(page: Int, cid: Int) = viewModelScope.launch { 32 | val projects = wanProjectRepository.getAllProjects(page, cid) 33 | if (projects is WanResult.Success) 34 | mAllProjects.value = projects.data?.datas 35 | } 36 | } -------------------------------------------------------------------------------- /base/src/main/java/com/samiu/base/http/NetWorkUtils.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.base.http 2 | 3 | import android.content.Context 4 | import android.net.ConnectivityManager 5 | import android.net.NetworkCapabilities 6 | import android.os.Build 7 | 8 | /** 9 | * @author Samiu 2020/3/3 10 | * @email samiuzhong@outlook.com 11 | */ 12 | class NetWorkUtils { 13 | 14 | companion object { 15 | fun isNetworkAvailable(context: Context): Boolean { 16 | val connectivityManager = 17 | context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 18 | val nw = connectivityManager.activeNetwork ?: return false 19 | val actNw = connectivityManager.getNetworkCapabilities(nw) ?: return false 20 | return when { 21 | actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true 22 | actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true 23 | //for other device how are able to connect with Ethernet 24 | actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true 25 | //for check internet over Bluetooth 26 | actNw.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true 27 | else -> false 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_twotone_folder.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_twotone_send.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/home/adapter/WanArticleAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.home.adapter 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.paging.PagingDataAdapter 6 | import com.samiu.wangank.bean.Article 7 | import com.samiu.wangank.bean.ArticleDiff 8 | import com.samiu.wangank.databinding.ItemWanArticleBinding 9 | 10 | /** 11 | * @author Samiu 2020/3/4 12 | * @email samiuzhong@outlook.com 13 | */ 14 | class WanArticleAdapter( 15 | private val listener: ArticleAdapterListener 16 | ) : PagingDataAdapter(ArticleDiff) { 17 | 18 | interface ArticleAdapterListener { 19 | fun onArticleClick(article: Article) 20 | fun onArticleStarChanged(article: Article, newValue: Boolean) 21 | } 22 | 23 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder { 24 | return ArticleViewHolder( 25 | ItemWanArticleBinding.inflate( 26 | LayoutInflater.from(parent.context), 27 | parent, 28 | false 29 | ), listener 30 | ) 31 | } 32 | 33 | override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) { 34 | getItem(position)?.let { 35 | holder.bind(it) 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/wxpub/adapter/WxArticleAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.wxpub.adapter 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.samiu.base.adapter.BaseSingleRecyclerAdapter 7 | import com.samiu.wangank.bean.Article 8 | import com.samiu.wangank.databinding.ItemWxArticleBinding 9 | 10 | /** 11 | * @author Samiu 2020/3/4 12 | * @email samiuzhong@outlook.com 13 | */ 14 | class WxArticleAdapter( 15 | private val listener: ArticleAdapterListener 16 | ) : BaseSingleRecyclerAdapter
() { 17 | 18 | interface ArticleAdapterListener { 19 | fun onArticleClick(article: Article) 20 | fun onArticleStarChanged(article: Article, newValue: Boolean) 21 | } 22 | 23 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { 24 | return WxArticleViewHolder( 25 | ItemWxArticleBinding.inflate( 26 | LayoutInflater.from(parent.context), 27 | parent, 28 | false 29 | ), listener 30 | ) 31 | } 32 | 33 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { 34 | if (holder is WxArticleViewHolder) holder.bind(mList[position]) 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_wan_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 12 | 17 | 18 | 19 | 27 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_twotone_drafts.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/bean/Article.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.bean 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | 5 | /** 6 | * @author Samiu 2020/3/3 7 | * @email samiuzhong@outlook.com 8 | */ 9 | data class Article( 10 | val id: Int, 11 | val originId: Int, 12 | val title: String, 13 | val chapterId: Int, 14 | val chapterName: String, 15 | val envelopePic: String, 16 | val link: String, 17 | val author: String?, 18 | val origin: String, 19 | val publishTime: Long, 20 | val zan: Int, 21 | val desc: String, 22 | val visible: Int, 23 | val niceDate: String, 24 | val niceShareDate: String, 25 | val courseId: Int, 26 | var collect: Boolean, 27 | val apkLink: String, 28 | val projectLink: String, 29 | val superChapterId: Int, 30 | val superChapterName: String?, 31 | val type: Int, 32 | val fresh: Boolean, 33 | val audit: Int, 34 | val prefix: String, 35 | val selfVisible: Int, 36 | val shareDate: Long, 37 | val shareUser: String?, 38 | val tags: Any, 39 | val userId: Int 40 | ) 41 | 42 | object ArticleDiff : DiffUtil.ItemCallback
() { 43 | override fun areItemsTheSame(oldItem: Article, newItem: Article) = 44 | oldItem.id == newItem.id 45 | 46 | override fun areContentsTheSame(oldItem: Article, newItem: Article) = 47 | oldItem.link == newItem.link 48 | } -------------------------------------------------------------------------------- /base/src/main/java/com/samiu/base/http/CacheInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.base.http 2 | 3 | import android.content.Context 4 | import okhttp3.CacheControl 5 | import okhttp3.Interceptor 6 | import okhttp3.Response 7 | 8 | /** 9 | * @author Samiu 2020/3/3 10 | * @email samiuzhong@outlook.com 11 | */ 12 | class CacheInterceptor(var context: Context) : Interceptor { 13 | 14 | override fun intercept(chain: Interceptor.Chain): Response { 15 | var request = chain.request() 16 | if (NetWorkUtils.isNetworkAvailable(context)) { 17 | val response = chain.proceed(request) 18 | val maxAge = 60 19 | return response.newBuilder() 20 | .removeHeader("Pragma") 21 | .removeHeader("Cache-Control") 22 | .header("Cache-Control", "public, max-age=" + maxAge) 23 | .build() 24 | } else { 25 | request = request.newBuilder() 26 | .cacheControl(CacheControl.FORCE_CACHE) 27 | .build() 28 | val response = chain.proceed(request) 29 | val maxStale = 60 * 60 * 24 * 3 30 | return response.newBuilder() 31 | .removeHeader("Pragma") 32 | .removeHeader("Cache-Control") 33 | .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale) 34 | .build(); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/util/JumpHelper.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.util 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.net.Uri 6 | import androidx.fragment.app.Fragment 7 | import com.samiu.wangank.global.TITLE 8 | import com.samiu.wangank.global.URL 9 | import com.samiu.wangank.ui.base.BrowserActivity 10 | import com.samiu.wangank.ui.base.MainActivity 11 | import com.samiu.wangank.ui.mine.share.WanShareActivity 12 | 13 | /** 14 | * @author Samiu 2020/3/4 15 | * @email samiuzhong@outlook.com 16 | */ 17 | fun desTo(fragment: Fragment, id: Int) { 18 | val navController = (fragment.requireActivity() as MainActivity).getNavController() 19 | try { 20 | val desId = navController.getBackStackEntry(id).destination.id 21 | navController.popBackStack(desId, false) 22 | } catch (e: IllegalArgumentException) { 23 | navController.navigate(id) 24 | } 25 | } 26 | 27 | fun Context.openNativeBrowser(url: String) { 28 | Intent(Intent.ACTION_VIEW, Uri.parse(url)).run { startActivity(this) } 29 | } 30 | 31 | fun Context.toBrowser(url: String, title: String) { 32 | val intent = Intent(this, BrowserActivity::class.java).apply { 33 | putExtra(URL, url) 34 | putExtra(TITLE, title) 35 | } 36 | startActivity(intent) 37 | } 38 | 39 | fun Context.toShare() { 40 | startActivity(Intent(this, WanShareActivity::class.java)) 41 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_browser.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 30 | 31 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_twotone_star.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/asl_edit_reply.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 21 | 22 | 26 | 27 | 31 | 32 | 36 | 37 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_twotone_error.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_twotone_star_on_background.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/mine/collect/adapter/CollectArticleAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.mine.collect.adapter 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.paging.PagingDataAdapter 6 | import com.samiu.wangank.bean.Article 7 | import com.samiu.wangank.bean.ArticleDiff 8 | import com.samiu.wangank.databinding.ItemCollectArticleBinding 9 | import com.samiu.wangank.databinding.ItemWanArticleBinding 10 | 11 | /** 12 | * @author Samiu 2020/3/4 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class CollectArticleAdapter( 16 | private val listener: ArticleAdapterListener 17 | ) : PagingDataAdapter(ArticleDiff) { 18 | 19 | interface ArticleAdapterListener { 20 | fun onArticleClick(article: Article) 21 | fun onArticleStarChanged(article: Article, newValue: Boolean) 22 | } 23 | 24 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CollectArticleViewHolder { 25 | return CollectArticleViewHolder( 26 | ItemCollectArticleBinding.inflate( 27 | LayoutInflater.from(parent.context), 28 | parent, 29 | false 30 | ), listener 31 | ) 32 | } 33 | 34 | override fun onBindViewHolder(holder: CollectArticleViewHolder, position: Int) { 35 | getItem(position)?.let { 36 | holder.bind(it) 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_twotone_inbox.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/navigation/main_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 16 | 17 | 21 | 22 | 26 | 27 | 31 | 32 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/home/adapter/WanBannerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.home.adapter 2 | 3 | import android.content.Context 4 | import android.view.ViewGroup 5 | import android.widget.ImageView 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.bumptech.glide.Glide 8 | import com.samiu.wangank.bean.Banner 9 | import com.youth.banner.adapter.BannerAdapter 10 | 11 | /** 12 | * @author Samiu 2020/3/3 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class WanBannerAdapter(var list: List) : BannerAdapter(list) { 16 | private lateinit var context: Context 17 | 18 | override fun onCreateHolder(parent: ViewGroup?, viewType: Int): ImageBannerHolder { 19 | parent?.context?.let { 20 | context = it 21 | } 22 | val imageView = ImageView(parent?.context) 23 | imageView.layoutParams = ViewGroup.LayoutParams( 24 | ViewGroup.LayoutParams.MATCH_PARENT, 25 | ViewGroup.LayoutParams.MATCH_PARENT 26 | ) 27 | imageView.scaleType = ImageView.ScaleType.CENTER_CROP 28 | return ImageBannerHolder(imageView) 29 | } 30 | 31 | override fun onBindView(holder: ImageBannerHolder?, data: Banner?, position: Int, size: Int) { 32 | if (holder is ImageBannerHolder) 33 | Glide.with(context).load(list[position].imagePath).into(holder.image) 34 | } 35 | 36 | class ImageBannerHolder(var image: ImageView) : RecyclerView.ViewHolder(image) 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/nav/adapter/NavigationAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.nav.adapter 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.ListAdapter 6 | import com.samiu.wangank.databinding.NavMenuItemLayoutBinding 7 | 8 | 9 | /** 10 | * [BottomNavDrawerFragment]的adapter 11 | * 12 | * @author Samiu 2020/3/31 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class NavigationAdapter( 16 | private val listener: NavigationAdapterListener 17 | ) : ListAdapter>( 18 | NavigationModelItem.NavModelItemDiff 19 | ) { 20 | 21 | interface NavigationAdapterListener { 22 | fun onNavMenuItemClicked(item: NavigationModelItem.NavMenuItem) 23 | } 24 | 25 | @Suppress("UNCHECKED_CAST") 26 | override fun onCreateViewHolder( 27 | parent: ViewGroup, 28 | viewType: Int 29 | ): NavigationViewHolder { 30 | return NavigationViewHolder.NavMenuItemViewHolder( 31 | NavMenuItemLayoutBinding.inflate( 32 | LayoutInflater.from(parent.context), 33 | parent, 34 | false 35 | ), 36 | listener 37 | ) as NavigationViewHolder 38 | } 39 | 40 | override fun onBindViewHolder( 41 | holder: NavigationViewHolder, 42 | position: Int 43 | ) { 44 | holder.bind(getItem(position)) 45 | } 46 | } -------------------------------------------------------------------------------- /res/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 48dp 18 | 12dp 19 | 20 | 21 | 56dp 22 | 32dp 23 | 8dp 24 | 32dp 25 | 26 | 27 | 48dp 28 | 32dp 29 | 56dp 30 | 31 | 32 | 42dp 33 | 34 | 32dp 35 | 36 | -------------------------------------------------------------------------------- /base/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.samiu.buildsrc.BuildConfig 2 | import com.samiu.buildsrc.Deps 3 | 4 | plugins { 5 | id("com.android.library") 6 | kotlin("android") 7 | kotlin("kapt") 8 | } 9 | 10 | android { 11 | compileSdk = BuildConfig.compileSdk 12 | 13 | defaultConfig { 14 | minSdk = BuildConfig.minSdk 15 | targetSdk = BuildConfig.targetSdk 16 | } 17 | 18 | compileOptions { 19 | sourceCompatibility = BuildConfig.javaVersion 20 | targetCompatibility = BuildConfig.javaVersion 21 | } 22 | kotlinOptions { 23 | jvmTarget = BuildConfig.kotlinJvmTarget 24 | } 25 | 26 | buildFeatures { 27 | dataBinding = true 28 | viewBinding = true 29 | } 30 | } 31 | 32 | 33 | dependencies { 34 | implementation(Deps.AndroidX.coreKtx) 35 | implementation(Deps.AndroidX.appcompat) 36 | 37 | api(Deps.AndroidX.viewpager2) 38 | api(Deps.AndroidX.constraintLayout) 39 | api(Deps.AndroidX.navigationUi) 40 | api(Deps.AndroidX.navigationFragment) 41 | api(Deps.AndroidX.lifecycleRuntime) 42 | api(Deps.AndroidX.lifecycleLivedata) 43 | api(Deps.AndroidX.lifecycleViewModel) 44 | api(Deps.AndroidX.swipeRefresh) 45 | api(Deps.AndroidX.pagingRuntime) 46 | 47 | api(Deps.Kotlin.kotlinxCoroutines) 48 | api(Deps.Koin.core) 49 | api(Deps.Koin.android) 50 | api(Deps.Koin.compat) 51 | api(Deps.Http.retrofit) 52 | api(Deps.Http.retrofitConverter) 53 | api(Deps.Http.okHttp) 54 | implementation(Deps.Http.cookieJar) 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_wan_wx_article.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 21 | 22 | 25 | 26 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /base/src/main/java/com/samiu/base/http/RetrofitClient.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.base.http 2 | 3 | import android.content.Context 4 | import com.franmontiel.persistentcookiejar.PersistentCookieJar 5 | import com.franmontiel.persistentcookiejar.cache.SetCookieCache 6 | import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor 7 | import okhttp3.OkHttpClient 8 | import retrofit2.Retrofit 9 | import retrofit2.converter.gson.GsonConverterFactory 10 | import java.util.concurrent.TimeUnit 11 | 12 | /** 13 | * @author Samiu 2020/3/3 14 | * @email samiuzhong@outlook.com 15 | */ 16 | abstract class BaseRetrofitClient(var context: Context, private var baseUrl: String) { 17 | companion object { 18 | private const val DEFAULT_TIMEOUT = 20L 19 | } 20 | 21 | private val cookieJar by lazy { 22 | PersistentCookieJar( 23 | SetCookieCache(), 24 | SharedPrefsCookiePersistor(context) 25 | ) 26 | } 27 | 28 | fun getService(serviceClass: Class): S { 29 | return Retrofit.Builder() 30 | .client( 31 | OkHttpClient.Builder() 32 | .cookieJar(cookieJar) 33 | .addInterceptor(CacheInterceptor(context)) 34 | .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) 35 | .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) 36 | .build() 37 | ) 38 | .addConverterFactory(GsonConverterFactory.create()) 39 | .baseUrl(baseUrl) 40 | .build().create(serviceClass) 41 | } 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/mine/share/WanShareActivity.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.mine.share 2 | 3 | import com.google.android.material.snackbar.Snackbar 4 | import com.samiu.base.ui.BaseActivity 5 | import com.samiu.base.ui.viewBinding 6 | import com.samiu.wangank.R 7 | import com.samiu.wangank.databinding.ActivityWanShareBinding 8 | import org.koin.androidx.viewmodel.ext.android.viewModel 9 | 10 | /** 11 | * 分享文章页 12 | * 13 | * @author Samiu 2022/3/10 14 | * @email samiuzhong@outlook.com 15 | */ 16 | class WanShareActivity : BaseActivity() { 17 | 18 | private val binding by viewBinding(ActivityWanShareBinding::inflate) 19 | private val viewModel: WanShareViewModel by viewModel() 20 | 21 | override fun getBindingRoot() = binding.root 22 | override fun initData() = Unit 23 | 24 | override fun initView() { 25 | binding.run { 26 | cancelBtn.setOnClickListener { finish() } 27 | loginBtn.setOnClickListener { 28 | viewModel.shareArticle(userName.text.toString(), password.text.toString()) 29 | } 30 | } 31 | } 32 | 33 | override fun startObserve() = viewModel.run { 34 | shareSuccess.observe(this@WanShareActivity) { 35 | Snackbar.make( 36 | binding.loginBtn, 37 | getString(R.string.share_success), 38 | Snackbar.LENGTH_SHORT 39 | ).show() 40 | } 41 | shareMsg.observe(this@WanShareActivity) { msg -> 42 | Snackbar.make(binding.loginBtn, msg, Snackbar.LENGTH_SHORT).show() 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/home/HomeArticlePagingSource.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.home 2 | 3 | import android.util.Log 4 | import androidx.paging.PagingSource 5 | import androidx.paging.PagingState 6 | import com.samiu.wangank.bean.Article 7 | import com.samiu.wangank.http.WanClient 8 | 9 | /** 10 | * @author Samiu 2022/3/5 11 | * @email samiuzhong@outlook.com 12 | */ 13 | class HomeArticlePagingSource : PagingSource() { 14 | 15 | override suspend fun load(params: LoadParams): LoadResult { 16 | return try { 17 | val currentPage = getCurrentPage(params.key) 18 | val response = WanClient.service.getHomeArticles(currentPage) 19 | Log.d("Paging3", "Paging加载Article数据:第${currentPage}页面") 20 | 21 | LoadResult.Page( 22 | data = response.data!!.datas, 23 | prevKey = if (currentPage > 0) currentPage - 1L else null, 24 | nextKey = if (response.data.datas.isNotEmpty()) currentPage + 1L else null 25 | ) 26 | } catch (exception: Exception) { 27 | Log.d("Paging3", "Paging加载Article数据,Error = $exception") 28 | LoadResult.Error(exception) 29 | } 30 | } 31 | 32 | override fun getRefreshKey(state: PagingState): Long? { 33 | return state.anchorPosition?.toLong() 34 | } 35 | 36 | private fun getCurrentPage(page: Long?): Long { 37 | var result = 0L 38 | page?.let { 39 | if (page > 0) 40 | result = page 41 | } 42 | return result 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/square/SquareArticlePagingSource.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.square 2 | 3 | import android.util.Log 4 | import androidx.paging.PagingSource 5 | import androidx.paging.PagingState 6 | import com.samiu.wangank.bean.Article 7 | import com.samiu.wangank.http.WanClient 8 | 9 | /** 10 | * @author Samiu 2022/3/8 11 | * @email samiuzhong@outlook.com 12 | */ 13 | class SquareArticlePagingSource : PagingSource() { 14 | 15 | override suspend fun load(params: LoadParams): LoadResult { 16 | return try { 17 | val currentPage = getCurrentPage(params.key) 18 | val response = WanClient.service.getSquareArticleList(currentPage) 19 | Log.d("Paging3", "Paging加载Article数据:第${currentPage}页面") 20 | 21 | LoadResult.Page( 22 | data = response.data!!.datas, 23 | prevKey = if (currentPage > 0) currentPage - 1L else null, 24 | nextKey = if (response.data.datas.isNotEmpty()) currentPage + 1L else null 25 | ) 26 | } catch (exception: Exception) { 27 | Log.d("Paging3", "Paging加载Article数据,Error = $exception") 28 | LoadResult.Error(exception) 29 | } 30 | } 31 | 32 | override fun getRefreshKey(state: PagingState): Long? { 33 | return state.anchorPosition?.toLong() 34 | } 35 | 36 | private fun getCurrentPage(page: Long?): Long { 37 | var result = 0L 38 | page?.let { 39 | if (page > 0) 40 | result = page 41 | } 42 | return result 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/mine/personal/CollectArticlePagingSource.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.mine.personal 2 | 3 | import android.util.Log 4 | import androidx.paging.PagingSource 5 | import androidx.paging.PagingState 6 | import com.samiu.wangank.bean.Article 7 | import com.samiu.wangank.http.WanClient 8 | 9 | /** 10 | * @author Samiu 2022/3/8 11 | * @email samiuzhong@outlook.com 12 | */ 13 | class CollectArticlePagingSource : PagingSource() { 14 | 15 | override suspend fun load(params: LoadParams): LoadResult { 16 | return try { 17 | val currentPage = getCurrentPage(params.key) 18 | val response = WanClient.service.getCollectArticles(currentPage) 19 | Log.d("Paging3", "Paging加载Article数据:第${currentPage}页面") 20 | 21 | LoadResult.Page( 22 | data = response.data!!.datas, 23 | prevKey = if (currentPage > 0) currentPage - 1L else null, 24 | nextKey = if (response.data.datas.isNotEmpty()) currentPage + 1L else null 25 | ) 26 | } catch (exception: Exception) { 27 | Log.d("Paging3", "Paging加载Article数据,Error = $exception") 28 | LoadResult.Error(exception) 29 | } 30 | } 31 | 32 | override fun getRefreshKey(state: PagingState): Long? { 33 | return state.anchorPosition?.toLong() 34 | } 35 | 36 | private fun getCurrentPage(page: Long?): Long { 37 | var result = 0L 38 | page?.let { 39 | if (page > 0) 40 | result = page 41 | } 42 | return result 43 | } 44 | } -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_settings.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/mine/personal/MyArticlePagingSource.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.mine.personal 2 | 3 | import android.util.Log 4 | import androidx.paging.PagingSource 5 | import androidx.paging.PagingState 6 | import com.samiu.wangank.bean.Article 7 | import com.samiu.wangank.http.WanClient 8 | 9 | /** 10 | * @author Samiu 2022/3/8 11 | * @email samiuzhong@outlook.com 12 | */ 13 | class MyArticlePagingSource : PagingSource() { 14 | 15 | override suspend fun load(params: LoadParams): LoadResult { 16 | return try { 17 | val currentPage = getCurrentPage(params.key) 18 | val response = WanClient.service.getMyArticles(currentPage) 19 | Log.d("Paging3", "Paging加载Article数据:第${currentPage}页面") 20 | 21 | LoadResult.Page( 22 | data = response.data!!.shareArticles.datas, 23 | prevKey = if (currentPage > 1) currentPage - 1L else null, 24 | nextKey = if (response.data.shareArticles.datas.isNotEmpty()) currentPage + 1L else null 25 | ) 26 | } catch (exception: Exception) { 27 | Log.d("Paging3", "Paging加载Article数据,Error = $exception") 28 | LoadResult.Error(exception) 29 | } 30 | } 31 | 32 | override fun getRefreshKey(state: PagingState): Long? { 33 | return state.anchorPosition?.toLong() 34 | } 35 | 36 | private fun getCurrentPage(page: Long?): Long { 37 | var result = 1L 38 | page?.let { 39 | if (page > 1) 40 | result = page 41 | } 42 | return result 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_wan_project.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | 22 | 23 | 26 | 27 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/util/ContextExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.util 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.graphics.Color 6 | import android.graphics.drawable.Drawable 7 | import android.util.TypedValue 8 | import android.view.animation.AnimationUtils 9 | import android.view.animation.Interpolator 10 | import androidx.annotation.AttrRes 11 | import androidx.annotation.ColorInt 12 | import androidx.annotation.DrawableRes 13 | import androidx.annotation.StyleRes 14 | import androidx.appcompat.content.res.AppCompatResources 15 | import androidx.core.content.res.use 16 | 17 | /** 18 | * @author Samiu 2020/3/31 19 | * @email samiuzhong@outlook.com 20 | */ 21 | @ColorInt 22 | @SuppressLint("Recycle") 23 | fun Context.themeColor( 24 | @AttrRes themeAttrId: Int 25 | ): Int { 26 | return obtainStyledAttributes( 27 | intArrayOf(themeAttrId) 28 | ).use { 29 | it.getColor(0, Color.MAGENTA) 30 | } 31 | } 32 | 33 | @StyleRes 34 | fun Context.themeStyle(@AttrRes attr: Int): Int { 35 | val tv = TypedValue() 36 | theme.resolveAttribute(attr, tv, true) 37 | return tv.data 38 | } 39 | 40 | @SuppressLint("Recycle") 41 | fun Context.themeInterpolator(@AttrRes attr: Int): Interpolator { 42 | return AnimationUtils.loadInterpolator( 43 | this, 44 | obtainStyledAttributes(intArrayOf(attr)).use { 45 | it.getResourceId(0, android.R.interpolator.fast_out_slow_in) 46 | } 47 | ) 48 | } 49 | 50 | fun Context.getDrawableOrNull(@DrawableRes id: Int?): Drawable? { 51 | return if (id == null || id == 0) null else AppCompatResources.getDrawable(this, id) 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/square/WanSquareFragment.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.square 2 | 3 | import androidx.lifecycle.lifecycleScope 4 | import androidx.recyclerview.widget.ItemTouchHelper 5 | import com.samiu.base.ui.BaseFragment 6 | import com.samiu.base.ui.viewBinding 7 | import com.samiu.wangank.R 8 | import com.samiu.wangank.databinding.FragmentRecyclerBinding 9 | import com.samiu.wangank.ui.home.adapter.ArticleListenerImpl 10 | import com.samiu.wangank.ui.home.adapter.ReboundingSwipeActionCallback 11 | import com.samiu.wangank.ui.home.adapter.WanArticleAdapter 12 | import kotlinx.coroutines.flow.collectLatest 13 | import org.koin.androidx.viewmodel.ext.android.viewModel 14 | 15 | /** 16 | * 广场页 17 | * 18 | * @author Samiu 2020/3/2 19 | * @email samiuzhong@outlook.com 20 | */ 21 | class WanSquareFragment : BaseFragment(R.layout.fragment_recycler) { 22 | 23 | private val binding by viewBinding(FragmentRecyclerBinding::bind) 24 | private val viewModel: WanSquareViewModel by viewModel() 25 | 26 | private lateinit var mAdapter: WanArticleAdapter 27 | 28 | override fun initView() { 29 | mAdapter = WanArticleAdapter(ArticleListenerImpl(requireContext())) 30 | binding.recycler.apply { 31 | val itemTouchHelper = ItemTouchHelper(ReboundingSwipeActionCallback()) 32 | itemTouchHelper.attachToRecyclerView(this) 33 | adapter = mAdapter 34 | } 35 | } 36 | 37 | override fun initData() { 38 | viewLifecycleOwner.lifecycleScope.launchWhenCreated { 39 | viewModel.articlePagingList.collectLatest { 40 | mAdapter.submitData(it) 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/nav_menu_item_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 16 | 17 | 18 | 37 | 38 | -------------------------------------------------------------------------------- /res/src/main/res/animator/fab_hide.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 23 | 30 | 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/nav/adapter/NavigationModelItem.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.nav.adapter 2 | 3 | import androidx.annotation.DrawableRes 4 | import androidx.annotation.StringRes 5 | import androidx.recyclerview.widget.DiffUtil 6 | 7 | /** 8 | * 这个密封类封装了[NavigationAdapter]能够展示的所有Item 9 | * 10 | * @author Samiu 2020/3/31 11 | * @email samiuzhong@outlook.com 12 | */ 13 | sealed class NavigationModelItem { 14 | 15 | /** 16 | * 菜单item 17 | */ 18 | data class NavMenuItem( 19 | val id: Int, 20 | @DrawableRes val icon: Int, 21 | @StringRes val titleRes: Int, 22 | var checked: Boolean 23 | ) : NavigationModelItem() 24 | 25 | /** 26 | * item局部刷新 27 | */ 28 | object NavModelItemDiff : DiffUtil.ItemCallback() { 29 | override fun areItemsTheSame( 30 | oldItem: NavigationModelItem, 31 | newItem: NavigationModelItem 32 | ): Boolean { 33 | return when { 34 | oldItem is NavMenuItem && newItem is NavMenuItem -> 35 | oldItem.id == newItem.id 36 | else -> oldItem == newItem 37 | } 38 | } 39 | 40 | override fun areContentsTheSame( 41 | oldItem: NavigationModelItem, 42 | newItem: NavigationModelItem 43 | ): Boolean { 44 | return when { 45 | oldItem is NavMenuItem && newItem is NavMenuItem -> 46 | oldItem.icon == newItem.icon && 47 | oldItem.titleRes == newItem.titleRes && 48 | oldItem.checked == newItem.checked 49 | else -> false 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /res/src/main/res/animator/fab_show.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 17 | 24 | 31 | 38 | -------------------------------------------------------------------------------- /res/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/home/adapter/ArticleListenerImpl.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.home.adapter 2 | 3 | import android.content.Context 4 | import com.samiu.wangank.util.toBrowser 5 | import com.samiu.wangank.bean.Article 6 | import com.samiu.wangank.http.BaseWanRepository 7 | import com.samiu.wangank.http.WanClient 8 | import kotlinx.coroutines.MainScope 9 | import kotlinx.coroutines.launch 10 | 11 | /** 12 | * @author Samiu 2020/7/6 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class ArticleListenerImpl( 16 | private val context: Context 17 | ) : WanArticleAdapter.ArticleAdapterListener, BaseWanRepository() { 18 | 19 | override fun onArticleClick(article: Article) { 20 | context.toBrowser(article.link, article.title) 21 | } 22 | 23 | override fun onArticleStarChanged(article: Article, newValue: Boolean) { 24 | article.collect = newValue 25 | if (newValue) 26 | collectArticle(article.id, article.originId) 27 | else 28 | cancelCollect(article.id, article.originId) 29 | } 30 | 31 | private fun collectArticle(id: Int, originId: Int) = MainScope().launch { 32 | if (originId != 0) 33 | doCollect(originId) 34 | else 35 | doCollect(id) 36 | } 37 | 38 | private fun cancelCollect(id: Int, originId: Int) = MainScope().launch { 39 | if (originId != 0) 40 | doCancelCollect(originId) 41 | else 42 | doCancelCollect(id) 43 | } 44 | 45 | private suspend fun doCollect(id: Int) = readyCall { 46 | call(WanClient.service.collectArticle(id)) 47 | } 48 | 49 | private suspend fun doCancelCollect(id: Int) = readyCall { 50 | call(WanClient.service.cancelCollectArticle(id)) 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/wxpub/adapter/WxArticleListenerImpl.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.wxpub.adapter 2 | 3 | import android.content.Context 4 | import com.samiu.wangank.util.toBrowser 5 | import com.samiu.wangank.bean.Article 6 | import com.samiu.wangank.http.BaseWanRepository 7 | import com.samiu.wangank.http.WanClient 8 | import kotlinx.coroutines.MainScope 9 | import kotlinx.coroutines.launch 10 | 11 | /** 12 | * @author Samiu 2020/7/6 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class WxArticleListenerImpl( 16 | private val context: Context 17 | ) : WxArticleAdapter.ArticleAdapterListener, BaseWanRepository() { 18 | 19 | override fun onArticleClick(article: Article) { 20 | context.toBrowser(article.link, article.title) 21 | } 22 | 23 | override fun onArticleStarChanged(article: Article, newValue: Boolean) { 24 | article.collect = newValue 25 | if (newValue) 26 | collectArticle(article.id, article.originId) 27 | else 28 | cancelCollect(article.id, article.originId) 29 | } 30 | 31 | private fun collectArticle(id: Int, originId: Int) = MainScope().launch { 32 | if (originId != 0) 33 | doCollect(originId) 34 | else 35 | doCollect(id) 36 | } 37 | 38 | private fun cancelCollect(id: Int, originId: Int) = MainScope().launch { 39 | if (originId != 0) 40 | doCancelCollect(originId) 41 | else 42 | doCancelCollect(id) 43 | } 44 | 45 | private suspend fun doCollect(id: Int) = readyCall { 46 | call(WanClient.service.collectArticle(id)) 47 | } 48 | 49 | private suspend fun doCancelCollect(id: Int) = readyCall { 50 | call(WanClient.service.cancelCollectArticle(id)) 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/mine/collect/WanCollectFragment.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.mine.collect 2 | 3 | import androidx.lifecycle.lifecycleScope 4 | import androidx.recyclerview.widget.ItemTouchHelper 5 | import com.samiu.base.ui.BaseFragment 6 | import com.samiu.base.ui.viewBinding 7 | import com.samiu.wangank.R 8 | import com.samiu.wangank.databinding.FragmentRecyclerBinding 9 | import com.samiu.wangank.ui.home.adapter.ArticleListenerImpl 10 | import com.samiu.wangank.ui.home.adapter.ReboundingSwipeActionCallback 11 | import com.samiu.wangank.ui.mine.collect.adapter.CollectArticleAdapter 12 | import com.samiu.wangank.ui.mine.collect.adapter.CollectArticleListenerImpl 13 | import kotlinx.coroutines.flow.collectLatest 14 | import org.koin.androidx.viewmodel.ext.android.viewModel 15 | 16 | /** 17 | * 收藏页面 18 | * 19 | * @author Samiu 2022/3/12 20 | * @email samiuzhong@outlook.com 21 | */ 22 | class WanCollectFragment : BaseFragment(R.layout.fragment_recycler) { 23 | 24 | private val binding by viewBinding(FragmentRecyclerBinding::bind) 25 | private val viewModel: WanCollectViewModel by viewModel() 26 | 27 | private lateinit var mAdapter: CollectArticleAdapter 28 | 29 | override fun initView() { 30 | mAdapter = CollectArticleAdapter(CollectArticleListenerImpl(requireContext())) 31 | binding.recycler.apply { 32 | val itemTouchHelper = ItemTouchHelper(ReboundingSwipeActionCallback()) 33 | itemTouchHelper.attachToRecyclerView(this) 34 | adapter = mAdapter 35 | } 36 | } 37 | 38 | override fun initData() { 39 | viewLifecycleOwner.lifecycleScope.launchWhenCreated { 40 | viewModel.articlePagingList.collectLatest { 41 | mAdapter.submitData(it) 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/mine/collect/adapter/CollectArticleListenerImpl.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.mine.collect.adapter 2 | 3 | import android.content.Context 4 | import com.samiu.wangank.util.toBrowser 5 | import com.samiu.wangank.bean.Article 6 | import com.samiu.wangank.http.BaseWanRepository 7 | import com.samiu.wangank.http.WanClient 8 | import kotlinx.coroutines.MainScope 9 | import kotlinx.coroutines.launch 10 | 11 | /** 12 | * @author Samiu 2020/7/6 13 | * @email samiuzhong@outlook.com 14 | */ 15 | class CollectArticleListenerImpl( 16 | private val context: Context 17 | ) : CollectArticleAdapter.ArticleAdapterListener, BaseWanRepository() { 18 | 19 | override fun onArticleClick(article: Article) { 20 | context.toBrowser(article.link, article.title) 21 | } 22 | 23 | override fun onArticleStarChanged(article: Article, newValue: Boolean) { 24 | article.collect = newValue 25 | if (newValue) 26 | collectArticle(article.id, article.originId) 27 | else 28 | cancelCollect(article.id, article.originId) 29 | } 30 | 31 | private fun collectArticle(id: Int, originId: Int) = MainScope().launch { 32 | if (originId != 0) 33 | doCollect(originId) 34 | else 35 | doCollect(id) 36 | } 37 | 38 | private fun cancelCollect(id: Int, originId: Int) = MainScope().launch { 39 | if (originId != 0) 40 | doCancelCollect(originId) 41 | else 42 | doCancelCollect(id) 43 | } 44 | 45 | private suspend fun doCollect(id: Int) = readyCall { 46 | call(WanClient.service.collectArticle(id)) 47 | } 48 | 49 | private suspend fun doCancelCollect(id: Int) = readyCall { 50 | call(WanClient.service.cancelCollectArticle(id)) 51 | } 52 | } -------------------------------------------------------------------------------- /base/src/main/java/com/samiu/base/adapter/BaseSingleRecyclerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.base.adapter 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.view.LayoutInflater 6 | import androidx.recyclerview.widget.RecyclerView 7 | 8 | /** 9 | * @author Samiu 2020/3/3 10 | * @email samiuzhong@outlook.com 11 | */ 12 | @SuppressLint("NotifyDataSetChanged") 13 | abstract class BaseSingleRecyclerAdapter constructor() : 14 | RecyclerView.Adapter() { 15 | 16 | private lateinit var context: Context 17 | lateinit var layoutInflater: LayoutInflater 18 | var mList: MutableList = ArrayList() 19 | 20 | constructor(context: Context) : this() { 21 | this.context = context 22 | layoutInflater = LayoutInflater.from(context) 23 | } 24 | 25 | /** 26 | * 获取Item的数量 27 | * 28 | * @return 29 | */ 30 | override fun getItemCount(): Int { 31 | return mList.size 32 | } 33 | 34 | /** 35 | * 添加一个Item 36 | * 37 | * @param item 38 | */ 39 | fun addItem(item: T) { 40 | mList.add(item) 41 | notifyDataSetChanged() 42 | } 43 | 44 | /** 45 | * 添加一个Item列表 46 | * 47 | * @param items 48 | */ 49 | fun addAll(items: List) { 50 | mList.addAll(items) 51 | notifyDataSetChanged() 52 | } 53 | 54 | /** 55 | * 替换Item列表 56 | * 57 | * @param list 58 | */ 59 | fun replaceAll(list: List) { 60 | mList.clear() 61 | addAll(list) 62 | } 63 | 64 | /** 65 | * 清空Item列表 66 | */ 67 | fun clearAll() { 68 | mList.clear() 69 | notifyDataSetChanged() 70 | } 71 | 72 | /** 73 | * 删除指定位置的Item 74 | * 75 | * @param index 76 | */ 77 | fun remove(index: Int) { 78 | mList.removeAt(index) 79 | notifyDataSetChanged() 80 | } 81 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/mine/WanMineRepository.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.mine 2 | 3 | import androidx.paging.Pager 4 | import androidx.paging.PagingConfig 5 | import com.samiu.wangank.bean.base.WanResult 6 | import com.samiu.wangank.global.DEFAULT_PAGE_SIZE 7 | import com.samiu.wangank.http.BaseWanRepository 8 | import com.samiu.wangank.http.WanClient 9 | import com.samiu.wangank.ui.mine.personal.CollectArticlePagingSource 10 | import com.samiu.wangank.ui.mine.personal.MyArticlePagingSource 11 | import com.samiu.wangank.ui.square.SquareArticlePagingSource 12 | 13 | /** 14 | * @author Samiu 2020/4/17 15 | * @email samiuzhong@outlook.com 16 | */ 17 | class WanMineRepository : BaseWanRepository() { 18 | 19 | /** 20 | * 登录 21 | */ 22 | suspend fun login(userName: String, passWord: String) = readyCall { 23 | call(WanClient.service.login(userName, passWord)) 24 | } 25 | 26 | /** 27 | * 登出 28 | */ 29 | suspend fun logout() = readyCall { 30 | call(WanClient.service.logout()) 31 | } 32 | 33 | /** 34 | * 我的收藏 35 | */ 36 | fun getCollectPaging() = Pager( 37 | PagingConfig( 38 | pageSize = DEFAULT_PAGE_SIZE, 39 | prefetchDistance = DEFAULT_PAGE_SIZE, 40 | ) 41 | ) { 42 | CollectArticlePagingSource() 43 | }.flow 44 | 45 | /** 46 | * 我分享的文章 47 | */ 48 | fun getArticlePaging() = Pager( 49 | PagingConfig( 50 | pageSize = DEFAULT_PAGE_SIZE, 51 | prefetchDistance = DEFAULT_PAGE_SIZE, 52 | ) 53 | ) { 54 | MyArticlePagingSource() 55 | }.flow 56 | 57 | /** 58 | * 分享文章 59 | */ 60 | suspend fun share(title: String, link: String): WanResult { 61 | val result: WanResult? 62 | result = readyCall { 63 | call(WanClient.service.shareArticle(title, link)) 64 | } 65 | return result 66 | } 67 | } -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.samiu.buildsrc.BuildConfig 2 | import com.samiu.buildsrc.Deps 3 | 4 | plugins { 5 | id("com.android.application") 6 | kotlin("android") 7 | kotlin("kapt") 8 | } 9 | 10 | android { 11 | compileSdk = BuildConfig.compileSdk 12 | 13 | defaultConfig { 14 | applicationId = BuildConfig.applicationId 15 | minSdk = BuildConfig.minSdk 16 | targetSdk = BuildConfig.targetSdk 17 | versionCode = BuildConfig.versionCode 18 | versionName = BuildConfig.versionName 19 | } 20 | 21 | buildTypes { 22 | release { 23 | isMinifyEnabled = false 24 | proguardFiles( 25 | getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" 26 | ) 27 | } 28 | } 29 | compileOptions { 30 | sourceCompatibility = BuildConfig.javaVersion 31 | targetCompatibility = BuildConfig.javaVersion 32 | } 33 | kotlinOptions { 34 | jvmTarget = BuildConfig.kotlinJvmTarget 35 | } 36 | 37 | buildFeatures { 38 | dataBinding = true 39 | viewBinding = true 40 | } 41 | } 42 | 43 | dependencies { 44 | implementation(Deps.AndroidX.coreKtx) 45 | implementation(Deps.AndroidX.appcompat) 46 | 47 | implementation(project(":base")) 48 | implementation(project(":res")) 49 | 50 | implementation(Deps.Http.agentWebCore) 51 | implementation(Deps.Http.agentWebFileChooser) 52 | implementation(Deps.Bumptech.glide) 53 | kapt(Deps.Bumptech.glideCompiler) 54 | implementation(Deps.Component.autoSize) 55 | implementation(Deps.Component.banner) 56 | implementation(Deps.Component.smartRefreshLayout) 57 | implementation(Deps.Component.smartRefreshHeader) 58 | implementation(Deps.Component.flowLayout) 59 | implementation(Deps.Component.materialDialog) 60 | implementation(Deps.Component.materialDialogLifecycle) 61 | implementation(Deps.Component.licensesDialog) 62 | implementation(Deps.Component.licensesDialog) 63 | } 64 | -------------------------------------------------------------------------------- /base/src/main/java/com/samiu/base/interactive/ZoomOutPageTransformer.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.base.interactive 2 | 3 | import android.view.View 4 | import androidx.viewpager2.widget.ViewPager2 5 | 6 | /** 7 | * @author Samiu 2020/3/3 8 | * @email samiuzhong@outlook.com 9 | */ 10 | class ZoomOutPageTransformer:ViewPager2.PageTransformer { 11 | 12 | companion object{ 13 | private const val MIN_SCALE = 0.85f 14 | private const val MIN_ALPHA = 0.5f 15 | } 16 | override fun transformPage(page: View, position: Float) { 17 | page.apply { 18 | val pageWidth = width 19 | val pageHeight = height 20 | when { 21 | position < -1 -> { // [-Infinity,-1) 22 | // This page is way off-screen to the left. 23 | alpha = 0f 24 | } 25 | position <= 1 -> { // [-1,1] 26 | // Modify the default slide transition to shrink the page as well 27 | val scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position)) 28 | val vertMargin = pageHeight * (1 - scaleFactor) / 2 29 | val horzMargin = pageWidth * (1 - scaleFactor) / 2 30 | translationX = if (position < 0) { 31 | horzMargin - vertMargin / 2 32 | } else { 33 | horzMargin + vertMargin / 2 34 | } 35 | 36 | // Scale the page down (between MIN_SCALE and 1) 37 | scaleX = scaleFactor 38 | scaleY = scaleFactor 39 | 40 | // Fade the page relative to its size. 41 | alpha = (MIN_ALPHA + 42 | (((scaleFactor - MIN_SCALE) / (1 - MIN_SCALE)) * (1 - MIN_ALPHA))) 43 | } 44 | else -> { // (1,+Infinity] 45 | // This page is way off-screen to the right. 46 | alpha = 0f 47 | } 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /res/src/main/res/drawable/ic_twotone_stars.xml: -------------------------------------------------------------------------------- 1 | 14 | 20 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 17 | 18 | 19 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 40 | 43 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/base/MineActivity.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.base 2 | 3 | import com.afollestad.materialdialogs.MaterialDialog 4 | import com.afollestad.materialdialogs.customview.customView 5 | import com.samiu.base.ui.BaseActivity 6 | import com.samiu.base.ui.viewBinding 7 | import com.samiu.wangank.R 8 | import com.samiu.wangank.databinding.ActivityMineBinding 9 | import com.samiu.wangank.global.GITHUB_PAGE 10 | import com.samiu.wangank.global.MY_BLOG 11 | import com.samiu.wangank.util.openNativeBrowser 12 | import de.psdev.licensesdialog.LicensesDialog 13 | import de.psdev.licensesdialog.licenses.MITLicense 14 | import de.psdev.licensesdialog.model.Notice 15 | 16 | /** 17 | * 关于页面 18 | * @author Samiu 2020/5/21 19 | * @email samiuzhong@outlook.com 20 | */ 21 | class MineActivity : BaseActivity() { 22 | 23 | private val binding by viewBinding(ActivityMineBinding::inflate) 24 | override fun getBindingRoot() = binding.root 25 | override fun initData() = Unit 26 | 27 | override fun initView() { 28 | binding.run { 29 | backIcon.setOnClickListener { finish() } 30 | text1.setOnClickListener { 31 | val notice = Notice(getString(R.string.app_name), GITHUB_PAGE, "", MITLicense()) 32 | LicensesDialog 33 | .Builder(this@MineActivity) 34 | .setNotices(notice) 35 | .build() 36 | .show() 37 | } 38 | text2.setOnClickListener { this@MineActivity.openNativeBrowser(GITHUB_PAGE) } 39 | text3.setOnClickListener { 40 | LicensesDialog 41 | .Builder(this@MineActivity) 42 | .setNotices(R.raw.licenses) 43 | .build() 44 | .show() 45 | } 46 | text4.setOnClickListener { this@MineActivity.openNativeBrowser(MY_BLOG) } 47 | text5.setOnClickListener { 48 | MaterialDialog(this@MineActivity).show { 49 | customView(R.layout.layout_image_dialog) 50 | } 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /res/src/main/res/values/shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 28 | 29 | 33 | 34 | 35 | 36 | 39 | 40 | 24dp 41 | 0dp 42 | 12dp 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/wxpub/adapter/WanTitleAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.wxpub.adapter 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.TextView 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.samiu.base.adapter.BaseSingleRecyclerAdapter 9 | import com.samiu.wangank.R 10 | import com.samiu.wangank.bean.SystemParent 11 | 12 | /** 13 | * @author Samiu 2020/3/7 14 | * @email samiuzhong@outlook.com 15 | */ 16 | class WanTitleAdapter(context: Context) : BaseSingleRecyclerAdapter(context) { 17 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { 18 | return TitleHolder(layoutInflater.inflate(R.layout.item_wan_verti_title, parent, false)) 19 | } 20 | 21 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { 22 | if (holder is TitleHolder) { 23 | val data = mList[position] 24 | with(holder.titleTv) { 25 | text = data.name 26 | if (data.isSelected) { 27 | setTextColor(resources.getColor(R.color.reply_blue_700, null)) 28 | background = resources.getDrawable(R.color.reply_blue_50, null) 29 | } else { 30 | setTextColor(resources.getColor(R.color.default_prompt_text_color_9496A4, null)) 31 | background = resources.getDrawable(R.color.color_F7F7F7, null) 32 | } 33 | } 34 | holder.itemView.setOnClickListener { 35 | listener(data.id) 36 | if (!data.isSelected) { 37 | for (index in mList.indices) 38 | mList[index].isSelected = index == position 39 | notifyDataSetChanged() 40 | } 41 | } 42 | } 43 | } 44 | 45 | private lateinit var listener: (Int) -> Unit 46 | fun setOnItemClick(listener: (Int) -> Unit) { 47 | this.listener = listener 48 | } 49 | 50 | class TitleHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 51 | var titleTv: TextView = itemView.findViewById(R.id.title_text) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/mine/login/WanLoginActivity.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.mine.login 2 | 3 | import android.content.Intent 4 | import android.widget.Toast 5 | import com.samiu.base.ui.BaseActivity 6 | import com.samiu.base.ui.viewBinding 7 | import com.samiu.wangank.R 8 | import com.samiu.wangank.databinding.ActivityWanLoginBinding 9 | import com.samiu.wangank.ui.mine.personal.WanPersonalActivity 10 | import org.koin.androidx.viewmodel.ext.android.viewModel 11 | 12 | /** 13 | * 登录页 14 | * 15 | * @author Samiu 2020/4/17 16 | * @email samiuzhong@outlook.com 17 | */ 18 | class WanLoginActivity : BaseActivity() { 19 | 20 | private val binding by viewBinding(ActivityWanLoginBinding::inflate) 21 | private val viewModel: WanLoginViewModel by viewModel() 22 | 23 | override fun getBindingRoot() = binding.root 24 | override fun initData() = Unit 25 | 26 | override fun initView() { 27 | binding.run { 28 | cancelBtn.setOnClickListener { finish() } 29 | loginBtn.setOnClickListener { 30 | if (userName.text!!.isNotBlank() && password.text!!.isNotBlank()) 31 | viewModel.login(userName.text.toString(), password.text.toString()) 32 | else 33 | Toast.makeText( 34 | this@WanLoginActivity, 35 | getString(R.string.input_correct_info), 36 | Toast.LENGTH_SHORT 37 | ).show() 38 | } 39 | } 40 | } 41 | 42 | override fun startObserve() = viewModel.run { 43 | loginSuccess.observe(this@WanLoginActivity) { aBoolean -> 44 | if (aBoolean) { 45 | Toast.makeText( 46 | this@WanLoginActivity, 47 | getString(R.string.log_in_success), 48 | Toast.LENGTH_SHORT 49 | ).show() 50 | startActivity(Intent(this@WanLoginActivity, WanPersonalActivity::class.java)) 51 | finish() 52 | } else 53 | Toast.makeText( 54 | this@WanLoginActivity, 55 | getString(R.string.input_correct_info), 56 | Toast.LENGTH_SHORT 57 | ).show() 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_wan_personal.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | 29 | 30 | 43 | 44 | 45 | 46 | 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/util/ShapeDrawableHelper.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.util 2 | 3 | import android.content.Context 4 | import android.content.res.ColorStateList 5 | import com.google.android.material.shape.MaterialShapeDrawable 6 | import com.samiu.wangank.R 7 | 8 | /** 9 | * 绘制ShapeDrawable 10 | * 11 | * @author Samiu 2020/9/17 12 | * @email samiuzhong@outlook.com 13 | */ 14 | fun drawShape( 15 | context: Context, cornerSize: Int?, color: Int 16 | ) = MaterialShapeDrawable( 17 | context, 18 | null, 19 | 0, 20 | 0 21 | ).apply { 22 | val realColor = context.resources.getColor(color,null) 23 | fillColor = ColorStateList.valueOf(realColor) 24 | shapeAppearanceModel = shapeAppearanceModel 25 | .toBuilder() 26 | .setAllCornerSizes(context.resources.getDimension(cornerSize ?: R.dimen.corner_0)) 27 | .build() 28 | } 29 | 30 | fun drawShape( 31 | context: Context, 32 | ltCorner: Int?, 33 | rtCorner: Int?, 34 | lbCorner: Int?, 35 | rbCorner: Int?, 36 | color: Int 37 | ) = MaterialShapeDrawable( 38 | context, 39 | null, 40 | 0, 41 | 0 42 | ).apply { 43 | val realColor = context.resources.getColor(color,null) 44 | fillColor = ColorStateList.valueOf(realColor) 45 | shapeAppearanceModel = shapeAppearanceModel 46 | .toBuilder() 47 | .setTopLeftCornerSize(context.resources.getDimension(ltCorner ?: R.dimen.corner_0)) 48 | .setTopRightCornerSize(context.resources.getDimension(rtCorner ?: R.dimen.corner_0)) 49 | .setBottomLeftCornerSize(context.resources.getDimension(lbCorner ?: R.dimen.corner_0)) 50 | .setBottomRightCornerSize(context.resources.getDimension(rbCorner ?: R.dimen.corner_0)) 51 | .build() 52 | } 53 | 54 | fun drawShape( 55 | context: Context, cornerSize: Int?, color: Int, lineColor: Int 56 | ) = MaterialShapeDrawable( 57 | context, 58 | null, 59 | 0, 60 | 0 61 | ).apply { 62 | val realColor = context.resources.getColor(color,null) 63 | val realLineColor = context.resources.getColor(lineColor,null) 64 | fillColor = ColorStateList.valueOf(realColor) 65 | strokeWidth = 1F 66 | strokeColor = ColorStateList.valueOf(realLineColor) 67 | shapeAppearanceModel = shapeAppearanceModel 68 | .toBuilder() 69 | .setAllCornerSizes(context.resources.getDimension(cornerSize ?: R.dimen.corner_0)) 70 | .build() 71 | } -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/base/module.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.base 2 | 3 | import com.samiu.wangank.http.WanClient 4 | import com.samiu.wangank.ui.home.WanHomeRepository 5 | import com.samiu.wangank.ui.home.WanHomeViewModel 6 | import com.samiu.wangank.ui.mine.WanMineRepository 7 | import com.samiu.wangank.ui.mine.collect.WanCollectFragment 8 | import com.samiu.wangank.ui.mine.collect.WanCollectViewModel 9 | import com.samiu.wangank.ui.mine.login.WanLoginViewModel 10 | import com.samiu.wangank.ui.mine.personal.WanPersonalViewModel 11 | import com.samiu.wangank.ui.mine.share.WanShareViewModel 12 | import com.samiu.wangank.ui.project.WanProjectRepository 13 | import com.samiu.wangank.ui.project.WanProjectViewModel 14 | import com.samiu.wangank.ui.search.WanSearchRepository 15 | import com.samiu.wangank.ui.search.WanSearchViewModel 16 | import com.samiu.wangank.ui.square.WanSquareRepository 17 | import com.samiu.wangank.ui.square.WanSquareViewModel 18 | import com.samiu.wangank.ui.system.SystemDisplayViewModel 19 | import com.samiu.wangank.ui.system.WanSystemRepository 20 | import com.samiu.wangank.ui.system.WanSystemViewModel 21 | import com.samiu.wangank.ui.wxpub.WanWxRepository 22 | import com.samiu.wangank.ui.wxpub.WanWxViewModel 23 | import org.koin.androidx.viewmodel.dsl.viewModel 24 | import org.koin.dsl.module 25 | 26 | /** 27 | * @author Samiu 2020/3/3 28 | * @email samiuzhong@outlook.com 29 | */ 30 | val viewModelModule = module { 31 | viewModel { WanHomeViewModel(get()) } 32 | viewModel { WanSquareViewModel(get()) } 33 | viewModel { WanProjectViewModel(get()) } 34 | viewModel { WanSystemViewModel(get()) } 35 | viewModel { WanWxViewModel(get()) } 36 | viewModel { WanSearchViewModel(get()) } 37 | viewModel { SystemDisplayViewModel(get()) } 38 | viewModel { WanLoginViewModel(get()) } 39 | viewModel { WanPersonalViewModel(get()) } 40 | viewModel { WanShareViewModel(get()) } 41 | viewModel { WanCollectViewModel(get()) } 42 | } 43 | 44 | val repositoryModule = module { 45 | single { WanClient.service } 46 | single { WanHomeRepository() } 47 | single { WanSquareRepository() } 48 | single { WanProjectRepository() } 49 | single { WanSystemRepository() } 50 | single { WanWxRepository() } 51 | single { WanSearchRepository() } 52 | single { WanMineRepository() } 53 | } 54 | 55 | val module = listOf( 56 | viewModelModule, 57 | repositoryModule 58 | ) -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_system_display.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 20 | 21 | 27 | 28 | 35 | 36 | 37 | 38 | 42 | 43 | 46 | 47 | 53 | 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/samiu/wangank/ui/project/adapter/WanTypeAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.wangank.ui.project.adapter 2 | 3 | import android.content.Context 4 | import android.text.Html 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.samiu.base.adapter.BaseSingleRecyclerAdapter 10 | import com.samiu.wangank.R 11 | import com.samiu.wangank.bean.SystemParent 12 | import com.samiu.wangank.util.drawShape 13 | 14 | /** 15 | * @author Samiu 2020/3/7 16 | * @email samiuzhong@outlook.com 17 | */ 18 | class WanTypeAdapter(context: Context) : BaseSingleRecyclerAdapter(context) { 19 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { 20 | return WanTypeHolder(layoutInflater.inflate(R.layout.item_wan_type, parent, false)) 21 | } 22 | 23 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { 24 | if (holder is WanTypeHolder) { 25 | holder.setIsRecyclable(false) 26 | with(holder.typeTv) { 27 | text = Html.fromHtml(mList[position].name,0) 28 | if (mList[position].isSelected) { 29 | setTextColor(resources.getColor(R.color.white, null)) 30 | background = drawShape( 31 | context, 32 | R.dimen.corner_20, 33 | R.color.reply_blue_700 34 | ) 35 | } else { 36 | setTextColor(resources.getColor(R.color.default_text_color_252F3B, null)) 37 | background = null 38 | } 39 | } 40 | holder.itemView.setOnClickListener { 41 | if (!mList[position].isSelected) { 42 | listener(mList[position].id) 43 | for (i in mList.indices) 44 | mList[i].isSelected = i == position 45 | notifyDataSetChanged() 46 | } 47 | } 48 | } 49 | } 50 | 51 | private lateinit var listener: (Int) -> Unit 52 | fun setOnItemClick(listener: (Int) -> Unit) { 53 | this.listener = listener 54 | } 55 | 56 | class WanTypeHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 57 | var typeTv: TextView = itemView.findViewById(R.id.type_text) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /base/src/main/java/com/samiu/base/ui/ViewBindingDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.samiu.base.ui 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.fragment.app.Fragment 7 | import androidx.lifecycle.DefaultLifecycleObserver 8 | import androidx.lifecycle.Lifecycle 9 | import androidx.lifecycle.LifecycleOwner 10 | import androidx.viewbinding.ViewBinding 11 | import kotlin.properties.ReadOnlyProperty 12 | import kotlin.reflect.KProperty 13 | 14 | /** 15 | * @author Samiu 2020/3/27 16 | * @email samiuzhong@outlook.com 17 | */ 18 | 19 | /** 20 | * Activity ViewBinding Delegate 21 | */ 22 | inline fun AppCompatActivity.viewBinding(crossinline bindingInflater: (LayoutInflater) -> T) = 23 | lazy(LazyThreadSafetyMode.NONE) { 24 | bindingInflater.invoke(layoutInflater) 25 | } 26 | 27 | /** 28 | * Fragment ViewBinding Delegate 29 | */ 30 | fun Fragment.viewBinding(viewBindingFactory: (View) -> T) = 31 | FragmentViewBindingDelegate(this, viewBindingFactory) 32 | 33 | class FragmentViewBindingDelegate( 34 | val fragment: Fragment, 35 | val viewBindingFactory: (View) -> T 36 | ) : ReadOnlyProperty { 37 | private var _binding: T? = null 38 | 39 | init { 40 | fragment.lifecycle.addObserver(object : DefaultLifecycleObserver { 41 | override fun onCreate(owner: LifecycleOwner) { 42 | fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner -> 43 | viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { 44 | override fun onDestroy(owner: LifecycleOwner) { 45 | _binding = null 46 | } 47 | }) 48 | } 49 | } 50 | }) 51 | } 52 | 53 | override fun getValue(thisRef: Fragment, property: KProperty<*>): T { 54 | val binding = _binding 55 | if (binding != null) { 56 | return binding 57 | } 58 | 59 | val lifecycle = fragment.viewLifecycleOwner.lifecycle 60 | if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) { 61 | throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.") 62 | } 63 | 64 | return viewBindingFactory(thisRef.requireView()).also { _binding = it } 65 | } 66 | } -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------