├── .gitignore ├── MvvM类图.puml ├── README.md ├── RecyclerView.puml ├── app ├── .gitignore ├── UploadToFir.py ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── minic │ │ └── kt │ │ ├── MainActivity.kt │ │ ├── base │ │ ├── App.kt │ │ ├── BaseVM.kt │ │ ├── Constant.kt │ │ └── viewbinder │ │ │ └── BaseViewBinder.kt │ │ ├── data │ │ ├── RetrofitProvider.kt │ │ ├── WARepository.kt │ │ ├── api │ │ │ └── WAndroidService.kt │ │ └── model │ │ │ ├── BResponse.kt │ │ │ └── gank │ │ │ ├── PagingData.kt │ │ │ └── home │ │ │ ├── Article.kt │ │ │ ├── ArticleData.kt │ │ │ ├── Banner.kt │ │ │ ├── ProjectTree.kt │ │ │ └── SystemTree.kt │ │ ├── ext │ │ └── CoroutineExt.kt │ │ ├── jetpack │ │ ├── binds │ │ │ ├── ImageBinds.kt │ │ │ └── Presenter.kt │ │ └── paging │ │ │ ├── BindingHolder.kt │ │ │ ├── ILoadData.kt │ │ │ ├── PagingAdapter.kt │ │ │ ├── PagingDataSource.kt │ │ │ ├── PagingDataSourceFactory.kt │ │ │ ├── PagingLoadHelper.kt │ │ │ └── SimpleViewHolder.kt │ │ ├── ui │ │ ├── activity │ │ │ ├── common │ │ │ │ ├── BaseBrowserActivity.kt │ │ │ │ └── BrowserActivity.kt │ │ │ └── test │ │ │ │ └── MotionLayoutActivity.kt │ │ └── fragment │ │ │ ├── HomeViewPagerFragment.kt │ │ │ ├── adapter │ │ │ ├── ProjectChildAdapter.kt │ │ │ ├── ProjectPagerAdapter.kt │ │ │ ├── WAPagerAdapter.kt │ │ │ └── viewbinder │ │ │ │ ├── ArticleViewBinder.kt │ │ │ │ ├── BannerViewBinder.kt │ │ │ │ └── SystemChildViewBinder.kt │ │ │ ├── home │ │ │ ├── HomeFragment.kt │ │ │ ├── MeFragment.kt │ │ │ ├── ProjectChildFragment.kt │ │ │ ├── ProjectFragment.kt │ │ │ ├── WeChartFragment.kt │ │ │ └── system │ │ │ │ ├── SystemChildFragment.kt │ │ │ │ └── SystemFragment.kt │ │ │ └── vm │ │ │ ├── HomeProjectVM.kt │ │ │ ├── HomeVM.kt │ │ │ ├── ProjectChildVM.kt │ │ │ └── SystemChildVM.kt │ │ ├── utils │ │ ├── FragmentChangeManager.kt │ │ ├── GlideImageLoader.kt │ │ ├── TimeUtils.kt │ │ └── ext │ │ │ └── ViewExt.kt │ │ └── widget │ │ └── superbutton │ │ ├── AttributeSetHelper.kt │ │ ├── ShapeBuilder.kt │ │ └── ShapeButton.kt │ └── res │ ├── anim │ ├── slide_in_left.xml │ ├── slide_in_right.xml │ ├── slide_out_left.xml │ └── slide_out_right.xml │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── ic_launcher_background.xml │ └── icon.png │ ├── layout │ ├── activity_main.xml │ ├── activity_motion_study.xml │ ├── fragment_browser.xml │ ├── fragment_home.xml │ ├── fragment_home_view_pager.xml │ ├── fragment_project.xml │ ├── fragment_project_child.xml │ ├── fragment_system.xml │ ├── fragment_test.xml │ ├── include_toolbar.xml │ ├── item_home_article.xml │ ├── item_home_banner.xml │ └── item_home_project_child.xml │ ├── menu │ ├── menu_browser.xml │ └── menu_main_nav.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── navigation │ └── nav_graph.xml │ ├── values-night-v21 │ └── styles.xml │ ├── values-night │ └── styles.xml │ ├── values │ ├── anim.xml │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── motion_study_screen.xml ├── base ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── error.json │ └── loading.json │ ├── java │ └── com │ │ └── minic │ │ └── base │ │ ├── aop │ │ ├── annotation │ │ │ └── SingleClick.kt │ │ └── aspect │ │ │ └── SingleClickAspect.kt │ │ ├── base │ │ ├── BaseActivity.kt │ │ ├── BaseApp.kt │ │ └── BaseFragment.kt │ │ ├── callback │ │ ├── ErrorCallback.kt │ │ └── LoadingCallback.kt │ │ ├── databinding │ │ └── viewmodel │ │ │ ├── IViewModel.kt │ │ │ ├── LifecycleViewModel.kt │ │ │ └── SingleLiveEvent.kt │ │ ├── extens │ │ ├── ActivityExt.kt │ │ ├── NetworkEx.kt │ │ ├── SnackBarExt.kt │ │ ├── ThreadExecutorExt.kt │ │ └── ViewModelExt.kt │ │ └── net │ │ ├── UrlConstant.kt │ │ ├── exception │ │ ├── CException.kt │ │ └── ExceptionExt.kt │ │ └── source │ │ ├── DefaultSource.kt │ │ ├── IDataSource.kt │ │ └── ISourceRequest.kt │ └── res │ ├── layout │ └── layout_lottie.xml │ └── values │ ├── colors.xml │ ├── ids.xml │ └── strings.xml ├── build.gradle ├── commonres ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── res │ ├── drawable-xxhdpi │ ├── icon_home_pager_not_selected.png │ ├── icon_home_pager_selected.png │ ├── icon_knowledge_hierarchy_not_selected.png │ ├── icon_knowledge_hierarchy_selected.png │ ├── icon_me_not_selected.png │ ├── icon_me_selected.png │ ├── icon_navigation_not_selected.png │ ├── icon_navigation_selected.png │ ├── icon_project_not_selected.png │ ├── icon_project_selected.png │ └── vector_account_circle_px.xml │ ├── drawable │ └── progress_bar.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ └── strings.xml ├── config.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── image_loader ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── minic │ │ └── imageload │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── minic │ │ │ └── imageload │ │ │ ├── Ext.kt │ │ │ ├── ImageFrom.kt │ │ │ ├── ImageLoader.kt │ │ │ ├── ImageLoaderOptions.kt │ │ │ └── ProgressDownLoad.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── minic │ └── imageload │ └── ExampleUnitTest.kt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/ 38 | .idea/tasks.xml 39 | .idea/assetWizardSettings.xml 40 | .idea/dictionaries 41 | 42 | # Keystore files 43 | # Uncomment the following line if you do not want to check your keystore files in. 44 | #*.jks 45 | 46 | # External native build folder generated in Android Studio 2.2 and later 47 | .externalNativeBuild 48 | 49 | # Google Services (e.g. APIs or Firebase) 50 | google-services.json 51 | 52 | # Freeline 53 | freeline.py 54 | freeline/ 55 | freeline_project_description.json 56 | 57 | # fastlane 58 | fastlane/report.xml 59 | fastlane/Preview.html 60 | fastlane/screenshots 61 | fastlane/test_output 62 | fastlane/readme.md -------------------------------------------------------------------------------- /MvvM类图.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | interface IViewModel 3 | 4 | interface LifecycleViewModel { 5 | # lifecycleOwner 6 | # onCreate() 7 | # onStart() 8 | # onResume() 9 | # ... 10 | } 11 | IViewModel <|-- LifecycleViewModel 12 | 13 | class BaseVM { 14 | # CoroutineSupport 15 | # ApiService 16 | } 17 | 18 | 19 | LifecycleViewModel <|.. BaseVM 20 | 21 | abstract BaseActivity { 22 | # DataBinding 23 | } 24 | 25 | 26 | class BaseModel { 27 | - code 28 | - message 29 | - data 30 | } 31 | 32 | class LoginActivity { 33 | } 34 | BaseActivity <|-- LoginActivity 35 | 36 | class LoginViewModel { 37 | 处理登陆逻辑,发送登陆请求, 38 | 拿到登陆成功信息,并且通知View层做出响应的操作响应 39 | } 40 | BaseVM <|-- LoginViewModel 41 | 42 | class LoginModel { 43 | - 登陆回调数据 44 | } 45 | BaseModel <|-- LoginModel 46 | 47 | LoginViewModel *-- LoginActivity : 包含 48 | LoginModel*-- LoginViewModel: 包含 49 | 50 | 51 | @enduml 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidJetPack 2 | 3 | ## MVVM架构UML图 4 | 5 | ![](http://ww1.sinaimg.cn/large/0064he0vly1g4a192qwl7j30oe10042g.jpg) 6 | 7 | 8 | -------------------------------------------------------------------------------- /RecyclerView.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | class RecyclerView 4 | 5 | abstract LayoutManager 6 | 7 | abstract ItemAnimator 8 | 9 | ItemAnimator <|-- DefaultItemAnimator 10 | 11 | RecyclerView --* LayoutManager : 包含 12 | RecyclerView --* DefaultItemAnimator : 包含 13 | 14 | @enduml 15 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/UploadToFir.py: -------------------------------------------------------------------------------- 1 | # encoding = utf-8 2 | import sys 3 | import traceback 4 | import requests 5 | 6 | requests.packages.urllib3.disable_warnings() 7 | 8 | 9 | def uploadtofir(): 10 | # 参数检查 11 | paramnum = 8 12 | syslen = len(sys.argv) 13 | if syslen < paramnum: 14 | print("please input param") 15 | return 16 | else: 17 | # 基础参数 18 | appname = sys.argv[1] # app名称 19 | apppackage = sys.argv[2] # 唯一包名,也即是bundle_id 20 | appversion = sys.argv[3] # app版本号 21 | appbuild = sys.argv[4] # app build号 22 | apitoken = sys.argv[5] # fir token 23 | apklogo = sys.argv[6] # 等待上传的APK logo路径 24 | apkpath = sys.argv[7] # 等待上传的APK logo路径 25 | # 第一步:获取fir上传凭证 26 | print("get fir upload certificate") 27 | icondict = {} # 后面上传图标和apk需要使用的参数,这里保存下来 28 | binarydict = {} 29 | try: 30 | req = requests.post("http://api.fir.im/apps", 31 | {'type': 'android', 'bundle_id': apppackage, 'api_token': apitoken}) 32 | resjson = req.json() 33 | icondict = (resjson["cert"]["icon"]) 34 | binarydict = (resjson["cert"]["binary"]) 35 | print("get fir upload certificate success") 36 | 37 | except Exception: 38 | print("get fir upload certificate error") 39 | traceback.print_exc() 40 | 41 | # 第二步:上传APK 42 | try: 43 | print("uploading apk......") 44 | apkfile = {'file': open(apkpath, 'rb')} 45 | param = {"key": binarydict["key"], 46 | "token": binarydict["token"], 47 | "x:name": appname, 48 | "x:version": appversion, 49 | "x:build": appbuild} 50 | req = requests.post(url=binarydict["upload_url"], files=apkfile, data=param, verify=False) 51 | except Exception as e: 52 | print("upload apk error") 53 | traceback.print_exc() 54 | 55 | # 第三步:上传APK logo 56 | try: 57 | print("uploading apklogo......") 58 | apklogofile = {'file': open(apklogo, 'rb')} 59 | param = {"key": icondict["key"], 60 | "token": icondict["token"]} 61 | req = requests.post(url=icondict["upload_url"], files=apklogofile, data=param, verify=False) 62 | except Exception: 63 | print("upload apk error") 64 | traceback.print_exc() 65 | 66 | # 第四步:获取APK最新下载地址 67 | queryurl = 'http://api.fir.im/apps/latest/%s?api_token=%s&type=android' % (apppackage, apitoken) 68 | try: 69 | req = requests.get(queryurl) 70 | update_url = (req.json()["update_url"]) 71 | print("upload apk success, update url is " + update_url) 72 | except Exception: 73 | print("upload apk error") 74 | traceback.print_exc() 75 | 76 | 77 | if __name__ == '__main__': 78 | uploadtofir() -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-kapt' 4 | apply plugin: 'kotlin-android-extensions' 5 | apply plugin: 'androidx.navigation.safeargs.kotlin' 6 | //apply plugin: 'android-aspectjx' 7 | apply plugin: 'upload.apk' 8 | 9 | uploadApk { 10 | fir { 11 | appName = "WanAndroid" 12 | iconPath = "src/main/res/mipmap-xxhdpi/ic_launcher.png" 13 | token = "368967b71745181a78ea8cb01ab237c5" 14 | } 15 | } 16 | 17 | 18 | def config = rootProject.ext.android // 配置 19 | 20 | android { 21 | compileSdkVersion config.compileSdkVersion 22 | defaultConfig { 23 | applicationId "com.minic.kt" 24 | minSdkVersion config.minSdkVersion 25 | targetSdkVersion config.targetSdkVersion 26 | versionCode config.versionCode 27 | versionName config.versionName 28 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 29 | } 30 | buildTypes { 31 | release { 32 | minifyEnabled false 33 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 34 | } 35 | debug { 36 | minifyEnabled false 37 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 38 | } 39 | } 40 | 41 | sourceSets { 42 | main.java.srcDirs += 'src/main/kotlin' 43 | } 44 | 45 | // dataBinding 46 | dataBinding { 47 | enabled = true 48 | } 49 | 50 | compileOptions { 51 | sourceCompatibility 1.8 52 | targetCompatibility 1.8 53 | } 54 | 55 | kotlinOptions { 56 | jvmTarget = "1.8" 57 | } 58 | } 59 | 60 | dependencies { 61 | implementation fileTree(include: ['*.jar'], dir: 'libs') 62 | implementation project(':base') 63 | implementation project(':commonres') 64 | implementation project(':image_loader') 65 | kapt "androidx.databinding:databinding-compiler:$gradle_version" 66 | kapt "com.github.bumptech.glide:compiler:$glide_version" 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | implementation "com.scwang.smartrefresh:SmartRefreshLayout:$smartrefresh_layout_version" 69 | implementation "com.scwang.smartrefresh:SmartRefreshHeader:$smartrefresh_layout_version" 70 | implementation "com.github.bumptech.glide:glide:$glide_version" 71 | implementation 'jp.wasabeef:glide-transformations:4.1.0' 72 | implementation "androidx.paging:paging-runtime:$paging_version" 73 | implementation 'com.youth.banner:banner:1.4.10' 74 | implementation 'com.drakeet.multitype:multitype:4.0.0' 75 | implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$savedstate_version" 76 | implementation 'de.hdodenhof:circleimageview:3.0.1' 77 | } 78 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | #组件化项目的混淆方案 2 | #组件化项目的Java代码混淆方案采用在集成模式下集中在app壳工程中混淆 3 | #各个业务组件不配置混淆文件(重要)。 4 | #集成开发模式下在app壳工程中build.gradle文件的release构建类型中开启混淆属性 5 | #其他buildTypes配置方案跟普通项目保持一致,Java混淆配置文件也放置在app壳工程中 6 | #各个业务组件的混淆配置规则都应该在app壳工程中的混淆配置文件中添加和修改。 7 | #之所以不采用在每个业务组件中开启混淆的方案,是因为 组件在集成模式下都被 Gradle 构建成了 release 类型的arr包 8 | #一旦业务组件的代码被混淆,而这时候代码中又出现了bug,将很难根据日志找出导致bug的原因; 9 | #另外每个业务组件中都保留一份混淆配置文件非常不便于修改和管理 10 | #这也是不推荐在业务组件的 build.gradle 文件中配置 buildTypes (构建类型)的原因。 11 | 12 | 13 | ·#********************************************************************* 14 | #*重要:添加第三方SDK必须查看作者是否有混淆规则 15 | #********************************************************************* 16 | 17 | #-----------------------------混淆开始------------------------------------------------------ 18 | #指定代码的压缩级别 19 | -optimizationpasses 5 20 | #包明不混合大小写 21 | -dontusemixedcaseclassnames 22 | #不去忽略非公共的库类 23 | -dontskipnonpubliclibraryclasses 24 | #优化 不优化输入的类文件 25 | -dontoptimize 26 | #预校验 27 | -dontpreverify 28 | #混淆时是否记录日志 29 | -verbose 30 | #生成原类名和混淆后的类名的映射文件 31 | -printmapping proguardMapping.txt 32 | # 混淆时所采用的算法 33 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 34 | #保护注解 35 | -keepattributes *Annotation*,InnerClasses 36 | #保持 native 方法不被混淆 37 | -keepclasseswithmembernames class * { 38 | native ; 39 | } 40 | #保持枚举 enum 类不被混淆 41 | -keepclassmembers enum * { 42 | public static **[] values(); 43 | public static ** valueOf(java.lang.String); 44 | } 45 | #保留自定义view 46 | -keep public class * extends android.view.View{ 47 | *** get*(); 48 | void set*(***); 49 | public (android.content.Context); 50 | public (android.content.Context, android.util.AttributeSet); 51 | public (android.content.Context, android.util.AttributeSet, int); 52 | } 53 | -keepclasseswithmembers class * { 54 | public (android.content.Context, android.util.AttributeSet); 55 | public (android.content.Context, android.util.AttributeSet, int); 56 | } 57 | # 保持哪些类不被混淆 58 | -keep public class * extends android.app.Fragment 59 | -keep public class * extends android.app.Activity 60 | -keep public class * extends android.app.Application 61 | -keep public class * extends android.app.Service 62 | -keep public class * extends android.view.View 63 | -keep public class * extends android.content.BroadcastReceiver 64 | -keep public class * extends android.content.ContentProvider 65 | -keep public class * extends android.app.backup.BackupAgentHelper 66 | -keep public class * extends android.preference.Preference 67 | -keep public class com.android.vending.licensing.ILicensingService 68 | 69 | -keep class com.minic.kt.data.model.data.**{*;} 70 | -keep class * implements retrofit2.Converter{*;} 71 | -keep class * extends Converter.Factory 72 | #序列化类不被混淆 73 | -keepclassmembers class * implements java.io.Serializable { 74 | static final long serialVersionUID; 75 | private static final java.io.ObjectStreamField[] serialPersistentFields; 76 | private void writeObject(java.io.ObjectOutputStream); 77 | private void readObject(java.io.ObjectInputStream); 78 | java.lang.Object writeReplace(); 79 | java.lang.Object readResolve(); 80 | } 81 | -keep class * implements android.os.Parcelable { 82 | public static final android.os.Parcelable$Creator *; 83 | } 84 | -keep class **.R$* { 85 | *; 86 | } 87 | -keepclassmembers class * { 88 | void *(**On*Event); 89 | } 90 | #忽略webview 91 | -keepclassmembers class fqcn.of.javascript.interface.for.webview { 92 | public *; 93 | } 94 | -keepclassmembers class * extends android.webkit.WebViewClient { 95 | public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap); 96 | public boolean *(android.webkit.WebView, java.lang.String); 97 | } 98 | -keepclassmembers class * extends android.webkit.WebViewClient { 99 | public void *(android.webkit.WebView, jav.lang.String); 100 | } 101 | #==================gson========================== 102 | -dontwarn com.google.** 103 | -keep class com.google.gson.** {*;} 104 | -keep class com.google.protobuf.** {*;} 105 | -keepattributes EnclosingMethod 106 | #-------------------Retrofit----------------------- 107 | -dontwarn retrofit2.** 108 | -keep class retrofit2.** { *; } 109 | -keepattributes Exceptions 110 | -keepattributes Signature 111 | -keepclassmembernames,allowobfuscation interface * { 112 | @retrofit2.http.* ; 113 | } 114 | -dontwarn org.codehaus.mojo.animal_sniffer.* 115 | #--------------------------okhttp3------------------------------------------------ 116 | -dontwarn okhttp3.** 117 | -dontwarn okio.** 118 | -dontwarn javax.annotation.** 119 | -dontwarn org.conscrypt.** 120 | -dontwarn javax.inject.** 121 | -keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase 122 | -dontwarn okhttp3.internal.platform.ConscryptPlatform 123 | #-------------------------databinding--------------------------------------------- 124 | -keepclassmembernames,allowobfuscation interface * { 125 | @android.databinding.* ; 126 | } 127 | #-------------------------Kotlin协程--------------------------------------------- 128 | -keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {} 129 | -keepnames class kotlinx.coroutines.CoroutineExceptionHandler {} 130 | -keepnames class kotlinx.coroutines.android.AndroidExceptionPreHandler {} 131 | -keepnames class kotlinx.coroutines.android.AndroidDispatcherFactory {} 132 | 133 | # Most of volatile fields are updated with AFU and should not be mangled 134 | -keepclassmembernames class kotlinx.** { 135 | volatile ; 136 | } 137 | #-------------------------AndroidX--------------------------------------------- 138 | -keep class com.google.android.material.** {*;} 139 | -keep class androidx.** {*;} 140 | -keep public class * extends androidx.** 141 | -keep interface androidx.** {*;} 142 | -dontwarn com.google.android.material.** 143 | -dontnote com.google.android.material.** 144 | -dontwarn androidx.** 145 | #-------------------------Glide--------------------------------------------- 146 | -keep public class * implements com.bumptech.glide.module.GlideModule 147 | -keep public class * extends com.bumptech.glide.module.AppGlideModule 148 | -keep public enum com.bumptech.glide.load.ImageHeaderParser$** { 149 | **[] $VALUES; 150 | public *; 151 | } 152 | 153 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt 2 | 3 | import android.os.Bundle 4 | import com.minic.base.base.BaseActivity 5 | import com.minic.kt.databinding.ActivityMainBinding 6 | 7 | 8 | class MainActivity : BaseActivity() { 9 | override fun getLayoutRes(): Int = R.layout.activity_main 10 | 11 | override fun initData(savedInstanceState: Bundle?) { 12 | } 13 | } 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/base/App.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.base 2 | 3 | import com.minic.base.base.BaseApp 4 | import com.minic.imageload.ImageFrom 5 | import com.minic.kt.utils.GlideImageLoader 6 | 7 | /** 8 | * @author :ChenYangYi 9 | * @date :2018/10/11/15:26 10 | * @description : 11 | * @github :https://github.com/chenyy0708 12 | */ 13 | class App : BaseApp() { 14 | override fun onCreate() { 15 | INSTANCE = this 16 | super.onCreate() 17 | ImageFrom.setImageLoader(GlideImageLoader()) 18 | } 19 | 20 | companion object { 21 | lateinit var INSTANCE: App 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/base/BaseVM.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.base 2 | 3 | import androidx.lifecycle.LifecycleOwner 4 | import androidx.lifecycle.MutableLiveData 5 | import com.minic.base.databinding.viewmodel.LifecycleViewModel 6 | 7 | 8 | open class BaseVM : LifecycleViewModel() { 9 | /** 10 | * throwable 11 | */ 12 | open val throwable = MutableLiveData() 13 | 14 | 15 | protected var isInit = false 16 | 17 | override fun onStart(lifecycleOwner: LifecycleOwner) { 18 | super.onStart(lifecycleOwner) 19 | isInit = true 20 | } 21 | 22 | override fun onCleared() { 23 | super.onCleared() 24 | isInit = false 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/base/Constant.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.base 2 | 3 | /** 4 | * @author :ChenYangYi 5 | * @date :2018/10/30/16:32 6 | * @description : 7 | * @github :https://github.com/chenyy0708 8 | */ 9 | class Constant { 10 | companion object { 11 | /** 12 | * 首页Banner 13 | */ 14 | const val HOME_BANNER = 100 15 | /** 16 | * 首页TAB 17 | */ 18 | const val HOME_TAB = 101 19 | /** 20 | * 推荐歌单 21 | */ 22 | const val RECOMMEND_SONG_LIST = 102 23 | /** 24 | * 最新音乐 25 | */ 26 | const val LATEST_MUSIC = 103 27 | /** 28 | * 最新音乐 29 | */ 30 | const val ANCHOR_STATION = 104 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/base/viewbinder/BaseViewBinder.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.base.viewbinder 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.annotation.LayoutRes 6 | import androidx.databinding.DataBindingUtil 7 | import androidx.databinding.ViewDataBinding 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.drakeet.multitype.ItemViewBinder 10 | import com.minic.kt.jetpack.paging.BindingHolder 11 | 12 | abstract class BaseViewBinder(@LayoutRes private val layoutId: Int) : ItemViewBinder>() { 13 | 14 | override fun onBindViewHolder(holder: BindingHolder, item: V) { 15 | bindTo(holder,holder.binding, item!!) 16 | } 17 | 18 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): BindingHolder { 19 | return BindingHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.context), 20 | layoutId, parent, false)) 21 | } 22 | 23 | /** 24 | * DataBind绑定Item 25 | */ 26 | abstract fun bindTo(holder:RecyclerView.ViewHolder,bind: VB, item: V) 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/data/RetrofitProvider.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.data 2 | 3 | import com.ihsanbal.logging.Level 4 | import com.ihsanbal.logging.LoggingInterceptor 5 | import com.minic.base.BuildConfig 6 | import com.minic.base.net.UrlConstant 7 | import okhttp3.OkHttpClient 8 | import okhttp3.internal.platform.Platform 9 | import retrofit2.Retrofit 10 | import retrofit2.converter.gson.GsonConverterFactory 11 | import java.util.concurrent.TimeUnit 12 | 13 | /** 14 | * 描述: 生成ApiService 15 | * 作者: ChenYy 16 | * 日期: 2019-10-23 15:01 17 | */ 18 | object RetrofitProvider { 19 | 20 | const val CONNECT_TIME_OUT: Long = 1000 * 30 21 | 22 | private val retrofit: Retrofit by lazy { 23 | val loggerInterceptor = LoggingInterceptor.Builder() 24 | .loggable(BuildConfig.DEBUG) 25 | .setLevel(Level.BASIC) 26 | .log(Platform.INFO) 27 | .build() 28 | val okHttpClient = OkHttpClient.Builder() 29 | .readTimeout(CONNECT_TIME_OUT, TimeUnit.MILLISECONDS) 30 | .connectTimeout(CONNECT_TIME_OUT, TimeUnit.MILLISECONDS) 31 | .writeTimeout(CONNECT_TIME_OUT, TimeUnit.MILLISECONDS) 32 | .addInterceptor(loggerInterceptor) 33 | .build() 34 | Retrofit.Builder().baseUrl(UrlConstant.WAN_ANDROID_URL) 35 | .client(okHttpClient) 36 | .addConverterFactory(GsonConverterFactory.create()) 37 | .build() 38 | } 39 | 40 | fun createService(clazz: Class): T { 41 | return retrofit.create(clazz) 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/data/WARepository.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.data 2 | 3 | import com.minic.kt.data.api.WAndroidService 4 | import com.minic.kt.data.model.BResponse 5 | import com.minic.kt.data.model.gank.PagingData 6 | import com.minic.kt.data.model.gank.home.* 7 | 8 | /** 9 | * @author :ChenYangYi 10 | * @date :2019-5-8 21:57:32 11 | * @description :WARepository 12 | * @github :https://github.com/chenyy0708 13 | */ 14 | object WARepository { 15 | 16 | private val apiService by lazy { 17 | RetrofitProvider.createService(WAndroidService::class.java) 18 | } 19 | 20 | suspend fun banners(): BResponse> = apiService.banners() 21 | suspend fun article(page: Int): BResponse
= apiService.article(page) 22 | suspend fun articleTop(): BResponse> = apiService.articleTop() 23 | suspend fun projectTree(): BResponse> = apiService.projectTree() 24 | suspend fun projectList(page: Int, cid: Int): BResponse> = apiService.projectList(page, cid) 25 | suspend fun systemTree(): BResponse> = apiService.systemTree() 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/data/api/WAndroidService.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.data.api 2 | 3 | import com.minic.kt.data.model.BResponse 4 | import com.minic.kt.data.model.gank.PagingData 5 | import com.minic.kt.data.model.gank.home.* 6 | import retrofit2.http.GET 7 | import retrofit2.http.Path 8 | import retrofit2.http.Query 9 | 10 | /** 11 | * @author :ChenYangYi 12 | * @date :2018/07/25/13:33 13 | * @description :WAndroidService 14 | * @github :https://github.com/chenyy0708 15 | */ 16 | interface WAndroidService { 17 | /** 18 | * Banner 19 | */ 20 | @GET("banner/json") 21 | suspend fun banners(): BResponse> 22 | 23 | /** 24 | * 文章列表 25 | */ 26 | @GET("article/list/{page}/json") 27 | suspend fun article(@Path("page") page: Int): BResponse
28 | 29 | /** 30 | * 置顶文章 31 | */ 32 | @GET("article/top/json") 33 | suspend fun articleTop(): BResponse> 34 | 35 | /** 36 | * 项目分类 37 | */ 38 | @GET("project/tree/json") 39 | suspend fun projectTree(): BResponse> 40 | 41 | /** 42 | * 项目列表数据 43 | */ 44 | @GET("project/list/{page}/json") 45 | suspend fun projectList(@Path("page") page: Int, @Query("cid") cid: Int): BResponse> 46 | 47 | /** 48 | * 体系首页 49 | */ 50 | @GET("tree/json") 51 | suspend fun systemTree(): BResponse> 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/data/model/BResponse.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.data.model 2 | 3 | /** 4 | * @ClassName: BResponse 5 | * @Description: 6 | * @Author: ChenYy 7 | * @Date: 2019-05-06 18:03 8 | */ 9 | data class BResponse( 10 | val `data`: T, 11 | val errorCode: Int, 12 | val errorMsg: String 13 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/data/model/gank/PagingData.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.data.model.gank 2 | 3 | data class PagingData(val curPage: Int, 4 | var pageCount: Int, 5 | val total: Int, 6 | val offset: Int, 7 | val datas: MutableList) -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/data/model/gank/home/Article.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.data.model.gank.home 2 | 3 | data class Article( 4 | val curPage: Int, 5 | var pageCount: Int, 6 | val total: Int, 7 | val offset: Int, 8 | val datas: MutableList 9 | ) 10 | 11 | 12 | 13 | data class TagData( 14 | val name: String, 15 | val url: String 16 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/data/model/gank/home/ArticleData.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.data.model.gank.home 2 | 3 | data class ArticleData( 4 | val apkLink: String, 5 | val author: String, 6 | val chapterName: String, 7 | val collect: Boolean, 8 | val desc: String, 9 | val link: String, 10 | val niceDate: String, 11 | val publishTime: String, 12 | val superChapterName: String, 13 | val envelopePic: String, 14 | val tags: MutableList, 15 | val title: String, 16 | val visible: Int, 17 | val zan: Int, 18 | val userId: Int, 19 | var isTopping: Boolean = false 20 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/data/model/gank/home/Banner.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.data.model.gank.home 2 | 3 | data class BannerData( 4 | val desc: String, 5 | val id: Long, 6 | val imagePath: String, 7 | val isVisible: Int, 8 | val order: Int, 9 | val title: String, 10 | val type: Int, 11 | val url: String 12 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/data/model/gank/home/ProjectTree.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.data.model.gank.home 2 | 3 | data class ProjectTree( 4 | val courseId: Int, 5 | val id: Int, 6 | val visible: Int, 7 | val order: Int, 8 | val parentChapterId: Int, 9 | val userControlSetTop: Boolean, 10 | val name: String 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/data/model/gank/home/SystemTree.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.data.model.gank.home 2 | 3 | /** 4 | * @ClassName: SystemTree 5 | * @Description: 6 | * @Author: ChenYy 7 | * @Date: 2019-09-27 15:07 8 | */ 9 | data class SystemTree( 10 | val courseId: Int, 11 | val id: Int, 12 | val order: Int, 13 | val parentChapterId: Int, 14 | val visible: Int, 15 | val userControlSetTop: Boolean, 16 | val name: String, 17 | val children: MutableList 18 | ) 19 | 20 | data class TreeData( 21 | val name: String, 22 | val courseId: Int, 23 | val id: Int, 24 | val parentChapterId: Int 25 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ext/CoroutineExt.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ext 2 | 3 | import com.minic.base.net.exception.CException 4 | import com.minic.kt.data.model.BResponse 5 | import kotlinx.coroutines.suspendCancellableCoroutine 6 | import kotlin.coroutines.resume 7 | import kotlin.coroutines.resumeWithException 8 | 9 | /** 10 | * @ClassName: CoroutineExt 11 | * @Description:网络请求返回结果统一处理 12 | * @Author: ChenYy 13 | * @Date: 2019-05-06 17:50 14 | */ 15 | suspend fun BResponse.awaitResponse(catchBlock: suspend (Throwable) -> Unit = {}): T? { 16 | var result: T? = null 17 | try { 18 | result = suspendCancellableCoroutine { cont -> 19 | if (null == this) { 20 | cont.resumeWithException(CException("No data")) 21 | } else { 22 | if (this.errorCode == 0) { 23 | cont.resume(this.data) 24 | } else { 25 | cont.resumeWithException(CException(this.errorMsg)) 26 | } 27 | } 28 | } 29 | } catch (e: Throwable) { 30 | catchBlock(e) 31 | return result 32 | } 33 | return result 34 | } 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/jetpack/binds/ImageBinds.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.jetpack.binds 2 | 3 | import android.widget.ImageView 4 | import androidx.databinding.BindingAdapter 5 | import com.minic.imageload.loadIV 6 | 7 | /** 8 | * 页面描述:ImageBinds 9 | */ 10 | 11 | @BindingAdapter("imageUrl") 12 | fun bindImgUrl(imageView: ImageView, url: String?) { 13 | url?.let { 14 | imageView.loadIV(it) 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/jetpack/binds/Presenter.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.jetpack.binds 2 | 3 | import android.view.View 4 | 5 | /** 6 | * 页面描述:Presenter 7 | */ 8 | interface Presenter : View.OnClickListener { 9 | override fun onClick(v: View?) 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/jetpack/paging/BindingHolder.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.jetpack.paging 2 | 3 | import androidx.databinding.ViewDataBinding 4 | import androidx.recyclerview.widget.RecyclerView 5 | 6 | /** 7 | * @ClassName: BindingHolder 8 | * @Description: 9 | * @Author: ChenYy 10 | * @Date: 2019-05-10 10:30 11 | */ 12 | class BindingHolder(val binding: VB) : RecyclerView.ViewHolder(binding.root) -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/jetpack/paging/ILoadData.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.jetpack.paging 2 | 3 | import androidx.paging.ItemKeyedDataSource 4 | 5 | /** 6 | * @ClassName: ILoadData 7 | * @Description:Paging加载数据 8 | * @Author: ChenYy 9 | * @Date: 2019-05-09 13:41 10 | */ 11 | interface ILoadData { 12 | /** 13 | * 加载数据,根据实际业务场景实现 14 | */ 15 | fun loadData(page: Int, rows: Int, callback: ItemKeyedDataSource.LoadCallback) 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/jetpack/paging/PagingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.jetpack.paging 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.annotation.LayoutRes 6 | import androidx.databinding.DataBindingUtil 7 | import androidx.databinding.ViewDataBinding 8 | import androidx.paging.PagedListAdapter 9 | import androidx.recyclerview.widget.DiffUtil 10 | import androidx.recyclerview.widget.RecyclerView 11 | 12 | /** 13 | * @ClassName: PagingAdapter 14 | * @Description:BasePagingAdapter 15 | * @Author: ChenYy 16 | * @Date: 2019-05-10 09:52 17 | */ 18 | abstract class PagingAdapter(@LayoutRes private val layoutId: Int, 19 | mDiffCallback: DiffUtil.ItemCallback) : 20 | PagedListAdapter>(mDiffCallback) { 21 | 22 | private var onItemClickListener: ItemClickListener? = null 23 | 24 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { 25 | val bindingHolder = BindingHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.context), 26 | layoutId, parent, false)) 27 | onItemClickListener?.let { 28 | bindingHolder.itemView.setOnClickListener { 29 | onItemClickListener!!(getItem(bindingHolder.layoutPosition)!!, bindingHolder.layoutPosition) 30 | } 31 | } 32 | return bindingHolder 33 | } 34 | 35 | override fun onBindViewHolder(holder: BindingHolder, position: Int) { 36 | val item = getItem(position) 37 | bindTo(holder,holder.binding, item!!) 38 | } 39 | 40 | /** 41 | * DataBind绑定Item 42 | */ 43 | abstract fun bindTo(holder:RecyclerView.ViewHolder,bind: VB, item: V) 44 | 45 | fun setOnItemClickListener(onItemClickListener: ItemClickListener) { 46 | this.onItemClickListener = onItemClickListener 47 | } 48 | } 49 | 50 | typealias ItemClickListener = (item: T, position: Int) -> Unit 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/jetpack/paging/PagingDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.jetpack.paging 2 | 3 | import androidx.paging.ItemKeyedDataSource 4 | 5 | /** 6 | * @ClassName: PagingDataSource 7 | * @Description:提供Paging数据 8 | * @Author: ChenYy 9 | * @Date: 2019-05-09 11:57 10 | */ 11 | 12 | const val FIRST_PAGE = 1 13 | 14 | class PagingDataSource(private val listener: ILoadData) : ItemKeyedDataSource() { 15 | 16 | var page: Int = FIRST_PAGE 17 | 18 | /** 19 | * 初始化列表数据 20 | */ 21 | override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback) { 22 | listener.loadData(FIRST_PAGE, params.requestedLoadSize, callback) 23 | } 24 | 25 | /** 26 | * 加载下一页数据 27 | */ 28 | override fun loadAfter(params: LoadParams, callback: LoadCallback) { 29 | listener.loadData(params.key, params.requestedLoadSize, callback) 30 | } 31 | 32 | /** 33 | * 加载上一页数据 34 | */ 35 | override fun loadBefore(params: LoadParams, callback: LoadCallback) { 36 | } 37 | 38 | override fun getKey(item: T): Int = page++ 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/jetpack/paging/PagingDataSourceFactory.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.jetpack.paging 2 | 3 | import androidx.paging.DataSource 4 | 5 | /** 6 | * @ClassName: PagingDataSourceFactory 7 | * @Description:Paging数据 8 | * @Author: ChenYy 9 | * @Date: 2019-05-09 11:56 10 | */ 11 | class PagingDataSourceFactory(private val listener: ILoadData) : DataSource.Factory() { 12 | override fun create(): DataSource { 13 | return PagingDataSource(listener) 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/jetpack/paging/PagingLoadHelper.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.jetpack.paging 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.paging.ItemKeyedDataSource 5 | import androidx.paging.LivePagedListBuilder 6 | import androidx.paging.PagedList 7 | 8 | /** 9 | * @ClassName: PagingLoadHelper 10 | * @Description:Paging初始化简化函数 11 | * @Author: ChenYy 12 | * @Date: 2019-05-09 16:08 13 | */ 14 | 15 | fun loadData(callback: (page: Int, rows: Int, callback: ItemKeyedDataSource.LoadCallback) -> Unit): LiveData> { 16 | return LivePagedListBuilder(PagingDataSourceFactory(object : ILoadData { 17 | override fun loadData(page: Int, rows: Int, callback: ItemKeyedDataSource.LoadCallback) { 18 | callback(page, rows, callback) 19 | } 20 | }), PagedList.Config.Builder() 21 | .setPageSize(10) //配置分页加载的数量 22 | 23 | .setEnablePlaceholders(false) //配置是否启动PlaceHolders 24 | .setInitialLoadSizeHint(10) //初始化加载的数量 25 | .build()) 26 | .build() 27 | 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/jetpack/paging/SimpleViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.jetpack.paging 2 | 3 | import android.util.SparseArray 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import androidx.annotation.IdRes 9 | import androidx.annotation.LayoutRes 10 | import androidx.recyclerview.widget.RecyclerView 11 | 12 | 13 | /** 14 | * @ClassName: SimpleViewHolder 15 | * @Description:通用ViewHolder 16 | * @Author: ChenYy 17 | * @Date: 2019-05-09 09:29 18 | */ 19 | class SimpleViewHolder(parent: ViewGroup, @LayoutRes layoutId: Int) : RecyclerView.ViewHolder( 20 | LayoutInflater.from(parent.context).inflate(layoutId, parent, false)) { 21 | 22 | private var mSparseArray: SparseArray? = null 23 | 24 | init { 25 | mSparseArray = SparseArray() 26 | } 27 | 28 | @Suppress("UNCHECKED_CAST") 29 | fun getView(@IdRes viewId: Int): T { 30 | var view = mSparseArray?.get(viewId) 31 | if (view == null) { 32 | view = itemView.findViewById(viewId) 33 | mSparseArray?.put(viewId, view) 34 | } 35 | return view as T 36 | } 37 | 38 | fun setText(@IdRes viewId: Int, str: String?): SimpleViewHolder { 39 | getView(viewId).text = str 40 | return this 41 | } 42 | 43 | fun loadIV(@IdRes viewId: Int, str: String?): SimpleViewHolder { 44 | return this 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/activity/common/BaseBrowserActivity.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.activity.common 2 | 3 | import android.annotation.SuppressLint 4 | import android.graphics.Bitmap 5 | import android.net.http.SslError 6 | import android.os.Build 7 | import android.os.Bundle 8 | import android.view.View 9 | import android.webkit.* 10 | import android.widget.ProgressBar 11 | import androidx.databinding.ViewDataBinding 12 | import com.minic.base.base.BaseActivity 13 | import com.minic.base.net.exception.showSuccess 14 | 15 | /** 16 | * @ClassName: BaseBrowserActivity 17 | * @Description: 18 | * @Author: ChenYy 19 | * @Date: 2019-05-14 16:46 20 | */ 21 | abstract class BaseBrowserActivity : BaseActivity() { 22 | abstract fun getWebView(): WebView 23 | abstract fun getProgressBar(): ProgressBar? 24 | 25 | override fun initData(savedInstanceState: Bundle?) { 26 | initWebViewSettings() 27 | } 28 | 29 | @SuppressLint("SetJavaScriptEnabled") 30 | private fun initWebViewSettings() { 31 | val webSettings = getWebView().settings 32 | webSettings.javaScriptEnabled = true 33 | webSettings.layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL 34 | webSettings.domStorageEnabled = true 35 | webSettings.defaultTextEncodingName = "UTF-8" 36 | webSettings.allowUniversalAccessFromFileURLs = true//解决js跨域问题 37 | webSettings.useWideViewPort = true 38 | webSettings.loadWithOverviewMode = true 39 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 40 | webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW//解决https页面中有http图片显示不出问题 41 | } 42 | getWebView().webViewClient = BaseWebViewClient() 43 | getWebView().webChromeClient = BaseWebChromeClient() 44 | } 45 | 46 | 47 | inner class BaseWebViewClient : WebViewClient() { 48 | override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { 49 | view.loadUrl(url) 50 | return true 51 | } 52 | 53 | override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { 54 | handler.proceed() // 接受所有网站的证书 55 | } 56 | 57 | override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { 58 | getProgressBar()?.visibility = View.VISIBLE 59 | super.onPageStarted(view, url, favicon) 60 | } 61 | } 62 | 63 | inner class BaseWebChromeClient : WebChromeClient() { 64 | override fun onProgressChanged(view: WebView, progress: Int) { 65 | getProgressBar()?.progress = progress 66 | if (progress >= 100) { 67 | getProgressBar()?.visibility = View.INVISIBLE 68 | showSuccess() 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/activity/common/BrowserActivity.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.activity.common 2 | 3 | import android.os.Bundle 4 | import android.view.Menu 5 | import android.view.MenuItem 6 | import android.view.View 7 | import android.webkit.WebView 8 | import android.widget.ProgressBar 9 | import com.minic.base.extens.initToolbar 10 | import com.minic.base.extens.showWarning 11 | import com.minic.base.net.exception.showLoading 12 | import com.minic.kt.R 13 | import com.minic.kt.databinding.FragmentBrowserBinding 14 | 15 | /** 16 | * @ClassName: BrowserActivity 17 | * @Description: 18 | * @Author: ChenYy 19 | * @Date: 2019-09-12 18:14 20 | */ 21 | class BrowserActivity : BaseBrowserActivity() { 22 | override fun getLayoutRes(): Int = R.layout.fragment_browser 23 | 24 | override fun getStatusLayout(): View = mBinding.webView 25 | 26 | override fun initData(savedInstanceState: Bundle?) { 27 | super.initData(savedInstanceState) 28 | initToolbar(mBinding.includeToolbar.toolBar, intent.getStringExtra("title")!!) 29 | showLoading() 30 | val url = intent.getStringExtra("url")!! 31 | mBinding.webView?.loadUrl(url) 32 | } 33 | 34 | override fun onCreateOptionsMenu(menu: Menu?): Boolean { 35 | menuInflater.inflate(R.menu.menu_browser, menu) 36 | return super.onCreateOptionsMenu(menu) 37 | } 38 | 39 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 40 | when (item.itemId) { 41 | R.id.share -> showWarning("分享") 42 | R.id.copy -> showWarning("复制") 43 | R.id.open_browser -> showWarning("从浏览器打开") 44 | } 45 | return true 46 | } 47 | 48 | override fun getWebView(): WebView = mBinding.webView 49 | 50 | override fun getProgressBar(): ProgressBar? = mBinding.progressHorizontal 51 | 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/activity/test/MotionLayoutActivity.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.activity.test 2 | 3 | import android.os.Bundle 4 | import com.minic.base.base.BaseActivity 5 | import com.minic.imageload.ImageFrom 6 | import com.minic.imageload.ImageLoaderOptions 7 | import com.minic.kt.R 8 | import com.minic.kt.databinding.ActivityMotionStudyBinding 9 | import com.minic.kt.utils.ext.dp2px 10 | import kotlinx.android.synthetic.main.activity_motion_study.* 11 | 12 | 13 | /** 14 | * 描述: MotionLayout动画测试 15 | * 作者: ChenYy 16 | * 日期: 2019-10-24 14:38 17 | */ 18 | class MotionLayoutActivity : BaseActivity() { 19 | 20 | var isOpen = false 21 | 22 | /** 23 | * 初始化数据 24 | */ 25 | override fun initData(savedInstanceState: Bundle?) { 26 | image_menu.setOnClickListener { 27 | if (isOpen) { 28 | mMotionLayout.transitionToStart() 29 | } else { 30 | mMotionLayout.transitionToEnd() 31 | } 32 | isOpen = !isOpen 33 | } 34 | ImageFrom.getImageLoader().load(this, "https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1328874460,2431333110&fm=26&gp=0.jpg", 35 | ImageLoaderOptions().apply { 36 | topLeftRadius = dp2px(10f) 37 | topRightRadius = dp2px(10f) 38 | bottomLeftRadius = dp2px(10f) 39 | bottomRightRadius = dp2px(10f) 40 | placeHolder = R.drawable.icon_navigation_not_selected 41 | errorHolder = R.drawable.icon_me_not_selected 42 | }, iv_default) 43 | } 44 | 45 | override fun getLayoutRes(): Int = R.layout.activity_motion_study 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/HomeViewPagerFragment.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment 2 | 3 | import android.os.Bundle 4 | import androidx.lifecycle.lifecycleScope 5 | import com.minic.base.base.BaseFragment 6 | import com.minic.base.extens.initToolbar 7 | import com.minic.kt.R 8 | import com.minic.kt.base.App 9 | import com.minic.kt.databinding.FragmentHomeViewPagerBinding 10 | import com.minic.kt.ui.fragment.adapter.WAPagerAdapter 11 | import kotlinx.coroutines.delay 12 | import kotlinx.coroutines.launch 13 | 14 | /** 15 | * @ClassName: HomeViewPagerFragment 16 | * @Description: 17 | * @Author: ChenYy 18 | * @Date: 2019-09-12 17:05 19 | */ 20 | class HomeViewPagerFragment : BaseFragment() { 21 | 22 | override fun getLayoutRes(): Int = R.layout.fragment_home_view_pager 23 | 24 | override fun initData(savedInstanceState: Bundle?) { 25 | mBinding.viewPager.apply { 26 | adapter = WAPagerAdapter(this@HomeViewPagerFragment).apply { 27 | // 默认加载全部的 28 | offscreenPageLimit = itemCount 29 | } 30 | // 静止手势滑动 31 | isUserInputEnabled = false 32 | // 显示第一个Page 33 | lifecycleScope.launch { 34 | delay(50) 35 | currentItem = 0 36 | } 37 | } 38 | mBinding.bottomNavView.setOnNavigationItemSelectedListener { 39 | when (it.itemId) { 40 | R.id.home -> mBinding.viewPager.currentItem = 0 41 | R.id.project -> mBinding.viewPager.currentItem = 1 42 | R.id.wechart -> mBinding.viewPager.currentItem = 2 43 | R.id.system -> mBinding.viewPager.currentItem = 3 44 | R.id.me -> mBinding.viewPager.currentItem = 4 45 | } 46 | return@setOnNavigationItemSelectedListener true 47 | } 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/adapter/ProjectChildAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.adapter 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.minic.base.extens.openActivity 6 | import com.minic.imageload.loadIV 7 | import com.minic.kt.R 8 | import com.minic.kt.data.model.gank.home.ArticleData 9 | import com.minic.kt.databinding.ItemHomeProjectChildBinding 10 | import com.minic.kt.jetpack.paging.PagingAdapter 11 | import com.minic.kt.ui.activity.common.BrowserActivity 12 | 13 | 14 | /** 15 | * @ClassName: HomeAdapter 16 | * @Description: 17 | * @Author: ChenYy 18 | * @Date: 2019-05-09 09:20 19 | */ 20 | class ProjectChildAdapter : PagingAdapter(R.layout.item_home_project_child, mDiffCallback) { 21 | override fun bindTo(holder: RecyclerView.ViewHolder, bind: ItemHomeProjectChildBinding, item: ArticleData) { 22 | bind.item = item 23 | bind.root.setOnClickListener { 24 | holder.itemView.context.openActivity( 25 | "url" to item.link, 26 | "title" to item.title 27 | ) 28 | } 29 | val array = holder.itemView.context.resources.getStringArray(R.array.author_imgs) 30 | bind.ivAvatar.loadIV(array.random()) 31 | bind.executePendingBindings() 32 | } 33 | } 34 | 35 | private val mDiffCallback = object : DiffUtil.ItemCallback() { 36 | override fun areItemsTheSame(oldItem: ArticleData, newItem: ArticleData): Boolean { 37 | return oldItem.niceDate === newItem.niceDate 38 | } 39 | 40 | override fun areContentsTheSame(oldItem: ArticleData, newItem: ArticleData): Boolean { 41 | return newItem == oldItem 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/adapter/ProjectPagerAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.minic.kt.ui.fragment.adapter 18 | 19 | import androidx.fragment.app.Fragment 20 | import androidx.viewpager2.adapter.FragmentStateAdapter 21 | 22 | class ProjectPagerAdapter(fragment: Fragment, val mFragments: MutableList) : FragmentStateAdapter(fragment) { 23 | override fun getItemCount() = mFragments.size 24 | 25 | override fun createFragment(position: Int): Fragment { 26 | return mFragments[position] 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/adapter/WAPagerAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.minic.kt.ui.fragment.adapter 18 | 19 | import androidx.fragment.app.Fragment 20 | import androidx.viewpager2.adapter.FragmentStateAdapter 21 | import com.minic.kt.ui.fragment.home.* 22 | import com.minic.kt.ui.fragment.home.system.SystemFragment 23 | 24 | class WAPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { 25 | 26 | private val tabFragmentsCreators = mutableListOf( 27 | HomeFragment(), 28 | ProjectFragment(), 29 | WeChartFragment(), 30 | SystemFragment(), 31 | MeFragment() 32 | ) 33 | 34 | override fun getItemCount() = tabFragmentsCreators.size 35 | 36 | override fun createFragment(position: Int): Fragment { 37 | return tabFragmentsCreators[position] 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/adapter/viewbinder/ArticleViewBinder.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.adapter.viewbinder 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.minic.base.extens.openActivity 6 | import com.minic.imageload.loadIV 7 | import com.minic.kt.R 8 | import com.minic.kt.base.viewbinder.BaseViewBinder 9 | import com.minic.kt.data.model.gank.home.ArticleData 10 | import com.minic.kt.databinding.ItemHomeArticleBinding 11 | import com.minic.kt.ui.activity.common.BrowserActivity 12 | 13 | 14 | class ArticleViewBinder : BaseViewBinder(R.layout.item_home_article) { 15 | override fun bindTo(holder: RecyclerView.ViewHolder, bind: ItemHomeArticleBinding, item: ArticleData) { 16 | bind.item = item 17 | bind.root.setOnClickListener { 18 | holder.itemView.context.openActivity( 19 | "url" to item.link, 20 | "title" to item.title 21 | ) 22 | } 23 | bind.viewLine.visibility = if (adapter.items.size == holder.adapterPosition) View.GONE else View.VISIBLE 24 | var array = holder.itemView.context.resources.getStringArray(R.array.author_imgs) 25 | bind.ivAvatar.loadIV(array.random()) 26 | bind.executePendingBindings() 27 | } 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/adapter/viewbinder/BannerViewBinder.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.adapter.viewbinder 2 | 3 | import android.content.Context 4 | import android.widget.ImageView 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.minic.imageload.loadIV 7 | import com.minic.kt.R 8 | import com.minic.kt.base.viewbinder.BaseViewBinder 9 | import com.minic.kt.data.model.gank.home.BannerData 10 | import com.minic.kt.databinding.ItemHomeBannerBinding 11 | import com.youth.banner.loader.ImageLoader 12 | 13 | 14 | class BannerViewBinder : BaseViewBinder, ItemHomeBannerBinding>(R.layout.item_home_banner) { 15 | override fun bindTo(holder: RecyclerView.ViewHolder, bind: ItemHomeBannerBinding, item: MutableList) { 16 | bind.banner.setImageLoader(GlideImageLoader()) 17 | bind.banner.setImages(item) 18 | bind.banner.start() 19 | bind.executePendingBindings() 20 | } 21 | 22 | class GlideImageLoader : ImageLoader() { 23 | override fun displayImage(context: Context?, path: Any?, imageView: ImageView?) { 24 | if (path is BannerData) { 25 | imageView?.loadIV(path.imagePath) 26 | } 27 | } 28 | } 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/adapter/viewbinder/SystemChildViewBinder.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.adapter.viewbinder 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import com.minic.kt.R 5 | import com.minic.kt.base.viewbinder.BaseViewBinder 6 | import com.minic.kt.data.model.gank.home.SystemTree 7 | import com.minic.kt.databinding.ItemHomeBannerBinding 8 | 9 | 10 | class SystemChildViewBinder : BaseViewBinder(R.layout.item_home_banner) { 11 | override fun bindTo(holder: RecyclerView.ViewHolder, bind: ItemHomeBannerBinding, item: SystemTree) { 12 | bind.executePendingBindings() 13 | } 14 | } 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/home/HomeFragment.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.home 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.viewModels 6 | import androidx.lifecycle.observe 7 | import androidx.recyclerview.widget.LinearLayoutManager 8 | import com.drakeet.multitype.MultiTypeAdapter 9 | import com.minic.base.base.BaseFragment 10 | import com.minic.base.net.exception.showLoading 11 | import com.minic.base.net.exception.showSuccess 12 | import com.minic.kt.R 13 | import com.minic.kt.data.model.gank.home.Article 14 | import com.minic.kt.databinding.FragmentHomeBinding 15 | import com.minic.kt.ui.fragment.adapter.viewbinder.ArticleViewBinder 16 | import com.minic.kt.ui.fragment.adapter.viewbinder.BannerViewBinder 17 | import com.minic.kt.ui.fragment.vm.HomeVM 18 | 19 | 20 | class HomeFragment : BaseFragment() { 21 | override fun getLayoutRes(): Int = R.layout.fragment_home 22 | private val adapter = MultiTypeAdapter() 23 | private val items = ArrayList() 24 | private var page: Int = 0 25 | 26 | override fun getStatusLayout(): View = mBinding.recyclerView 27 | 28 | /** 29 | * 刷新 30 | */ 31 | private val refreshBlock: (MutableList?) -> Unit = { 32 | it?.let { 33 | showSuccess() 34 | items.clear() 35 | items.addAll(it) 36 | adapter.notifyDataSetChanged() 37 | mBinding.refreshLayout.finishRefresh() 38 | } 39 | } 40 | 41 | /** 42 | * 加载更多 43 | */ 44 | private val loadMoreBlock: (Article?) -> Unit = { 45 | it?.let { 46 | items.addAll(it.datas) 47 | adapter.notifyItemInserted(items.size - it.datas.size) 48 | if (page == it.pageCount) { 49 | mBinding.refreshLayout.finishLoadMoreWithNoMoreData() 50 | } else { 51 | mBinding.refreshLayout.finishLoadMore() 52 | } 53 | } 54 | } 55 | 56 | private val homeViewModel: HomeVM by viewModels() 57 | 58 | override fun initData(savedInstanceState: Bundle?) { 59 | initListener() 60 | mBinding.vm = homeViewModel 61 | viewLifecycleOwner.lifecycle.addObserver(homeViewModel) 62 | adapter.register(BannerViewBinder()) 63 | adapter.register(ArticleViewBinder()) 64 | adapter.items = items 65 | mBinding.recyclerView.layoutManager = LinearLayoutManager(mContext) 66 | mBinding.recyclerView.adapter = adapter 67 | } 68 | 69 | private fun initListener() { 70 | homeViewModel.mItems.observe(viewLifecycleOwner, refreshBlock) 71 | homeViewModel.article.observe(viewLifecycleOwner, loadMoreBlock) 72 | homeViewModel.isRefreshData.observe(viewLifecycleOwner) { showLoading() } 73 | mBinding.refreshLayout.setOnRefreshListener { 74 | page = 0 75 | homeViewModel.getData() 76 | } 77 | mBinding.refreshLayout.setOnLoadMoreListener { 78 | page++ 79 | homeViewModel.getArticle(page) 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/home/MeFragment.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.home 2 | 3 | import android.os.Bundle 4 | import com.minic.base.base.BaseFragment 5 | import com.minic.base.extens.openActivity 6 | import com.minic.kt.R 7 | import com.minic.kt.databinding.FragmentTestBinding 8 | import com.minic.kt.ui.activity.test.MotionLayoutActivity 9 | 10 | 11 | class MeFragment : BaseFragment() { 12 | 13 | override fun getLayoutRes(): Int = R.layout.fragment_test 14 | 15 | override fun initData(savedInstanceState: Bundle?) { 16 | mBinding.tv.text = "我的" 17 | 18 | mBinding.tv.setOnClickListener { 19 | mContext?.openActivity() 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/home/ProjectChildFragment.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.home 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.lifecycle.observe 6 | import androidx.recyclerview.widget.LinearLayoutManager 7 | import com.minic.base.base.BaseFragment 8 | import com.minic.base.net.exception.doError 9 | import com.minic.kt.R 10 | import com.minic.kt.databinding.FragmentProjectChildBinding 11 | import com.minic.kt.ui.fragment.adapter.ProjectChildAdapter 12 | import com.minic.kt.ui.fragment.vm.ProjectChildVM 13 | 14 | 15 | class ProjectChildFragment : BaseFragment() { 16 | override fun getLayoutRes(): Int = R.layout.fragment_project_child 17 | override fun getStatusLayout(): View = mBinding.recyclerView 18 | 19 | var viewModel: ProjectChildVM? = null 20 | 21 | companion object { 22 | fun newInstance(projectType: Int): ProjectChildFragment { 23 | val bundle = Bundle() 24 | val fragment = ProjectChildFragment() 25 | bundle.putInt(PROJECT_TYPE, projectType) 26 | fragment.arguments = bundle 27 | return fragment 28 | } 29 | 30 | const val PROJECT_TYPE = "project_type" 31 | 32 | } 33 | 34 | override fun initData(savedInstanceState: Bundle?) { 35 | initListener() 36 | val cid = arguments?.getInt(PROJECT_TYPE) 37 | mBinding.vm = ProjectChildVM(cid!!).apply { viewModel = this } 38 | viewModel?.let { viewLifecycleOwner.lifecycle.addObserver(it) } 39 | val adapter = ProjectChildAdapter() 40 | mBinding.recyclerView.layoutManager = LinearLayoutManager(mContext) 41 | mBinding.recyclerView.adapter = adapter 42 | viewModel?.mList?.observe(viewLifecycleOwner) { adapter.submitList(it) } 43 | 44 | viewModel?.refreshComplete?.observe(viewLifecycleOwner) { mBinding.swipeLayout.isRefreshing = !it } 45 | 46 | } 47 | 48 | private fun initListener() { 49 | mBinding.swipeLayout.setOnRefreshListener { viewModel?.mList?.value?.dataSource?.invalidate() } 50 | viewModel?.throwable?.observe(viewLifecycleOwner) { doError(it) } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/home/ProjectFragment.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.home 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.viewModels 5 | import androidx.lifecycle.observe 6 | import androidx.viewpager2.widget.ViewPager2 7 | import com.google.android.material.tabs.TabLayoutMediator 8 | import com.minic.base.base.BaseFragment 9 | import com.minic.kt.R 10 | import com.minic.kt.base.App 11 | import com.minic.kt.databinding.FragmentProjectBinding 12 | import com.minic.kt.ui.fragment.adapter.ProjectPagerAdapter 13 | import com.minic.kt.ui.fragment.vm.HomeProjectVM 14 | 15 | class ProjectFragment : BaseFragment() { 16 | 17 | override fun getLayoutRes(): Int = R.layout.fragment_project 18 | 19 | private val viewModule: HomeProjectVM by viewModels() 20 | 21 | override fun initData(savedInstanceState: Bundle?) { 22 | mBinding.vm = viewModule 23 | viewLifecycleOwner.lifecycle.addObserver(viewModule) 24 | mBinding.viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL 25 | viewModule.mList.observe(viewLifecycleOwner) { list -> 26 | mBinding.viewPager.adapter = ProjectPagerAdapter(this, list.map { ProjectChildFragment.newInstance(it.id) }.toMutableList()) 27 | TabLayoutMediator(mBinding.tabLayout, mBinding.viewPager) { tab, position -> 28 | tab.text = list[position].name 29 | }.attach() 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/home/WeChartFragment.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.home 2 | 3 | import android.os.Bundle 4 | import com.minic.base.base.BaseFragment 5 | import com.minic.kt.R 6 | import com.minic.kt.base.App 7 | import com.minic.kt.databinding.FragmentTestBinding 8 | 9 | 10 | class WeChartFragment : BaseFragment() { 11 | 12 | override fun getLayoutRes(): Int = R.layout.fragment_test 13 | 14 | override fun initData(savedInstanceState: Bundle?) { 15 | mBinding.tv.text = "公众号" 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/home/system/SystemChildFragment.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.home.system 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.viewModels 5 | import androidx.lifecycle.observe 6 | import com.drakeet.multitype.MultiTypeAdapter 7 | import com.minic.base.base.BaseFragment 8 | import com.minic.kt.R 9 | import com.minic.kt.databinding.FragmentProjectChildBinding 10 | import com.minic.kt.ui.fragment.adapter.viewbinder.SystemChildViewBinder 11 | import com.minic.kt.ui.fragment.vm.SystemChildVM 12 | 13 | 14 | class SystemChildFragment : BaseFragment() { 15 | 16 | private val multiTypeAdapter = MultiTypeAdapter() 17 | private val items = ArrayList() 18 | 19 | override fun getLayoutRes(): Int = R.layout.fragment_project_child 20 | val viewModel: SystemChildVM by viewModels() 21 | 22 | companion object { 23 | fun newInstance(): SystemChildFragment { 24 | return SystemChildFragment() 25 | } 26 | } 27 | 28 | override fun initData(savedInstanceState: Bundle?) { 29 | viewLifecycleOwner.lifecycle.addObserver(viewModel) 30 | mBinding.recyclerView.apply { 31 | adapter = multiTypeAdapter?.apply { 32 | register(SystemChildViewBinder()) 33 | items = this@SystemChildFragment.items 34 | } 35 | } 36 | viewModel.systemData.observe(viewLifecycleOwner) { 37 | items.clear() 38 | items.addAll(it) 39 | multiTypeAdapter.notifyDataSetChanged() 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/home/system/SystemFragment.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.home.system 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import androidx.viewpager2.adapter.FragmentStateAdapter 6 | import com.google.android.material.tabs.TabLayoutMediator 7 | import com.minic.base.base.BaseFragment 8 | import com.minic.kt.R 9 | import com.minic.kt.databinding.FragmentSystemBinding 10 | import com.minic.kt.ui.fragment.home.MeFragment 11 | 12 | 13 | class SystemFragment : BaseFragment() { 14 | 15 | override fun getLayoutRes(): Int = R.layout.fragment_system 16 | 17 | val mFragments = linkedMapOf( 18 | "体系" to SystemChildFragment(), 19 | "导航" to MeFragment() 20 | ) 21 | 22 | 23 | override fun initData(savedInstanceState: Bundle?) { 24 | mBinding.viewPager.adapter = object : FragmentStateAdapter(this) { 25 | override fun getItemCount(): Int = mFragments.size 26 | 27 | override fun createFragment(position: Int): Fragment { 28 | return mFragments.toList()[position].second 29 | } 30 | } 31 | TabLayoutMediator(mBinding.tabLayout, mBinding.viewPager) { tab, position -> 32 | tab.text = mFragments.toList()[position].first 33 | }.attach() 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/vm/HomeProjectVM.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.vm 2 | 3 | import androidx.lifecycle.LifecycleOwner 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.viewModelScope 6 | import com.minic.base.databinding.viewmodel.SingleLiveEvent 7 | import com.minic.kt.base.BaseVM 8 | import com.minic.kt.data.WARepository 9 | import com.minic.kt.data.model.gank.home.ProjectTree 10 | import com.minic.kt.ext.awaitResponse 11 | import kotlinx.coroutines.launch 12 | 13 | class HomeProjectVM : BaseVM() { 14 | 15 | val mList: MutableLiveData> = SingleLiveEvent() 16 | 17 | override fun onCreate(lifecycleOwner: LifecycleOwner) { 18 | super.onCreate(lifecycleOwner) 19 | viewModelScope.launch { 20 | WARepository.projectTree().awaitResponse { 21 | }?.let { 22 | mList.value = it 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/vm/HomeVM.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.vm 2 | 3 | import androidx.lifecycle.LifecycleOwner 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.viewModelScope 6 | import com.minic.base.databinding.viewmodel.SingleLiveEvent 7 | import com.minic.kt.base.BaseVM 8 | import com.minic.kt.data.WARepository 9 | import com.minic.kt.data.model.gank.home.Article 10 | import com.minic.kt.ext.awaitResponse 11 | import kotlinx.coroutines.launch 12 | 13 | /** 14 | * @author :ChenYangYi 15 | * @date :2018/09/27/15:50 16 | * @description : 17 | * @github :https://github.com/chenyy0708 18 | */ 19 | class HomeVM : BaseVM() { 20 | val article: MutableLiveData
= SingleLiveEvent() 21 | val isRefreshData: MutableLiveData = SingleLiveEvent() 22 | 23 | val mItems: MutableLiveData> = SingleLiveEvent() 24 | private val items = mutableListOf() 25 | 26 | override fun onCreate(lifecycleOwner: LifecycleOwner) { 27 | super.onCreate(lifecycleOwner) 28 | if (!isInit) { 29 | isRefreshData.value = true 30 | getData() 31 | } 32 | } 33 | 34 | fun getData() { 35 | items.clear() 36 | viewModelScope.launch { 37 | getBanner() 38 | getArticleTop() 39 | getArticle() 40 | } 41 | } 42 | 43 | private suspend fun getBanner() { 44 | WARepository.banners().awaitResponse { 45 | throwable.value = it 46 | }?.apply { 47 | items.add(this) 48 | } 49 | } 50 | 51 | private suspend fun getArticleTop() { 52 | WARepository.articleTop().awaitResponse { 53 | throwable.value = it 54 | }?.apply { 55 | // 置顶数据 56 | forEach { it.isTopping = true } 57 | items.addAll(this) 58 | } 59 | } 60 | 61 | private suspend fun getArticle() { 62 | WARepository.article(0).awaitResponse { 63 | throwable.value = it 64 | }?.apply { 65 | items.addAll(this.datas) 66 | mItems.value = items 67 | } 68 | } 69 | 70 | fun getArticle(page: Int) { 71 | viewModelScope.launch { 72 | WARepository.article(page).awaitResponse { 73 | throwable.value = it 74 | }?.apply { 75 | article.value = this 76 | } 77 | } 78 | } 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/vm/ProjectChildVM.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.vm 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.map 5 | import androidx.lifecycle.switchMap 6 | import androidx.lifecycle.viewModelScope 7 | import com.minic.base.extens.logD 8 | import com.minic.kt.base.BaseVM 9 | import com.minic.kt.data.WARepository 10 | import com.minic.kt.data.model.gank.home.ArticleData 11 | import com.minic.kt.ext.awaitResponse 12 | import com.minic.kt.jetpack.paging.loadData 13 | import kotlinx.coroutines.launch 14 | 15 | class ProjectChildVM(private val cid: Int) : BaseVM() { 16 | var mList = loadData { page, _, callback -> 17 | viewModelScope.launch { 18 | logD(msg = "加载数据$page") 19 | if (page == 1) { 20 | refreshComplete.value = true 21 | } 22 | WARepository.projectList(page, cid).awaitResponse { 23 | throwable.value = it 24 | }?.let { 25 | callback.onResult(it.datas) 26 | } 27 | } 28 | } 29 | val refreshComplete = MutableLiveData() 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/ui/fragment/vm/SystemChildVM.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.ui.fragment.vm 2 | 3 | import androidx.lifecycle.LifecycleOwner 4 | import androidx.lifecycle.viewModelScope 5 | import com.minic.base.databinding.viewmodel.SingleLiveEvent 6 | import com.minic.kt.base.BaseVM 7 | import com.minic.kt.data.WARepository 8 | import com.minic.kt.data.model.gank.home.SystemTree 9 | import com.minic.kt.ext.awaitResponse 10 | import kotlinx.coroutines.launch 11 | 12 | /** 13 | * @author :ChenYangYi 14 | * @date :2019年09月27日15:00:19 15 | * @description : 16 | * @github :https://github.com/chenyy0708 17 | */ 18 | class SystemChildVM : BaseVM() { 19 | 20 | val systemData = SingleLiveEvent>() 21 | 22 | override fun onCreate(lifecycleOwner: LifecycleOwner) { 23 | super.onCreate(lifecycleOwner) 24 | viewModelScope.launch { 25 | WARepository.systemTree().awaitResponse()?.let { systemData.value = it } 26 | } 27 | } 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/utils/FragmentChangeManager.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.utils 2 | 3 | import androidx.fragment.app.Fragment 4 | import androidx.fragment.app.FragmentManager 5 | 6 | /** 7 | * @ClassName: FragmentChangeManager 8 | * @Description: 9 | * @Author: ChenYy 10 | * @Date: 2019-08-02 17:41 11 | */ 12 | class FragmentChangeManager(private val mFragmentManager: FragmentManager, private val mContainerViewId: Int, private val mFragments: List) { 13 | /** 当前选中的Tab */ 14 | private var mCurrentTab: Int = 0 15 | 16 | init { 17 | initFragments() 18 | } 19 | 20 | /** 初始化fragments */ 21 | private fun initFragments() { 22 | for (fragment in mFragments) { 23 | mFragmentManager.beginTransaction().add(mContainerViewId, fragment).hide(fragment).commit() 24 | } 25 | setFragments(0) 26 | } 27 | 28 | /** 界面切换控制 */ 29 | fun setFragments(index: Int) { 30 | for (i in 0 until mFragments.size) { 31 | val ft = mFragmentManager.beginTransaction() 32 | val fragment = mFragments[i] 33 | if (i == index) { 34 | ft.show(fragment) 35 | } else { 36 | ft.hide(fragment) 37 | } 38 | ft.commit() 39 | } 40 | mCurrentTab = index 41 | } 42 | 43 | fun getCurrentTab(): Int { 44 | return mCurrentTab 45 | } 46 | 47 | fun getCurrentFragment(): Fragment { 48 | return mFragments[mCurrentTab] 49 | } 50 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/utils/GlideImageLoader.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.utils 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.graphics.Bitmap 6 | import android.graphics.drawable.Drawable 7 | import android.widget.ImageView 8 | import androidx.lifecycle.LiveData 9 | import androidx.lifecycle.MutableLiveData 10 | import com.bumptech.glide.Glide 11 | import com.bumptech.glide.load.MultiTransformation 12 | import com.bumptech.glide.load.Transformation 13 | import com.bumptech.glide.load.engine.DiskCacheStrategy 14 | import com.bumptech.glide.load.resource.bitmap.CenterCrop 15 | import com.bumptech.glide.load.resource.bitmap.FitCenter 16 | import com.bumptech.glide.request.RequestOptions 17 | import com.bumptech.glide.request.target.CustomTarget 18 | import com.bumptech.glide.request.transition.Transition 19 | import com.minic.imageload.ImageFrom 20 | import com.minic.imageload.ImageLoader 21 | import com.minic.imageload.ImageLoaderOptions 22 | import com.minic.imageload.ProgressDownLoad 23 | import jp.wasabeef.glide.transformations.CropCircleWithBorderTransformation 24 | import jp.wasabeef.glide.transformations.RoundedCornersTransformation 25 | 26 | 27 | /** 28 | * 描述: 29 | * 作者: ChenYy 30 | * 日期: 2019-10-23 18:44 31 | */ 32 | class GlideImageLoader : ImageLoader { 33 | override fun loadProgress(context: Context, url: String): LiveData> { 34 | return MutableLiveData>() 35 | } 36 | 37 | override fun loadBitmap(context: Context, url: String): LiveData { 38 | return loadBitmap(context, url, ImageLoaderOptions()) 39 | } 40 | 41 | override fun loadBitmap(context: Context, url: String, options: ImageLoaderOptions): LiveData { 42 | val liveData = MutableLiveData() 43 | Glide.with(context) 44 | .asBitmap() 45 | .load(url) 46 | .into(object : CustomTarget() { 47 | override fun onLoadCleared(placeholder: Drawable?) { 48 | } 49 | 50 | override fun onResourceReady(resource: Bitmap, transition: Transition?) { 51 | liveData.value = resource 52 | } 53 | 54 | }) 55 | return liveData 56 | } 57 | 58 | override fun load(context: Context, url: String, imageView: ImageView) { 59 | load(context, url, ImageFrom.getDefaultOptions() ?: ImageLoaderOptions(), imageView) 60 | } 61 | 62 | override fun load(context: Context, url: String, options: ImageLoaderOptions, imageView: ImageView) { 63 | val requestOptions = RequestOptions() 64 | .applyPlaceErrorHolder(options) 65 | .applyCacheStrategy(options) 66 | .applyBitmapTransforms(options) 67 | Glide.with(context) 68 | .load(url) 69 | .apply(requestOptions) 70 | .into(imageView) 71 | } 72 | 73 | @SuppressLint("CheckResult") 74 | fun RequestOptions.applyPlaceErrorHolder(options: ImageLoaderOptions): RequestOptions { 75 | placeholder(options.placeHolder) 76 | error(options.errorHolder) 77 | return this 78 | } 79 | 80 | @SuppressLint("CheckResult") 81 | fun RequestOptions.applyCacheStrategy(options: ImageLoaderOptions): RequestOptions { 82 | skipMemoryCache(options.isSkipMemoryCache) 83 | diskCacheStrategy(if (options.isSkipDiskCache) DiskCacheStrategy.NONE else DiskCacheStrategy.ALL) 84 | return this 85 | } 86 | 87 | @SuppressLint("CheckResult") 88 | fun RequestOptions.applyBitmapTransforms(options: ImageLoaderOptions): RequestOptions { 89 | val transforms = mutableListOf>() 90 | // 缩放类型 91 | when (options.scaleType) { 92 | ImageLoaderOptions.CENTER_CROP -> transforms.add(CenterCrop()) 93 | ImageLoaderOptions.FIT_CENTER -> transforms.add(FitCenter()) 94 | else -> transforms.add(CenterCrop()) 95 | } 96 | //全圆角/纯圆形/四个方向有部分圆角 97 | if (options.radius > 0) { // 全圆角 98 | transforms.add(RoundedCornersTransformation(options.radius, 0)) 99 | } else if (options.isCircle) { // 圆形 100 | transforms.add(CropCircleWithBorderTransformation(options.borderSize, options.borderColor)) 101 | } else { // 分别四个圆角 102 | if (options.topLeftRadius > 0) { 103 | transforms.add(RoundedCornersTransformation(options.topLeftRadius, 104 | 0, RoundedCornersTransformation.CornerType.TOP_LEFT)) 105 | } 106 | if (options.topRightRadius > 0) { 107 | transforms.add(RoundedCornersTransformation(options.topRightRadius, 108 | 0, RoundedCornersTransformation.CornerType.TOP_RIGHT)) 109 | } 110 | if (options.bottomLeftRadius > 0) { 111 | transforms.add(RoundedCornersTransformation(options.bottomLeftRadius, 112 | 0, RoundedCornersTransformation.CornerType.BOTTOM_LEFT)) 113 | } 114 | if (options.bottomRightRadius > 0) { 115 | transforms.add(RoundedCornersTransformation(options.bottomRightRadius, 116 | 0, RoundedCornersTransformation.CornerType.BOTTOM_RIGHT)) 117 | } 118 | } 119 | // 应用于Glide 120 | if (transforms.isNotEmpty()) { 121 | transform(MultiTransformation(transforms)) 122 | } 123 | return this 124 | } 125 | 126 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/utils/TimeUtils.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.utils 2 | 3 | import android.annotation.SuppressLint 4 | import java.text.SimpleDateFormat 5 | import java.util.* 6 | 7 | class TimeUtils private constructor() { 8 | 9 | private val minute = (60 * 1000).toLong()// 1分钟 10 | private val hour = 60 * minute// 1小时 11 | private val day = 24 * hour// 1天 12 | private val month = 31 * day// 月 13 | private val year = 12 * month// 年 14 | 15 | private val FORMAT_TYPE = "yyyy-MM-dd'T'HH:mm:ss.SSS" 16 | @SuppressLint("SimpleDateFormat") 17 | private val format = SimpleDateFormat(FORMAT_TYPE) 18 | 19 | companion object { 20 | private var instance: TimeUtils? = null 21 | get() { 22 | if (field == null) { 23 | field = TimeUtils() 24 | } 25 | return field 26 | } 27 | 28 | fun get(): TimeUtils { 29 | return instance!! 30 | } 31 | } 32 | 33 | private fun getString2Date(str: String): Date? { 34 | var date: Date? = null 35 | try { 36 | date = format.parse(str) 37 | } catch (e: Exception) { 38 | date = null 39 | } 40 | return date 41 | } 42 | 43 | /** 44 | * 返回文字描述的日期 45 | * @return 46 | */ 47 | fun getTimeFormatText(str: String): String? { 48 | val date = getString2Date(str) ?: return null 49 | val diff = Date().time - date.time 50 | var r: Long = 0 51 | if (diff > year) { 52 | r = diff / year 53 | return r.toString() + "年前" 54 | } 55 | if (diff > month) { 56 | r = diff / month 57 | return r.toString() + "个月前" 58 | } 59 | if (diff > day) { 60 | r = diff / day 61 | return r.toString() + "天前" 62 | } 63 | if (diff > hour) { 64 | r = diff / hour 65 | return r.toString() + "个小时前" 66 | } 67 | if (diff > minute) { 68 | r = diff / minute 69 | return r.toString() + "分钟前" 70 | } 71 | return "刚刚" 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/utils/ext/ViewExt.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.utils.ext 2 | 3 | import android.view.View 4 | import com.minic.kt.base.App 5 | 6 | /** 7 | * @ClassName: ViewExt 8 | * @Description: 9 | * @Author: ChenYy 10 | * @Date: 2019-05-10 17:01 11 | */ 12 | 13 | fun dp2px(dpValue: Float): Int { 14 | val scale = App.INSTANCE.resources.displayMetrics.density 15 | return (dpValue * scale + 0.5f).toInt() 16 | } 17 | 18 | fun View.gone() { 19 | visibility = View.GONE 20 | } 21 | 22 | fun View.visible() { 23 | visibility = View.VISIBLE 24 | } 25 | 26 | 27 | fun View.invisible() { 28 | visibility = View.INVISIBLE 29 | } 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/widget/superbutton/AttributeSetHelper.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.widget.superbutton 2 | 3 | import android.content.Context 4 | import android.graphics.drawable.GradientDrawable 5 | import android.util.AttributeSet 6 | import com.minic.kt.R 7 | 8 | /** 9 | *
 10 |  *      @author : Allen
 11 |  *      e-mail  : lygttpod@163.com
 12 |  *      date    : 2019/09/09
 13 |  *      desc    :
 14 |  * 
15 | */ 16 | class AttributeSetHelper { 17 | 18 | private val defaultColor = 0x20000000 19 | private val defaultSelectorColor = 0x20000000 20 | 21 | fun initShapeBuilderFromAttributeSet(context: Context, attrs: AttributeSet?): ShapeBuilder { 22 | if (attrs == null) return ShapeBuilder() 23 | val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ShapeView) 24 | 25 | val shapeType = typedArray.getInt(R.styleable.ShapeView_shapeType, GradientDrawable.RECTANGLE) 26 | 27 | val solidColor = typedArray.getColor(R.styleable.ShapeView_shapeSolidColor, defaultColor) 28 | 29 | val selectorPressedColor = typedArray.getColor(R.styleable.ShapeView_shapeSelectorPressedColor, defaultSelectorColor) 30 | val selectorDisableColor = typedArray.getColor(R.styleable.ShapeView_shapeSelectorDisableColor, defaultSelectorColor) 31 | val selectorNormalColor = typedArray.getColor(R.styleable.ShapeView_shapeSelectorNormalColor, defaultSelectorColor) 32 | 33 | val cornersRadius = typedArray.getDimensionPixelSize(R.styleable.ShapeView_shapeCornersRadius, 0).toFloat() 34 | val cornersTopLeftRadius = typedArray.getDimensionPixelSize(R.styleable.ShapeView_shapeCornersTopLeftRadius, 0).toFloat() 35 | val cornersTopRightRadius = typedArray.getDimensionPixelSize(R.styleable.ShapeView_shapeCornersTopRightRadius, 0).toFloat() 36 | val cornersBottomLeftRadius = typedArray.getDimensionPixelSize(R.styleable.ShapeView_shapeCornersBottomLeftRadius, 0).toFloat() 37 | val cornersBottomRightRadius = typedArray.getDimensionPixelSize(R.styleable.ShapeView_shapeCornersBottomRightRadius, 0).toFloat() 38 | 39 | val strokeWidth = typedArray.getDimensionPixelSize(R.styleable.ShapeView_shapeStrokeWidth, 0) 40 | val strokeDashWidth = typedArray.getDimensionPixelSize(R.styleable.ShapeView_shapeStrokeDashWidth, 0).toFloat() 41 | val strokeDashGap = typedArray.getDimensionPixelSize(R.styleable.ShapeView_shapeStrokeDashGap, 0).toFloat() 42 | 43 | val strokeColor = typedArray.getColor(R.styleable.ShapeView_shapeStrokeColor, defaultColor) 44 | 45 | val sizeWidth = typedArray.getDimensionPixelSize(R.styleable.ShapeView_shapeSizeWidth, 0) 46 | val sizeHeight = typedArray.getDimensionPixelSize(R.styleable.ShapeView_shapeSizeHeight, dip2px(context, 48f)) 47 | 48 | val gradientAngle = typedArray.getFloat(R.styleable.ShapeView_shapeGradientAngle, -1f).toInt() 49 | val gradientCenterX = typedArray.getDimensionPixelSize(R.styleable.ShapeView_shapeGradientCenterX, 0) 50 | val gradientCenterY = typedArray.getDimensionPixelSize(R.styleable.ShapeView_shapeGradientCenterY, 0) 51 | val gradientGradientRadius = typedArray.getDimensionPixelSize(R.styleable.ShapeView_shapeGradientGradientRadius, 0) 52 | 53 | val gradientStartColor = typedArray.getColor(R.styleable.ShapeView_shapeGradientStartColor, -1) 54 | val gradientCenterColor = typedArray.getColor(R.styleable.ShapeView_shapeGradientCenterColor, -1) 55 | val gradientEndColor = typedArray.getColor(R.styleable.ShapeView_shapeGradientEndColor, -1) 56 | 57 | val gradientType = typedArray.getInt(R.styleable.ShapeView_shapeGradientType, 0) 58 | val gradientUseLevel = typedArray.getBoolean(R.styleable.ShapeView_shapeGradientUseLevel, false) 59 | 60 | val useSelector = typedArray.getBoolean(R.styleable.ShapeView_shapeUseSelector, false) 61 | 62 | typedArray.recycle() 63 | 64 | val shapeBuilder = ShapeBuilder() 65 | shapeBuilder 66 | .setShapeType(shapeType) 67 | .setShapeCornersRadius(cornersRadius) 68 | .setShapeCornersTopLeftRadius(cornersTopLeftRadius) 69 | .setShapeCornersTopRightRadius(cornersTopRightRadius) 70 | .setShapeCornersBottomRightRadius(cornersBottomRightRadius) 71 | .setShapeCornersBottomLeftRadius(cornersBottomLeftRadius) 72 | .setShapeSolidColor(solidColor) 73 | .setShapeStrokeColor(strokeColor) 74 | .setShapeStrokeWidth(strokeWidth) 75 | .setShapeStrokeDashWidth(strokeDashWidth) 76 | .setShapeStrokeDashGap(strokeDashGap) 77 | .setShapeUseSelector(useSelector) 78 | .setShapeSelectorNormalColor(selectorNormalColor) 79 | .setShapeSelectorPressedColor(selectorPressedColor) 80 | .setShapeSelectorDisableColor(selectorDisableColor) 81 | .setShapeSizeWidth(sizeWidth) 82 | .setShapeSizeHeight(sizeHeight) 83 | .setShapeGradientType(gradientType) 84 | .setShapeGradientAngle(gradientAngle) 85 | .setShapeGradientGradientRadius(gradientGradientRadius) 86 | .setShapeGradientUseLevel(gradientUseLevel) 87 | .setShapeGradientCenterX(gradientCenterX) 88 | .setShapeGradientCenterY(gradientCenterY) 89 | .setShapeGradientStartColor(gradientStartColor) 90 | .setShapeGradientCenterColor(gradientCenterColor) 91 | .setShapeGradientEndColor(gradientEndColor) 92 | 93 | return shapeBuilder 94 | } 95 | 96 | 97 | /** 98 | * 单位转换工具类 99 | * 100 | * @param context 上下文对象 101 | * @param dipValue 值 102 | * @return 返回值 103 | */ 104 | private fun dip2px(context: Context, dipValue: Float): Int { 105 | val scale = context.resources.displayMetrics.density 106 | return (dipValue * scale + 0.5f).toInt() 107 | } 108 | } -------------------------------------------------------------------------------- /app/src/main/java/com/minic/kt/widget/superbutton/ShapeButton.kt: -------------------------------------------------------------------------------- 1 | package com.minic.kt.widget.superbutton 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import androidx.appcompat.widget.AppCompatButton 6 | 7 | /** 8 | *
 9 |  *      @author : Allen
10 |  *      e-mail  : lygttpod@163.com
11 |  *      date    : 2019/09/09
12 |  *      desc    :
13 |  * 
14 | */ 15 | class ShapeButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : AppCompatButton(context, attrs, defStyleAttr) { 16 | private var shapeBuilder: ShapeBuilder = ShapeBuilder() 17 | 18 | init { 19 | shapeBuilder = AttributeSetHelper().initShapeBuilderFromAttributeSet(context, attrs) 20 | shapeBuilder.into(this) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/app/src/main/res/drawable/icon.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_motion_study.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 12 | 13 | 14 | 22 | 23 | 30 | 31 | 38 | 39 | 48 | 49 | 50 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_browser.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 15 | 16 | 19 | 20 | 24 | 25 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_home_view_pager.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 17 | 18 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_project.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 18 | 19 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_project_child.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_system.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 14 | 15 | 16 | 17 | 22 | 23 | 26 | 27 | 34 | 35 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/include_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_home_article.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | 25 | 26 | 41 | 42 | 53 | 54 | 64 | 65 | 66 | 78 | 79 | 87 | 88 | 110 | 111 | 112 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_home_banner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_home_project_child.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | 25 | 26 | 41 | 42 | 55 | 56 | 67 | 68 | 69 | 81 | 82 | 90 | 91 | 113 | 114 | 115 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_browser.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main_nav.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 | 20 | 21 | 25 | 26 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/navigation/nav_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/values-night-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | 18 | 24 | 25 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/values/anim.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 200 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | @android:color/black 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1.5dp 4 | 4dp 5 | 6 | 5dp 7 | 8dp 8 | 9dp 9 | 10dp 10 | 15dp 11 | 12 | 12sp 13 | 13sp 14 | 14sp 15 | 15sp 16 | 16sp 17 | 17sp 18 | 18sp 19 | 19sp 20 | 21 | 6dp 22 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Gank 3 | open_toggle 4 | close_toggle 5 | 分享 6 | 复制 7 | 从浏览器打开 8 | 9 | 10 | https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2809156533,927877682&fm=26&gp=0.jpg 11 | https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2499107440,3201198713&fm=26&gp=0.jpg 12 | https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2954407842,3286872892&fm=26&gp=0.jpg 13 | https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3716438606,4272118910&fm=26&gp=0.jpg 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 17 | 18 | 24 | 25 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/xml/motion_study_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 16 | 19 | 22 | 23 | 24 | 34 | 37 | 40 | 41 | 42 | 52 | 55 | 58 | 59 | 60 | 61 | 62 | 71 | 74 | 77 | 78 | 79 | 88 | 91 | 94 | 95 | 96 | 105 | 108 | 111 | 112 | 113 | 114 | 119 | 122 | 123 | 124 | 130 | 131 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /base/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /base/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-kapt' 4 | 5 | def config = rootProject.ext.android // 配置 6 | 7 | android { 8 | compileSdkVersion config.compileSdkVersion 9 | 10 | 11 | defaultConfig { 12 | minSdkVersion config.minSdkVersion 13 | targetSdkVersion config.targetSdkVersion 14 | versionCode config.versionCode 15 | versionName config.versionName 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | 27 | // dataBinding 28 | dataBinding { 29 | enabled = true 30 | } 31 | 32 | } 33 | 34 | 35 | dependencies { 36 | implementation fileTree(dir: 'libs', include: ['*.jar']) 37 | testImplementation 'junit:junit:4.12' 38 | androidTestImplementation 'androidx.test:runner:1.3.0-alpha02' 39 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha02' 40 | // androidx 41 | api "androidx.appcompat:appcompat:1.1.0" 42 | api 'androidx.constraintlayout:constraintlayout:2.0.0-beta3' 43 | api "com.google.android.material:material:$materialVersion" 44 | api "androidx.legacy:legacy-support-v4:1.0.0" 45 | api "androidx.cardview:cardview:$supportVersion" 46 | api 'androidx.core:core-ktx:1.1.0' 47 | api "androidx.viewpager2:viewpager2:$viewPagerVersion" 48 | // nav_version 49 | api "androidx.navigation:navigation-fragment-ktx:$nav_version" 50 | api "androidx.navigation:navigation-ui-ktx:$nav_version" 51 | // ViewModel and LiveData 52 | api "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" 53 | api "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" 54 | api "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" 55 | api "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" 56 | // DataBinding 57 | kapt "androidx.databinding:databinding-compiler:$gradle_version" 58 | // Retrofit OkHttp 59 | api "com.squareup.retrofit2:retrofit:$retrofitVersion" 60 | api "com.squareup.retrofit2:converter-gson:$retrofitVersion" 61 | // 协程 62 | api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" 63 | // 侧滑返回 64 | api 'cn.bingoogolapple:bga-swipebacklayout:1.1.9@aar' 65 | // SnackBar 66 | api 'io.github.tonnyl:light:1.1.1' 67 | // 多状态布局 68 | api 'com.github.chenyy0708:LoadSir:2.0.0' 69 | // Lottie动画 70 | api "com.airbnb.android:lottie:$lottieVersion" 71 | api 'com.github.ihsanbal:LoggingInterceptor:3.0.0' 72 | } 73 | -------------------------------------------------------------------------------- /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. 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/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/aop/annotation/SingleClick.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.aop.annotation 2 | 3 | /** 4 | * 防止View被连续点击 5 | */ 6 | @Retention(AnnotationRetention.BINARY) 7 | @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) 8 | annotation class SingleClick 9 | -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/aop/aspect/SingleClickAspect.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.aop.aspect 2 | 3 | 4 | /** 5 | * 防止View被连续点击,间隔时间500ms 6 | */ 7 | // 8 | //@Aspect 9 | //class SingleClickAspect { 10 | // 11 | // @Pointcut("execution(@com.cyy.base.aop.annotation.SingleClick * *(..))") //方法切入点 12 | // fun methodAnnotated() { 13 | // 14 | // } 15 | // 16 | // @Around("methodAnnotated()")//在连接点进行方法替换 17 | // @Throws(Throwable::class) 18 | // fun aroundJoinPoint(joinPoint: ProceedingJoinPoint) { 19 | // var view: View? = null 20 | // for (arg in joinPoint.args) { 21 | // if (arg is View) view = arg 22 | // } 23 | // if (view != null) { 24 | // val tag = view.getTag(TIME_TAG) 25 | // val lastClickTime = if (tag != null) tag as Long else 0 26 | // if (BuildConfig.DEBUG) { 27 | // Log.d(TAG, "lastClickTime:" + lastClickTime) 28 | // } 29 | // val currentTime = Calendar.getInstance().timeInMillis 30 | // if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) {//过滤掉600毫秒内的连续点击 31 | // view.setTag(TIME_TAG, currentTime) 32 | // if (BuildConfig.DEBUG) { 33 | // Log.d(TAG, "currentTime:" + currentTime) 34 | // } 35 | // joinPoint.proceed()//执行原方法 36 | // } 37 | // } 38 | // } 39 | // 40 | // companion object { 41 | // 42 | // val TAG = "SingleClickAspect" 43 | // val MIN_CLICK_DELAY_TIME = 1000 44 | // internal var TIME_TAG = R.id.click_time 45 | // } 46 | //} 47 | -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.base 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.view.View 6 | import androidx.annotation.LayoutRes 7 | import androidx.appcompat.app.AppCompatActivity 8 | import androidx.databinding.DataBindingUtil 9 | import androidx.databinding.ViewDataBinding 10 | import com.kingja.loadsir.core.LoadService 11 | import com.kingja.loadsir.core.LoadSir 12 | 13 | /** 14 | * @author :ChenYangYi 15 | * @date :2018/07/24/14:38 16 | * @description :Activity基类 17 | * @github :https://github.com/chenyy0708 18 | */ 19 | abstract class BaseActivity : AppCompatActivity(){ 20 | 21 | /** 22 | * DataBind 23 | */ 24 | protected lateinit var mBinding: VB 25 | 26 | /** 27 | * 上下文 28 | */ 29 | protected lateinit var mContext: Context 30 | 31 | /** 32 | * 多状态布局管理类 33 | */ 34 | open lateinit var mLoadService: LoadService 35 | 36 | override fun onCreate(savedInstanceState: Bundle?) { 37 | super.onCreate(savedInstanceState) 38 | initBinding() 39 | mContext = this 40 | // 注册多状态布局 41 | mLoadService = LoadSir.getDefault().register(getStatusLayout()) 42 | initData(savedInstanceState) 43 | 44 | } 45 | 46 | /** 47 | * 返回被多状态布局包裹的ViewGroup,默认为Activity根布局 48 | */ 49 | protected open fun getStatusLayout(): View { 50 | return mBinding.root 51 | } 52 | 53 | /** 54 | * 创建DataBinding 55 | */ 56 | private fun initBinding() { 57 | if (getLayoutRes() != 0) { 58 | mBinding = DataBindingUtil.setContentView(this, getLayoutRes()) 59 | // 绑定LifeCycle 60 | mBinding.apply { 61 | lifecycleOwner = this@BaseActivity 62 | } 63 | } 64 | } 65 | 66 | /** 67 | * 初始化数据 68 | */ 69 | protected abstract fun initData(savedInstanceState: Bundle?) 70 | 71 | @LayoutRes 72 | abstract fun getLayoutRes(): Int 73 | } -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/base/BaseApp.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.base 2 | 3 | import android.app.Application 4 | import com.kingja.loadsir.callback.SuccessCallback 5 | import com.kingja.loadsir.core.LoadSir 6 | import com.minic.base.callback.ErrorCallback 7 | import com.minic.base.callback.LoadingCallback 8 | 9 | /** 10 | * @author :ChenYangYi 11 | * @date :2018/07/25/13:46 12 | * @description : 13 | * @github :https://github.com/chenyy0708 14 | */ 15 | open class BaseApp : Application() { 16 | 17 | override fun onCreate() { 18 | super.onCreate() 19 | // 初始化多状态布局 20 | LoadSir.beginBuilder() 21 | .addCallback(LoadingCallback()) 22 | .addCallback(ErrorCallback()) 23 | .setDefaultCallback(SuccessCallback::class.java) 24 | .commit() 25 | } 26 | } -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.base 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import androidx.annotation.LayoutRes 9 | import androidx.databinding.DataBindingUtil 10 | import androidx.databinding.ViewDataBinding 11 | import androidx.fragment.app.Fragment 12 | import com.kingja.loadsir.core.LoadService 13 | import com.kingja.loadsir.core.LoadSir 14 | 15 | /** 16 | * @author :ChenYangYi 17 | * @date :2018/07/24/14:38 18 | * @description :Fragment基类 19 | * @github :https://github.com/chenyy0708 20 | */ 21 | abstract class BaseFragment : Fragment() { 22 | 23 | /** 24 | * DataBind 25 | */ 26 | protected lateinit var mBinding: VB 27 | 28 | /** 29 | * Fragment根布局 30 | */ 31 | private lateinit var mRootView: View 32 | 33 | /** 34 | * 上下文 35 | */ 36 | protected var mContext: Context? = null 37 | 38 | /** 39 | * 多状态布局管理类 40 | */ 41 | open lateinit var mLoadService: LoadService 42 | 43 | 44 | override fun onCreateView(inflater: LayoutInflater, 45 | container: ViewGroup?, 46 | savedInstanceState: Bundle?): View? { 47 | mRootView = LayoutInflater.from(context).inflate(getLayoutRes(), container, false) 48 | return mRootView 49 | } 50 | 51 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 52 | super.onViewCreated(view, savedInstanceState) 53 | initBinding(view) 54 | mContext = this.activity 55 | // 注册多状态布局 56 | mLoadService = LoadSir.getDefault().register(getStatusLayout()) 57 | initData(savedInstanceState) 58 | } 59 | 60 | /** 61 | * 返回被多状态布局包裹的ViewGroup,默认为Activity根布局 62 | */ 63 | protected open fun getStatusLayout(): View { 64 | return mBinding.root 65 | } 66 | 67 | /** 68 | * 创建DataBinding 69 | */ 70 | private fun initBinding(rootView: View) { 71 | mBinding = DataBindingUtil.bind(rootView)!! 72 | // 绑定LifeCycle 73 | mBinding.apply { 74 | lifecycleOwner = this@BaseFragment 75 | } 76 | } 77 | 78 | /** 79 | * 初始化数据 80 | */ 81 | abstract fun initData(savedInstanceState: Bundle?) 82 | 83 | @LayoutRes 84 | abstract fun getLayoutRes(): Int 85 | 86 | } -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/callback/ErrorCallback.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.callback 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import com.airbnb.lottie.LottieAnimationView 6 | import com.kingja.loadsir.callback.Callback 7 | import com.minic.base.R 8 | 9 | /** 10 | * @author :ChenYangYi 11 | * @date :2018/09/30/11:13 12 | * @description :Lottie动画错误页面 13 | * @github :https://github.com/chenyy0708 14 | */ 15 | 16 | class ErrorCallback : Callback() { 17 | override fun onCreateView(): Int = R.layout.layout_lottie 18 | 19 | override fun onViewCreate(context: Context?, view: View?) { 20 | super.onViewCreate(context, view) 21 | view!!.findViewById(R.id.animation_view).apply { 22 | setAnimation("error.json") 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/callback/LoadingCallback.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.callback 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import com.airbnb.lottie.LottieAnimationView 6 | import com.minic.base.R 7 | import com.kingja.loadsir.callback.Callback 8 | 9 | /** 10 | * @author :ChenYangYi 11 | * @date :2018/09/30/11:13 12 | * @description :Lottie动画加载页面 13 | * @github :https://github.com/chenyy0708 14 | */ 15 | 16 | class LoadingCallback : Callback() { 17 | override fun onCreateView(): Int = R.layout.layout_lottie 18 | 19 | override fun onViewCreate(context: Context?, view: View?) { 20 | super.onViewCreate(context, view) 21 | val lottieView = view!!.findViewById(R.id.animation_view) 22 | lottieView.setAnimation("loading.json") 23 | } 24 | } -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/databinding/viewmodel/IViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.databinding.viewmodel 2 | 3 | import androidx.lifecycle.Lifecycle 4 | import androidx.lifecycle.LifecycleObserver 5 | import androidx.lifecycle.LifecycleOwner 6 | import androidx.lifecycle.OnLifecycleEvent 7 | import org.jetbrains.annotations.NotNull 8 | 9 | interface IViewModel : LifecycleObserver { 10 | 11 | @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) 12 | fun onCreate(@NotNull lifecycleOwner: LifecycleOwner) 13 | 14 | @OnLifecycleEvent(Lifecycle.Event.ON_START) 15 | fun onStart(@NotNull lifecycleOwner: LifecycleOwner) 16 | 17 | @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) 18 | fun onResume(@NotNull lifecycleOwner: LifecycleOwner) 19 | 20 | @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) 21 | fun onPause(@NotNull lifecycleOwner: LifecycleOwner) 22 | 23 | @OnLifecycleEvent(Lifecycle.Event.ON_STOP) 24 | fun onStop(@NotNull lifecycleOwner: LifecycleOwner) 25 | 26 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 27 | fun onDestroy(@NotNull lifecycleOwner: LifecycleOwner) 28 | 29 | @OnLifecycleEvent(Lifecycle.Event.ON_ANY) 30 | fun onLifecycleChanged(@NotNull lifecycleOwner: LifecycleOwner, 31 | @NotNull event: Lifecycle.Event) 32 | } -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/databinding/viewmodel/LifecycleViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.databinding.viewmodel 2 | 3 | import androidx.annotation.CallSuper 4 | import androidx.lifecycle.Lifecycle 5 | import androidx.lifecycle.LifecycleOwner 6 | import androidx.lifecycle.ViewModel 7 | import androidx.lifecycle.viewModelScope 8 | import com.minic.base.extens.logD 9 | 10 | /** 11 | * @author :ChenYangYi 12 | * @date :2018/09/29/15:28 13 | * @description :生命周期绑定ViewModel 14 | * @github :https://github.com/chenyy0708 15 | */ 16 | 17 | open class LifecycleViewModel : ViewModel(), IViewModel { 18 | 19 | 20 | @CallSuper 21 | override fun onCreate(lifecycleOwner: LifecycleOwner) { 22 | } 23 | 24 | @CallSuper 25 | override fun onStart(lifecycleOwner: LifecycleOwner) { 26 | } 27 | 28 | @CallSuper 29 | override fun onResume(lifecycleOwner: LifecycleOwner) { 30 | } 31 | 32 | @CallSuper 33 | override fun onPause(lifecycleOwner: LifecycleOwner) { 34 | } 35 | 36 | @CallSuper 37 | override fun onStop(lifecycleOwner: LifecycleOwner) { 38 | } 39 | 40 | @CallSuper 41 | override fun onDestroy(lifecycleOwner: LifecycleOwner) { 42 | } 43 | 44 | @CallSuper 45 | override fun onLifecycleChanged(lifecycleOwner: LifecycleOwner, 46 | event: Lifecycle.Event) { 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/databinding/viewmodel/SingleLiveEvent.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.databinding.viewmodel 2 | 3 | import android.util.Log 4 | 5 | import androidx.annotation.MainThread 6 | import androidx.lifecycle.LifecycleOwner 7 | import androidx.lifecycle.MutableLiveData 8 | import androidx.lifecycle.Observer 9 | 10 | import java.util.concurrent.atomic.AtomicBoolean 11 | 12 | class SingleLiveEvent : MutableLiveData() { 13 | 14 | private val mPending = AtomicBoolean(false) 15 | 16 | @MainThread 17 | override fun observe(owner: LifecycleOwner, observer: Observer) { 18 | 19 | if (hasActiveObservers()) { 20 | Log.w(TAG, "Multiple observers registered but only one will be notified of changes.") 21 | } 22 | 23 | // Observe the internal MutableLiveData 24 | super.observe(owner, Observer { t -> 25 | if (mPending.compareAndSet(true, false)) { 26 | observer.onChanged(t) 27 | } 28 | }) 29 | } 30 | 31 | @MainThread 32 | override fun setValue(t: T?) { 33 | mPending.set(true) 34 | super.setValue(t) 35 | } 36 | 37 | /** 38 | * Used for cases where T is Void, to make calls cleaner. 39 | */ 40 | @MainThread 41 | fun call() { 42 | value = null 43 | } 44 | 45 | companion object { 46 | 47 | private val TAG = "SingleLiveEvent" 48 | } 49 | } -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/extens/ActivityExt.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.extens 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.util.Log 7 | import androidx.annotation.ColorRes 8 | import androidx.annotation.DimenRes 9 | import androidx.appcompat.app.AppCompatActivity 10 | import androidx.appcompat.widget.Toolbar 11 | import androidx.core.content.ContextCompat 12 | import androidx.fragment.app.Fragment 13 | import java.io.Serializable 14 | 15 | /** 16 | * @author :ChenYangYi 17 | * @date :2018/09/29/15:18 18 | * @description :Activity 扩展函数 19 | * @github :https://github.com/chenyy0708 20 | */ 21 | 22 | /** 23 | * 获取颜色值--扩展函数 24 | */ 25 | fun Activity.getCompactColor(@ColorRes colorRes: Int): Int = ContextCompat.getColor(this, colorRes) 26 | 27 | /** 28 | * dp转px--扩展函数 29 | */ 30 | fun Activity.dpToPx(@DimenRes resID: Int): Int = this.resources.getDimensionPixelOffset(resID) 31 | 32 | /** 33 | * 跳转页面--扩展函数 34 | */ 35 | fun Activity.navigateToActivity(c: Class<*>) { 36 | val intent = Intent() 37 | intent.setClass(this, c) 38 | startActivity(intent) 39 | } 40 | 41 | /** 42 | * 初始化Toolbar 43 | */ 44 | fun AppCompatActivity.initToolbar(mToolbar: Toolbar, mTitle: String = "", isBack: Boolean = true) { 45 | setSupportActionBar(mToolbar) 46 | supportActionBar!!.title = mTitle 47 | supportActionBar!!.setDisplayHomeAsUpEnabled(isBack) 48 | mToolbar.setNavigationOnClickListener { onBackPressed() } 49 | } 50 | 51 | /** 52 | * 初始化Toolbar 53 | */ 54 | fun Fragment.initToolbar(mToolbar: Toolbar, mTitle: String = "", isBack: Boolean = true) { 55 | if (activity is AppCompatActivity) { 56 | (activity as AppCompatActivity)?.apply { 57 | setSupportActionBar(mToolbar) 58 | supportActionBar!!.title = mTitle 59 | supportActionBar!!.setDisplayHomeAsUpEnabled(isBack) 60 | } 61 | } 62 | } 63 | 64 | inline fun Context.openActivity( 65 | vararg params: Pair) { 66 | val intent = Intent(this, T::class.java) 67 | params.forEach { 68 | val value = it.second 69 | if (value is Int) { 70 | intent.putExtra(it.first, value) 71 | } 72 | if (value is Double) { 73 | intent.putExtra(it.first, value) 74 | } 75 | if (value is Float) { 76 | intent.putExtra(it.first, value) 77 | } 78 | if (value is CharSequence) { 79 | intent.putExtra(it.first, value) 80 | } 81 | if (value is Boolean) { 82 | intent.putExtra(it.first, value) 83 | } 84 | if (value is Serializable) { 85 | intent.putExtra(it.first, value) 86 | } 87 | } 88 | startActivity(intent) 89 | } 90 | 91 | /** 92 | * 打印日志--扩展函数 93 | */ 94 | fun Any.logD(tag: String = "GANK_LOG", 95 | msg: String? = "") { 96 | Log.d(tag, msg) 97 | } 98 | -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/extens/NetworkEx.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.extens 2 | 3 | 4 | /** 5 | * 描述: Retrofit网络请求 6 | * 作者: ChenYy 7 | * 日期: 2019-12-27 11:03 8 | */ 9 | // 10 | //fun LifecycleOwner.quickLaunch(block: RequestCarrier.() -> Unit) { 11 | // val requestCarrier = RequestCarrier() 12 | // block(requestCarrier) 13 | // 14 | // if (requestCarrier.request == null) { 15 | // Helper.showLog("请求request不能为空!") 16 | // return 17 | // } 18 | // requestCarrier.request?.subscribeOn(Schedulers.io()) 19 | // ?.observeOn(AndroidSchedulers.mainThread()) 20 | // ?.subscribe(Consumer { 21 | // it?.let { 22 | // requestCarrier.success?.invoke(it) 23 | // } 24 | // }, HttpErrorConsumer { 25 | // requestCarrier.fail?.invoke(it) 26 | // })?.addToOwner(this) 27 | //} 28 | // 29 | //class RequestCarrier { 30 | // var request: Observable? = null 31 | // var fail: ((throwable: Throwable) -> Unit)? = null 32 | // var success: ((rsp: T) -> Unit)? = null 33 | //} 34 | // 35 | //fun RequestCarrier.request(block: () -> Observable) { 36 | // request = block() 37 | //} 38 | // 39 | //fun RequestCarrier.onSuccess(block: (rsp: T) -> Unit) { 40 | // success = block 41 | //} 42 | // 43 | //fun RequestCarrier.onFail(block: (throwable: Throwable) -> Unit) { 44 | // fail = block 45 | //} -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/extens/SnackBarExt.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.extens 2 | 3 | import android.R 4 | import android.app.Activity 5 | import com.google.android.material.snackbar.Snackbar 6 | import androidx.core.content.ContextCompat 7 | import androidx.fragment.app.Fragment 8 | import io.github.tonnyl.light.make 9 | import io.github.tonnyl.light.warning 10 | 11 | /** 12 | * @author :ChenYangYi 13 | * @date :2018/09/30/09:56 14 | * @description :SnackBar扩展函数 15 | * @github :https://github.com/chenyy0708 16 | */ 17 | 18 | /** 19 | * 提示消息 20 | */ 21 | fun Activity.showMsg(message: String) { 22 | make( 23 | this.window.decorView, 24 | message, 25 | Snackbar.LENGTH_SHORT, 26 | null, 27 | ContextCompat.getColor(this, io.github.tonnyl.light.R.color.color_normal), 28 | ContextCompat.getColor(this, R.color.white)).show() 29 | } 30 | 31 | /** 32 | * 提示消息 33 | */ 34 | fun Fragment.showMsg(message: String) { 35 | make( 36 | this.view!!, 37 | message, 38 | Snackbar.LENGTH_SHORT, 39 | null, 40 | ContextCompat.getColor(view!!.context, io.github.tonnyl.light.R.color.color_normal), 41 | ContextCompat.getColor(view!!.context, R.color.white)).show() 42 | } 43 | 44 | /** 45 | * 警告 46 | */ 47 | fun Activity.showWarning(message: String) { 48 | warning(this.window.decorView, message, Snackbar.LENGTH_SHORT).show() 49 | } 50 | 51 | /** 52 | * 警告 53 | */ 54 | fun Fragment.showWarning(message: String) { 55 | warning(this.activity?.window!!.decorView, message, Snackbar.LENGTH_SHORT).show() 56 | } 57 | 58 | -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/extens/ThreadExecutorExt.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.extens 2 | 3 | import android.os.Handler 4 | import android.os.Looper 5 | import androidx.lifecycle.Lifecycle 6 | import androidx.lifecycle.LifecycleObserver 7 | import androidx.lifecycle.LifecycleOwner 8 | import androidx.lifecycle.OnLifecycleEvent 9 | import java.util.concurrent.Executor 10 | import java.util.concurrent.Executors 11 | 12 | 13 | /** 14 | * 描述: 方便子线程主线程切换任务 15 | * 作者: ChenYy 16 | * 日期: 2019-10-28 11:24 17 | */ 18 | 19 | // 管理Activity/Fragment 和 对应的 LifecycleObserver 20 | internal val threadLifecycleMap = hashMapOf() 21 | 22 | // 子线程 线程池 23 | internal val executorService by lazy { 24 | Executors.newCachedThreadPool() 25 | } 26 | 27 | // 主线程 28 | internal val mainThreadExecutor by lazy { 29 | MainThreadExecutor() 30 | } 31 | 32 | // 获取当前Activity/Fragment对应的TreadLifecycleObserver 33 | internal fun getTreadObserver(owner: LifecycleOwner): TreadLifecycleObserver { 34 | var observer: TreadLifecycleObserver? 35 | if (threadLifecycleMap.containsKey(owner)) { 36 | observer = threadLifecycleMap[owner] 37 | if (observer != null) { 38 | return observer 39 | } 40 | } 41 | observer = TreadLifecycleObserver(owner) 42 | threadLifecycleMap[owner] = observer 43 | return observer 44 | } 45 | 46 | // 主线程Handler 47 | class MainThreadExecutor : Executor { 48 | private val handler = Handler(Looper.getMainLooper()) 49 | 50 | override fun execute(r: Runnable) { 51 | handler.post(r) 52 | } 53 | } 54 | 55 | // 监听Activity/Fragment的生命周期 56 | open class TreadLifecycleObserver(private val lifecycleOwner: LifecycleOwner) : LifecycleObserver { 57 | 58 | var isActive = true 59 | 60 | init { 61 | lifecycleOwner.lifecycle.addObserver(this) 62 | } 63 | 64 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 65 | open fun onDestroy() { 66 | isActive = false 67 | lifecycleOwner.lifecycle.removeObserver(this) 68 | threadLifecycleMap.remove(lifecycleOwner) 69 | } 70 | } 71 | 72 | // 开启子线程任务 73 | fun LifecycleOwner.launch(block: TreadLifecycleObserver.() -> Unit) { 74 | val observer = getTreadObserver(this) 75 | val runnable = Runnable { 76 | block(observer) 77 | } 78 | executorService.execute(runnable) 79 | } 80 | 81 | // 运行在UI线程的扩展函数 82 | fun TreadLifecycleObserver.uiThread(block: () -> Unit) { 83 | val runnable = Runnable { 84 | block() 85 | } 86 | if (isActive) { // 防止内存泄露和View空指针 87 | mainThreadExecutor.execute(runnable) 88 | } 89 | } -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/extens/ViewModelExt.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.extens 2 | 3 | import androidx.lifecycle.LifecycleOwner 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.Observer 6 | 7 | /** 8 | * @author :ChenYangYi 9 | * @date :2018/07/24/14:38 10 | * @github :https://github.com/chenyy0708 11 | */ 12 | 13 | -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/net/UrlConstant.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.net 2 | 3 | 4 | /** 5 | * @author :ChenYangYi 6 | * @date :2018/07/25/13:36 7 | * @description :Url管理 8 | * @github :https://github.com/chenyy0708 9 | */ 10 | class UrlConstant { 11 | companion object { 12 | const val WAN_ANDROID_URL: String = "https://www.wanandroid.com/" 13 | const val WAN_ANDROID: String = "wan_android" 14 | } 15 | } -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/net/exception/CException.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.net.exception 2 | 3 | /** 4 | * @author :ChenYangYi 5 | * @date :2018/10/08/16:36 6 | * @description :自定义错误 7 | * @github :https://github.com/chenyy0708 8 | */ 9 | class CException(val msg: String) : Exception() -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/net/exception/ExceptionExt.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.net.exception 2 | 3 | import androidx.databinding.ViewDataBinding 4 | import android.widget.TextView 5 | import com.minic.base.R 6 | import com.minic.base.base.BaseActivity 7 | import com.minic.base.base.BaseFragment 8 | import com.minic.base.callback.ErrorCallback 9 | import com.minic.base.callback.LoadingCallback 10 | import com.minic.base.extens.showMsg 11 | import java.net.UnknownHostException 12 | 13 | /** 14 | * @author :ChenYangYi 15 | * @date :2018/10/08/16:42 16 | * @description : 17 | * @github :https://github.com/chenyy0708 18 | */ 19 | 20 | 21 | fun BaseFragment.doError(throwable: Throwable?) { 22 | when (throwable) { 23 | is UnknownHostException -> showError() 24 | is CException -> showMsg(throwable.msg) 25 | else -> showError() 26 | } 27 | } 28 | 29 | /** 30 | * 多状态布局切换 31 | */ 32 | fun BaseFragment.showSuccess() = mLoadService.showSuccess() 33 | 34 | fun BaseFragment.showLoading() = mLoadService.showCallback(LoadingCallback::class.java) 35 | fun BaseFragment.showError() = mLoadService.showCallback(ErrorCallback::class.java) 36 | fun BaseFragment.showError(msg: String) { 37 | mLoadService.setCallBack(ErrorCallback::class.java) { _, view -> 38 | val tvMsg = view.findViewById(R.id.tv_message) 39 | tvMsg.text = msg 40 | } 41 | mLoadService.showCallback(ErrorCallback::class.java) 42 | } 43 | 44 | 45 | fun BaseActivity.showSuccess() = mLoadService.showSuccess() 46 | fun BaseActivity.showLoading() = mLoadService.showCallback(LoadingCallback::class.java) 47 | fun BaseActivity.showError() = mLoadService.showCallback(ErrorCallback::class.java) 48 | fun BaseActivity.showError(msg: String) { 49 | mLoadService.setCallBack(ErrorCallback::class.java) { _, view -> 50 | val tvMsg = view.findViewById(R.id.tv_message) 51 | tvMsg.text = msg 52 | } 53 | mLoadService.showCallback(ErrorCallback::class.java) 54 | } 55 | 56 | -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/net/source/DefaultSource.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.net.source 2 | 3 | import android.content.Context 4 | import androidx.lifecycle.Lifecycle 5 | import androidx.lifecycle.LifecycleOwner 6 | import androidx.lifecycle.OnLifecycleEvent 7 | 8 | 9 | /** 10 | * 描述: 适用于App内多个请求数据共用,类似用户信息多个地方需要刷新等操作 11 | * 作者: ChenYy 12 | * 日期: 2019-11-28 17:51 13 | */ 14 | abstract class DefaultSource(private val lifecycleOwner: LifecycleOwner) : ISourceRequest { 15 | 16 | private val remoteSource by lazy { 17 | createRemoteSource(lifecycleOwner) 18 | } 19 | 20 | private val localSource by lazy { 21 | createLocalSource("AnalysisSource", lifecycleOwner as Context) 22 | } 23 | 24 | protected val mEventBus by lazy { 25 | if (lifecycleOwner !is Context) { 26 | throw IllegalArgumentException("lifecycleOwner must be Context") 27 | } 28 | // EventBusManager(lifecycleOwner as Context) 29 | } 30 | 31 | init { 32 | if (lifecycleOwner !is Context) { 33 | throw IllegalArgumentException("lifecycleOwner must be Context") 34 | } 35 | lifecycleOwner.lifecycle.addObserver(this) 36 | // lifecycleOwner.lifecycle.addObserver(mEventBus) 37 | // registerEvent(mEventBus) 38 | } 39 | 40 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 41 | fun onDestroy() { 42 | lifecycleOwner.lifecycle.removeObserver(this) 43 | // lifecycleOwner.lifecycle.removeObserver(mEventBus) 44 | } 45 | 46 | protected var block: (response: T) -> Unit = {} 47 | 48 | override fun getResponse() { 49 | localSource.getResponse() 50 | } 51 | 52 | override fun refreshData() { 53 | remoteSource.getResponse() 54 | } 55 | 56 | protected abstract fun createRemoteSource(lifecycleOwner: LifecycleOwner): IDataSource 57 | protected abstract fun createLocalSource(sourceKey: String, context: Context): IDataSource 58 | 59 | // abstract fun registerEvent(eventBusManager: EventBusManager) 60 | 61 | fun register(block: (response: T) -> Unit) { 62 | this.block = block 63 | } 64 | } -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/net/source/IDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.net.source 2 | 3 | /** 4 | * 描述: 5 | * 作者: ChenYy 6 | * 日期: 2019-11-28 16:54 7 | */ 8 | interface IDataSource{ 9 | fun getResponse() 10 | } -------------------------------------------------------------------------------- /base/src/main/java/com/minic/base/net/source/ISourceRequest.kt: -------------------------------------------------------------------------------- 1 | package com.minic.base.net.source 2 | 3 | import androidx.lifecycle.LifecycleObserver 4 | 5 | 6 | /** 7 | * 描述: 通用请求数据,扩展网络和本地 8 | * 作者: ChenYy 9 | * 日期: 2019-11-28 16:43 10 | */ 11 | interface ISourceRequest: LifecycleObserver { 12 | fun getResponse() 13 | fun refreshData() 14 | } -------------------------------------------------------------------------------- /base/src/main/res/layout/layout_lottie.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 26 | -------------------------------------------------------------------------------- /base/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #263238 4 | -------------------------------------------------------------------------------- /base/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /base/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | base 3 | 4 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | apply from: "config.gradle" 3 | buildscript { 4 | ext.kotlin_version = '1.3.50' 5 | ext.gradle_version = '3.4.0' 6 | 7 | repositories { 8 | google() 9 | jcenter() 10 | maven { url 'https://jitpack.io' } 11 | } 12 | dependencies { 13 | classpath "com.android.tools.build:gradle:$gradle_version" 14 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 15 | classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.1.0" 16 | classpath 'com.github.chenyy0708:UploadApkPlugin:1.4.0' 17 | // aspectj 18 | // classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.2' 19 | // NOTE: Do not place your application dependencies here; they belong 20 | // in the individual module build.gradle files 21 | } 22 | } 23 | 24 | allprojects { 25 | repositories { 26 | google() 27 | jcenter() 28 | maven { url "https://jitpack.io" } 29 | maven { url "https://dl.bintray.com/tonnyl/maven" } 30 | } 31 | } 32 | 33 | task clean(type: Delete) { 34 | delete rootProject.buildDir 35 | } 36 | -------------------------------------------------------------------------------- /commonres/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /commonres/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | def config = rootProject.ext.android // 配置 4 | 5 | android { 6 | compileSdkVersion config.compileSdkVersion 7 | 8 | 9 | 10 | defaultConfig { 11 | minSdkVersion config.minSdkVersion 12 | targetSdkVersion config.targetSdkVersion 13 | versionCode config.versionCode 14 | versionName config.versionName 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | 27 | } 28 | 29 | dependencies { 30 | implementation fileTree(dir: 'libs', include: ['*.jar']) 31 | } 32 | -------------------------------------------------------------------------------- /commonres/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /commonres/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /commonres/src/main/res/drawable-xxhdpi/icon_home_pager_not_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/commonres/src/main/res/drawable-xxhdpi/icon_home_pager_not_selected.png -------------------------------------------------------------------------------- /commonres/src/main/res/drawable-xxhdpi/icon_home_pager_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/commonres/src/main/res/drawable-xxhdpi/icon_home_pager_selected.png -------------------------------------------------------------------------------- /commonres/src/main/res/drawable-xxhdpi/icon_knowledge_hierarchy_not_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/commonres/src/main/res/drawable-xxhdpi/icon_knowledge_hierarchy_not_selected.png -------------------------------------------------------------------------------- /commonres/src/main/res/drawable-xxhdpi/icon_knowledge_hierarchy_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/commonres/src/main/res/drawable-xxhdpi/icon_knowledge_hierarchy_selected.png -------------------------------------------------------------------------------- /commonres/src/main/res/drawable-xxhdpi/icon_me_not_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/commonres/src/main/res/drawable-xxhdpi/icon_me_not_selected.png -------------------------------------------------------------------------------- /commonres/src/main/res/drawable-xxhdpi/icon_me_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/commonres/src/main/res/drawable-xxhdpi/icon_me_selected.png -------------------------------------------------------------------------------- /commonres/src/main/res/drawable-xxhdpi/icon_navigation_not_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/commonres/src/main/res/drawable-xxhdpi/icon_navigation_not_selected.png -------------------------------------------------------------------------------- /commonres/src/main/res/drawable-xxhdpi/icon_navigation_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/commonres/src/main/res/drawable-xxhdpi/icon_navigation_selected.png -------------------------------------------------------------------------------- /commonres/src/main/res/drawable-xxhdpi/icon_project_not_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/commonres/src/main/res/drawable-xxhdpi/icon_project_not_selected.png -------------------------------------------------------------------------------- /commonres/src/main/res/drawable-xxhdpi/icon_project_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/commonres/src/main/res/drawable-xxhdpi/icon_project_selected.png -------------------------------------------------------------------------------- /commonres/src/main/res/drawable-xxhdpi/vector_account_circle_px.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | -------------------------------------------------------------------------------- /commonres/src/main/res/drawable/progress_bar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /commonres/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /commonres/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11sp 4 | 12sp 5 | 13sp 6 | 14sp 7 | 15sp 8 | 16sp 9 | 17sp 10 | 18sp 11 | 19sp 12 | 20sp 13 | 21sp 14 | 22sp 15 | 23sp 16 | 24sp 17 | 25sp 18 | -------------------------------------------------------------------------------- /commonres/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | commonRes 3 | 4 | -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | 3 | android = [ // 配置信息 4 | compileSdkVersion: 29, 5 | minSdkVersion : 21, 6 | targetSdkVersion : 29, 7 | versionCode : 1, 8 | versionName : "1.0.0" 9 | ] 10 | 11 | supportVersion = '1.0.0-beta01' 12 | materialVersion = '1.2.0-alpha01' 13 | retrofitVersion = "2.6.0" 14 | lifecycle_version = '2.2.0-beta01' 15 | savedstate_version = '1.0.0-beta01' 16 | lottieVersion = "3.1.0" 17 | coroutines_version = "1.2.1" 18 | smartrefresh_layout_version = "1.1.0-andx-7" 19 | glide_version = "4.9.0" 20 | paging_version = "2.1.0" 21 | nav_version = "2.1.0" 22 | viewPagerVersion = '1.0.0-beta05' 23 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | # �� Android gradle ��� 3.1.0 Cannary 6 �� ������ ��ѡ���� �������� 16 | android.databinding.enableV2=true 17 | android.useAndroidX=true 18 | android.enableJetifier=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun May 05 17:16:12 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | 20 | PRG=`dirname "$PRG"`"/$link" 21 | fi 22 | done 23 | SAVED="`pwd`" 24 | cd "`dirname \"$PRG\"`/" >/dev/null 25 | APP_HOME="`pwd -P`" 26 | cd "$SAVED" >/dev/null 27 | 28 | APP_NAME="Gradle" 29 | APP_BASE_NAME=`basename "$0"` 30 | 31 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 32 | DEFAULT_JVM_OPTS="" 33 | 34 | # Use the maximum available, or set MAX_FD != -1 to use that value. 35 | MAX_FD="maximum" 36 | 37 | warn () { 38 | echo "$*" 39 | } 40 | 41 | die () { 42 | echo 43 | echo "$*" 44 | echo 45 | exit 1 46 | } 47 | 48 | # OS specific support (must be 'true' or 'false'). 49 | cygwin=false 50 | msys=false 51 | darwin=false 52 | nonstop=false 53 | case "`uname`" in 54 | CYGWIN* ) 55 | cygwin=true 56 | ;; 57 | Darwin* ) 58 | darwin=true 59 | ;; 60 | MINGW* ) 61 | msys=true 62 | ;; 63 | NONSTOP* ) 64 | nonstop=true 65 | ;; 66 | esac 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | JAVACMD=`cygpath --unix "$JAVACMD"` 118 | 119 | # We build the pattern for arguments to be converted via cygpath 120 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 121 | SEP="" 122 | for dir in $ROOTDIRSRAW ; do 123 | ROOTDIRS="$ROOTDIRS$SEP$dir" 124 | SEP="|" 125 | done 126 | OURCYGPATTERN="(^($ROOTDIRS))" 127 | # Add a user-defined pattern to the cygpath arguments 128 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 129 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 130 | fi 131 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 132 | i=0 133 | for arg in "$@" ; do 134 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 135 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 136 | 137 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 138 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 139 | else 140 | eval `echo args$i`="\"$arg\"" 141 | fi 142 | i=$((i+1)) 143 | done 144 | case $i in 145 | (0) set -- ;; 146 | (1) set -- "$args0" ;; 147 | (2) set -- "$args0" "$args1" ;; 148 | (3) set -- "$args0" "$args1" "$args2" ;; 149 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 150 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 151 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 152 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 153 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 154 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 155 | esac 156 | fi 157 | 158 | # Escape application args 159 | save () { 160 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 161 | echo " " 162 | } 163 | APP_ARGS=$(save "$@") 164 | 165 | # Collect all arguments for the java command, following the shell quoting and substitution rules 166 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 167 | 168 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 169 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 170 | cd "$(dirname "$0")" 171 | fi 172 | 173 | exec "$JAVACMD" "$@" 174 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /image_loader/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /image_loader/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | def config = rootProject.ext.android // 配置 5 | 6 | android { 7 | compileSdkVersion config.compileSdkVersion 8 | defaultConfig { 9 | minSdkVersion config.minSdkVersion 10 | targetSdkVersion config.targetSdkVersion 11 | versionCode config.versionCode 12 | versionName config.versionName 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | } 15 | 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" 28 | } 29 | -------------------------------------------------------------------------------- /image_loader/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenyy0708/AndroidJetPack/64662252f6bfe6eaa341ed6a638df50de31ff848/image_loader/consumer-rules.pro -------------------------------------------------------------------------------- /image_loader/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /image_loader/src/androidTest/java/com/minic/imageload/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.minic.imageload 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.minic.image_loader.test", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /image_loader/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /image_loader/src/main/java/com/minic/imageload/Ext.kt: -------------------------------------------------------------------------------- 1 | package com.minic.imageload 2 | 3 | import android.widget.ImageView 4 | import com.minic.imageload.ImageLoaderOptions.Companion.NORMAL_VALUE 5 | 6 | 7 | /** 8 | * 描述: ImageView加载Url扩展函数 9 | * 作者: ChenYy 10 | * 日期: 2019-10-23 18:52 11 | */ 12 | 13 | fun ImageView.loadIV(url: String) { 14 | ImageFrom.getImageLoader().load(context, url, ImageFrom.getDefaultOptions() 15 | ?: ImageLoaderOptions(), this) 16 | } 17 | 18 | fun ImageView.loadIV(url: String, 19 | placeHolder: Int = NORMAL_VALUE, 20 | radius: Int = NORMAL_VALUE, 21 | isSkipMemoryCache: Boolean = false, 22 | isSkipDiskCache: Boolean = false) { 23 | ImageFrom.getImageLoader().load(context, url, ImageLoaderOptions().apply { 24 | this.placeHolder = placeHolder 25 | this.radius = radius 26 | this.isSkipMemoryCache = isSkipMemoryCache 27 | this.isSkipDiskCache = isSkipDiskCache 28 | }, this) 29 | } 30 | -------------------------------------------------------------------------------- /image_loader/src/main/java/com/minic/imageload/ImageFrom.kt: -------------------------------------------------------------------------------- 1 | package com.minic.imageload 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.widget.ImageView 6 | import androidx.lifecycle.LiveData 7 | 8 | /** 9 | * 描述: 图片加载配置类 10 | * 作者: ChenYy 11 | * 日期: 2019-10-23 16:17 12 | */ 13 | object ImageFrom { 14 | 15 | private var mImageLoader: ImageLoader? = null 16 | 17 | private var options: ImageLoaderOptions? = null 18 | 19 | init { 20 | // 默认的图片加载配置 21 | options = ImageLoaderOptions().apply { 22 | } 23 | } 24 | 25 | fun getImageLoader(): ImageLoader { 26 | if(mImageLoader == null) { 27 | throw Throwable("ImageLoader Can not be null ") 28 | } 29 | return mImageLoader!! 30 | } 31 | 32 | fun setImageLoader(imageLoader: ImageLoader):ImageFrom { 33 | this.mImageLoader = imageLoader 34 | return this 35 | } 36 | 37 | fun setImageLoaderOptions(options: ImageLoaderOptions):ImageFrom { 38 | this.options = options 39 | return this 40 | } 41 | 42 | fun getDefaultOptions():ImageLoaderOptions? { 43 | return options 44 | } 45 | } -------------------------------------------------------------------------------- /image_loader/src/main/java/com/minic/imageload/ImageLoader.kt: -------------------------------------------------------------------------------- 1 | package com.minic.imageload 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.widget.ImageView 6 | import androidx.lifecycle.LiveData 7 | 8 | 9 | /** 10 | * 描述: 图片加载契约类 11 | * 作者: ChenYy 12 | * 日期: 2019-10-23 16:02 13 | */ 14 | interface ImageLoader { 15 | 16 | /** 17 | * 加载图片 18 | * 19 | * @param context 上下文 20 | * @param url 图片Url 21 | * @param imageView ImageView 22 | */ 23 | fun load(context: Context, url: String, imageView: ImageView) 24 | 25 | /** 26 | * 加载图片 27 | * 28 | * @param context 上下文 29 | * @param url 图片Url 30 | * @param options 图片加载配置 31 | * @param imageView ImageView 32 | */ 33 | fun load(context: Context, url: String, options: ImageLoaderOptions, imageView: ImageView) 34 | 35 | /** 36 | * 加载图片Bitmap 37 | * 38 | * @param context 上下文 39 | * @param url 图片Url 40 | * @param options 图片加载配置 41 | * @param imageView ImageView 42 | */ 43 | fun loadBitmap(context: Context, url: String, options: ImageLoaderOptions): LiveData 44 | 45 | /** 46 | * 加载图片Bitmap 47 | * 48 | * @param context 上下文 49 | * @param url 图片Url 50 | * @param options 图片加载配置 51 | * @param imageView ImageView 52 | */ 53 | fun loadBitmap(context: Context, url: String): LiveData 54 | 55 | /** 56 | * 加载图片进度 57 | * 58 | * @param context 上下文 59 | * @param url 图片Url 60 | * @param options 图片加载配置 61 | * @param imageView ImageView 62 | */ 63 | fun loadProgress(context: Context, url: String): LiveData> 64 | 65 | } -------------------------------------------------------------------------------- /image_loader/src/main/java/com/minic/imageload/ImageLoaderOptions.kt: -------------------------------------------------------------------------------- 1 | package com.minic.imageload 2 | 3 | import android.graphics.Color 4 | 5 | 6 | /** 7 | * 描述: 图片加载配置类 8 | * 作者: ChenYy 9 | * 日期: 2019-10-23 16:05 10 | */ 11 | class ImageLoaderOptions { 12 | companion object { 13 | // 没有设置值,默认实现 14 | const val NORMAL_VALUE = -1 15 | const val CENTER_CROP = 0 16 | const val FIT_CENTER = 1 17 | } 18 | 19 | 20 | // 占位图 21 | var placeHolder = NORMAL_VALUE 22 | // 加载失败 23 | var errorHolder = NORMAL_VALUE 24 | // 缩放类型 25 | var scaleType = NORMAL_VALUE 26 | // 跳过内存缓存 27 | var isSkipMemoryCache = false 28 | // 跳过磁盘缓存 29 | var isSkipDiskCache = false 30 | // 圆形 31 | var isCircle = false 32 | var borderSize = 0 33 | var borderColor = Color.TRANSPARENT 34 | // 圆角 35 | var radius = 0 36 | var topLeftRadius = 0 37 | var topRightRadius = 0 38 | var bottomLeftRadius = 0 39 | var bottomRightRadius = 0 40 | } -------------------------------------------------------------------------------- /image_loader/src/main/java/com/minic/imageload/ProgressDownLoad.kt: -------------------------------------------------------------------------------- 1 | package com.minic.imageload 2 | 3 | 4 | /** 5 | * 描述: 下载进度 6 | * 作者: ChenYy 7 | * 日期: 2019-10-25 11:36 8 | */ 9 | 10 | data class ProgressDownLoad( 11 | val downLoad: T, 12 | val progress: Float 13 | ) -------------------------------------------------------------------------------- /image_loader/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | image_loader 3 | 4 | -------------------------------------------------------------------------------- /image_loader/src/test/java/com/minic/imageload/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.minic.imageload 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':base', ':commonres', ':image_loader' 2 | //setBinding(new Binding([gradle: this])) 3 | //evaluate(new File( 4 | // settingsDir.parentFile, 5 | // "flutter_lib/.android/include_flutter.groovy" 6 | //)) 7 | --------------------------------------------------------------------------------