This commit is contained in:
coco
2026-07-03 16:23:31 +08:00
commit 7a4fb0e6ae
1979 changed files with 101570 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
*.iml
.gradle
/local.properties
/.idea/
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
secring.gpg
+227
View File
@@ -0,0 +1,227 @@
[toc]
# WanAndroid
🔥 🔥 🔥 一个充满设计感的WanAndroid APP,采用Kotlin 语言,组件化开发,MVVM+JetPack架构设计,**Arouter**、**LiveData**、**ViewModel**、**Room**、**Retrofit2**、**协程Coroutines**、**Flow**等流行技术。
## API
[**玩 Android 开放 API**](http://www.wanandroid.com/blog/show/2)
## 项目截图
| ![](screenshot/page_1.jpg) | ![](screenshot/page_2.jpg) | ![](screenshot/page_3.jpg) |
| --- | --- | --- |
| ![](screenshot/page_4.jpg) | ![](screenshot/page_5.jpg) | ![](screenshot/page_6.jpg) |
## 项目说明
- #### 由于项目中使用了Hilt和Arouter,有大量的注解,因此当build项目失败之后,请clean之后再build。
> Login-Plugin、apt_annotation、ap_compiler文件夹涉及到使用***APT+ASM+自定义Gradle插***件技术,和项目其他功能相关性不大,如果对这几个技术不感兴趣,可暂时不管这几个文件夹的code。
- 通用依赖库在common文件夹下,子组件都在modules文件夹下面。
- 控制子组件单独运行的开关在根目录下的**config.gradle**文件里面。
- 整个项目结构清晰简单,将每个tab做成一个module,让你快速上手组件化知识。
- 那如何将一个tab当成一个module的呢?具体是怎么实现的呢?具体代码可以查看MainActivity里面的写法。
- 该项目主要是学习如何将项目拆分module,是为了拆分module而拆分,实际项目中需要根据业务去拆分module。
- **login模块和content模块由于改动较小,所以将这两个模块已上传到maven上面;APP壳工程既可以源码依赖,也可以aar依赖。**
### 子组件独立运行
*由于项目中使用到有**Hilt**注解,因此需要在子组件的Application添加@HiltAndroidApp注解;但是当子组件合并到APP主工程的时候,由于RootApplication也有@HiltAndroidApp注解,就会导致编译不通过;因此在将子组件合并到APP主工程的时候,需要移除子组件的@HiltAndroidApp注解*
### 网络请求框架使用:
1. Retrofit2 + 协程Coroutines + Flow技术搭建,因此每个模块都有涉及。
2. 该网络请求框架同时支持多个BaseUrl以及动态改变BaseUrl;如果使用的网络接口的baseUrl不是http://www.wanandroid.com 则直接在Retrofit注解上写上完整的请求接口地址即可。具体的实现方式是自定义拦截器,将旧的baseUrl替换成新的即可,
详情可见:**MultiBaseUrlInterceptor**
### Room:
1. 使用到Room的模块主要是module-project模块,涉及到Room的增删改查,定义关联表等知识。
2. 在组件化的情况下,如果某个子组件需要用到数据库,就不要和其他的组件或app工程使用同一个DB;如果这样就会出现耦合,其实如果某个子组件需要用到DB,那么就要为该组件定义一个DB;因为从业务上来讲,该业务被划成子组件,就和其他组件关系不大,因此需要一个独立的DB。
### composeUI:
module-compose模块使用的是compose开发的界面,主要用来学习compose
### Hilt:
1. 组件化使用Hilt,需要在主工程和子module中加入hilt相关依赖
2. ViewModel中使用@HiltViewModel注解,则在Fragment或者Activity中无法只用Inject来实例化ViewModel,具体实例化方法参考@HiltViewModel注解注释的内容
3. ViewModel中使用@HiltViewModel注解,是使用HiltViewModelFactory来创建ViewModel实例,提供了灵活性
4. ViewModel中使用@ActivityRetainedScoped注解,则在Fragment或者Activity中直接用Inject来实例化ViewModel
### Flow StateFlow
1. 使用Flow替代LiveData可以参考module-wechat模块
2. 对于项目中是否有必要将LiveData替换为Flow,可以参考这篇[文章](https://juejin.cn/post/7007602776502960165) 和这个[视频](https://www.youtube.com/watch?v=7VDf-W82ZYE)
### Log工具类
基于[timber](https://github.com/JakeWharton/timber)封装了一个工具类Logs,方便用户开发阶段和release阶段收集日志。
查看线上业务逻辑的log,需要打印INFO及以上级别的日志,即Logs.i;异常仍然使用Logs.e
默认的日志Tag,可在Logs文件中的TAG变量中修改;如果需要不同文件有自己的tag,则可以如下设置:
```
Logs.e(t, tag)
```
## 一些知识点
1. 使用Flow,不管是请求网络返回数据还是从DB中返回数据的时候,已经处于main线程了;网络请求和从DB中查询数据操作是在子线程。
2. 开启混淆的时候,所有的实体bean都必须加上**@Keep**注解,让其不混淆
3. 组件化混淆的时候,可以将通用的和第三方库的混淆配置规则放在base里面,然后每个组件如果有单独的库或不需要混淆的地方,单独配置规则
4. proguard-rules.pro文件是给Library模块自己使用的混淆规则;
consumer-rules.pro文件则是会合并到app的混淆规则中,是给包括app在内的其他模块调用时使用的混淆规则;
详细说明可见该[文档](https://www.freesion.com/article/38811202153/)
5. [LiveData粘性事件](https://tech.meituan.com/2018/07/26/android-livedatabus.html)的解决方式有两种,一种是hook LiveData,将Observer的mLastVersion变量设置成和LiveData的mVersion变量一致;另一种是使用SingleLiveData;具体实现方式参见代码里面的LiveDataBus和SingleLiveData;注意如果使用SingleLiveData的话,如果多个页面使用同一个SingleLiveData对象注册observer,那么只有第一个页面能收到订阅数据;原因是在定义的原子变量身上。
6. [MVC、MVP、MVVM真正区别](https://www.bilibili.com/read/cv6670642)
MVCview直接model进行交互
MVC缺点:
1.Activity管理了过多的数据处理逻辑
2.业务逻辑无法复用
3.异步任务会出现很多嵌套回调
4.业务逻辑很多的情况下,Manager也会很臃肿
MVP:出现它就是为了取代Activity的地位
缺点:
1.接口文件过多
2.如果页面逻辑复杂,Activity需要实现非常多的接口,并且这些接口和具体页面绑定,无法复用
3.p层直接持有view层的引用,或者会用到Context,会很容易导致内存泄漏
4.有时候p层需要明确使用Activity的时候,需要做强制转换
5.多个页面无法复用同一个接口
6.总的来说,p层和view层是高度绑定的,p层和定义的那些接口无法高效的复用
MVVMview通过ViewModel订阅所需数据,ViewModel向View提供数据改变的接口,当view改变引起数据改变或者数据源发生改变时,ViewModel通过订阅告诉viewview进行视图更新。
优点:
1. ViewModel只提供数据订阅和数据接口,真正做到了和UI分离;
2. ViewModel伴随View生命周期,不会出现内存泄漏问题
3. 多个页面有相同数据来源,ViewModel可以复用相同接口并直接获取数据不会重复请求
4. 可以实现多个页面数据共享问题,并在Activity销毁重建的时候,数据不会丢失
## Arouter使用
#### 1. 使用room之后,组件化操作的时候,如果子module有数据存储需求,由于AppDatabase在主module中,则处理方式有两种:
1.1 在service模块,提供方法的时候,将对应的bean转为string,然后在子module中调用service提供的方法的时候,将获取到的数据转为string即可
1.2 在service模块,提供方法的时候,定义相应的bean即可
1.3 以上做法其实比较耦合,如果子module有数据存储需求,其实应该子module应该有一个单独的db。
#### 2. 每个模块需要有
```class
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
generateStubs = true
}
```
#### 3. 每个模块的路由路径的一级目录不能相同
#### 4. 传递参数的时候,参数名称不能是关键字。如:title
#### 5. 接收参数的时候,使用@Autowired注解的时候,变量不能被赋值
#### 6. 接收参数的时候,可以不使用@Autowired 注解,使用intent.extras 详见ContentActivity
#### 7. 不同module的布局文件存在同名的情况下,需要按照module的名称命名。
1. 比如登录模块的toolbar模块,命名为:reg_login_toolbarcontent模块的toolbar命名为:content_toolbar
#### 8. 对提供的服务使用@Autowired注解获取实例的时候,不能是private,否则编译不通过
#### 9. 接上一条,在使用服务的实例的之前,需要调用
```class
ARouter.getInstance().inject(this)
```
#### 10. 拦截器的使用
在跳转到某个页面的时候,如果目标页面是需要已经登录了才能跳转的页面,那么可以使用Arouter的拦截器实现;如果登录了就直接跳转到目标页面,如果没有登录就跳转到登录页面。拦截器实现如下:
```class
/**
* @Description:
* 该拦截器的作用是:统一页面跳转时,判断用户是否已经登录,
* 将业务层判断用户是否登录的逻辑统一到这里,业务层就不需要做if else判断了
* @Author: wangyuebin
* @Date: 2021/9/17 2:25 下午
*/
@Interceptor(name = "login", priority = 1)
class LoginInterceptor : IInterceptor {
/**
* 该集合保存的是需要登录成功之后才跳转的页面,也就是有@RequireLogin注解的页面
*/
private lateinit var pageList: List<String>
private var isLogin: Boolean = false
override fun init(context: Context?) {
pageList = RefletionUtils.getRequireLoginPages()
isLogin = RefletionUtils.getLoginStatus()
}
/**
* 在该项目中,也可以使用AppUtils.isLogin来直接判断是否登录,但是这样就耦合了AppUtils
* 如果不想耦合,就可以使用RefletionUtils.getLoginStatus()来判断是否登录,不过
* 前提是需要为AppUtils的isLogin变量增加@InjectLogin。这样做的好处就是:
* 可以将LoginInterceptor和RefletionUtils单独拿出来作为一个lib。
*/
override fun process(postcard: Postcard, callback: InterceptorCallback) {
// 判断哪些页面需要登录 (在整个应用中,有些页面需要登录,有些是不需要登录的)
if (pageList.contains(postcard.destination.canonicalName)) {
if (RefletionUtils.getLoginStatus()) { // 如果已经登录了,则默认不做任何处理
callback.onContinue(postcard)
} else { // 未登录,拦截
callback.onInterrupt(null)
}
} else {
callback.onContinue(postcard)
}
}
}
```
从上面代码中可以看出,只需要统计已经登录了才能跳转的页面,将其存入一个List集合中,然后在拦截器里面做判断就好了。
但是使用Arouter只能解决跳转页面的时候判断是否已经登录,在项目中,有些功能是不需要跳转页面就要判断是否登录,登录才执行某个操作,没登录就跳转到登录页面,这个时候Arouter就不适用了;例如本项目中的收藏功能。
## APT+ASM+自定义Gradle插件
- 在进入**我的收藏**页面的时候,使用了注解`@RequireLogin``@InjectLogin`两个注解判断该页面是否是需要登录才能进的页面以及是否已经登录了;因此涉及到***APT***技术
- ASM+自定义Gradle插件部分目前只是做了基本的尝试,目的是学习如何自定义Gradle插件以及字节码插装。
## 编译ijkPlayer,使其支持rtsp及https
- 编译的步骤如下:https://juejin.cn/post/7131343514742292511/
- 编译生成的so已集成至module-media模块中
## 疑问?
1. 如果新增一个module,或者新增一个功能,需要用到某个常量,然后主app也要用到某个该常量,那么该常量应该定义在哪里?base里面?如果定义在base里面,那么就会经常动base;如果不定义在base里面,那么该定义在哪里?
2. Arouter的路由应该放在哪里?如果放在common-base中,那么就要经常动base,如果放在某个module中,那么其他的module就无法使用,除非在该再定义一个相同的路由。
## 鼓励
通过这个项目希望能够帮助大家更好地学习 Jetpack 与 MVVM 架构。欢迎您提出项目架构设计中设计不合理的地方或者提出更优的解决方案,大家共同进步。如果你喜欢 WanAndroid的设计,感觉本项目的源代码对你的学习有所帮助,可以点右上角 "Star" 支持一下,谢谢!^_^
## 主要开源框架
- [Retrofit](https://github.com/square/retrofit)
- [okhttp](https://github.com/square/okhttp)
- [Glide](https://github.com/bumptech/glide)
- [BRVH](https://github.com/CymChad/BaseRecyclerViewAdapterHelper)
- [Logger](https://github.com/orhanobut/logger)
- [AgentWeb](https://github.com/Justson/AgentWeb)
- [BGABanner-Android](https://github.com/bingoogolapple/BGABanner-Android)
- [XXPermissions](https://github.com/getActivity/XXPermissions)
- [Arouter](https://github.com/alibaba/ARouter)
## LICENSE
```
Copyright (C) bbgo, Open source codes for study only.
Do not use for commercial purpose.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
+3
View File
@@ -0,0 +1,3 @@
/build
proguardMapping.txt
/release
+122
View File
@@ -0,0 +1,122 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-parcelize'
apply plugin: 'com.google.dagger.hilt.android'
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
signingConfigs {
wanandroid {
keyAlias 'wanandroid.keystore'
keyPassword 'wanandroid'
storeFile file('wanandroid.keystore')
storePassword 'wanandroid'
}
}
defaultConfig {
applicationId rootProject.ext.android.applicationId
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
multiDexEnabled true
consumerProguardFiles "consumer-rules.pro"
javaCompileOptions {
annotationProcessorOptions {
arguments += [
"room.schemaLocation":"$projectDir/schemas".toString(),
"room.incremental":"true",
"room.expandProjection":"true",
AROUTER_MODULE_NAME: project.getName()
]
}
}
}
buildTypes {
release {
minifyEnabled true
shrinkResources true // 移除无用的resource文件
zipAlignEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.wanandroid
}
debug {//开发自己的debug包
initWith release
debuggable true
minifyEnabled false
shrinkResources false // 移除无用的resource文件
proguardFiles.clear() // 不混淆
signingConfig signingConfigs.wanandroid
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
buildFeatures {
viewBinding true
}
lintOptions {
checkReleaseBuilds false
// Or, if you prefer, you can continue to check for errors in release builds,
// but continue the build even when errors are found:
abortOnError false
}
}
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
generateStubs = true
useBuildCache = true
javacOptions {
option("-Xmaxerrs", 500)
}
}
def isBuildModule = rootProject.ext.module.isBuildModule
dependencies {
implementation project(":common:common-base")
implementation project(":common:common-service")
if (!Boolean.valueOf(isBuildModule)) {
implementation project(":modules:module-content")
implementation project(":modules:module-login")
implementation project(":modules:module-home")
implementation project(":modules:module-wechat")
implementation project(":modules:module-sys")
implementation project(":modules:module-square")
implementation project(":modules:module-project")
// implementation project(":modules:module-compose")
implementation project(":modules:module-collect")
implementation project(":modules:module-media")
}
implementation rootProject.ext.roomLibs
kapt rootProject.ext.compiler["roomCompiler"]
implementation rootProject.ext.other["rvHelper"]
kapt rootProject.ext.compiler["arouterCompiler"]
compileOnly(rootProject.ext.jetpack["hilt"])
kapt rootProject.ext.compiler["hiltAndroidCompiler"]
debugImplementation "io.github.didi.dokit:dokitx:3.5.0"
}
+25
View File
@@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
# Arouter
-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bbgo.wanandroid">
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- 配置权限,用来记录应用配置信息 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 重力加速度传感器权限 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- Android8.0安装apk需要请求安装权限 -->
<uses-permission android:name="android.permission.VIBRATE" /> <!-- 小米push -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.hardware.sensor.accelerometer" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application
android:name=".RootApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".SplashActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".main.MainActivity"
android:screenOrientation="portrait"
android:launchMode="singleTask">
</activity>
<!--<activity
android:name=".login.ui.LoginActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".login.ui.RegisterActivity"
android:screenOrientation="portrait">
</activity>-->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path" />
</provider>
</application>
</manifest>
@@ -0,0 +1,13 @@
package com.bbgo.wanandroid
import com.bbgo.common_base.BaseApplication
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class RootApplication : BaseApplication() {
override fun onCreate() {
super.onCreate()
}
}
@@ -0,0 +1,100 @@
package com.bbgo.wanandroid
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Typeface
import android.os.Bundle
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import com.bbgo.common_base.BaseApplication
import com.bbgo.common_base.base.BaseActivity
import com.bbgo.common_base.util.AppUtil
import com.bbgo.library_statusbar.NotchScreenManager
import com.bbgo.wanandroid.databinding.ActivitySplashBinding
import com.bbgo.wanandroid.main.MainActivity
class SplashActivity : BaseActivity<ActivitySplashBinding>() {
private var textTypeface: Typeface?=null
private var descTypeFace: Typeface?=null
private var alphaAnimation: AlphaAnimation?=null
init {
textTypeface = Typeface.createFromAsset(BaseApplication.getContext().assets, "fonts/Lobster-1.4.otf")
descTypeFace = Typeface.createFromAsset(BaseApplication.getContext().assets, "fonts/FZLanTingHeiS-L-GB-Regular.TTF")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
NotchScreenManager.getInstance().setDisplayInNotch(this)
initView()
/*lifecycleScope.launch {
val time = System.currentTimeMillis()
val task1 = withContext(Dispatchers.IO) {
delay(2000)
Logs.d("1.执行task1.... [当前线程为:${Thread.currentThread().name}]")
"one"
}
val task2 = withContext(Dispatchers.IO) {
delay(1000)
Logs.d("2.执行task2.... [当前线程为:${Thread.currentThread().name}]")
"two"
}
Logs.d("task1 = $task1 , task2 = $task2 , 耗时 ${System.currentTimeMillis()-time} ms [当前线程为:${Thread.currentThread().name}]")
}*/
/*lifecycleScope.launch {
val time = System.currentTimeMillis()
val task1 = async(Dispatchers.IO) {
delay(2000)
Logs.d("1.执行task1.... [当前线程为:${Thread.currentThread().name}]")
"async task one"
}
val task2 = async(Dispatchers.IO) {
delay(1000)
Logs.d("2.执行task2.... [当前线程为:${Thread.currentThread().name}]")
"async task two"
}
Logs.d("task1 = ${task1.await()} , task2 = ${task2.await()} , 耗时 ${System.currentTimeMillis()-time} ms [当前线程为:${Thread.currentThread().name}]")
}*/
}
@SuppressLint("SetTextI18n")
private fun initView() {
binding.tvAppName.typeface = textTypeface
binding.tvSplashDesc.typeface = descTypeFace
binding.tvVersionName.text = "v${AppUtil.appVersionName}"
//渐变展示启动屏
alphaAnimation= AlphaAnimation(0.3f, 1.0f)
alphaAnimation?.duration = 2000
alphaAnimation?.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationEnd(arg0: Animation) {
redirectTo()
}
override fun onAnimationRepeat(animation: Animation) {
}
override fun onAnimationStart(animation: Animation) {
}
})
binding.ivLogo.startAnimation(alphaAnimation)
}
private fun redirectTo() {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish()
}
override fun inflateViewBinding() = ActivitySplashBinding.inflate(layoutInflater)
}
@@ -0,0 +1,372 @@
package com.bbgo.wanandroid.main
import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
import android.view.MenuItem
import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import com.alibaba.android.arouter.facade.Postcard
import com.alibaba.android.arouter.facade.annotation.Autowired
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.facade.callback.NavigationCallback
import com.alibaba.android.arouter.launcher.ARouter
import com.bbgo.common_base.base.BaseActivity
import com.bbgo.common_base.bus.BusKey
import com.bbgo.common_base.bus.LiveDataBus
import com.bbgo.common_base.constants.Constants
import com.bbgo.common_base.constants.RouterPath
import com.bbgo.common_base.event.ScrollEvent
import com.bbgo.common_base.ext.Prefs
import com.bbgo.common_base.ext.Resource
import com.bbgo.common_base.ext.observe
import com.bbgo.common_base.ext.showToast
import com.bbgo.common_base.util.AppUtil
import com.bbgo.common_base.util.DialogUtil
import com.bbgo.common_service.login.LoginOutService
import com.bbgo.wanandroid.R
import com.bbgo.wanandroid.databinding.ActivityMainBinding
import com.bbgo.wanandroid.databinding.NavHeaderMainBinding
import com.google.android.material.navigation.NavigationBarView
import com.google.android.material.navigation.NavigationView
import dagger.hilt.android.AndroidEntryPoint
@Route(path = RouterPath.Main.PAGE_MAIN)
@AndroidEntryPoint
class MainActivity : BaseActivity<ActivityMainBinding>(), NavigationView.OnNavigationItemSelectedListener {
private lateinit var navHeaderBinding: NavHeaderMainBinding
@Autowired(name = RouterPath.LoginRegister.SERVICE_LOGOUT)
lateinit var loginOutService: LoginOutService
//默认为0
private var mIndex = 0
//退出时间
private var mExitTime: Long = 0
private var homeFragment: Fragment? = null
private var weChatFragment: Fragment? = null
private var sysFragment: Fragment? = null
private var squareFragment: Fragment? = null
private var projectFragment: Fragment? = null
private val mDialog by lazy {
DialogUtil.getWaitDialog(this, getString(R.string.login_ing))
}
private val mainViewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
if (savedInstanceState != null) {
mIndex = savedInstanceState.getInt("currTabIndex")
}
super.onCreate(savedInstanceState)
setContentView(binding.root)
initView()
observe()
}
private fun initView() {
ARouter.getInstance().inject(this)
initBus()
navHeaderBinding = NavHeaderMainBinding.inflate(layoutInflater)
binding.navView.addHeaderView(navHeaderBinding.root)
binding.bottomNavigation.selectedItemId = mIndex
binding.bottomNavigation.labelVisibilityMode = NavigationBarView.LABEL_VISIBILITY_LABELED
switchFragment(mIndex)
binding.actionBar.run {
title = getString(R.string.app_name)
setSupportActionBar(this.toolbar)
}
binding.bottomNavigation.setOnNavigationItemSelectedListener {
when (it.itemId) {
R.id.action_home ->
switchFragment(Constants.FragmentIndex.HOME_INDEX)
R.id.action_wechat ->
switchFragment(Constants.FragmentIndex.WECHAT_INDEX)
R.id.action_system ->
switchFragment(Constants.FragmentIndex.SYS_INDEX)
R.id.action_square ->
switchFragment(Constants.FragmentIndex.SQUARE_INDEX)
R.id.action_project ->
switchFragment(Constants.FragmentIndex.PROJECT_INDEX)
}
true
}
val toggle = ActionBarDrawerToggle(this, binding.drawerLayout, binding.actionBar.toolbar,
R.string.navigation_drawer_open, R.string.navigation_drawer_close)
binding.drawerLayout.addDrawerListener(toggle)
toggle.syncState()
binding.navView.setNavigationItemSelectedListener(this)
setUserInfo()
navHeaderBinding.root.setOnClickListener {
if (AppUtil.isLogin) {
return@setOnClickListener
}
ARouter.getInstance().build(RouterPath.LoginRegister.PAGE_LOGIN).navigation()
}
binding.floatingBtn.setOnClickListener(onFABClickListener)
}
private fun observe() {
observe(mainViewModel.logOutLiveData, ::handleLogOut)
}
private fun initBus() {
LiveDataBus.get().with(BusKey.LOGIN_SUCCESS, Any::class.java).observe(this) {
val userId = Prefs.getString(Constants.USER_ID)
val userName = Prefs.getString(Constants.USER_NAME)
if (userId.isEmpty()) return@observe
navHeaderBinding.userIdLayout.visibility = View.VISIBLE
navHeaderBinding.tvUserId.text = userId
navHeaderBinding.tvUsername.text = userName
binding.navView.menu.findItem(R.id.nav_logout).title = getString(R.string.nav_logout)
}
}
private fun handleLogOut(status: Resource<String>) {
when (status) {
is Resource.Loading -> {
mDialog.show()
}
is Resource.Error -> {
mDialog.dismiss()
showToast(status.exception.stackTraceToString())
}
else -> {
mDialog.dismiss()
showToast(getString(R.string.logout_success))
navHeaderBinding.tvUsername.text = getString(R.string.go_login)
navHeaderBinding.userIdLayout.visibility = View.GONE
navHeaderBinding.tvUserGrade.text = getString(R.string.nav_line_2)
navHeaderBinding.tvUserRank.text = getString(R.string.nav_line_2)
Prefs.clear()
binding.navView.menu.findItem(R.id.nav_logout).title = getString(R.string.login)
AppUtil.isLogin = false
}
}
}
private fun setUserInfo() {
var userName = Prefs.getString(Constants.USER_NAME)
if (userName.isEmpty()) {
userName = getString(R.string.go_login)
navHeaderBinding.tvUsername.text = userName
return
}
AppUtil.isLogin = true
val userId = Prefs.getString(Constants.USER_ID)
navHeaderBinding.userIdLayout.visibility = View.VISIBLE
navHeaderBinding.tvUserId.text = userId
navHeaderBinding.tvUsername.text = userName
}
private fun switchFragment(position: Int) {
val transaction = supportFragmentManager.beginTransaction()
hideFragment(transaction)
when(position) {
Constants.FragmentIndex.HOME_INDEX ->
homeFragment?.let {
transaction.show(it)
} ?: kotlin.run {
ARouter.getInstance().build(RouterPath.Home.PAGE_HOME).navigation()
?.let {
homeFragment = it as Fragment
homeFragment?.let {
binding.actionBar.tvTitle.text = getString(R.string.app_name)
transaction.add(R.id.container, it, null)
}
}
}
Constants.FragmentIndex.WECHAT_INDEX ->
weChatFragment?.let { transaction.show(it) } ?: kotlin.run {
ARouter.getInstance().build(RouterPath.WeChat.PAGE_WECHAT).navigation()
?.let {
weChatFragment = it as Fragment
weChatFragment?.let {
binding.actionBar.tvTitle.text = getString(R.string.wechat)
weChatFragment = it
transaction.add(R.id.container, it, null)
}
}
}
Constants.FragmentIndex.SYS_INDEX ->
sysFragment?.let { transaction.show(it) } ?: kotlin.run {
ARouter.getInstance().build(RouterPath.Sys.PAGE_SYS).navigation()
?.let {
sysFragment = it as Fragment
sysFragment?.let {
binding.actionBar.tvTitle.text = getString(R.string.knowledge_system)
sysFragment = it
transaction.add(R.id.container, it, null)
}
}
}
Constants.FragmentIndex.SQUARE_INDEX ->
squareFragment?.let { transaction.show(it) } ?: kotlin.run {
ARouter.getInstance().build(RouterPath.Square.PAGE_SQUARE).navigation()
?.let {
squareFragment = it as Fragment
squareFragment?.let {
binding.actionBar.tvTitle.text = getString(R.string.square)
squareFragment = it
transaction.add(R.id.container, it, null)
}
}
}
Constants.FragmentIndex.PROJECT_INDEX ->
projectFragment?.let { transaction.show(it) } ?: kotlin.run {
ARouter.getInstance().build(RouterPath.Project.PAGE_PROJECT).navigation()
?.let {
projectFragment = it as Fragment
projectFragment?.let {
binding.actionBar.tvTitle.text = getString(R.string.project)
projectFragment = it
transaction.add(R.id.container, it, null)
}
}
}
}
mIndex = position
transaction.commitAllowingStateLoss()
}
private fun hideFragment(transaction: FragmentTransaction) {
homeFragment?.let { transaction.hide(it) }
weChatFragment?.let { transaction.hide(it) }
sysFragment?.let { transaction.hide(it) }
squareFragment?.let { transaction.hide(it) }
projectFragment?.let { transaction.hide(it) }
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
when(item.itemId) {
R.id.nav_collect -> {
ARouter.getInstance().build(RouterPath.Compose.PAGE_COMPOSE)
.navigation(this, object : NavigationCallback {
override fun onFound(postcard: Postcard) {
}
override fun onLost(postcard: Postcard) {
}
override fun onArrival(postcard: Postcard?) {
}
override fun onInterrupt(postcard: Postcard) {
val bundle = postcard.extras
ARouter.getInstance().build(RouterPath.LoginRegister.PAGE_LOGIN)
.with(bundle)
.withString(Constants.ROUTER_PATH, postcard.path)
.navigation()
}
})
}
R.id.nav_video -> {
ARouter.getInstance().build(RouterPath.Media.PAGE_VIDEO).navigation()
}
R.id.nav_setting -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
AppUtil.requestIgnoreBatteryOptimizations(this)
}
}
R.id.nav_logout -> {
if (AppUtil.isLogin) {
mainViewModel.logOut(loginOutService)
} else {
ARouter.getInstance().build(RouterPath.LoginRegister.PAGE_LOGIN).navigation()
binding.drawerLayout.closeDrawers()
}
}
}
return false
}
override fun recreate() {
kotlin.runCatching {
val fragmentTransaction = supportFragmentManager.beginTransaction()
homeFragment?.let {
fragmentTransaction.remove(it)
}
squareFragment?.let {
fragmentTransaction.remove(it)
}
sysFragment?.let {
fragmentTransaction.remove(it)
}
projectFragment?.let {
fragmentTransaction.remove(it)
}
weChatFragment?.let {
fragmentTransaction.remove(it)
}
fragmentTransaction.commitAllowingStateLoss()
}.onFailure {
it.printStackTrace()
}
super.recreate()
}
/**
* FAB 监听
*/
private val onFABClickListener = View.OnClickListener {
when (mIndex) {
Constants.FragmentIndex.HOME_INDEX -> {
LiveDataBus.get().with(BusKey.SCROLL_TOP).value = ScrollEvent(0)
}
Constants.FragmentIndex.WECHAT_INDEX -> {
LiveDataBus.get().with(BusKey.SCROLL_TOP).value = ScrollEvent(1)
}
Constants.FragmentIndex.SYS_INDEX -> {
LiveDataBus.get().with(BusKey.SCROLL_TOP).value = ScrollEvent(2)
}
Constants.FragmentIndex.SQUARE_INDEX -> {
LiveDataBus.get().with(BusKey.SCROLL_TOP).value = ScrollEvent(3)
}
Constants.FragmentIndex.PROJECT_INDEX -> {
LiveDataBus.get().with(BusKey.SCROLL_TOP).value = ScrollEvent(4)
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("currTabIndex", mIndex)
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (System.currentTimeMillis().minus(mExitTime) <= 2000) {
finish()
} else {
mExitTime = System.currentTimeMillis()
// showToast("再按一次退出程序")
}
return true
}
return super.onKeyDown(keyCode, event)
}
override fun inflateViewBinding() = ActivityMainBinding.inflate(layoutInflater)
}
@@ -0,0 +1,33 @@
package com.bbgo.wanandroid.main
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.bbgo.common_base.ext.Resource
import com.bbgo.common_service.login.LoginOutService
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
/**
* author: wangyb
* date: 3/29/21 9:31 PM
* description: todo
*/
class MainViewModel : ViewModel() {
val logOutLiveData = MutableLiveData<Resource<String>>()
fun logOut(loginOutService: LoginOutService) {
viewModelScope.launch {
loginOutService.logOut()
.catch {
}
.collectLatest {
logOutLiveData.value = Resource.Success(it)
}
}
}
}
@@ -0,0 +1,49 @@
package com.bbgo.wanandroid.widget;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat;
/**
* Created by Meiji on 2016/12/12.
*/
public class BottomNavigationBehavior extends CoordinatorLayout.Behavior<View> {
private ObjectAnimator outAnimator, inAnimator;
public BottomNavigationBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
// 垂直滑动
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes, int type) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed, int type) {
if (dy > 0) {// 上滑隐藏
if (outAnimator == null) {
outAnimator = ObjectAnimator.ofFloat(child, "translationY", 0, child.getHeight());
outAnimator.setDuration(200);
}
if (!outAnimator.isRunning() && child.getTranslationY() <= 0) {
outAnimator.start();
}
} else if (dy < 0) {// 下滑显示
if (inAnimator == null) {
inAnimator = ObjectAnimator.ofFloat(child, "translationY", child.getHeight(), 0);
inAnimator.setDuration(200);
}
if (!inAnimator.isRunning() && child.getTranslationY() >= child.getHeight()) {
inAnimator.start();
}
}
}
}
@@ -0,0 +1,129 @@
package com.bbgo.wanandroid.widget
import android.content.Context
import android.graphics.*
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
import com.bbgo.wanandroid.R
/**
* Created by chenxz on 2018/4/22.
*/
class CircleImageView(context: Context, attrs: AttributeSet) : AppCompatImageView(context, attrs) {
private val mType: Int
private val mBorderColor: Int
private val mBorderWidth: Int
private val mRectRoundRadius: Int
private val mPaintBitmap = Paint(Paint.ANTI_ALIAS_FLAG)
private val mPaintBorder = Paint(Paint.ANTI_ALIAS_FLAG)
private val mRectBorder = RectF()
private val mRectBitmap = RectF()
private var mRawBitmap: Bitmap? = null
private var mShader: BitmapShader? = null
private val mMatrix = Matrix()
init {
//取xml文件中设定的参数
val ta = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView)
mType = ta.getInt(R.styleable.CircleImageView_type, DEFAULT_TYPE)
mBorderColor = ta.getColor(R.styleable.CircleImageView_borderColor, DEFAULT_BORDER_COLOR)
mBorderWidth = ta.getDimensionPixelSize(R.styleable.CircleImageView_borderWidth, dip2px(DEFAULT_BORDER_WIDTH))
mRectRoundRadius = ta.getDimensionPixelSize(R.styleable.CircleImageView_rectRoundRadius, dip2px(DEFAULT_RECT_ROUND_RADIUS))
ta.recycle()
}
override fun onDraw(canvas: Canvas) {
val rawBitmap = getBitmap(drawable)
if (rawBitmap != null && mType != TYPE_NONE) {
val viewWidth = width
val viewHeight = height
val viewMinSize = Math.min(viewWidth, viewHeight)
val dstWidth = (if (mType == TYPE_CIRCLE) viewMinSize else viewWidth).toFloat()
val dstHeight = (if (mType == TYPE_CIRCLE) viewMinSize else viewHeight).toFloat()
val halfBorderWidth = mBorderWidth / 2.0f
val doubleBorderWidth = (mBorderWidth * 2).toFloat()
if (mShader == null || rawBitmap != mRawBitmap) {
mRawBitmap = rawBitmap
mShader = BitmapShader(mRawBitmap!!, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
}
if (mShader != null) {
mMatrix.setScale((dstWidth - doubleBorderWidth) / rawBitmap.width, (dstHeight - doubleBorderWidth) / rawBitmap.height)
mShader!!.setLocalMatrix(mMatrix)
}
mPaintBitmap.shader = mShader
mPaintBorder.style = Paint.Style.STROKE
mPaintBorder.strokeWidth = mBorderWidth.toFloat()
mPaintBorder.color = if (mBorderWidth > 0) mBorderColor else Color.TRANSPARENT
if (mType == TYPE_CIRCLE) {
val radius = viewMinSize / 2.0f
canvas.drawCircle(radius, radius, radius - halfBorderWidth, mPaintBorder)
canvas.translate(mBorderWidth.toFloat(), mBorderWidth.toFloat())
canvas.drawCircle(radius - mBorderWidth, radius - mBorderWidth, radius - mBorderWidth, mPaintBitmap)
} else if (mType == TYPE_ROUNDED_RECT) {
mRectBorder.set(halfBorderWidth, halfBorderWidth, dstWidth - halfBorderWidth, dstHeight - halfBorderWidth)
mRectBitmap.set(0.0f, 0.0f, dstWidth - doubleBorderWidth, dstHeight - doubleBorderWidth)
val borderRadius = if (mRectRoundRadius - halfBorderWidth > 0.0f) mRectRoundRadius - halfBorderWidth else 0.0f
val bitmapRadius = if (mRectRoundRadius - mBorderWidth > 0.0f) (mRectRoundRadius - mBorderWidth).toFloat() else 0.0f
canvas.drawRoundRect(mRectBorder, borderRadius, borderRadius, mPaintBorder)
canvas.translate(mBorderWidth.toFloat(), mBorderWidth.toFloat())
canvas.drawRoundRect(mRectBitmap, bitmapRadius, bitmapRadius, mPaintBitmap)
}
} else {
super.onDraw(canvas)
}
}
private fun dip2px(dipVal: Int): Int {
val scale = resources.displayMetrics.density
return (dipVal * scale + 0.5f).toInt()
}
private fun getBitmap(drawable: Drawable?): Bitmap? {
if (drawable == null) return null
return when (drawable) {
is BitmapDrawable -> drawable.bitmap
is ColorDrawable -> {
val rect = drawable.getBounds()
val width = rect.right - rect.left
val height = rect.bottom - rect.top
val color = drawable.color
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.drawARGB(Color.alpha(color), Color.red(color), Color.green(color), Color.blue(color))
bitmap
}
else -> null
}
}
companion object {
/**
* android.widget.ImageView
*/
val TYPE_NONE = 0
/**
* 圆形
*/
val TYPE_CIRCLE = 1
/**
* 圆角矩形
*/
val TYPE_ROUNDED_RECT = 2
private val DEFAULT_TYPE = TYPE_NONE
private val DEFAULT_BORDER_COLOR = Color.TRANSPARENT
private val DEFAULT_BORDER_WIDTH = 0
private val DEFAULT_RECT_ROUND_RADIUS = 0
}
}
@@ -0,0 +1,40 @@
package com.bbgo.wanandroid.widget
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.ViewCompat
import com.google.android.material.floatingactionbutton.FloatingActionButton
/**
* Created by chenxz on 2018/5/13.
*
* FAB 行为控制器
*/
class ScaleDownShowBehavior : FloatingActionButton.Behavior {
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout,
child: FloatingActionButton,
directTargetChild: View,
target: View,
axes: Int,
type: Int): Boolean {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||
super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
}
@SuppressLint("RestrictedApi")
override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionButton, target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type)
if (dyConsumed > 0 && child.visibility == View.VISIBLE) {
child.visibility = View.INVISIBLE
} else if (dyConsumed < 0 && child.visibility != View.VISIBLE) {
child.show()
}
}
}
@@ -0,0 +1,86 @@
package com.bbgo.wanandroid.widget
import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.View
import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.OrientationHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
/**
* Created by chenxz on 2018/5/9.
*
* 简单的 RecyclerView 分割线
*/
class SpaceItemDecoration(context: Context) : RecyclerView.ItemDecoration() {
private var mDivider: Drawable? = null
private val mSectionOffsetV: Int = 0
private val mSectionOffsetH: Int = 0
private var mDrawOver = true
private var attrs: IntArray = intArrayOf(android.R.attr.listDivider)
init {
var a = context.obtainStyledAttributes(attrs)
mDivider = a.getDrawable(0)
a.recycle()
}
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDrawOver(c, parent, state)
if (mDivider != null && mDrawOver) {
draw(c, parent)
}
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDraw(c, parent, state)
if (mDivider != null && mDrawOver) {
draw(c, parent)
}
}
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
if (getOrientation(parent.layoutManager!!) == RecyclerView.VERTICAL) {
outRect.set(mSectionOffsetH, 0, mSectionOffsetH, mSectionOffsetV)
} else {
outRect.set(0, 0, mSectionOffsetV, 0)
}
}
private fun draw(c: Canvas, parent: RecyclerView) {
val left = parent.paddingLeft
val right = parent.width - parent.paddingRight
val childCount = parent.childCount
for (i in 0 until childCount) {
val child = parent.getChildAt(i)
val params = child.layoutParams as RecyclerView.LayoutParams
val top = child.bottom + params.bottomMargin + Math.round(ViewCompat.getTranslationY(child))
val bottom = top + if (mDivider!!.intrinsicHeight <= 0) 1 else mDivider!!.intrinsicHeight
mDivider?.let {
it.setBounds(left, top, right, bottom)
it.draw(c)
}
}
}
private fun getOrientation(layoutManager: RecyclerView.LayoutManager): Int {
if (layoutManager is LinearLayoutManager) {
return layoutManager.orientation
} else if (layoutManager is StaggeredGridLayoutManager) {
return layoutManager.orientation
}
return OrientationHelper.HORIZONTAL
}
}
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator">
<scale android:fromXScale="2.0" android:toXScale="1.0"
android:fromYScale="2.0" android:toYScale="1.0"
android:pivotX="50%p" android:pivotY="50%p"
android:duration="@android:integer/config_mediumAnimTime" />
</set>
@@ -0,0 +1,10 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator"
android:zAdjustment="top">
<scale android:fromXScale="1.0" android:toXScale=".5"
android:fromYScale="1.0" android:toYScale=".5"
android:pivotX="50%p" android:pivotY="50%p"
android:duration="@android:integer/config_mediumAnimTime" />
<alpha android:fromAlpha="1.0" android:toAlpha="0"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="1000"
android:fromAlpha="0"
android:interpolator="@android:anim/decelerate_interpolator"
android:toAlpha="1.0"/>
</set>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="1500"
android:fromAlpha="1.0"
android:interpolator="@android:anim/decelerate_interpolator"
android:toAlpha="0"/>
</set>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="-50%p" android:toYDelta="0" android:duration="1000"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="1000" />
</set>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="0" android:toYDelta="-50%p" android:duration="1000"/>
<alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="1000" />
</set>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/list_divider" />
</shape>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:color="@color/Red"
android:width="@dimen/dp_05"/>
<corners android:radius="@dimen/dp_2" />
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/Grey300"/>
</shape>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="@dimen/dp_05"
android:color="@color/colorAccent" />
<corners android:radius="@dimen/dp_2" />
</shape>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/colorAccent"/>
<corners android:radius="@dimen/dp_2" />
</shape>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<solid android:color="#A6000000" />
<padding android:left="8dp" android:top="4dp" android:right="8dp" android:bottom="4dp" />
<corners android:radius="2dp" />
</shape>
</item>
<item>
<shape>
<solid android:color="#99000000" />
<padding android:left="8dp" android:top="4dp" android:right="8dp" android:bottom="4dp" />
<corners android:radius="2dp" />
</shape>
</item>
</selector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
</vector>
@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108"
android:tint="#FFFFFF">
<group android:scaleX="2.39598"
android:scaleY="2.39598"
android:translateX="25.24824"
android:translateY="21.25494">
<path
android:pathData="M17.6,11.48 L19.44,8.3a0.63,0.63 0,0 0,-1.09 -0.63l-1.88,3.24a11.43,11.43 0,0 0,-8.94 0L5.65,7.67a0.63,0.63 0,0 0,-1.09 0.63L6.4,11.48A10.81,10.81 0,0 0,1 20L23,20A10.81,10.81 0,0 0,17.6 11.48ZM7,17.25A1.25,1.25 0,1 1,8.25 16,1.25 1.25,0 0,1 7,17.25ZM17,17.25A1.25,1.25 0,1 1,18.25 16,1.25 1.25,0 0,1 17,17.25Z"
android:fillColor="#FF000000"/>
</group>
</vector>
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#00BCD4"
android:alpha="0.8">
<path
android:fillColor="@android:color/white"
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
</vector>
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#9E9E9E"
android:alpha="0.8">
<path
android:fillColor="@android:color/white"
android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
</vector>
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="1024.0"
android:viewportWidth="1024.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M482,0l64,0 0,533.3 -64,0 0,-533.3Z" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M695.4,132.9l0,54.2c128,69.8 225.5,208.6 225.5,368.5 0,230.1 -184.9,416.6 -415,416.6 -230.1,0 -406,-186.6 -406,-416.6C99.8,395.7 183.4,256.9 332.7,187.1l0,-54.2C162,206.5 52.1,368 52.1,555.5c0,257 203.4,465.3 460.3,465.3 256.9,0 459.5,-208.3 459.5,-465.3C971.9,368 866,206.5 695.4,132.9z" />
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M810.67,490.67l-23.47,61.87L725.33,552.53l51.2,38.4L753.07,661.33l57.6,-42.67 57.6,42.67 -23.47,-70.4 51.2,-38.4 -61.87,0L810.67,490.67zM539.73,448l57.6,-42.67 57.6,42.67 -23.47,-70.4 51.2,-38.4 -61.87,0L597.33,277.33l-23.47,61.87L512,339.2l51.2,38.4L539.73,448zM981.33,128l-85.33,0 -32,-85.33L832,128l-85.33,0 70.4,53.33L785.07,277.33l81.07,-59.73 81.07,59.73 -32,-96L981.33,128zM362.67,339.2c0,-113.07 40.53,-215.47 106.67,-296.53C230.4,64 42.67,266.67 42.67,512c0,260.27 209.07,469.33 469.33,469.33 147.2,0 298.67,-68.27 384,-172.8 -14.93,2.13 -49.07,2.13 -64,2.13C571.73,810.67 362.67,599.47 362.67,339.2zM512,938.67C277.33,938.67 85.33,746.67 85.33,512c0,-187.73 119.47,-349.87 292.27,-405.33 -36.27,72.53 -57.6,153.6 -57.6,234.67 0,268.8 204.8,488.53 467.2,512C710.4,906.67 610.13,938.67 512,938.67z" />
</vector>
@@ -0,0 +1,8 @@
<vector android:height="24dp" android:viewportHeight="1024"
android:viewportWidth="1024" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M775,536.2 L456.8,536.2c-26,0 -47,-21 -47,-47 0,-26 21,-47 47,-47l318.2,0c26,0 47,21 47,47C822,515.2 800.8,536.2 775,536.2L775,536.2z"/>
<path android:fillColor="#FF000000" android:pathData="M775,722.2 L456.8,722.2c-26,0 -47,-21 -47,-47s21,-47 47,-47l318.2,0c26,0 47,21 47,47S800.8,722.2 775,722.2L775,722.2z"/>
<path android:fillColor="#FF000000" android:pathData="M991,875.8 L991,281.4c0,-72.2 -65.8,-65.4 -65.8,-65.4s-392.8,0.4 -371.8,0c-22.4,0.4 -33.8,-11.8 -33.8,-11.8s-15.6,-27 -43.8,-69.4c-29.4,-44.6 -63.6,-37.4 -63.6,-37.4L123,97.4C42.8,97.4 42,174.6 42,174.6L42,872c0,86 65,75.4 65,75.4l824.2,0C1000.8,947.4 991,875.8 991,875.8L991,875.8zM932,840.6c0,26.6 -21.4,48 -48,48L149,888.6c-26.6,0 -48,-21.4 -48,-48L101,343c0,-26.6 21.4,-48 48,-48L884,295c26.6,0 48,21.4 48,48L932,840.6 932,840.6z"/>
<path android:fillColor="#FF000000" android:pathData="M282.2,489.2m-50.2,0a25.1,25.1 0,1 0,100.4 0,25.1 25.1,0 1,0 -100.4,0Z"/>
<path android:fillColor="#FF000000" android:pathData="M282.2,675.2m-50.2,0a25.1,25.1 0,1 0,100.4 0,25.1 25.1,0 1,0 -100.4,0Z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M512,928c-17.6,0 -32,-14.4 -32,-32V128c0,-17.6 14.4,-32 32,-32s32,14.4 32,32v768c0,17.6 -14.4,32 -32,32zM896,928c-17.6,0 -32,-14.4 -32,-32V320c0,-17.6 14.4,-32 32,-32s32,14.4 32,32v576c0,17.6 -14.4,32 -32,32zM128,928c-17.6,0 -32,-14.4 -32,-32V512c0,-17.6 14.4,-32 32,-32s32,14.4 32,32v384c0,17.6 -14.4,32 -32,32z" />
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M433.69,392.95c101.86,0 207.8,-22.4 271.43,-65.12l0,77.76 58.95,0L764.06,235.33c0,-102.36 -170.17,-157.62 -330.28,-157.62C273.68,77.71 103.41,132.97 103.41,235.33l0,464.69c0,102.36 170.16,157.62 330.28,157.62l0,-58.84c-168.17,0 -271.43,-57.56 -271.43,-98.78L162.26,560.33c63.63,42.61 169.47,65.12 271.43,65.12l0,-58.95c-168.17,0 -271.43,-57.55 -271.43,-98.77L162.26,327.93C225.88,370.55 331.83,392.95 433.69,392.95L433.69,392.95zM433.69,136.55c168.17,0 271.43,57.56 271.43,98.78 0,41.22 -103.25,98.77 -271.43,98.77 -168.17,0 -271.43,-57.55 -271.43,-98.77C162.26,194.11 265.61,136.55 433.69,136.55L433.69,136.55zM697.75,425.21c-146.27,0 -222.74,56.26 -222.74,111.82l0,298.61c0,55.56 76.57,111.82 222.74,111.82 145.18,0 221.54,-55.46 222.74,-110.62l0.1,0L920.59,537.03C920.49,481.47 844.02,425.21 697.75,425.21L697.75,425.21zM697.75,484.06c108.14,0 163.89,37.14 163.89,52.97s-55.76,52.97 -163.89,52.97c-108.13,0 -163.89,-37.14 -163.89,-52.97S589.61,484.06 697.75,484.06L697.75,484.06zM697.75,888.61c-108.13,0 -163.89,-37.14 -163.89,-52.97l0,-71.3c37.34,20.32 92.21,33.86 163.89,33.86 71.69,0 126.46,-13.54 163.89,-33.86l0,71.3C861.64,851.47 805.88,888.61 697.75,888.61L697.75,888.61zM697.75,739.26c-108.13,0 -163.89,-37.14 -163.89,-52.97l0,-71.29c37.34,20.31 92.21,33.85 163.89,33.85 71.69,0 126.46,-13.54 163.89,-33.85l0,71.29C861.64,702.12 805.88,739.26 697.75,739.26L697.75,739.26zM697.75,739.26" />
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M550.06,106.67c6.34,0 11.82,4.5 13.06,10.75l17.19,85.8c2.97,14.83 13.57,26.97 27.84,31.94 10.47,3.63 21.12,8.04 31.59,13.12 5.89,2.84 12.22,4.25 18.56,4.25 8.28,0 16.53,-2.41 23.66,-7.15l73.09,-48.68c2.65,-1.77 5.35,-2.15 7.17,-2.15 2.54,0 6.25,0.68 9.49,3.93l53.93,53.93c4.44,4.44 5.12,11.5 1.62,16.75L778.67,342.08c-8.36,12.54 -9.49,28.57 -2.94,42.15 5.21,10.82 9.62,21.44 13.16,31.62 4.97,14.25 17.11,24.83 31.91,27.8l85.82,17.19c0.49,0.11 0.96,0.19 1.45,0.26 5.5,1.77 9.37,6.91 9.37,12.74l0,76.18c0,6.34 -4.46,11.82 -10.73,13.06l-85.82,17.13c-14.85,2.97 -26.99,13.57 -31.96,27.86 -3.63,10.47 -8.04,21.12 -13.12,31.59 -6.57,13.61 -5.46,29.67 2.92,42.24l48.62,72.94c3.5,5.27 2.79,12.33 -1.66,16.79l-53.8,53.8c-2.58,2.58 -5.99,3.99 -9.56,3.99 -2.56,0 -5.12,-0.79 -7.32,-2.24l-72.94,-48.62c-7.13,-4.76 -15.38,-7.17 -23.66,-7.17 -6.29,0 -12.63,1.39 -18.47,4.22 -10.82,5.21 -21.46,9.62 -31.62,13.16 -14.29,4.95 -24.87,17.11 -27.84,31.94l-17.22,85.89C561.98,912.81 556.5,917.33 550.17,917.33l-76.18,0c-6.36,0 -11.86,-4.5 -13.12,-10.73l-17.13,-85.82c-2.97,-14.83 -13.57,-26.99 -27.86,-31.96 -10.54,-3.65 -21.12,-8.06 -31.47,-13.08 -5.91,-2.86 -12.29,-4.29 -18.65,-4.29 -8.28,0 -16.53,2.41 -23.66,7.17l-72.83,48.55c-2.28,1.51 -4.86,2.3 -7.47,2.3 -2.54,0 -6.25,-0.68 -9.49,-3.9L198.4,771.65c-4.46,-4.44 -5.14,-11.48 -1.62,-16.77l48.55,-72.94c8.38,-12.59 9.47,-28.67 2.86,-42.28 -5.03,-10.37 -9.43,-20.95 -13.08,-31.47 -4.95,-14.29 -17.11,-24.9 -31.94,-27.86l-85.91,-17.22c-6.21,-1.24 -10.71,-6.72 -10.71,-13.06l0,-76.18c0,-6.36 4.5,-11.86 10.73,-13.1l85.87,-17.13c14.85,-2.97 27.01,-13.57 31.96,-27.86 3.65,-10.52 8.04,-21.12 13.08,-31.47 6.61,-13.61 5.5,-29.72 -2.88,-42.3l-48.62,-72.94c-3.5,-5.25 -2.79,-12.33 1.66,-16.79L252.27,198.4c3.24,-3.24 6.95,-3.9 9.49,-3.9 2.65,0 5.14,0.77 7.36,2.24l72.87,48.62c7.13,4.76 15.38,7.17 23.68,7.17 6.36,0 12.74,-1.41 18.65,-4.29 10.33,-5.01 20.93,-9.43 31.47,-13.1 14.25,-4.97 24.83,-17.11 27.8,-31.91l17.19,-85.78C462.02,111.21 467.54,106.67 473.88,106.67L550.06,106.67M511.98,725.33c117.63,0 213.33,-95.7 213.33,-213.33s-95.7,-213.33 -213.33,-213.33 -213.33,95.7 -213.33,213.33S394.35,725.33 511.98,725.33M550.06,64l-76.18,0c-26.67,0 -49.69,18.88 -54.95,45.03l-17.19,85.8c-12.37,4.31 -24.43,9.37 -36.07,15.02l-72.87,-48.62c-9.51,-6.34 -20.27,-9.41 -31.04,-9.41 -14.51,0 -28.8,5.55 -39.66,16.41l-53.89,53.89C149.33,241 146.43,270.57 161.22,292.76l48.62,72.94c-5.65,11.65 -10.71,23.7 -15.02,36.14l-85.87,17.13c-26.22,5.21 -45.03,28.22 -45.03,54.95l0,76.18c0,26.67 18.88,49.69 45.03,54.89l85.87,17.19c4.31,12.44 9.34,24.47 15.02,36.14l-48.55,72.94c-14.78,22.19 -11.88,51.82 7,70.63l53.89,53.89c10.86,10.86 25.15,16.41 39.66,16.41 10.75,0 21.57,-3.14 31.04,-9.41l72.94,-48.62c11.65,5.65 23.7,10.69 36.14,15.02l17.13,85.82C424.26,941.18 447.27,960 473.98,960l76.18,0c26.67,0 49.69,-18.88 54.89,-45.1l17.19,-85.82c12.44,-4.31 24.47,-9.41 36.14,-15.02l72.94,48.62c9.47,6.34 20.33,9.41 30.98,9.41 14.51,0 28.8,-5.61 39.66,-16.41l53.89,-53.89c18.88,-18.88 21.78,-48.45 7,-70.63l-48.62,-72.94c5.65,-11.71 10.71,-23.74 15.02,-36.18l85.82,-17.13c26.15,-5.16 45.03,-28.22 45.03,-54.89l0,-76.18c0,-26.67 -18.88,-49.69 -45.1,-54.95l0,0.11 -85.82,-17.19c-4.31,-12.37 -9.41,-24.43 -15.02,-36.07l48.62,-72.94c14.78,-22.23 11.88,-51.82 -7,-70.63l-53.89,-53.89C791.04,157.44 776.75,151.89 762.24,151.89c-10.71,0 -21.57,3.07 -30.98,9.41L658.35,209.86c-11.71,-5.65 -23.74,-10.71 -36.18,-15.02l-17.19,-85.8C599.74,82.82 576.73,64 550.06,64L550.06,64zM511.98,682.67c-94.25,0 -170.67,-76.42 -170.67,-170.67s76.42,-170.67 170.67,-170.67 170.67,76.42 170.67,170.67S606.23,682.67 511.98,682.67L511.98,682.67z" />
</vector>
@@ -0,0 +1,7 @@
<vector android:height="24dp" android:viewportHeight="1024"
android:viewportWidth="1024" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#ffffff" android:pathData="M814.93,900.27H177.07c-46.93,0 -85.33,-38.4 -85.33,-85.33v-597.33c0,-46.93 38.4,-85.33 85.33,-85.33h458.67c12.8,0 21.33,8.53 21.33,21.33s-8.53,21.33 -21.33,21.33H177.07c-23.47,0 -42.67,19.2 -42.67,42.67v597.33c0,23.47 19.2,42.67 42.67,42.67h637.87c23.47,0 42.67,-19.2 42.67,-42.67v-204.8c0,-12.8 8.53,-21.33 21.33,-21.33s21.33,8.53 21.33,21.33v204.8c0,46.93 -38.4,85.33 -85.33,85.33zM953.6,339.2c-6.4,0 -12.8,-2.13 -17.07,-6.4l-151.47,-172.8c-8.53,-8.53 -6.4,-21.33 2.13,-29.87 8.53,-8.53 21.33,-6.4 29.87,2.13l151.47,172.8c8.53,8.53 6.4,21.33 -2.13,29.87 -4.27,2.13 -8.53,4.27 -12.8,4.27z"/>
<path android:fillColor="#ffffff" android:pathData="M802.13,512c-4.27,0 -10.67,-2.13 -14.93,-4.27 -8.53,-8.53 -10.67,-21.33 -2.13,-29.87l151.47,-172.8c8.53,-8.53 21.33,-10.67 29.87,-2.13 8.53,8.53 10.67,21.33 2.13,29.87l-151.47,172.8c-2.13,4.27 -8.53,6.4 -14.93,6.4z"/>
<path android:fillColor="#ffffff" android:pathData="M514.13,552.53c-2.13,0 -6.4,0 -8.53,-2.13 -10.67,-4.27 -14.93,-17.07 -10.67,-27.73C554.67,377.6 697.6,283.73 853.33,283.73c29.87,0 59.73,4.27 87.47,10.67 10.67,2.13 19.2,14.93 17.07,25.6 -2.13,10.67 -14.93,19.2 -25.6,17.07 -25.6,-6.4 -51.2,-8.53 -78.93,-8.53 -140.8,0 -266.67,83.2 -322.13,211.2 -2.13,6.4 -10.67,12.8 -17.07,12.8z"/>
<path android:fillColor="#ffffff" android:pathData="M484.27,650.67m-32,0a32,32 0,1 0,64 0,32 32,0 1,0 -64,0Z"/>
</vector>
@@ -0,0 +1,6 @@
<vector android:height="24dp" android:viewportHeight="1024"
android:viewportWidth="1024" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#000000" android:pathData="M249.6,484.27h4.27c10.67,0 19.2,-6.4 21.33,-17.07 17.07,-76.8 70.4,-140.8 142.93,-172.8 10.67,-4.27 14.93,-17.07 10.67,-27.73s-17.07,-14.93 -27.73,-10.67c-85.33,36.27 -147.2,110.93 -166.4,202.67 -4.27,12.8 2.13,23.47 14.93,25.6z"/>
<path android:fillColor="#000000" android:pathData="M998.4,384c-14.93,-49.07 -66.13,-81.07 -151.47,-98.13C776.53,177.07 652.8,106.67 512,106.67 300.8,106.67 125.87,270.93 108.8,477.87 40.53,533.33 10.67,590.93 25.6,640c14.93,51.2 72.53,83.2 151.47,96C247.47,844.8 371.2,917.33 512,917.33c211.2,0 386.13,-164.27 403.2,-371.2 68.27,-55.47 98.13,-113.07 83.2,-162.13zM512,149.33c200.53,0 362.67,162.13 362.67,362.67v12.8c-32,23.47 -70.4,46.93 -115.2,68.27 -14.93,-40.53 -53.33,-68.27 -98.13,-68.27 -59.73,0 -106.67,46.93 -106.67,106.67 0,12.8 2.13,23.47 6.4,34.13h-4.27c-140.8,36.27 -266.67,44.8 -354.13,32C168.53,644.27 149.33,580.27 149.33,512c0,-200.53 162.13,-362.67 362.67,-362.67zM597.33,631.47c0,-36.27 27.73,-64 64,-64s64,27.73 64,64 -27.73,64 -64,64 -64,-29.87 -64,-64zM66.13,629.33c-6.4,-25.6 8.53,-57.6 42.67,-93.87 2.13,53.33 17.07,104.53 38.4,151.47 -44.8,-12.8 -74.67,-34.13 -81.07,-57.6zM512,874.67c-110.93,0 -211.2,-51.2 -277.33,-130.13 17.07,2.13 34.13,2.13 51.2,2.13 87.47,0 187.73,-12.8 281.6,-38.4 4.27,-2.13 10.67,-2.13 14.93,-4.27 19.2,21.33 46.93,34.13 78.93,34.13 57.6,0 102.4,-44.8 106.67,-102.4 38.4,-17.07 72.53,-36.27 100.27,-55.47C836.27,748.8 689.07,874.67 512,874.67zM915.2,488.53c-2.13,-53.33 -17.07,-104.53 -38.4,-151.47 44.8,12.8 74.67,34.13 81.07,59.73 8.53,25.6 -8.53,57.6 -42.67,91.73z"/>
<path android:fillColor="#000000" android:pathData="M516.27,251.73m-32,0a32,32 0,1 0,64 0,32 32,0 1,0 -64,0Z"/>
</vector>
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M682.67,234.67L213.33,234.67c-8.53,0 -21.33,-8.53 -21.33,-21.33s8.53,-21.33 21.33,-21.33h469.33c12.8,0 21.33,8.53 21.33,21.33s-8.53,21.33 -21.33,21.33zM512,448L213.33,448c-12.8,0 -21.33,-8.53 -21.33,-21.33s8.53,-21.33 21.33,-21.33h298.67c12.8,0 21.33,8.53 21.33,21.33s-8.53,21.33 -21.33,21.33zM704,512c-140.8,0 -256,115.2 -256,256s115.2,256 256,256 256,-115.2 256,-256 -110.93,-256 -256,-256zM704,981.33c-119.47,0 -213.33,-93.87 -213.33,-213.33s93.87,-213.33 213.33,-213.33 213.33,93.87 213.33,213.33 -93.87,213.33 -213.33,213.33z" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M435.2,896H149.33c-25.6,0 -42.67,-17.07 -42.67,-42.67V85.33c0,-25.6 17.07,-42.67 42.67,-42.67h597.33c25.6,0 42.67,17.07 42.67,42.67v396.8c12.8,4.27 29.87,8.53 42.67,17.07V85.33c0,-46.93 -38.4,-85.33 -85.33,-85.33h-597.33c-46.93,0 -85.33,38.4 -85.33,85.33v768c0,46.93 38.4,85.33 85.33,85.33h311.47c-8.53,-12.8 -17.07,-29.87 -25.6,-42.67z" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M704,793.6c-12.8,0 -21.33,-8.53 -21.33,-21.33v-128c0,-12.8 8.53,-21.33 21.33,-21.33s21.33,8.53 21.33,21.33v128c0,12.8 -8.53,21.33 -21.33,21.33z" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M853.33,772.27c0,12.8 -8.53,21.33 -21.33,21.33h-128c-12.8,0 -21.33,-8.53 -21.33,-21.33s8.53,-21.33 21.33,-21.33h128c12.8,0 21.33,8.53 21.33,21.33z" />
</vector>
@@ -0,0 +1,8 @@
<vector android:height="24dp" android:viewportHeight="1024"
android:viewportWidth="1024" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M499.06,347.96c27.69,0 45.75,-18.66 45.75,-45.75 0,-27.09 -18.66,-45.75 -45.75,-45.75 -26.49,0 -54.78,17.46 -54.78,45.75C444.28,329.9 472.57,347.96 499.06,347.96z"/>
<path android:fillColor="#FF000000" android:pathData="M299.8,301.6c0,-27.09 -18.66,-45.75 -45.75,-45.75 -26.49,0 -54.78,17.46 -54.78,45.75 0,28.29 28.29,45.75 54.78,45.75C281.74,347.96 299.8,329.29 299.8,301.6z"/>
<path android:fillColor="#FF000000" android:pathData="M1024,625.48c0,-67.42 -30.1,-132.44 -84.28,-183.01 -53.58,-49.97 -125.22,-80.67 -201.07,-87.29 0,-1.2 0,-2.41 0,-3.61 -31.91,-148.09 -190.83,-260.06 -370.23,-260.06C164.95,91.5 0,232.97 0,406.95 0,499.66 46.96,583.94 136.65,649.56l-30.7,92.71c-2.41,7.22 0,14.45 5.42,19.26 5.42,4.82 13.85,5.42 19.87,2.41l116.79,-58.39 13.85,3.01 0.6,0c36.12,7.22 67.42,13.85 105.95,13.85 7.22,0 34.92,-2.41 43.95,-6.62 44.55,108.36 163.74,181.2 297.39,181.2 33.71,0 68.03,-7.83 102.34,-16.25l89.7,48.76c2.41,1.2 5.42,2.41 8.43,2.41 4.21,0 8.43,-1.2 11.44,-4.21 6.02,-4.82 7.83,-12.04 6.02,-19.26l-22.88,-75.25C981.26,772.36 1024,698.32 1024,625.48zM864.47,803.67c-6.02,4.21 -8.43,12.04 -6.02,18.66l12.64,42.74 -54.18,-29.5c-2.41,-1.2 -5.42,-2.41 -8.43,-2.41 -1.2,0 -3.01,0 -4.21,0.6 -31.3,7.83 -63.81,16.25 -95.72,16.25 -146.29,0 -265.48,-100.53 -265.48,-223.94 0,-123.41 119.2,-223.94 265.48,-223.94 143.88,0 264.88,102.34 264.88,223.94C974.03,687.48 935.51,750.69 864.47,803.67zM181.8,640.53c2.41,-7.22 0,-15.05 -6.62,-19.87C90.9,561.66 48.16,490.03 48.16,407.55 48.16,258.86 191.44,137.86 367.82,137.86c154.11,0 291.97,93.91 322.07,218.53 -164.35,3.61 -297.39,120.4 -297.39,261.87 0,18.66 2.41,37.93 7.83,57.19 -0.6,0 -1.81,0 -2.41,0 -9.63,0.6 -19.87,1.2 -30.1,1.2 -34.31,0 -63.81,-6.02 -95.72,-12.64l-19.26,-3.61c-3.61,-0.6 -7.83,0 -11.44,1.2l-80.07,40.33L181.8,640.53z"/>
<path android:fillColor="#FF000000" android:pathData="M612.83,509.89c-18.66,0 -37.32,18.66 -37.32,37.32 0,18.66 18.66,37.32 37.32,37.32 28.29,0 45.75,-19.26 45.75,-37.32C658.59,529.16 641.13,509.89 612.83,509.89z"/>
<path android:fillColor="#FF000000" android:pathData="M804.87,509.89c-18.66,0 -36.72,18.66 -36.72,37.32 0,18.66 18.06,37.32 36.72,37.32 28.29,0 45.75,-19.26 45.75,-37.32C850.62,529.16 833.17,509.89 804.87,509.89z"/>
</vector>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/colorPrimary" android:state_checked="true"/>
<item android:color="@color/textColorPrimary" android:state_checked="false"/>
</selector>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<item>
<shape>
<size
android:width="360dp"
android:height="360dp" />
<solid android:color="@color/colorPrimary" />
</shape>
</item>
<!--<item>
<bitmap
android:gravity="center"
android:src="@drawable/splash_logo" />
</item>-->
</layer-list>
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/action_bar"
layout="@layout/toolbar"/>
<include layout="@layout/container"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
style="@style/Widget.Design.BottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom"
android:background="@color/viewBackground"
app:elevation="16dp"
app:itemIconTint="@drawable/nav_item_color_state"
app:itemTextColor="@drawable/nav_item_color_state"
app:layout_behavior=".widget.BottomNavigationBehavior"
app:menu="@menu/bottom_navigation_main"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floating_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:background="@color/colorPrimary"
android:layout_marginEnd="@dimen/dp_20"
android:layout_marginRight="@dimen/dp_20"
android:layout_marginBottom="@dimen/dp_60"
android:src="@drawable/ic_arrow_upward_white_24dp"
app:layout_behavior=".widget.ScaleDownShowBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="@color/viewBackground"
android:fitsSystemWindows="true"
app:itemIconTint="@color/item_nav_color_tv"
app:itemTextColor="@color/item_nav_color_tv"
app:menu="@menu/nav_menu" />
</androidx.drawerlayout.widget.DrawerLayout>
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_logo"
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_marginTop="@dimen/dp_200"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:scaleType="fitXY"
android:src="@drawable/splash_logo" />
<TextView
android:id="@+id/tv_app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_logo"
android:layout_marginTop="10dp"
android:text="@string/app_name"
android:textColor="@android:color/black"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_splash_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/tv_version_name"
android:text="@string/splash_text"
android:textColor="@android:color/white"
android:layout_above="@+id/tv_version_name"
android:layout_marginBottom="10dp"
android:textSize="14sp" />
<TextView
android:id="@+id/tv_version_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="60dp"
tools:text="v1.0"
android:textColor="@color/White"
android:textSize="12sp" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,8 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/viewBackground"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_view_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
android:gravity="bottom"
android:orientation="vertical"
android:paddingLeft="@dimen/dp_16"
android:paddingTop="@dimen/dp_36"
android:paddingRight="@dimen/dp_16"
android:paddingBottom="@dimen/dp_10"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<ImageView
android:id="@+id/iv_rank"
android:layout_width="@dimen/dp_20"
android:layout_height="@dimen/dp_20"
android:layout_gravity="end"
android:src="@drawable/ic_rank_white_24dp" />
<com.bbgo.wanandroid.widget.CircleImageView
android:layout_width="@dimen/avatar_width"
android:layout_height="@dimen/avatar_height"
android:layout_gravity="center_horizontal"
app:borderColor="@color/White"
app:borderWidth="2dp"
app:srcCompat="@drawable/ic_default_avatar"
app:type="circle" />
<TextView
android:id="@+id/tv_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/dp_12"
android:text="@string/go_login"
android:textColor="@color/White"
android:textSize="@dimen/sp_16"
android:textStyle="bold" />
<LinearLayout
android:id="@+id/user_id_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/dp_8"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/nav_id"
android:textColor="@color/Grey100"
android:textSize="@dimen/sp_12" />
<TextView
android:id="@+id/tv_user_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/nav_line_4"
android:textColor="@color/Grey100"
android:textSize="@dimen/sp_12" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/dp_8"
android:gravity="center"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/nav_grade"
android:textColor="@color/Grey100"
android:textSize="@dimen/sp_12" />
<TextView
android:id="@+id/tv_user_grade"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/nav_line_2"
android:textColor="@color/Grey100"
android:textSize="@dimen/sp_12" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginStart="@dimen/dp_8"
android:text="@string/nav_rank"
android:textColor="@color/Grey100"
android:textSize="@dimen/sp_12"
android:layout_marginLeft="@dimen/dp_8" />
<TextView
android:id="@+id/tv_user_rank"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/nav_line_2"
android:textColor="@color/Grey100"
android:textSize="@dimen/sp_12" />
</LinearLayout>
</LinearLayout>
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="@style/AppTheme.AppBarOverlay"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:titleTextAppearance="@style/Toolbar.TitleText">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:singleLine="true"
android:textColor="@color/viewBackground"
android:textSize="@dimen/sp_18" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_home"
android:enabled="true"
android:icon="@drawable/ic_home_black_24dp"
android:title="@string/home"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_wechat"
android:enabled="true"
android:icon="@drawable/ic_wechat_black_24dp"
android:title="@string/wechat"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_system"
android:enabled="true"
android:icon="@drawable/ic_apps_black_24dp"
android:title="@string/knowledge_system"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_square"
android:enabled="true"
android:icon="@drawable/ic_square_black_24dp"
android:title="@string/square"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_project"
android:enabled="true"
android:icon="@drawable/ic_project_black_24dp"
android:title="@string/project"
app:showAsAction="ifRoom" />
</menu>
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<group android:id="@+id/other">
<!--<item
android:id="@+id/nav_score"
android:icon="@drawable/ic_score_white_24dp"
android:title="@string/nav_my_score"
app:actionViewClass="android.widget.TextView" />-->
<item
android:id="@+id/nav_collect"
android:icon="@drawable/ic_like_not"
android:title="@string/nav_my_collect" />
<item
android:id="@+id/nav_video"
android:icon="@drawable/ic_share_white_24dp"
android:title="@string/my_share" />
<!--<item
android:id="@+id/nav_todo"
android:icon="@drawable/ic_todo_default_24dp"
android:title="@string/nav_todo" />-->
<!--<item
android:id="@+id/nav_night_mode"
android:icon="@drawable/ic_night_24dp"
android:title="@string/nav_night_mode" />-->
<item
android:id="@+id/nav_setting"
android:icon="@drawable/ic_setting_24dp"
android:title="@string/nav_setting" />
<item
android:id="@+id/nav_logout"
android:icon="@drawable/ic_logout_white_24dp"
android:title="@string/login" />
</group>
</menu>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/colorPrimary"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#35464e</color>
<color name="colorPrimaryDark">#212a2f</color>
<color name="colorAccent">@color/Cyan</color>
<color name="textColorPrimary">#616161</color>
<color name="viewBackground">@color/Grey800</color>
<color name="windowBackground">@color/Grey900</color>
<color name="line_divider">@color/Grey700</color>
<color name="list_divider">@color/Grey600</color>
<color name="mask_color">#111111</color>
<color name="common_color">@color/Grey300</color>
<color name="item_title">@color/Grey300</color>
<color name="item_author">@color/Grey500</color>
<color name="item_desc">@color/Grey500</color>
<color name="item_time">@color/Grey500</color>
<color name="item_date">@color/Grey500</color>
<color name="item_chapter">@color/Grey500</color>
<color name="item_nav_color_tv">@color/Grey500</color>
<color name="vertical_tab_layout_bg">@color/Grey800</color>
<color name="vertical_tab_layout_indicator_color">@color/Grey600</color>
<color name="item_flow_layout_bg">@color/Grey700</color>
<color name="arrow_color">@color/Grey400</color>
<color name="sticky_header_bg">#515151</color>
</resources>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="textColorPrimary" format="color" />
<attr name="viewBackground" format="color" />
</resources>
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">@color/Cyan</color>
<color name="colorPrimaryDark">@color/transparent</color>
<color name="colorAccent">@color/Cyan</color>
<color name="textColorPrimary">#616161</color>
<color name="viewBackground">@color/White</color>
<color name="windowBackground">@color/Grey100</color>
<color name="line_divider">@color/Grey300</color>
<color name="list_divider">@color/Grey400</color>
<color name="mask_color">#f5f5f5</color>
<color name="transparent">#00000000</color>
<!-- item -->
<color name="common_color">#19191B</color>
<color name="item_title">#19191B</color>
<color name="item_author">@color/Grey700</color>
<color name="item_desc">@color/Grey600</color>
<color name="item_time">@color/Grey600</color>
<color name="item_date">@color/Grey600</color>
<color name="item_chapter">@color/Grey600</color>
<color name="item_tag_tv">#ffFFFFFF</color>
<color name="item_nav_color_tv">#19191B</color>
<color name="color_title_bg">#CCFFFFFF</color>
<color name="color_about_tv">@color/Grey600</color>
<color name="transparent_75">#ba000000</color>
<color name="vertical_tab_layout_bg">@color/Grey100</color>
<color name="vertical_tab_layout_indicator_color">@color/White</color>
<color name="item_flow_layout_bg">@color/Grey200</color>
<color name="arrow_color">@color/Grey700</color>
<color name="sticky_header_bg">@color/Grey100</color>
<color name="light_red">#e26d2e</color>
<color name="deep_red">#dc3b1f</color>
<color name="while_most_color">#e4e4e4</color>
<color name="title_black">#222222</color>
<color name="grey_search">#FF999999</color>
<color name="White">#FFFFFF</color>
<color name="Black">#000000</color>
<color name="Red">#f44336</color>
<color name="Pink">#e91e63</color>
<color name="Purple">#9c27b0</color>
<color name="Deep_Purple">#673ab7</color>
<color name="Indigo">#3f51b5</color>
<color name="Blue">#2196f3</color>
<color name="Light_Blue">#03a9f4</color>
<color name="Cyan">#00bcd4</color>
<color name="Teal">#009688</color>
<color name="Green">#4caf50</color>
<color name="Light_Green">#8bc34a</color>
<color name="Lime">#cddc39</color>
<color name="Yellow">#ffeb3b</color>
<color name="Amber">#ffc107</color>
<color name="Orange">#ff9800</color>
<color name="Deep_Orange">#ff5722</color>
<color name="Brown">#795548</color>
<color name="Grey">#9e9e9e</color>
<color name="Blue_Grey">#607d8b</color>
<color name="Grey50">#FAFAFA</color>
<color name="Grey100">#F5F5F5</color>
<color name="Grey200">#EEEEEE</color>
<color name="Grey300">#E0E0E0</color>
<color name="Grey400">#bdbdbd</color>
<color name="Grey500">#9e9e9e</color>
<color name="Grey600">#757575</color>
<color name="Grey700">#616161</color>
<color name="Grey800">#424242</color>
<color name="Grey900">#212121</color>
</resources>
@@ -0,0 +1,85 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="avatar_width">80dp</dimen>
<dimen name="avatar_height">80dp</dimen>
<dimen name="appbar_layout_height">260dp</dimen>
<!-- item -->
<dimen name="item_tv_title">16sp</dimen>
<dimen name="item_tv_content">14sp</dimen>
<dimen name="item_tv_date">12sp</dimen>
<dimen name="item_tv_tag">12sp</dimen>
<dimen name="item_tv_author">12sp</dimen>
<dimen name="item_tv_via">12sp</dimen>
<dimen name="item_content_padding">10dp</dimen>
<dimen name="item_img_width">120dp</dimen>
<dimen name="item_img_height">90dp</dimen>
<dimen name="textSize_small_10">10sp</dimen>
<dimen name="textSize_small">12sp</dimen>
<dimen name="textSize_content">14sp</dimen>
<dimen name="textSize_titleSmall">16sp</dimen>
<dimen name="textSize_title">18sp</dimen>
<!-- dp -->
<dimen name="dp_05">0.5dp</dimen>
<dimen name="dp_1">1dp</dimen>
<dimen name="dp_2">2dp</dimen>
<dimen name="dp_3">3dp</dimen>
<dimen name="dp_4">4dp</dimen>
<dimen name="dp_5">5dp</dimen>
<dimen name="dp_6">6dp</dimen>
<dimen name="dp_8">8dp</dimen>
<dimen name="dp_10">10dp</dimen>
<dimen name="dp_12">12dp</dimen>
<dimen name="dp_16">16dp</dimen>
<dimen name="dp_18">18dp</dimen>
<dimen name="dp_20">20dp</dimen>
<dimen name="dp_24">24dp</dimen>
<dimen name="dp_26">26dp</dimen>
<dimen name="dp_28">28dp</dimen>
<dimen name="dp_30">30dp</dimen>
<dimen name="dp_36">36dp</dimen>
<dimen name="dp_46">46dp</dimen>
<dimen name="dp_50">50dp</dimen>
<dimen name="dp_54">54dp</dimen>
<dimen name="dp_60">60dp</dimen>
<dimen name="dp_80">80dp</dimen>
<dimen name="dp_90">90dp</dimen>
<dimen name="dp_100">100dp</dimen>
<dimen name="dp_110">110dp</dimen>
<dimen name="dp_168">168dp</dimen>
<dimen name="dp_180">180dp</dimen>
<dimen name="dp_200">200dp</dimen>
<dimen name="dp_230">230dp</dimen>
<dimen name="dp_240">240dp</dimen>
<dimen name="dp_260">260dp</dimen>
<dimen name="dp_280">280dp</dimen>
<dimen name="dp_300">300dp</dimen>
<!-- sp -->
<dimen name="sp_8">8sp</dimen>
<dimen name="sp_9">9sp</dimen>
<dimen name="sp_10">10sp</dimen>
<dimen name="sp_12">12sp</dimen>
<dimen name="sp_13">13sp</dimen>
<dimen name="sp_14">14sp</dimen>
<dimen name="sp_15">15sp</dimen>
<dimen name="sp_16">16sp</dimen>
<dimen name="sp_17">17sp</dimen>
<dimen name="sp_18">18sp</dimen>
<dimen name="sp_20">20sp</dimen>
<dimen name="sp_22">22sp</dimen>
<dimen name="sp_24">24sp</dimen>
<dimen name="sp_40">40sp</dimen>
<dimen name="sp_50">50sp</dimen>
<!--BottomNavigationView 的选中没有选中的字体大小-->
<dimen name="design_bottom_navigation_active_text_size">@dimen/sp_13</dimen>
<dimen name="design_bottom_navigation_text_size">@dimen/sp_12</dimen>
</resources>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="expandable_text"/>
<item type="id" name="expand_collapse"/>
<item name="statusbarutil_fake_status_bar_view" type="id" />
<item name="statusbarutil_translucent_view" type="id" />
</resources>
@@ -0,0 +1,182 @@
<resources>
<string name="app_name">玩Android</string>
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
<string name="home">首页</string>
<string name="knowledge_system">体系</string>
<string name="navigation">导航</string>
<string name="project">项目</string>
<string name="wechat">公众号</string>
<string name="exit_tip">再按一次退出程序</string>
<string name="share_article_url">%1$s分享【%2$s】:%3$s</string>
<string name="confirm">确定</string>
<string name="cancel">取消</string>
<string name="no_data">暂无数据</string>
<string name="no_network">网络异常</string>
<string name="no_network_tip">请检查网络后点击重新加载</string>
<string name="data_error">数据获取失败</string>
<string name="network_unavailable_tip">网络连接不可用,请检查网络设置!</string>
<!-- Menu Action -->
<string name="action_search">搜索</string>
<string name="action_share">分享</string>
<string name="action_like">收藏</string>
<string name="action_browser">用浏览器打开</string>
<!-- Nav Action -->
<string name="nav_my_collect">我的收藏</string>
<string name="nav_setting">系统设置</string>
<string name="nav_about_us">关于我们</string>
<string name="nav_logout">退出登录</string>
<string name="nav_night_mode">夜间模式</string>
<string name="nav_todo">TODO</string>
<string name="nav_my_score">我的积分</string>
<string name="nav_id">ID:</string>
<string name="nav_grade">等级:</string>
<string name="nav_rank">排名:</string>
<string name="score">积分</string>
<string name="score_detail">积分明细</string>
<string name="help_label">帮助</string>
<string name="score_list">积分排行榜</string>
<!-- Login -->
<string name="username">用户名</string>
<string name="password">密码</string>
<string name="go_login">去登陆</string>
<string name="login">登录</string>
<string name="register">注册</string>
<string name="no_account">没有账号?去注册</string>
<string name="username_not_empty">用户名不能为空</string>
<string name="password_not_empty">密码不能为空</string>
<string name="have_account">已有账户?去登录</string>
<string name="enter_password_again">再输入一次密码</string>
<string name="confirm_password_not_empty">确认密码不能为空</string>
<string name="password_cannot_match">两次输入的密码不相同</string>
<string name="login_success">登录成功</string>
<string name="login_fail">登录失败</string>
<string name="register_success">注册成功</string>
<string name="register_fail">注册失败</string>
<string name="confirm_logout">确认退出登录?</string>
<string name="logout_success">已退出登录</string>
<string name="logout_ing">正在退出登录</string>
<string name="login_tint">请先登录</string>
<string name="login_ing">登录中...</string>
<string name="register_ing">注册中...</string>
<string name="user_login">用户登录</string>
<string name="login_tip">请使用WanAndroid账号登录!</string>
<string name="user_register">注册用户</string>
<string name="register_tip">用户注册后才可以登录!</string>
<!-- Home -->
<string name="collect_success">收藏成功</string>
<string name="cancel_collect_success">已取消收藏</string>
<string name="new_fresh"></string>
<string name="top_tip">置顶</string>
<!-- Collect -->
<string name="collect">收藏</string>
<string name="anonymous">匿名</string>
<!-- Search -->
<string name="search_tint">发现更多干货</string>
<string name="hot_search">热门搜索</string>
<string name="clear_all">清空</string>
<string name="history_search">搜索历史</string>
<string name="search_null_tint">快来搜点干货吧</string>
<!-- AboutUs -->
<string name="about_us">关于我们</string>
<string name="changelog">更新日志</string>
<string name="about_developers_label">主要开发者</string>
<string name="source_code">源代码</string>
<string name="about_libraries_label">开源许可</string>
<string name="about_libraries_summary">查看开源许可信息</string>
<string name="copyright">版权声明</string>
<string name="copyright_summary">仅作个人及非商业用途</string>
<string name="version">版本</string>
<string name="current_version">当前版本&#160;</string>
<string name="official_website">官方网站</string>
<string name="official_website_url">https://www.wanandroid.com</string>
<!-- Setting -->
<string name="setting">设置</string>
<string name="basic_setting">基本设置</string>
<string name="switch_photo_mode">无图模式</string>
<string name="switch_no_photo_mode_summary">开启后在非Wifi环境下不加载图片</string>
<string name="switch_show_top_summary">开启后将不会显示首页置顶文章</string>
<string name="switch_show_top_title">是否显示首页置顶文章</string>
<string name="switch_night_mode">夜间模式</string>
<string name="switch_night_mode_summary">切换夜间皮肤</string>
<string name="clear_cache">清除缓存</string>
<string name="clear_cache_successfully">清理成功</string>
<string name="other_setting">其他设置</string>
<string name="about_settings">关于</string>
<string name="choose_theme_color">主题颜色</string>
<string name="choose_theme_color_summary">自定义主题颜色</string>
<string name="back">返回</string>
<string name="done">完成</string>
<string name="custom">自定义</string>
<string name="nav_bar_coloration">导航栏着色</string>
<string name="nav_bar_coloration_summary">导航栏颜色跟随主题颜色</string>
<string name="choose_icon">更换图标</string>
<string name="choose_icon_summary">重启App生效(可以将App从最近任务列表删除来重启)</string>
<string name="slidable_title">滑动返回</string>
<string name="slidable_summary">类似 iOS 左侧滑动返回</string>
<string name="auto_night_mode">自动切换夜间模式</string>
<string name="auto_night_mode_summary">设置切换的时间</string>
<string name="auto_night_mode_subsummary">重启App生效</string>
<string name="auto_night_category">时间</string>
<string name="auto_night_start">夜间模式开始时间</string>
<string name="auto_day_start">日间模式开始时间</string>
<string name="check_version_fail">检查新版本失败,请稍后重试</string>
<string name="check_version_ing">正在检查,请稍候...</string>
<string name="check_no_version">当前已是最新版本了</string>
<string name="scan_code_download">扫码下载</string>
<string name="scan_share_tip">好用的话推荐给你的小伙伴吧</string>
<string name="scan_code_tip">好用的话推荐给你的小伙伴吧\n( 建议使用浏览器扫描二维码下载 ^_^ )</string>
<!-- TODO工具 -->
<string name="delete_success">删除成功</string>
<string name="delete">删除</string>
<string name="mark_done">标为已完成</string>
<string name="restore">复原</string>
<string name="completed">已完成</string>
<string name="confirm_delete">确认删除</string>
<string name="add">新增</string>
<string name="notodo">待办</string>
<string name="save">保存</string>
<string name="non_compulsory">非必填</string>
<string name="details">详情:</string>
<string name="compulsory">必填</string>
<string name="title">标题:</string>
<string name="date">日期:</string>
<string name="title_tip">标题不能为空!</string>
<string name="save_success">保存成功</string>
<string name="edit">编辑</string>
<string name="see">查看</string>
<string name="save_ing">正在保存...</string>
<string name="switch_label">切换</string>
<string name="priority_0">一般</string>
<string name="priority_1">重要</string>
<!-- 广场 -->
<string name="square">广场</string>
<string name="my_share">我的分享</string>
<string name="share_article">分享文章</string>
<string name="share_article_title">文章标题</string>
<string name="share_article_title_tip">100字以内</string>
<string name="share_article_link">文章链接</string>
<string name="share_article_link_tip">例如:https://www.wanandroid.com</string>
<string name="share_success">分享成功</string>
<string name="share_article_delete">已删除分享的文章</string>
<string name="audited">待审核</string>
<string name="submit_ing">正在提交...</string>
<string name="nav_line_4" translatable="false">----</string>
<string name="nav_line_2" translatable="false">--</string>
<string name="splash_text">每日精选文章和干货,让你大开眼界</string>
</resources>
@@ -0,0 +1,130 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="textColorPrimary">@color/textColorPrimary</item>
<item name="viewBackground">@color/viewBackground</item>
<!--关闭启动窗口-->
<item name="android:windowDisablePreview">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="windowNoTitle">true</item>
<item name="windowActionBar">true</item>
<item name="android:listDivider">@drawable/bg_divider</item>
<item name="android:windowBackground">@drawable/splash_bg</item>
</style>
<style name="SplashTheme" parent="AppTheme">
<item name="android:windowBackground">@drawable/splash_bg</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
<style name="MyTabLayoutStyle" parent="Base.Widget.Design.TabLayout">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:background">@color/colorPrimary</item>
<item name="tabIndicatorColor">@color/viewBackground</item>
<item name="tabIndicatorHeight">@dimen/dp_1</item>
<item name="tabSelectedTextColor">@color/viewBackground</item>
<item name="tabTextAppearance">@style/MyTabTextAppearance</item>
</style>
<style name="MyTabTextAppearance">
<item name="android:textSize">@dimen/sp_14</item>
<item name="textAllCaps">false</item>
<item name="android:textColor">?android:textColorSecondary</item>
</style>
<style name="WindowAnimationFadeInOut">
<item name="android:windowEnterAnimation">@anim/fade_in</item>
<item name="android:windowExitAnimation">@anim/fade_out</item>
</style>
<style name="ToolbarStyle" parent="AppTheme">
<item name="android:textColorSecondary">@android:color/white</item>
<item name="searchHintIcon">@null</item>
</style>
<!-- 设置Toolbar标题字体的大小 -->
<style name="Toolbar.TitleText" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
<item name="android:textSize">@dimen/sp_18</item>
</style>
<!-- 浮动窗口动画 -->
<style name="anim_float_view">
<item name="android:windowEnterAnimation">@anim/push_up_in</item>
<item name="android:windowExitAnimation">@anim/push_up_out</item>
</style>
<style name="MenuLabelsStyle">
<item name="android:background">@drawable/fab_label_background</item>
<item name="android:textColor">#FFFFFF</item>
<item name="android:textSize">@dimen/sp_14</item>
<item name="android:maxLines">2</item>
<item name="android:ellipsize">end</item>
</style>
<style name="RecyclerViewStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
<item name="android:scrollbarSize">@dimen/dp_2</item>
<item name="android:scrollbars">vertical</item>
</style>
<style name="TabLayout" parent="Base.Widget.Design.TabLayout">
<item name="tabIndicatorColor">@color/colorPrimary</item>
<item name="tabIndicatorHeight">2dp</item>
<item name="tabPaddingStart">12dp</item>
<item name="tabPaddingEnd">12dp</item>
<item name="tabBackground">@color/colorPrimary</item>
<item name="tabTextAppearance">@style/TextAppearance.Design.Tab</item>
<item name="tabSelectedTextColor">@color/colorPrimary</item>
<item name="tabTextColor">@color/colorPrimary</item>
</style>
<declare-styleable name="CircleProgressView">
<attr name="cpv_progressNormalColor" format="color"/>
<attr name="cpv_progressReachColor" format="color"/>
<attr name="cpv_progressTextColor" format="color"/>
<attr name="cpv_progressTextSize" format="dimension"/>
<attr name="cpv_progressTextOffset" format="dimension"/>
<attr name="cpv_progressNormalSize" format="dimension"/>
<attr name="cpv_progressReachSize" format="dimension"/>
<attr name="cpv_radius" format="dimension"/>
<attr name="cpv_progressTextVisible" format="boolean"/>
<attr name="cpv_progressStartArc" format="integer"/>
<attr name="cpv_progressTextSkewX" format="dimension"/>
<attr name="cpv_progressTextPrefix" format="string"/>
<attr name="cpv_progressTextSuffix" format="string"/>
<attr name="cpv_innerBackgroundColor" format="color"/>
<attr name="cpv_progressStyle" format="enum">
<enum name="Normal" value="0"/>
<enum name="FillInner" value="1"/>
<enum name="FillInnerArc" value="2"/>
</attr>
<attr name="cpv_innerProgressColor" format="color"/>
<attr name="cpv_innerPadding" format="dimension"/>
<attr name="cpv_outerColor" format="color"/>
<attr name="cpv_outerSize" format="dimension"/>
<attr name="cpv_reachCapRound" format="boolean"/>
</declare-styleable>
<declare-styleable name="CircleImageView">
<attr name="type" format="enum">
<enum name="none" value="0" />
<enum name="circle" value="1" />
<enum name="rect_rounded" value="2" />
</attr>
<attr name="borderColor" format="color" />
<attr name="borderWidth" format="dimension" />
<attr name="rectRoundRadius" format="dimension" />
</declare-styleable>
</resources>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="transition_movie_img">transition_movie_img</string>
<string name="transition_news_img">transition_news_img</string>
<string name="transition_book_img">transition_book_img</string>
</resources>
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!-- 每个path代表的意思可参见FileProvider文件 -->
<!-- new File("/") -->
<root-path
name="root"
path="" />
<!-- context.getFilesDir() -->
<files-path
name="files"
path="." />
<!-- context.getCacheDir() -->
<cache-path
name="cache"
path="." />
<!-- Environment.getExternalStorageDirectory() -->
<external-path
name="external"
path="." />
<!-- ContextCompat.getExternalFilesDirs(context, null) -->
<external-files-path
name="name"
path="." />
<!-- ContextCompat.getExternalCacheDirs(context) -->
<external-cache-path
name="name"
path="." />
</paths>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" overridePins="true" /> <!--信任系统证书-->
<!-- <certificates src="user" overridePins="true" /> &lt;!&ndash;信任用户证书&ndash;&gt;-->
</trust-anchors>
</base-config>
</network-security-config>
Binary file not shown.
+37
View File
@@ -0,0 +1,37 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply from: "config.gradle"
buildscript {
ext {
kotlin_version = "1.5.21"
compose_version = '1.0.1'
}
repositories {
mavenCentral()
google()
maven { url 'https://maven.aliyun.com/repository/public' }
maven {url 'https://maven.aliyun.com/repository/google/'}
maven {url 'https://maven.aliyun.com/repository/gradle-plugin/'}
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10'
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.43.2'
}
}
allprojects {
repositories {
mavenCentral()
google()
maven { url 'https://maven.aliyun.com/repository/public' }
maven {url 'https://maven.aliyun.com/repository/google/'}
maven {url 'https://maven.aliyun.com/repository/gradle-plugin/'}
maven { url 'https://jitpack.io' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
@@ -0,0 +1 @@
/build
@@ -0,0 +1,88 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-parcelize'
apply plugin: 'com.google.dagger.hilt.android'
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
multiDexEnabled true
consumerProguardFiles "consumer-rules.pro"
javaCompileOptions {
annotationProcessorOptions {
arguments += [
"room.schemaLocation":"$projectDir/schemas".toString(),
"room.incremental":"true",
"room.expandProjection":"true",
AROUTER_MODULE_NAME: project.getName()
]
}
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
buildFeatures {
viewBinding true
}
}
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
generateStubs = true
useBuildCache = true
javacOptions {
option("-Xmaxerrs", 500)
}
}
dependencies {
api rootProject.ext.ktxLibs
// surpport
api rootProject.ext.commonUILibs
// Android Jetpack 组件
api rootProject.ext.jetpackLibs
// net
api rootProject.ext.netLibs
// glide
api rootProject.ext.glideLibs
kapt rootProject.ext.compiler["glideCompiler"]
// arouter
api rootProject.ext.arouterLibs
kapt rootProject.ext.compiler["arouterCompiler"]
kapt rootProject.ext.compiler["hiltAndroidCompiler"]
api rootProject.ext.otherLibs
}
@@ -0,0 +1,239 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
###########################通用配置start#########################################
#1.基本指令区
# 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
-optimizationpasses 5
# 混合时不使用大小写混合,混合后的类名为小写
-dontusemixedcaseclassnames
# 指定不去忽略非公共库的类
-dontskipnonpubliclibraryclasses
# 指定不去忽略非公共库的类成员
-dontskipnonpubliclibraryclassmembers
# 不进行优化,建议使用此选项,
-dontoptimize
# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify 能够加快混淆速度
-dontpreverify
# 使我们的项目混淆后产生映射文件包含有类名->混淆后类名的映射关系
-verbose
# 使用printmapping指定映射文件的名称
-printmapping proguardMapping.txt
# 屏蔽警告
-ignorewarnings
# 指定混淆是采用的算法,后面的参数是一个过滤器这个过滤器是谷歌推荐的算法,一般不做更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*
#添加支持的jar(引入libs下的所有jar包)
#-libraryjars libs(*.jar)
#将文件来源重命名为“SourceFile”字符串
-renamesourcefileattribute SourceFile
# 保留Annotation不混淆
-keepattributes *Annotation*
-keep class * extends java.lang.annotation.Annotation {*;}
# 避免混淆泛型
-keepattributes Signature
#保持反射不被混淆
-keepattributes EnclosingMethod
#保持异常不被混淆
-keepattributes Exceptions
#保持内部类不被混淆
-keepattributes Exceptions,InnerClasses
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
#2.默认保留区
# 保留我们使用的四大组件,自定义的Application等等这些类不被混淆
# 因为这些子类都有可能被外部调用
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
-keep public class com.google.vending.licensing.ILicensingService
# 保留support下的所有类及其内部类
-keep class android.support.** {*;}
# 保留继承的
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
-keep public class * extends androidx.appcompat.**
-keep class com.google.android.material.** {*;}
-keep class androidx.** {*;}
-keep public class * extends androidx.**
-keep interface androidx.** {*;}
-dontwarn com.google.android.material.**
-dontnote com.google.android.material.**
-dontwarn androidx.**
# 保留本地native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留在Activity中的方法参数是view的方法,这样一来我们在layout中写的onClick就不会被影响
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
# 保留枚举类不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 保留我们自定义控件(继承自View)不被混淆
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
# 保留Parcelable序列化类不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# 保留Serializable序列化的类不被混淆
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# 保留R下面的资源
-keep class **.R$* {
*;
}
# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
void *(**On*Listener);
}
# 避免layout中onclick方法(android:onclick="onClick")混淆
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
# 对WebView的处理
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.webView, java.lang.String);
}
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}
# AndroidX混淆
-keep class com.google.android.material.** {*;}
-keep class androidx.** {*;}
-keep public class * extends androidx.**
-keep interface androidx.** {*;}
-dontwarn com.google.android.material.**
-dontnote com.google.android.material.**
-dontwarn androidx.**
###########################通用配置end#########################################
###########################第三方库start#########################################
#Architecture
-dontwarn android.arch.**
-keep public class android.arch.**{*;}
#bugly
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
#okhttp
-dontwarn okhttp3.**
-dontwarn okio.**
-dontwarn javax.annotation.**
# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
#retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Exceptions
#glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
-dontwarn com.bumptech.glide.load.model.stream.**
# EventBus
-keepclassmembers class * {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
# Arouter
-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep public class com.alibaba.android.arouter.facade.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
###########################第三方库end#########################################
-keep class com.bbgo.common_base.bean.** {*;}
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bbgo.common_base">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
@@ -0,0 +1,29 @@
package com.bbgo.common_base
import android.app.Application
import android.content.Context
import com.alibaba.android.arouter.launcher.ARouter
import com.tencent.mmkv.MMKV
open class BaseApplication : Application() {
override fun onCreate() {
super.onCreate()
context = this
MMKV.initialize(this)
if (BuildConfig.DEBUG) { // 这两行必须写在init之前,否则这些配置在init过程中将无效
ARouter.openLog() // 打印日志
ARouter.openDebug() // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
}
ARouter.init(this)
}
companion object {
private var context: BaseApplication? = null
@JvmStatic
fun getContext(): Context {
return context!!
}
}
}
@@ -0,0 +1,59 @@
package com.bbgo.common_base.base
import android.app.Activity
import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import com.bbgo.common_base.util.ActivityCollector
import com.bbgo.library_statusbar.NotchScreenManager
import java.lang.ref.WeakReference
/**
* author: wangyb
* date: 2021/5/20 10:03 上午
* description: todo
*/
abstract class BaseActivity<VB: ViewBinding> : AppCompatActivity() {
/**
* 日志输出标志
*/
protected val TAG: String = this.javaClass.simpleName
protected lateinit var binding: VB
/**
* 当前Activity的实例。
*/
private var activity: Activity? = null
/** 当前Activity的弱引用,防止内存泄露 */
private var activityWR: WeakReference<Activity>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = inflateViewBinding()
setContentView(binding.root)
NotchScreenManager.getInstance().setDisplayInNotch(this)
activity = this
activityWR = WeakReference(activity!!)
ActivityCollector.pushTask(activityWR)
}
abstract fun inflateViewBinding(): VB
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
finish()
}
return super.onOptionsItemSelected(item)
}
override fun onDestroy() {
super.onDestroy()
activity = null
ActivityCollector.removeTask(activityWR)
}
}
@@ -0,0 +1,75 @@
package com.bbgo.common_base.base
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
/**
* author: wangyb
* date: 2021/5/20 3:01 下午
* description: todo
*/
abstract class BaseFragment<VB: ViewBinding> : Fragment() {
/**
* 日志输出标志
*/
protected val TAG: String = this.javaClass.simpleName
private var _binding: VB? = null
protected val binding get() = _binding!!
private var isLoaded = false
/**
* 防止回退到上一级页面时还会init view的问题
*/
private var isInitializedRootView = false
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = inflateViewBinding(inflater, container)
return _binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (isInitializedRootView) return
super.onViewCreated(view, savedInstanceState)
initView()
observe()
isInitializedRootView = true
}
override fun onResume() {
super.onResume()
if (!isLoaded && !isHidden) {
lazyLoad()
isLoaded = true
}
}
abstract fun inflateViewBinding(inflater: LayoutInflater, container: ViewGroup?): VB
/**
* 初始化 View
*/
abstract fun initView()
/**
* 懒加载
*/
abstract fun lazyLoad()
abstract fun observe()
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
@@ -0,0 +1,30 @@
package com.bbgo.common_base.base
import android.util.Log
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.util.concurrent.atomic.AtomicBoolean
class SingleLiveData<T> : MutableLiveData<T>() {
private val TAG = "SingleLiveData"
private val pending = AtomicBoolean(false)
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Log.d(TAG, "multi observers registered but only one will be notified of changes")
}
super.observe(owner) {
if (pending.compareAndSet(true, false)) {
observer.onChanged(it)
}
}
}
override fun setValue(value: T) {
pending.set(true)
super.setValue(value)
}
}
@@ -0,0 +1,14 @@
package com.bbgo.common_base.bean
import androidx.annotation.Keep
/**
* @Description:
* @Author: wangyuebin
* @Date: 2021/8/13 11:40 上午
*/
@Keep
open class BaseBean {
var errorCode: Int = 0
var errorMsg: String = ""
}
@@ -0,0 +1,12 @@
package com.bbgo.common_base.bean
import androidx.annotation.Keep
/**
* @Description:
* @Author: wangyuebin
* @Date: 2021/8/13 11:41 上午
*/
@Keep
class HttpResult<T>(val data: T) : BaseBean()
@@ -0,0 +1,14 @@
package com.bbgo.common_base.bus
/**
* @Description:
* @Author: wangyuebin
* @Date: 2021/8/17 10:17 上午
*/
class BusKey {
companion object {
const val COLLECT = "COLLECT"
const val SCROLL_TOP = "SCROLL_TOP"
const val LOGIN_SUCCESS = "LOGIN_SUCCESS"
}
}
@@ -0,0 +1,92 @@
package com.bbgo.common_base.bus
import androidx.lifecycle.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
/**
* @AuthorWangyuebin
* @Date2022-09-19
* @Description
*/
object EventFlowBus {
private val busMap = mutableMapOf<String, EventBus<*>>()
private val busStickMap = mutableMapOf<String, StickEventBus<*>>()
@Synchronized
fun <T> with(key: String): EventBus<T> {
var eventBus = busMap[key]
if (eventBus == null) {
eventBus = EventBus<T>(key)
busMap[key] = eventBus
}
return eventBus as EventBus<T>
}
@Synchronized
fun <T> withStick(key: String): StickEventBus<T> {
var eventBus = busStickMap[key]
if (eventBus == null) {
eventBus = StickEventBus<T>(key)
busStickMap[key] = eventBus
}
return eventBus as StickEventBus<T>
}
//真正实现类
open class EventBus<T>(private val key: String) : LifecycleEventObserver {
//私有对象用于发送消息
private val _events: MutableSharedFlow<T> by lazy {
obtainEvent()
}
//暴露的公有对象用于接收消息
val events = _events.asSharedFlow()
open fun obtainEvent(): MutableSharedFlow<T> = MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST)
//主线程接收数据
fun register(lifecycleOwner: LifecycleOwner, action: (t: T) -> Unit) {
lifecycleOwner.lifecycle.addObserver(this)
lifecycleOwner.lifecycleScope.launch {
events.collect {
try {
action(it)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
//协程中发送数据
suspend fun post(event: T) {
_events.emit(event)
}
//主线程发送数据
fun post(scope: CoroutineScope, event: T) {
scope.launch {
_events.emit(event)
}
}
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
//自动销毁
if (event == Lifecycle.Event.ON_DESTROY) {
val subscriptCount = _events.subscriptionCount.value
if (subscriptCount <= 0)
busMap.remove(key)
}
}
}
class StickEventBus<T>(key: String) : EventBus<T>(key) {
override fun obtainEvent(): MutableSharedFlow<T> = MutableSharedFlow(1, 1, BufferOverflow.DROP_OLDEST)
}
}
@@ -0,0 +1,125 @@
package com.bbgo.common_base.bus
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.lang.Exception
import java.lang.NullPointerException
import java.util.HashMap
class LiveDataBus private constructor() {
private val bus: MutableMap<String, BusMutableLiveData<Any>>
init {
bus = HashMap()
}
private object SingletonHolder {
val DEFAULT_BUS = LiveDataBus()
}
companion object {
fun get(): LiveDataBus {
return SingletonHolder.DEFAULT_BUS
}
}
fun <T> with(key: String, type: Class<T>?): MutableLiveData<T> {
if (!bus.containsKey(key)) {
val mutableLiveData = BusMutableLiveData<Any>()
bus[key] = mutableLiveData
return mutableLiveData as MutableLiveData<T>
}
return bus[key] as MutableLiveData<T>
}
fun with(key: String): MutableLiveData<Any> {
return with(key, Any::class.java)
}
private class ObserverWrapper<T>(val observer: Observer<in T>) : Observer<T> {
override fun onChanged(t: T) {
if (isCallOnObserve) {
return
}
observer.onChanged(t)
}
private val isCallOnObserve: Boolean
get() {
val stackTrace = Thread.currentThread().stackTrace
if (stackTrace.isNotEmpty()) {
for (element in stackTrace) {
if ("androidx.lifecycle.LiveData" == element.className && "observeForever" == element.methodName) {
return true
}
}
}
return false
}
}
private class BusMutableLiveData<T> : MutableLiveData<T>() {
private val observerMap: MutableMap<Observer<in T>, Observer<in T>> = HashMap()
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner, observer)
try {
hook(observer)
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun observeForever(observer: Observer<in T>) {
if (!observerMap.containsKey(observer)) {
observerMap[observer] = ObserverWrapper(observer)
}
observerMap[observer]?.let { super.observeForever(it) }
}
override fun removeObserver(observer: Observer<in T>) {
val realObserver = if (observerMap.containsKey(observer)) {
observerMap.remove(observer) as Observer<in T>
} else {
observer
}
super.removeObserver(realObserver)
}
private fun hook(observer: Observer<in T>) {
kotlin.runCatching {
//get wrapper's version
val classLiveData = LiveData::class.java
val fieldObservers = classLiveData.getDeclaredField("mObservers")
fieldObservers.isAccessible = true
val objectObservers = fieldObservers[this]
val classObservers: Class<*> = objectObservers.javaClass
val methodGet = classObservers.getDeclaredMethod("get", Any::class.java)
methodGet.isAccessible = true
val objectWrapperEntry = methodGet.invoke(objectObservers, observer)
var objectWrapper: Any? = null
if (objectWrapperEntry is Map.Entry<*, *>) {
objectWrapper = objectWrapperEntry.value
}
if (objectWrapper == null) {
throw NullPointerException("Wrapper can not be bull!")
}
val classObserverWrapper = objectWrapper.javaClass.superclass
val fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion")
fieldLastVersion.isAccessible = true
//get livedata's version
val fieldVersion = classLiveData.getDeclaredField("mVersion")
fieldVersion.isAccessible = true
val objectVersion = fieldVersion[this]
//set wrapper's version
fieldLastVersion[objectWrapper] = objectVersion
}.onFailure {
}
}
}
}
@@ -0,0 +1,57 @@
package com.bbgo.common_base.constants
/**
* author: wangyb
* date: 2021/5/21 4:38 下午
* description: todo
*/
object Constants {
const val DB_INSTANCE = "DB_INSTANCE"
const val TOKEN = "token"
const val USER_ID = "userid"
const val USER_NAME = "username"
const val PASSWORD = "password"
const val APP_TAG = "WanAndroid"
/**
* url key
*/
const val CONTENT_URL_KEY = "url"
/**
* title key
*/
const val CONTENT_TITLE_KEY = "title"
/**
* id key
*/
const val CONTENT_ID_KEY = "id"
/**
* id key
*/
const val CONTENT_CID_KEY = "cid"
/**
* share key
*/
const val CONTENT_SHARE_TYPE = "text/plain"
const val POSITION = "position"
const val COLLECT = "isCollect"
const val ROUTER_PATH = "routerPath"
object CollectType {
const val COLLECT = "COLLECT"
const val UNCOLLECT = "UNCOLLECT"
const val UNKNOWN = "UNKNOWN"
}
object FragmentIndex {
const val HOME_INDEX = 0
const val WECHAT_INDEX = 1
const val SYS_INDEX = 2
const val SQUARE_INDEX = 3
const val PROJECT_INDEX = 4
}
}
@@ -0,0 +1,96 @@
package com.bbgo.common_base.constants
/**
* @Description: Arouter路由表
* 不同的module,一级目录必须不能相同
*
* @Author: wangyuebin
* @Date: 2021/9/10 4:00 下午
*/
class RouterPath {
/**
* 登录注册 组件
*/
object LoginRegister {
private const val LOGIN_REGISTER = "module_login"
const val PAGE_LOGIN = "/$LOGIN_REGISTER/login"
const val PAGE_REGISTER = "/$LOGIN_REGISTER/register"
const val SERVICE_LOGOUT = "/$LOGIN_REGISTER/logout"
}
/**
* 主页
*/
object Main {
private const val MAIN = "app_main"
const val PAGE_MAIN = "/$MAIN/main"
}
object Collect {
private const val COLLECT = "module_collect"
const val SERVICE_COLLECT = "/$COLLECT/collect"
}
/**
* 内容 web 组件
*/
object Content {
private const val CONTENT = "module_content"
const val PAGE_CONTENT = "/$CONTENT/content"
}
/**
* 首页 组件
*/
object Home {
private const val HOME = "module_home"
const val PAGE_HOME = "/$HOME/home"
}
/**
* 微信公众号 组件
*/
object WeChat {
private const val WECHAT = "module_wechat"
const val PAGE_WECHAT = "/$WECHAT/wechat"
}
/**
* 体系 组件
*/
object Sys {
private const val SYS = "module_sys"
const val PAGE_SYS = "/$SYS/sys"
}
/**
* 体系 组件
*/
object Square {
private const val SQUARE = "module_square"
const val PAGE_SQUARE = "/$SQUARE/square"
}
/**
* 项目 组件
*/
object Project {
private const val PROJECT = "module_project"
const val PAGE_PROJECT = "/$PROJECT/project"
}
/**
* compose 组件
*/
object Compose {
private const val COMPOSE = "module_compose"
const val PAGE_COMPOSE = "/$COMPOSE/compose"
}
object Media {
private const val MEDIA = "module_media"
const val PAGE_VIDEO = "/$MEDIA/video"
}
}
@@ -0,0 +1,62 @@
//package com.bbgo.common_base.di
//
//import com.bbgo.common_base.BaseApplication
//import com.bbgo.common_base.constants.HttpConstant
//import com.bbgo.common_base.net.GsonTypeAdapterFactory
//import com.bbgo.common_base.net.ServiceCreators
//import com.bbgo.common_base.net.interceptor.*
//import com.google.gson.GsonBuilder
//import dagger.Module
//import dagger.Provides
//import dagger.hilt.InstallIn
//import dagger.hilt.components.SingletonComponent
//import okhttp3.Cache
//import okhttp3.OkHttpClient
//import retrofit2.Retrofit
//import retrofit2.converter.gson.GsonConverterFactory
//import java.io.File
//import javax.inject.Singleton
//
///**
// * @Description:
// * @Author: wangyuebin
// * @Date: 2021/8/30 5:05 下午
// */
//
//@Module
//@InstallIn(SingletonComponent::class)
//object NetWorkModule {
//
// private val BASE_URL = "https://wanandroid.com/"
//
// //设置 请求的缓存的大小跟位置
// private val cacheFile = File(BaseApplication.getContext().cacheDir, "cache")
// private val cache = Cache(cacheFile, HttpConstant.MAX_CACHE_SIZE)
//
// @Provides
// @Singleton
// fun provideOkHttpClient(): OkHttpClient {
// return OkHttpClient.Builder()
// .addInterceptor(MultiBaseUrlInterceptor())
// .addInterceptor(LoggingInterceptor())
// .addInterceptor(HeaderInterceptor())
// .addInterceptor(SaveCookieInterceptor())
// .addInterceptor(CacheInterceptor())
// .cache(cache)
// .addInterceptor(BasicParamsInterceptor())
// .build()
// }
//
// @Provides
// @Singleton
// fun provideRetrofit(): Retrofit {
// return Retrofit.Builder()
// .baseUrl(BASE_URL)
// .client(ServiceCreators.httpClient)
// .addConverterFactory(
// GsonConverterFactory.create(
// GsonBuilder().registerTypeAdapterFactory(
// GsonTypeAdapterFactory()
// ).create())).build()
// }
//}
@@ -0,0 +1,11 @@
package com.bbgo.common_base.event
import androidx.annotation.Keep
/**
* author: wangyb
* date: 2021/5/27 8:12 下午
* description: todo
*/
@Keep
data class MessageEvent(val indexPage: Int, val type: String, val position: Int, val pageId: Int)
@@ -0,0 +1,3 @@
package com.bbgo.common_base.event
data class ScrollEvent(val index: Int)
@@ -0,0 +1,58 @@
/*
* Copyright (C) guolin, Suzhou Quxiang Inc. Open source codes for study only.
* Do not use for commercial purpose.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.bbgo.common_base.ext
import com.bbgo.common_base.BaseApplication
/**
* 根据手机的分辨率将dp转成为px。
*/
fun dp2px(dp: Float): Int {
val scale = BaseApplication.getContext().resources.displayMetrics.density
return (dp * scale + 0.5f).toInt()
}
/**
* 根据手机的分辨率将px转成dp。
*/
fun px2dp(px: Float): Int {
val scale = BaseApplication.getContext().resources.displayMetrics.density
return (px / scale + 0.5f).toInt()
}
/**
* 获取屏幕宽值。
*/
val screenWidth
get() = BaseApplication.getContext().resources.displayMetrics.widthPixels
/**
* 获取屏幕高值。
*/
val screenHeight
get() = BaseApplication.getContext().resources.displayMetrics.heightPixels
/**
* 获取屏幕像素:对获取的宽高进行拼接。例:1080X2340。
*/
fun screenPixel(): String {
BaseApplication.getContext().resources.displayMetrics.run {
return "${widthPixels}X${heightPixels}"
}
}
@@ -0,0 +1,21 @@
package com.bbgo.common_base.ext
/**
* Created by AhmedEltaher
*/
class Error(val code: Int, val description: String) {
constructor(exception: Exception) : this(code = DEFAULT_ERROR, description = exception.message
?: "")
}
const val HTTP_REQUEST_ERROR = -1
const val NO_INTERNET_CONNECTION = -1
const val NETWORK_ERROR = -2
const val DEFAULT_ERROR = -3
const val PASS_WORD_ERROR = -101
const val USER_NAME_ERROR = -102
const val CHECK_YOUR_FIELDS = -103
const val SEARCH_ERROR = -104
const val USER_REGISTERED_ERROR = -105
const val USER_NOT_LOGIN = -1001
@@ -0,0 +1,20 @@
package com.bbgo.common_base.ext
import androidx.lifecycle.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
fun <T> LifecycleOwner.observe(liveData: LiveData<T>, action: (t: T) -> Unit) {
liveData.observe(this, { it?.let { t -> action(t) } })
}
inline fun LifecycleOwner.collectWithLifeCycle(viewLifecycleOwner: LifecycleOwner,
minActiveSate: Lifecycle.State = Lifecycle.State.STARTED,
crossinline block: suspend CoroutineScope.() -> Unit
) {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(minActiveSate) {
block()
}
}
}
@@ -0,0 +1,89 @@
package com.bbgo.common_base.ext
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
import com.tencent.mmkv.MMKV
object Prefs {
private val mmkv = MMKV.defaultMMKV()!!
fun putInt(key: String, value: Int) {
mmkv.encode(key, value)
}
fun getInt(key: String, defaultValue: Int = -1): Int {
return mmkv.decodeInt(key, defaultValue)
}
fun putIntAsync(key: String, value: Int) {
mmkv.putInt(key, value).apply()
}
fun putString(key: String, value: String) {
mmkv.encode(key, value)
}
fun putStringAsync(key: String, value: String) {
mmkv.putString(key, value).apply()
}
fun getString(key: String, defaultValue: String = ""): String {
return mmkv.decodeString(key, defaultValue) ?: defaultValue
}
fun putDouble(key: String, value: Double) {
mmkv.encode(key, value)
}
fun putDoubleAsync(key: String, value: Float) {
mmkv.putFloat(key, value).apply()
}
fun getDouble(key: String, defaultValue: Double = 0.00): Double {
return mmkv.decodeDouble(key, defaultValue)
}
fun putLong(key: String, value: Long) {
mmkv.encode(key, value)
}
fun putLongAsync(key: String, value: Long) {
mmkv.putLong(key, value).apply()
}
fun getLong(key: String, defaultValue: Long = 0L): Long {
return mmkv.decodeLong(key, defaultValue)
}
fun putBoolean(key: String, value: Boolean) {
mmkv.encode(key, value)
}
fun putBooleanAsync(key: String, value: Boolean) {
mmkv.putBoolean(key, value).apply()
}
fun getBoolean(key: String, defaultValue: Boolean = false): Boolean {
return mmkv.decodeBool(key, defaultValue)
}
fun clear() {
mmkv.clear().apply()
}
/*****************************************************************************/
/**
* dataStore 存入数据默认就是异步,没有同步方法
* 取数据异步通过Flow实现
*/
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
fun put(key: String, value: Any) {
}
}

Some files were not shown because too many files have changed in this diff Show More