第一次提交
16
.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
/.idea/
|
||||
88
README.md
@@ -1,93 +1,9 @@
|
||||
# trustlend
|
||||
# 红色的贷款项目
|
||||
|
||||
|
||||
|
||||
## Getting started
|
||||
## 架构采用MVVM结构
|
||||
|
||||
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
||||
|
||||
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
|
||||
|
||||
## Add your files
|
||||
|
||||
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
||||
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
|
||||
|
||||
```
|
||||
cd existing_repo
|
||||
git remote add origin http://git.dengshikj.com/xuhuixiang/trustlend.git
|
||||
git branch -M master
|
||||
git push -uf origin master
|
||||
```
|
||||
|
||||
## Integrate with your tools
|
||||
|
||||
- [ ] [Set up project integrations](http://git.dengshikj.com/xuhuixiang/trustlend/-/settings/integrations)
|
||||
|
||||
## Collaborate with your team
|
||||
|
||||
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
||||
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
||||
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
||||
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
||||
- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
|
||||
|
||||
## Test and Deploy
|
||||
|
||||
Use the built-in continuous integration in GitLab.
|
||||
|
||||
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
|
||||
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
||||
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
||||
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
||||
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
||||
|
||||
***
|
||||
|
||||
# Editing this README
|
||||
|
||||
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
|
||||
|
||||
## Suggestions for a good README
|
||||
|
||||
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
||||
|
||||
## Name
|
||||
Choose a self-explaining name for your project.
|
||||
|
||||
## Description
|
||||
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
||||
|
||||
## Badges
|
||||
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
||||
|
||||
## Visuals
|
||||
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
||||
|
||||
## Installation
|
||||
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
||||
|
||||
## Usage
|
||||
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
||||
|
||||
## Support
|
||||
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
||||
|
||||
## Roadmap
|
||||
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
||||
|
||||
## Contributing
|
||||
State if you are open to contributions and what your requirements are for accepting them.
|
||||
|
||||
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
||||
|
||||
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
||||
|
||||
## Authors and acknowledgment
|
||||
Show your appreciation to those who have contributed to the project.
|
||||
|
||||
## License
|
||||
For open source projects, say how it is licensed.
|
||||
|
||||
## Project status
|
||||
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
||||
|
||||
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
3
app/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
12
app/.idea/gradle.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
9
app/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
8
app/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/videomark.iml" filepath="$PROJECT_DIR$/.idea/modules/videomark.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
app/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
112
app/build.gradle
Normal file
@@ -0,0 +1,112 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.dskj.daikuan"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 30
|
||||
versionCode 110
|
||||
versionName "1.1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
// configurations.all {
|
||||
// resolutionStrategy { force 'androidx.core:core-ktx:1.6.0' }
|
||||
// }
|
||||
ndk {
|
||||
abiFilters "arm64-v8a"//,"armeabi-v7a", "x86", "x86_64"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
debug {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
useLibrary 'org.apache.http.legacy'
|
||||
//启用DataBinding
|
||||
buildFeatures{
|
||||
dataBinding = true
|
||||
// for view binding :
|
||||
// viewBinding = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('videomark.jks')
|
||||
storePassword "123456"
|
||||
keyAlias 'videomark'
|
||||
keyPassword "123456"
|
||||
}
|
||||
release {
|
||||
storeFile file('videomark.jks')
|
||||
storePassword "123456"
|
||||
keyAlias 'videomark'
|
||||
keyPassword "123456"
|
||||
}
|
||||
}
|
||||
lintOptions {
|
||||
checkReleaseBuilds false
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
||||
configurations.all{
|
||||
resolutionStrategy{
|
||||
force 'androidx.core:core:1.6.0'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.github.tbruyelle:rxpermissions:0.10.2'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
testImplementation "junit:junit:4.13.1"
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
//viewmode需要引入的依赖 livedate 之类的就依赖他了
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
|
||||
|
||||
implementation project(path: ':basicLib')
|
||||
implementation "androidx.fragment:fragment-ktx:1.4.0"
|
||||
implementation "androidx.core:core-ktx:1.3.2"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'io.reactivex:rxjava:1.3.0'
|
||||
implementation 'io.reactivex:rxandroid:1.2.1'
|
||||
// okhttp 3
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
|
||||
|
||||
implementation 'com.github.Hitomis:transferee:1.6.1'
|
||||
// 添加 Glide 图片加载器
|
||||
implementation 'com.github.Hitomis.transferee:GlideImageLoader:1.6.1'
|
||||
implementation 'com.gyf.immersionbar:immersionbar:2.3.3'
|
||||
|
||||
implementation project(path: ':matisse')
|
||||
|
||||
|
||||
//以下3个 下拉刷新和加载更多依赖
|
||||
implementation 'com.scwang.smart:refresh-layout-kernel:2.0.1'
|
||||
//核心必须依赖
|
||||
implementation 'com.scwang.smart:refresh-header-classics:2.0.1'
|
||||
//经典刷新头
|
||||
implementation 'com.scwang.smart:refresh-footer-classics:2.0.1'
|
||||
implementation 'com.scwang.smart:refresh-footer-ball:2.0.1'
|
||||
|
||||
}
|
||||
451
app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,451 @@
|
||||
# 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
|
||||
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
public *;
|
||||
}
|
||||
#指定代码的压缩级别
|
||||
-optimizationpasses 5
|
||||
|
||||
#包明不混合大小写
|
||||
-dontusemixedcaseclassnames
|
||||
|
||||
#不去忽略非公共的库类
|
||||
-dontskipnonpubliclibraryclasses
|
||||
|
||||
#优化 不优化输入的类文件
|
||||
-dontoptimize
|
||||
|
||||
#预校验
|
||||
-dontpreverify
|
||||
|
||||
#混淆时是否记录日志
|
||||
-verbose
|
||||
|
||||
# 混淆时所采用的算法
|
||||
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
|
||||
|
||||
#忽略警告
|
||||
-ignorewarning
|
||||
|
||||
|
||||
-keep public class * extends android.app.Fragment
|
||||
-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 class com.bikao.cleanmark.view.** { *; }
|
||||
-keep class com.coremedia.iso.** {*;}
|
||||
-keep class nu.xom.** {*;}
|
||||
-keep class com.googlecode.mp4parser.** {*;}
|
||||
-keep class com.mp4parser.** {*;}
|
||||
-keep class com.coremedia.iso.** {*;}
|
||||
-keep class org.aspectj.** {*;}
|
||||
|
||||
-keep class jp.co.recruit_lifestyle.android.floatingview.** {*;}
|
||||
|
||||
# 如果有引用v4包可以添加下面这行
|
||||
-keep class android.support.v4.**{ *;}
|
||||
|
||||
# 如果引用了v4或者v7包
|
||||
-dontwarn android.support.**
|
||||
|
||||
# 保持 native 方法不被混淆
|
||||
-keepclasseswithmembernames class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
-keep class com.bikao.dkplayer.** {*;}
|
||||
|
||||
# 保护注解
|
||||
-keepattributes *Annotation*
|
||||
-keep class * extends java.lang.annotation.Annotation {*;}
|
||||
|
||||
# 泛型与反射
|
||||
-keepattributes Signature
|
||||
-keepattributes EnclosingMethod
|
||||
|
||||
# 不混淆内部类
|
||||
-keepattributes InnerClasses
|
||||
|
||||
# gson
|
||||
-dontwarn com.google.**
|
||||
-keep class com.google.gson.** {*;}
|
||||
|
||||
|
||||
#保持枚举 enum 类不被混淆
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
# otto混淆规则
|
||||
-keepattributes *Annotation*
|
||||
-keepclassmembers class ** {
|
||||
@com.squareup.otto.Subscribe public *;
|
||||
@com.squareup.otto.Produce public *;
|
||||
}
|
||||
#所有bean都不要混淆
|
||||
-keep class * implements java.io.Serializable {*;}
|
||||
-keepclassmembers class * implements java.io.Serializable {*;}
|
||||
-keep class * implements java.lang.Comparable {*;}
|
||||
-keepclassmembers class * implements java.lang.Comparable {*;}
|
||||
-keep class com.bikao.cleanmark.bean.** { *; }
|
||||
|
||||
#一般网络层都不进行混淆
|
||||
-keep class com.xxx.xxx.http.** {*;}
|
||||
|
||||
#如果你遇到一些控件无法Inflate,报NullPointException,比如ListView,NavigationView等等
|
||||
#-keep class org.xmlpull.v1.** {*;}
|
||||
|
||||
# OkHttp
|
||||
-dontwarn com.squareup.okhttp.**
|
||||
-keep class com.squareup.okhttp.** {*;}
|
||||
-keep interface com.squareup.okhttp.** {*;}
|
||||
-dontwarn okio.**
|
||||
|
||||
# Realm
|
||||
-keep class io.realm.annotations.RealmModule
|
||||
-keep @io.realm.annotations.RealmModule class *
|
||||
-keep class io.realm.internal.Keep
|
||||
-keep @io.realm.internal.Keep class * { *; }
|
||||
-dontwarn javax.**
|
||||
-dontwarn io.realm.**
|
||||
|
||||
#友盟
|
||||
-keep class com.umeng.** {*;}
|
||||
|
||||
-keep class com.uc.** {*;}
|
||||
|
||||
-keepclassmembers class * {
|
||||
public <init> (org.json.JSONObject);
|
||||
}
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
-keep class com.zui.** {*;}
|
||||
-keep class com.miui.** {*;}
|
||||
-keep class com.heytap.** {*;}
|
||||
-keep class a.** {*;}
|
||||
-keep class com.vivo.** {*;}
|
||||
|
||||
-keep public class com.adwl.location.R$*{
|
||||
public static final int *;
|
||||
}
|
||||
|
||||
#lambda
|
||||
-dontwarn java.lang.invoke.*
|
||||
-dontwarn **$$Lambda$*
|
||||
|
||||
#基线包使用,生成mapping.txt
|
||||
-printmapping mapping.txt
|
||||
#生成的mapping.txt在app/buidl/outputs/mapping/release路径下,移动到/app路径下
|
||||
|
||||
#修复后的项目使用,保证混淆结果一致
|
||||
#-applymapping mapping.txt
|
||||
|
||||
#Glide
|
||||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
}
|
||||
|
||||
-keep class com.bytedance.sdk.openadsdk.** { *; }
|
||||
-keep public interface com.bytedance.sdk.openadsdk.downloadnew.** {*;}
|
||||
-keep class com.pgl.sys.ces.* {*;}
|
||||
|
||||
# for DexGuard only
|
||||
#-keepresourcexmlelements manifest/application/meta-data@value=GlideModule
|
||||
|
||||
#butterknife
|
||||
-dontwarn butterknife.internal.**
|
||||
# -keep class butterknife.** { *; }
|
||||
# -dontwarn butterknife.internal.**
|
||||
# -keep class **$$ViewBinder { *; }
|
||||
# -keepclasseswithmembernames class * { @butterknife.* <fields>;}
|
||||
# -keepclasseswithmembernames class * { @butterknife.* <methods>;}
|
||||
|
||||
#SweetAlert
|
||||
-keep class cn.pedant.SweetAlert.**{*;}
|
||||
-keep class cn.pedant.SweetAlert.Rotate3dAnimation {
|
||||
public <init>(...);
|
||||
}
|
||||
|
||||
#retrofit2
|
||||
# Platform calls Class.forName on typeswhich do not exist on Android to determine platform.
|
||||
-dontnote retrofit2.Platform
|
||||
|
||||
# Platform used when running on RoboVM oniOS. Will not be used at runtime.
|
||||
-dontnote retrofit2.Platform$IOS$MainThreadExecutor
|
||||
|
||||
# Platform used when running on Java 8 VMs.Will not be used at runtime.
|
||||
-dontwarn retrofit2.Platform$Java8
|
||||
|
||||
# Retain generic type information for useby reflection by converters and adapters.
|
||||
-keepattributes Signature
|
||||
|
||||
# Retain declared checked exceptions foruse by a Proxy instance.
|
||||
-keepattributes Exceptions
|
||||
|
||||
# Retrofit
|
||||
-dontwarn retrofit2.**
|
||||
-keep class retrofit2.** { *; }
|
||||
|
||||
# Retrolambda
|
||||
-dontwarn java.lang.invoke.*
|
||||
|
||||
|
||||
# RxJava RxAndroid
|
||||
-dontwarn sun.misc.**
|
||||
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
|
||||
long producerIndex;
|
||||
long consumerIndex;
|
||||
}
|
||||
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
|
||||
rx.internal.util.atomic.LinkedQueueNode producerNode;
|
||||
}
|
||||
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
|
||||
rx.internal.util.atomic.LinkedQueueNode consumerNode;
|
||||
}
|
||||
|
||||
# EventBus混淆
|
||||
-keepclassmembers class ** {
|
||||
public void onEvent*(***);
|
||||
}
|
||||
# Only required if you use AsyncExecutor
|
||||
-keepclassmembers class * extends de.greenrobot.event.util.ThrowableFailureEvent {
|
||||
public <init>(java.lang.Throwable);
|
||||
}
|
||||
# Don't warn for missing support classes
|
||||
-dontwarn de.greenrobot.event.util.*$Support
|
||||
-dontwarn de.greenrobot.event.util.*$SupportManagerFragment
|
||||
|
||||
-keep class com.gyf.immersionbar.* {*;}
|
||||
-dontwarn com.gyf.immersionbar.**
|
||||
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
-keep public class com.bikao.cleanmark.R$*{
|
||||
public static final int *;
|
||||
}
|
||||
|
||||
#ijk
|
||||
#ijkplayer
|
||||
-keep class tv.danmaku.ijk.media.player.** {*;}
|
||||
-keep class tv.danmaku.ijk.media.player.IjkMediaPlayer{*;}
|
||||
-keep class tv.danmaku.ijk.media.player.ffmpeg.FFmpegApi{*;}
|
||||
|
||||
# //smack
|
||||
-keep class org.jxmpp.** {*;}
|
||||
-keep class de.measite.** {*;}
|
||||
-keep class org.jivesoftware.** {*;}
|
||||
-keep class org.xmlpull.** {*;}
|
||||
-dontwarn org.xbill.**
|
||||
-keep class org.xbill.** {*;}
|
||||
|
||||
# //eventbus
|
||||
-keepattributes *Annotation*
|
||||
-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);
|
||||
}
|
||||
|
||||
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
|
||||
-keep @com.facebook.common.internal.DoNotStrip class *
|
||||
-keepclassmembers class * {
|
||||
@com.facebook.common.internal.DoNotStrip *;
|
||||
}
|
||||
# Keep native methods
|
||||
-keepclassmembers class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
-dontwarn okio.**
|
||||
-dontwarn com.squareup.okhttp.**
|
||||
-dontwarn okhttp3.**
|
||||
-dontwarn javax.annotation.**
|
||||
-dontwarn com.android.volley.toolbox.**
|
||||
-dontwarn com.facebook.infer.**
|
||||
|
||||
|
||||
|
||||
-keep class com.iflytek.**{*;}
|
||||
|
||||
#UShare
|
||||
-dontshrink
|
||||
-dontoptimize
|
||||
-dontwarn com.google.android.maps.**
|
||||
-dontwarn android.webkit.WebView
|
||||
-dontwarn com.umeng.**
|
||||
-dontwarn com.tencent.weibo.sdk.**
|
||||
-dontwarn com.facebook.**
|
||||
-keep public class javax.**
|
||||
-keep public class android.webkit.**
|
||||
-dontwarn android.support.v4.**
|
||||
-keep enum com.facebook.**
|
||||
-keepattributes Exceptions,InnerClasses,Signature
|
||||
-keepattributes *Annotation*
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
-keep public interface com.facebook.**
|
||||
-keep public interface com.tencent.**
|
||||
-keep public interface com.umeng.socialize.**
|
||||
-keep public interface com.umeng.socialize.sensor.**
|
||||
-keep public interface com.umeng.scrshot.**
|
||||
-keep public class com.umeng.socialize.* {*;}
|
||||
-keep class com.facebook.**
|
||||
-keep class com.facebook.** { *; }
|
||||
-keep class com.umeng.scrshot.**
|
||||
-keep public class com.tencent.** {*;}
|
||||
-keep class com.umeng.socialize.sensor.**
|
||||
-keep class com.umeng.socialize.handler.**
|
||||
-keep class com.umeng.socialize.handler.*
|
||||
-keep class com.umeng.weixin.handler.**
|
||||
-keep class com.umeng.weixin.handler.*
|
||||
-keep class com.umeng.qq.handler.**
|
||||
-keep class com.umeng.qq.handler.*
|
||||
-keep class UMMoreHandler{*;}
|
||||
-keep class com.tencent.mm.sdk.modelmsg.WXMediaMessage {*;}
|
||||
-keep class com.tencent.mm.sdk.modelmsg.** implements com.tencent.mm.sdk.modelmsg.WXMediaMessage$IMediaObject {*;}
|
||||
-keep class im.yixin.sdk.api.YXMessage {*;}
|
||||
-keep class im.yixin.sdk.api.** implements im.yixin.sdk.api.YXMessage$YXMessageData{*;}
|
||||
-keep class com.tencent.mm.sdk.** {
|
||||
*;
|
||||
}
|
||||
-keep class com.tencent.mm.opensdk.** {
|
||||
*;
|
||||
}
|
||||
-keep class com.tencent.wxop.** {
|
||||
*;
|
||||
}
|
||||
-keep class com.tencent.mm.sdk.** {
|
||||
*;
|
||||
}
|
||||
-keep class com.umeng.** {*;}
|
||||
-dontwarn twitter4j.**
|
||||
-keep class twitter4j.** { *; }
|
||||
-keep class com.tencent.** {*;}
|
||||
-dontwarn com.tencent.**
|
||||
-keep class com.kakao.** {*;}
|
||||
-dontwarn com.kakao.**
|
||||
-keep public class com.umeng.com.umeng.soexample.R$*{
|
||||
public static final int *;
|
||||
}
|
||||
-keep public class com.linkedin.android.mobilesdk.R$*{
|
||||
public static final int *;
|
||||
}
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
-keep class com.tencent.open.TDialog$*
|
||||
-keep class com.tencent.open.TDialog$* {*;}
|
||||
-keep class com.tencent.open.PKDialog
|
||||
-keep class com.tencent.open.PKDialog {*;}
|
||||
-keep class com.tencent.open.PKDialog$*
|
||||
-keep class com.tencent.open.PKDialog$* {*;}
|
||||
-keep class com.umeng.socialize.impl.ImageImpl {*;}
|
||||
-keep class com.sina.** {*;}
|
||||
-dontwarn com.sina.**
|
||||
-keep class com.alipay.share.sdk.** {
|
||||
*;
|
||||
}
|
||||
-keep class * implements android.os.Parcelable {*;}
|
||||
|
||||
-keepnames class * implements android.os.Parcelable {
|
||||
public static final ** CREATOR;
|
||||
}
|
||||
-keepclassmembers class * implements android.os.Parcelable {*;}
|
||||
|
||||
-keep class com.linkedin.** { *; }
|
||||
-keep class com.android.dingtalk.share.ddsharemodule.** { *; }
|
||||
-keepattributes Signature
|
||||
#UShare
|
||||
|
||||
|
||||
# 请开发者根据自己实际情况给第三方库的添加相应的混淆设置
|
||||
-dontwarn com.androidquery.**
|
||||
-keep class com.androidquery.** { *;}
|
||||
|
||||
-dontwarn tv.danmaku.**
|
||||
-keep class tv.danmaku.** { *;}
|
||||
|
||||
-dontwarn androidx.**
|
||||
|
||||
# 如果使用了tbs版本的sdk需要进行以下配置
|
||||
-keep class com.tencent.smtt.** { *; }
|
||||
-dontwarn dalvik.**
|
||||
-dontwarn com.tencent.smtt.**
|
||||
|
||||
-keep class com.google.android.exoplayer2.** { *; }
|
||||
-dontwarn com.google.android.exoplayer2.**
|
||||
|
||||
-keep class com.aplayer.** { *; }
|
||||
-dontwarn com.aplayer.**
|
||||
|
||||
-dontwarn com.tencent.bugly.**
|
||||
-keep public class com.tencent.bugly.**{*;}
|
||||
|
||||
|
||||
-keep class com.lljjcoder.**{
|
||||
*;
|
||||
}
|
||||
|
||||
-dontwarn demo.**
|
||||
-keep class demo.**{*;}
|
||||
-dontwarn net.sourceforge.pinyin4j.**
|
||||
-keep class net.sourceforge.pinyin4j.**{*;}
|
||||
-keep class net.sourceforge.pinyin4j.format.**{*;}
|
||||
-keep class net.sourceforge.pinyin4j.format.exception.**{*;}
|
||||
|
||||
-keepattributes *Annotation*
|
||||
-keepattributes Exceptions
|
||||
-keepattributes InnerClasses
|
||||
-keepattributes Signature
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
-keep class com.bytedance.sdk.openadsdk.** { *; }
|
||||
-keep class com.bytedance.frameworks.** { *; }
|
||||
|
||||
-keep class ms.bd.c.Pgl.**{*;}
|
||||
-keep class com.bytedance.mobsec.metasec.ml.**{*;}
|
||||
|
||||
-keep class com.ss.android.**{*;}
|
||||
|
||||
-keep class com.bytedance.embedapplog.** {*;}
|
||||
-keep class com.bytedance.embed_dr.** {*;}
|
||||
|
||||
-keep class com.bykv.vk.** {*;}
|
||||
|
||||
-ignorewarnings
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.bikao.cleanmark;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
assertEquals("com.bikao.watermark", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
73
app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.dskj.daikuan">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
<!-- <uses-permission android:name="android.permission.CALL_PHONE"/>-->
|
||||
|
||||
<application
|
||||
android:name=".InitApp"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/logo_img"
|
||||
android:label="@string/app_name"
|
||||
android:largeHeap="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:roundIcon="@mipmap/logo_img"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppThemeLogin"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:replace="android:allowBackup">
|
||||
<!-- <meta-data-->
|
||||
<!-- android:name="com.google.android.actions"-->
|
||||
<!-- android:resource="@xml/animate" />-->
|
||||
|
||||
<uses-library
|
||||
android:name="org.apache.http.legacy"
|
||||
android:required="false" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.activity.StartUpActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.dskj.daikuan.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true"
|
||||
tools:replace="android:authorities">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths"
|
||||
tools:replace="android:resource" />
|
||||
</provider>
|
||||
|
||||
<activity
|
||||
android:name=".ui.activity.MainActivity"
|
||||
android:exported="true"></activity>
|
||||
<activity
|
||||
android:name=".ui.activity.LoginActivity"
|
||||
android:exported="true"></activity>
|
||||
<activity
|
||||
android:name=".ui.activity.ShenQingActivity"
|
||||
android:exported="true"></activity>
|
||||
<activity
|
||||
android:name=".ui.activity.XiangQingActivity"
|
||||
android:exported="true"></activity>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
156
app/src/main/java/com/dskj/daikuan/InitApp.java
Normal file
@@ -0,0 +1,156 @@
|
||||
package com.dskj.daikuan;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.dskj.daikuan.bean.UserBean;
|
||||
import com.dskj.daikuan.config.Config;
|
||||
import com.dskj.daikuan.utils.DeviceUtil;
|
||||
import com.dskj.daikuan.utils.GsonUtils;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Application 主入口
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public class InitApp extends Application {
|
||||
public static Context AppContext;
|
||||
public static InitApp initApp;
|
||||
|
||||
public static Context getAppContext() {
|
||||
return AppContext;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(base);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
AppContext = getApplicationContext();
|
||||
initApp = this;
|
||||
DeviceUtil.init(this);
|
||||
initARouter();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void initARouter() {
|
||||
|
||||
}
|
||||
|
||||
//----------------------------SharedPreferences的全局存储实现 人懒 懒得抽文件---------------------------------------
|
||||
|
||||
public static void saveInt(String key, int value) {
|
||||
SharedPreferences sp = AppContext.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putInt(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static void saveFloat(String key, float value) {
|
||||
SharedPreferences sp = AppContext.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putFloat(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static void saveLong(String key, long value) {
|
||||
SharedPreferences sp = AppContext.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putLong(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static void saveString(String key, String value) {
|
||||
SharedPreferences sp = AppContext.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putString(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static void deleteKey(String key) {
|
||||
SharedPreferences sp = AppContext.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
|
||||
if (!sp.contains(key)) return;
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.remove(key);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static void saveBoolean(String key, boolean value) {
|
||||
SharedPreferences sp = AppContext.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putBoolean(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static int getInt(String key, int defValue) {
|
||||
SharedPreferences sp = AppContext.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
|
||||
return sp.getInt(key, defValue);
|
||||
}
|
||||
|
||||
public static float getFloat(String key, float defValue) {
|
||||
SharedPreferences sp = AppContext.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
|
||||
return sp.getFloat(key, defValue);
|
||||
}
|
||||
|
||||
public static long getLong(String key, long defValue) {
|
||||
SharedPreferences sp = AppContext.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
|
||||
return sp.getLong(key, defValue);
|
||||
}
|
||||
|
||||
public static boolean getBoolean(String key, boolean defValue) {
|
||||
SharedPreferences sp = AppContext.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
|
||||
return sp.getBoolean(key, defValue);
|
||||
}
|
||||
|
||||
public static String getString(String key, String defValue) {
|
||||
Context context = AppContext;
|
||||
if (context == null) {
|
||||
return defValue;
|
||||
}
|
||||
SharedPreferences sp = context.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
|
||||
return sp.getString(key, defValue);
|
||||
}
|
||||
|
||||
public static UserBean getUserBean() {
|
||||
Context context = AppContext;
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
String name = getString("user_bean","");
|
||||
if(TextUtils.isEmpty(name)){
|
||||
return null;
|
||||
}
|
||||
return GsonUtils.getObjFromJSON(name,UserBean.class);
|
||||
}
|
||||
|
||||
public static String getToken() {
|
||||
Context context = AppContext;
|
||||
if (context == null) {
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static void setToken(String value) {
|
||||
SharedPreferences sp = AppContext.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putString(Config.API_TOKEN_TAG, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
}
|
||||
68
app/src/main/java/com/dskj/daikuan/adapter/BaseAdapter.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package com.dskj.daikuan.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public abstract class BaseAdapter extends RecyclerView.Adapter implements View.OnClickListener, View.OnLongClickListener {
|
||||
|
||||
private Context mContext;
|
||||
private OnItemClickListener mItemClickListener;
|
||||
|
||||
public BaseAdapter(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public abstract RecyclerView.ViewHolder getViewHolder(View itemView);
|
||||
|
||||
public abstract int getItemViewResource();
|
||||
|
||||
public abstract void bindItemData(RecyclerView.ViewHolder holder, int position);
|
||||
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
int resource = getItemViewResource();
|
||||
if (resource <= 0) return null;
|
||||
|
||||
View view = LayoutInflater.from(mContext).inflate(resource, parent, false);
|
||||
view.setOnClickListener(this);
|
||||
view.setOnLongClickListener(this);
|
||||
return getViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||
bindItemData(holder, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onClick(View v) {
|
||||
if (mItemClickListener != null) {
|
||||
mItemClickListener.onItemClick(v);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
if (mItemClickListener != null) {
|
||||
mItemClickListener.onItemLongClick(v);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public void setItemClickListener(OnItemClickListener itemClickListener) {
|
||||
mItemClickListener = itemClickListener;
|
||||
}
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(View v);
|
||||
void onItemLongClick(View v);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.dskj.daikuan.adapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CommunityAdapter extends FragmentStateAdapter {
|
||||
private ArrayList<String> listTitle;
|
||||
private List<Fragment> listFragment;
|
||||
|
||||
|
||||
public CommunityAdapter(@NonNull FragmentActivity fragmentActivity, ArrayList<String> listTitle, List<Fragment> listFragment) {
|
||||
super(fragmentActivity);
|
||||
this.listTitle = listTitle;
|
||||
this.listFragment = listFragment;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
return listFragment.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return listFragment.size();
|
||||
}
|
||||
|
||||
}
|
||||
34
app/src/main/java/com/dskj/daikuan/adapter/GoodsAdapter.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.dskj.daikuan.adapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GoodsAdapter extends FragmentStateAdapter {
|
||||
private ArrayList<String> listTitle;
|
||||
private List<Fragment> listFragment;
|
||||
|
||||
|
||||
public GoodsAdapter(@NonNull FragmentActivity fragmentActivity, ArrayList<String> listTitle, List<Fragment> listFragment) {
|
||||
super(fragmentActivity);
|
||||
this.listTitle = listTitle;
|
||||
this.listFragment = listFragment;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
return listFragment.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return listFragment.size();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.dskj.daikuan.adapter.comm;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 通用列表适配器
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class CommonAdapter<T> extends RecyclerView.Adapter<ViewHolder> {
|
||||
|
||||
protected Context mContext;
|
||||
protected int mLayoutId;
|
||||
protected List<T> mDatas;
|
||||
protected LayoutInflater mInflater;
|
||||
|
||||
ViewHolder viewHolder;
|
||||
|
||||
|
||||
public CommonAdapter(Context context, int layoutId, List<T> datas) {
|
||||
mContext = context;
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mLayoutId = layoutId;
|
||||
mDatas = datas;
|
||||
}
|
||||
public void setDates(List<T> dates){
|
||||
this.mDatas=dates;
|
||||
// notifyItemRangeChanged(0,mDatas.size());
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
public void addDates(List<T> dates){
|
||||
this.mDatas.addAll(dates);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void addDates(int localSize){
|
||||
int size=mDatas.size();
|
||||
notifyItemRangeChanged(size,localSize);
|
||||
}
|
||||
public List<T> getDates(){
|
||||
return this.mDatas;
|
||||
}
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
viewHolder = ViewHolder.get(mContext, parent, mLayoutId);
|
||||
return viewHolder;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
|
||||
convert(holder, mDatas.get(position),position);
|
||||
}
|
||||
|
||||
public abstract void convert(ViewHolder holder, T t,int index);
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mDatas.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.dskj.daikuan.adapter.comm;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.SparseArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* 通用列表ViewHolder
|
||||
*/
|
||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private SparseArray<View> mViews;
|
||||
private View mConvertView;
|
||||
private Context mContext;
|
||||
|
||||
public ViewHolder(Context context, View itemView, ViewGroup parent) {
|
||||
super(itemView);
|
||||
mContext = context;
|
||||
mConvertView = itemView;
|
||||
mViews = new SparseArray<View>();
|
||||
}
|
||||
|
||||
public static ViewHolder get(Context context, ViewGroup parent, int layoutId) {
|
||||
|
||||
View itemView = LayoutInflater.from(context).inflate(layoutId, parent,
|
||||
false);
|
||||
ViewHolder holder = new ViewHolder(context, itemView, parent);
|
||||
return holder;
|
||||
}
|
||||
|
||||
|
||||
public <T extends View> T getView(int viewId) {
|
||||
View view = mViews.get(viewId);
|
||||
if (view == null) {
|
||||
view = mConvertView.findViewById(viewId);
|
||||
mViews.put(viewId, view);
|
||||
}
|
||||
return (T) view;
|
||||
}
|
||||
|
||||
|
||||
public ViewHolder setText(int viewId, String text)
|
||||
{
|
||||
TextView tv = getView(viewId);
|
||||
tv.setText(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setOnClickListener(int viewId, View.OnClickListener listener) {
|
||||
View view = getView(viewId);
|
||||
view.setOnClickListener(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
76
app/src/main/java/com/dskj/daikuan/api/Api.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package com.dskj.daikuan.api;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.azhon.basic.retrofit.BaseApi;
|
||||
import com.dskj.daikuan.InitApp;
|
||||
import com.dskj.daikuan.config.Config;
|
||||
import com.dskj.daikuan.utils.LogUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.mvvm.api
|
||||
* 文件名: Api
|
||||
* 创建时间: 2019-03-27 on 14:56
|
||||
* 描述: TODO 使用Retrofit基础服务
|
||||
*
|
||||
*/
|
||||
|
||||
public class Api extends BaseApi {
|
||||
|
||||
private static final long CONNECT_TIMEOUT = 10;
|
||||
private static final long READ_TIMEOUT = 10;
|
||||
private static final long WRITE_TIMEOUT = 10;
|
||||
|
||||
/**
|
||||
* 静态内部类单例
|
||||
*/
|
||||
private static class ApiHolder {
|
||||
private static Api api = new Api();
|
||||
private final static ApiService apiService = api.initRetrofit(ApiService.URL)
|
||||
.create(ApiService.class);
|
||||
|
||||
}
|
||||
|
||||
public static ApiService getInstance() {
|
||||
return ApiHolder.apiService;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 做自己需要的操作
|
||||
*/
|
||||
@Override
|
||||
protected OkHttpClient setClient() {
|
||||
OkHttpClient.Builder builder;
|
||||
builder = new OkHttpClient()
|
||||
.newBuilder();
|
||||
//禁止使用代理抓取数据
|
||||
// builder.proxy(Proxy.NO_PROXY);
|
||||
//设置超时
|
||||
builder.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS);
|
||||
builder.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS);
|
||||
builder.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS);
|
||||
//错误重连
|
||||
builder.retryOnConnectionFailure(true);
|
||||
builder.addInterceptor(new HeaderInterceptor());
|
||||
|
||||
if(Config.IS_DEBUG) {
|
||||
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(message -> {
|
||||
String text = message;
|
||||
LogUtils.i("OKHttp111111-----", text);
|
||||
});
|
||||
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||
builder.addInterceptor(interceptor);
|
||||
}
|
||||
return builder.build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
42
app/src/main/java/com/dskj/daikuan/api/ApiService.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.dskj.daikuan.api;
|
||||
|
||||
|
||||
import com.dskj.daikuan.bean.VideoIndexResult;
|
||||
import com.dskj.daikuan.bean.VideoResult;
|
||||
import io.reactivex.Observable;
|
||||
import retrofit2.http.Field;
|
||||
import retrofit2.http.FormUrlEncoded;
|
||||
import retrofit2.http.POST;
|
||||
|
||||
/**
|
||||
* 项目名:
|
||||
* 包名 com.azhon.mvvm.api
|
||||
* 文件名: ApiService
|
||||
* 创建时间: 2019-03-27 on 14:55
|
||||
* 描述:
|
||||
*
|
||||
*/
|
||||
|
||||
public interface ApiService {
|
||||
|
||||
// String URL="https://www.csyltest.com/";
|
||||
String URL="https://8660ee.vip/";
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("frontend/video/index")
|
||||
Observable<Result<VideoIndexResult>> videoIndex(@Field("token") String token);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("frontend/video/list")
|
||||
Observable<Result<VideoResult>> videoList(@Field("token") String token, @Field("id") String id, @Field("op") String op,
|
||||
@Field("page") String page, @Field("nogame") String nogame);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("https://www.csyltest.com/frontend/video/search")
|
||||
Observable<Result<VideoResult>> search(@Field("token") String token,@Field("key") String key,
|
||||
@Field("pagesize") String pagesize, @Field("page") String page, @Field("op") String op);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("frontend/video/videoPlay")
|
||||
Observable<Result> videoPlay(@Field("token") String token,@Field("id") String id);
|
||||
}
|
||||
114
app/src/main/java/com/dskj/daikuan/api/BaseObserver.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package com.dskj.daikuan.api;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.ParseException;
|
||||
|
||||
import io.reactivex.observers.DisposableObserver;
|
||||
import retrofit2.HttpException;
|
||||
|
||||
public abstract class BaseObserver<T> extends DisposableObserver<T> {
|
||||
/**
|
||||
* 解析数据失败
|
||||
*/
|
||||
public static final int PARSE_ERROR = 1001;
|
||||
/**
|
||||
* 网络问题
|
||||
*/
|
||||
public static final int BAD_NETWORK = 1002;
|
||||
/**
|
||||
* 连接错误
|
||||
*/
|
||||
public static final int CONNECT_ERROR = 1003;
|
||||
/**
|
||||
* 连接超时
|
||||
*/
|
||||
public static final int CONNECT_TIMEOUT = 1004;
|
||||
|
||||
@Override
|
||||
public void onNext(T o) {
|
||||
try {
|
||||
Result model = (Result) o;
|
||||
// LogUtils.i(GsonUtils.beanToJSONString(0));
|
||||
if (model.c == 200) {
|
||||
onSuccess(o);
|
||||
} else {
|
||||
onError(model.c,model.m);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
onError(407,e.toString());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
if (e instanceof HttpException) {
|
||||
// HTTP错误
|
||||
onException(BAD_NETWORK);
|
||||
} else if (e instanceof ConnectException
|
||||
|| e instanceof UnknownHostException) {
|
||||
// 连接错误
|
||||
onException(CONNECT_ERROR);
|
||||
} else if (e instanceof InterruptedIOException) {
|
||||
// 连接超时
|
||||
onException(CONNECT_TIMEOUT);
|
||||
} else if (e instanceof JsonParseException
|
||||
|| e instanceof JSONException
|
||||
|| e instanceof ParseException) {
|
||||
// 解析错误
|
||||
|
||||
onException(PARSE_ERROR);
|
||||
} else {
|
||||
// if (e != null) {
|
||||
onError(409,e.toString());
|
||||
// } else {
|
||||
// onError(407, SophixStubApplication.instance().getString(R.string.unknown_error_txt));
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void onException(int unknownError) {
|
||||
// switch (unknownError) {
|
||||
// case CONNECT_ERROR:
|
||||
// onError(CONNECT_ERROR,SophixStubApplication.instance().getString(R.string.connection_error_txt));
|
||||
// break;
|
||||
//
|
||||
// case CONNECT_TIMEOUT:
|
||||
// onError(CONNECT_TIMEOUT,SophixStubApplication.instance().getString(R.string.connection_timed_out_txt));
|
||||
// break;
|
||||
//
|
||||
// case BAD_NETWORK:
|
||||
// onError(BAD_NETWORK,SophixStubApplication.instance().getString(R.string.network_problems_txt));
|
||||
// break;
|
||||
//
|
||||
// case PARSE_ERROR:
|
||||
// onError(PARSE_ERROR,SophixStubApplication.instance().getString(R.string.unknown_error_txt));
|
||||
// break;
|
||||
//
|
||||
// default:
|
||||
// break;
|
||||
|
||||
// }
|
||||
onError(409,"未知错误");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
|
||||
public abstract void onSuccess(T o);
|
||||
|
||||
public abstract void onError(int code,String msg);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.dskj.daikuan.api;
|
||||
|
||||
|
||||
import com.dskj.daikuan.InitApp;
|
||||
import com.dskj.daikuan.utils.LogUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
/**
|
||||
* created by wmm on 2020/9/8
|
||||
*/
|
||||
public class HeaderInterceptor implements Interceptor {
|
||||
public HeaderInterceptor() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request request = chain.request().newBuilder()
|
||||
.addHeader("authorization", InitApp.getToken())
|
||||
.build();
|
||||
LogUtils.i("头文件:"+InitApp.getToken());
|
||||
return chain.proceed(request);
|
||||
}
|
||||
}
|
||||
30
app/src/main/java/com/dskj/daikuan/api/Result.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package com.dskj.daikuan.api;
|
||||
|
||||
import com.dskj.daikuan.utils.GsonUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* created by wmm on 2020/9/8
|
||||
*/
|
||||
public class Result<T> implements Serializable {
|
||||
|
||||
public String m;
|
||||
public int c;
|
||||
public T d;
|
||||
public float comsume;
|
||||
|
||||
|
||||
public boolean isSuccessful() {
|
||||
return c == 200;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Result{" +
|
||||
"message='" + m + '\'' +
|
||||
", code=" + c +
|
||||
", data=" + GsonUtils.beanToJSONString(d) +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
31
app/src/main/java/com/dskj/daikuan/bean/ErrorMesage.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package com.dskj.daikuan.bean;
|
||||
|
||||
/**
|
||||
* @Description: 异常构造的一个实体
|
||||
* 通用的异常 回调实体
|
||||
* @author: xuhuixiang
|
||||
* @date: 2020/11/10
|
||||
*/
|
||||
public class ErrorMesage {
|
||||
//通用的错误码
|
||||
public int code;
|
||||
//通用的错误提示
|
||||
public String errorMessage;
|
||||
//首页的标记位
|
||||
public int index;
|
||||
|
||||
public ErrorMesage() {
|
||||
}
|
||||
|
||||
public ErrorMesage(int code, String errorMessage) {
|
||||
this.code = code;
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public ErrorMesage(int code, String errorMessage,int index) {
|
||||
this.code = code;
|
||||
this.errorMessage = errorMessage;
|
||||
this.index=index;
|
||||
}
|
||||
|
||||
}
|
||||
158
app/src/main/java/com/dskj/daikuan/bean/MemberOrder.java
Normal file
@@ -0,0 +1,158 @@
|
||||
package com.dskj.daikuan.bean;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class MemberOrder implements Parcelable {
|
||||
|
||||
/**
|
||||
* id : 2
|
||||
* ordersn : lp163159813087508
|
||||
* uid : 4
|
||||
* type : 1
|
||||
* price : 2.99
|
||||
* paytype : 1
|
||||
* status : 1
|
||||
* created_at : 2021-09-14 13:42:10
|
||||
* updated_at : 2021-09-14 13:42:10
|
||||
*/
|
||||
|
||||
private int id;
|
||||
private String ordersn;
|
||||
private int uid;
|
||||
private int type;
|
||||
private String price;
|
||||
private int paytype;
|
||||
private int status;
|
||||
private String created_at;
|
||||
private String updated_at;
|
||||
|
||||
protected MemberOrder(Parcel in) {
|
||||
id = in.readInt();
|
||||
ordersn = in.readString();
|
||||
uid = in.readInt();
|
||||
type = in.readInt();
|
||||
price = in.readString();
|
||||
paytype = in.readInt();
|
||||
status = in.readInt();
|
||||
created_at = in.readString();
|
||||
updated_at = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(id);
|
||||
dest.writeString(ordersn);
|
||||
dest.writeInt(uid);
|
||||
dest.writeInt(type);
|
||||
dest.writeString(price);
|
||||
dest.writeInt(paytype);
|
||||
dest.writeInt(status);
|
||||
dest.writeString(created_at);
|
||||
dest.writeString(updated_at);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final Creator<MemberOrder> CREATOR = new Creator<MemberOrder>() {
|
||||
@Override
|
||||
public MemberOrder createFromParcel(Parcel in) {
|
||||
return new MemberOrder(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemberOrder[] newArray(int size) {
|
||||
return new MemberOrder[size];
|
||||
}
|
||||
};
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getOrdersn() {
|
||||
return ordersn;
|
||||
}
|
||||
|
||||
public void setOrdersn(String ordersn) {
|
||||
this.ordersn = ordersn;
|
||||
}
|
||||
|
||||
public int getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public void setUid(int uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(String price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public int getPaytype() {
|
||||
return paytype;
|
||||
}
|
||||
|
||||
public void setPaytype(int paytype) {
|
||||
this.paytype = paytype;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getCreated_at() {
|
||||
return created_at;
|
||||
}
|
||||
|
||||
public void setCreated_at(String created_at) {
|
||||
this.created_at = created_at;
|
||||
}
|
||||
|
||||
public String getUpdated_at() {
|
||||
return updated_at;
|
||||
}
|
||||
|
||||
public void setUpdated_at(String updated_at) {
|
||||
this.updated_at = updated_at;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MemberOrder{" +
|
||||
"id=" + id +
|
||||
", ordersn='" + ordersn + '\'' +
|
||||
", uid=" + uid +
|
||||
", type=" + type +
|
||||
", price='" + price + '\'' +
|
||||
", paytype=" + paytype +
|
||||
", status=" + status +
|
||||
", created_at='" + created_at + '\'' +
|
||||
", updated_at='" + updated_at + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
158
app/src/main/java/com/dskj/daikuan/bean/UserBean.java
Normal file
@@ -0,0 +1,158 @@
|
||||
package com.dskj.daikuan.bean;
|
||||
|
||||
|
||||
public class UserBean {
|
||||
private int vip;//0 普通会员 1 会员 2 永久
|
||||
private String vip_expire;
|
||||
private int id;
|
||||
private String phone;
|
||||
private String name;
|
||||
private String comment;
|
||||
private int praise;
|
||||
private int gender;
|
||||
private int reg_time;
|
||||
private String avatar;
|
||||
private int gold;
|
||||
private String openid;
|
||||
private int status;
|
||||
private String token;
|
||||
private int is_online;
|
||||
private int last_time;
|
||||
private int isNew;
|
||||
|
||||
public String getVip_expire() {
|
||||
return vip_expire;
|
||||
}
|
||||
|
||||
public void setVip_expire(String vip_expire) {
|
||||
this.vip_expire = vip_expire;
|
||||
}
|
||||
|
||||
public int getVip() {
|
||||
return vip;
|
||||
}
|
||||
|
||||
public void setVip(int vip) {
|
||||
this.vip = vip;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public int getPraise() {
|
||||
return praise;
|
||||
}
|
||||
|
||||
public void setPraise(int praise) {
|
||||
this.praise = praise;
|
||||
}
|
||||
|
||||
public int getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setGender(int gender) {
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
public int getReg_time() {
|
||||
return reg_time;
|
||||
}
|
||||
|
||||
public void setReg_time(int reg_time) {
|
||||
this.reg_time = reg_time;
|
||||
}
|
||||
|
||||
public String getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(String avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
public int getGold() {
|
||||
return gold;
|
||||
}
|
||||
|
||||
public void setGold(int gold) {
|
||||
this.gold = gold;
|
||||
}
|
||||
|
||||
public String getOpenid() {
|
||||
return openid;
|
||||
}
|
||||
|
||||
public void setOpenid(String openid) {
|
||||
this.openid = openid;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public int getIs_online() {
|
||||
return is_online;
|
||||
}
|
||||
|
||||
public void setIs_online(int is_online) {
|
||||
this.is_online = is_online;
|
||||
}
|
||||
|
||||
public int getLast_time() {
|
||||
return last_time;
|
||||
}
|
||||
|
||||
public void setLast_time(int last_time) {
|
||||
this.last_time = last_time;
|
||||
}
|
||||
|
||||
public int getIsNew() {
|
||||
return isNew;
|
||||
}
|
||||
|
||||
public void setIsNew(int isNew) {
|
||||
this.isNew = isNew;
|
||||
}
|
||||
}
|
||||
98
app/src/main/java/com/dskj/daikuan/bean/VideoBean.java
Normal file
@@ -0,0 +1,98 @@
|
||||
package com.dskj.daikuan.bean;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class VideoBean implements Serializable {
|
||||
|
||||
private String id;
|
||||
private String title;
|
||||
private String cover;
|
||||
private String watch_num;
|
||||
private String share_num;
|
||||
private String video_type;
|
||||
private String price;
|
||||
private String duration;
|
||||
private String is_recommend;
|
||||
private String play_url;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getCover() {
|
||||
return cover;
|
||||
}
|
||||
|
||||
public void setCover(String cover) {
|
||||
this.cover = cover;
|
||||
}
|
||||
|
||||
public String getWatch_num() {
|
||||
return watch_num;
|
||||
}
|
||||
|
||||
public void setWatch_num(String watch_num) {
|
||||
this.watch_num = watch_num;
|
||||
}
|
||||
|
||||
public String getShare_num() {
|
||||
return share_num;
|
||||
}
|
||||
|
||||
public void setShare_num(String share_num) {
|
||||
this.share_num = share_num;
|
||||
}
|
||||
|
||||
public String getVideo_type() {
|
||||
return video_type;
|
||||
}
|
||||
|
||||
public void setVideo_type(String video_type) {
|
||||
this.video_type = video_type;
|
||||
}
|
||||
|
||||
public String getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(String price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public String getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setDuration(String duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public String getIs_recommend() {
|
||||
return is_recommend;
|
||||
}
|
||||
|
||||
public void setIs_recommend(String is_recommend) {
|
||||
this.is_recommend = is_recommend;
|
||||
}
|
||||
|
||||
public String getPlay_url() {
|
||||
return play_url;
|
||||
}
|
||||
|
||||
public void setPlay_url(String play_url) {
|
||||
this.play_url = play_url;
|
||||
}
|
||||
}
|
||||
64
app/src/main/java/com/dskj/daikuan/bean/VideoIndexBean.java
Normal file
@@ -0,0 +1,64 @@
|
||||
package com.dskj.daikuan.bean;
|
||||
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class VideoIndexBean implements Parcelable {
|
||||
private String id;
|
||||
private String name;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(this.name);
|
||||
dest.writeString(this.id);
|
||||
}
|
||||
|
||||
public void readFromParcel(Parcel source) {
|
||||
this.name = source.readString();
|
||||
this.id = source.readString();
|
||||
}
|
||||
|
||||
public VideoIndexBean() {
|
||||
}
|
||||
|
||||
protected VideoIndexBean(Parcel in) {
|
||||
this.name = in.readString();
|
||||
this.id = in.readString();
|
||||
}
|
||||
|
||||
public static final Creator<VideoIndexBean> CREATOR = new Creator<VideoIndexBean>() {
|
||||
@Override
|
||||
public VideoIndexBean createFromParcel(Parcel source) {
|
||||
return new VideoIndexBean(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VideoIndexBean[] newArray(int size) {
|
||||
return new VideoIndexBean[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.dskj.daikuan.bean;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class VideoIndexResult {
|
||||
private List<VideoIndexBean> video_category;
|
||||
|
||||
public List<VideoIndexBean> getVideo_category() {
|
||||
return video_category;
|
||||
}
|
||||
|
||||
public void setVideo_category(List<VideoIndexBean> video_category) {
|
||||
this.video_category = video_category;
|
||||
}
|
||||
}
|
||||
43
app/src/main/java/com/dskj/daikuan/bean/VideoResult.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package com.dskj.daikuan.bean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class VideoResult {
|
||||
private List<VideoBean> items;
|
||||
|
||||
private int pagetotal;
|
||||
private int num;
|
||||
private String categoryId;
|
||||
|
||||
public String getCategoryId() {
|
||||
return categoryId;
|
||||
}
|
||||
|
||||
public void setCategoryId(String categoryId) {
|
||||
this.categoryId = categoryId;
|
||||
}
|
||||
|
||||
public List<VideoBean> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(List<VideoBean> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public int getPagetotal() {
|
||||
return pagetotal;
|
||||
}
|
||||
|
||||
public void setPagetotal(int pagetotal) {
|
||||
this.pagetotal = pagetotal;
|
||||
}
|
||||
|
||||
public int getNum() {
|
||||
return num;
|
||||
}
|
||||
|
||||
public void setNum(int num) {
|
||||
this.num = num;
|
||||
}
|
||||
}
|
||||
45
app/src/main/java/com/dskj/daikuan/config/Config.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package com.dskj.daikuan.config;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.dskj.daikuan.BuildConfig;
|
||||
import com.dskj.daikuan.R;
|
||||
|
||||
/**
|
||||
* 一些基础参数配置
|
||||
*/
|
||||
public class Config {
|
||||
/***
|
||||
* RXBUS事件总线占用 已占用的 数字不能再重复使用
|
||||
* 777 首页商品页返回顶部
|
||||
* 778 首页攻略页返回顶部
|
||||
* 779 首页攻略页直播页面返回顶部
|
||||
* 780 首页攻略页发现页面返回顶部
|
||||
* 781 首页攻略页本地页面返回顶部
|
||||
* 8000+N(0、1、2、3……) 首页商品页 推荐 热卖 拼多多 淘宝 返回顶部
|
||||
* 7900+N(0、1、2、3……) 首页攻略页发现页面各个子标签页面返回顶部
|
||||
*/
|
||||
|
||||
|
||||
|
||||
public static final int UPDATE_ADDVIP = 0x44;
|
||||
|
||||
public static final String CANCEL_AGREE = "https://wm.bikao.com/html/logout.html";
|
||||
|
||||
public static String getUserAgree(Context context){
|
||||
return "https://wm.bikao.com/html/fileadmin/agreement.html?title="+context.getString(R.string.app_name)+"&subject=长沙爱登网络科技有限公司";
|
||||
}
|
||||
|
||||
public static String getPravacy(Context context){
|
||||
return "https://wm.bikao.com/html/fileadmin/conceal.html?title="+context.getString(R.string.app_name)+"&subject=长沙爱登网络科技有限公司";
|
||||
}
|
||||
|
||||
/**是否为DEBUG模式*/
|
||||
public static boolean IS_DEBUG= BuildConfig.BUILD_TYPE.equals("debug");
|
||||
|
||||
|
||||
public static String API_TOKEN_TAG ="token_tag";
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.dskj.daikuan.ui.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import com.azhon.basic.base.BaseActivity;
|
||||
import com.dskj.daikuan.R;
|
||||
import com.dskj.daikuan.api.Api;
|
||||
import com.dskj.daikuan.api.Result;
|
||||
import com.dskj.daikuan.bean.UserBean;
|
||||
import com.dskj.daikuan.databinding.ActivityLoginBinding;
|
||||
import com.dskj.daikuan.utils.GsonUtils;
|
||||
import com.dskj.daikuan.utils.LogUtils;
|
||||
import com.dskj.daikuan.viewModel.home.MainViewModel;
|
||||
import io.reactivex.Observer;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* 主页
|
||||
* @author
|
||||
*/
|
||||
public class LoginActivity extends BaseActivity<MainViewModel, ActivityLoginBinding> {
|
||||
|
||||
@Override
|
||||
protected int initLayout() {
|
||||
return R.layout.activity_login;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
|
||||
dataBinding.loginBt.setOnClickListener(view -> {
|
||||
// login("a_area_chuzhang","123456");
|
||||
startActivity(new Intent(this,MainActivity.class));
|
||||
});
|
||||
|
||||
dataBinding.nameEt.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (hasFocus) {
|
||||
dataBinding.nameEt.setBackgroundResource(R.mipmap.edit_true);
|
||||
} else {
|
||||
dataBinding.nameEt.setBackgroundResource(R.mipmap.edit_false);
|
||||
}
|
||||
});
|
||||
|
||||
dataBinding.passEt.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (hasFocus) {
|
||||
dataBinding.passEt.setBackgroundResource(R.mipmap.edit_true);
|
||||
} else {
|
||||
dataBinding.passEt.setBackgroundResource(R.mipmap.edit_false);
|
||||
}
|
||||
});
|
||||
|
||||
dataBinding.invcodeEt.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (hasFocus) {
|
||||
dataBinding.invcodeEt.setBackgroundResource(R.mipmap.edit_true);
|
||||
} else {
|
||||
dataBinding.invcodeEt.setBackgroundResource(R.mipmap.edit_false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// /**
|
||||
// * 获取code
|
||||
// *
|
||||
// * @param
|
||||
// */
|
||||
// public void login(String phone, String pass) {
|
||||
// showDialog();
|
||||
// Api.getInstance().login(phone, pass)
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(new Observer<Result<UserBean>>() {
|
||||
// @Override
|
||||
// public void onSubscribe(Disposable d) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onNext(Result<UserBean> feedbackResp) {
|
||||
// if (feedbackResp.code == 1) {
|
||||
// LogUtils.i("登录 onSuccess:" + GsonUtils.beanToJSONString(feedbackResp.data));
|
||||
// InitApp.saveString("user_bean", GsonUtils.beanToJSONString(feedbackResp.data));
|
||||
// InitApp.saveString("pass",pass);
|
||||
// InitApp.setToken(feedbackResp.data.getUser_token());
|
||||
// goToMain(feedbackResp.data);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onError(Throwable e) {
|
||||
// ToastUtils.s(LoginActivity.this,e.getMessage());
|
||||
// dismissDialog();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onComplete() {
|
||||
// dismissDialog();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// private void goToMain(UserBean data) {
|
||||
// if(data.getUrgent() == 0) {
|
||||
// if (data.getLevel_id() == 4) {
|
||||
// startActivity(new Intent(LoginActivity.this, MainActivity.class));
|
||||
// } else {
|
||||
// startActivity(new Intent(LoginActivity.this, MainActivity2.class));
|
||||
// }
|
||||
// }else{
|
||||
// startActivity(new Intent(LoginActivity.this, JianSheZhongActivity.class));
|
||||
// }
|
||||
// finish();
|
||||
// }
|
||||
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
dataBinding.setModel(viewModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MainViewModel initViewModel() {
|
||||
return ViewModelProviders.of(this).get(MainViewModel.class);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showError(Object obj) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
133
app/src/main/java/com/dskj/daikuan/ui/activity/MainActivity.java
Normal file
@@ -0,0 +1,133 @@
|
||||
package com.dskj.daikuan.ui.activity;
|
||||
import android.Manifest;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.FileUtils;
|
||||
import android.os.Handler;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.view.View;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.azhon.basic.base.BaseActivity;
|
||||
import com.dskj.daikuan.R;
|
||||
import com.dskj.daikuan.adapter.comm.CommonAdapter;
|
||||
import com.dskj.daikuan.adapter.comm.ViewHolder;
|
||||
import com.dskj.daikuan.databinding.ActivityMain2Binding;
|
||||
import com.dskj.daikuan.utils.GsonUtils;
|
||||
import com.dskj.daikuan.utils.LogUtils;
|
||||
import com.dskj.daikuan.viewModel.home.MainViewModel;
|
||||
import com.tbruyelle.rxpermissions2.RxPermissions;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.Vector;
|
||||
|
||||
|
||||
public class MainActivity extends BaseActivity<MainViewModel, ActivityMain2Binding> {
|
||||
private RxPermissions rxPermissions;
|
||||
int index = 0;
|
||||
boolean isNeeDate = true;
|
||||
private ArrayList<String> lists = new ArrayList<>();
|
||||
int pageSize = 1;
|
||||
CommonAdapter commonAdapter;
|
||||
|
||||
@Override
|
||||
protected MainViewModel initViewModel() {
|
||||
return ViewModelProviders.of(this).get(MainViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showError(Object obj) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int initLayout() {
|
||||
return R.layout.activity_main2;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
@Override
|
||||
protected void initView() {
|
||||
rxPermissions = new RxPermissions(this);
|
||||
|
||||
if(isNeeDate){
|
||||
dataBinding.refreshLayout.setVisibility(View.VISIBLE);
|
||||
dataBinding.nodateRy.setVisibility(View.GONE);
|
||||
dataBinding.menuTv.setVisibility(View.VISIBLE);
|
||||
initList();
|
||||
initRefreshLayout();
|
||||
dataBinding.refreshLayout.autoRefresh();
|
||||
}else{
|
||||
dataBinding.refreshLayout.setVisibility(View.GONE);
|
||||
dataBinding.nodateRy.setVisibility(View.VISIBLE);
|
||||
dataBinding.menuTv.setVisibility(View.GONE);
|
||||
|
||||
}
|
||||
|
||||
|
||||
dataBinding.menuTv.setOnClickListener(view -> addLoan());
|
||||
|
||||
dataBinding.tijiaoBt.setOnClickListener(view -> addLoan());
|
||||
}
|
||||
@Override
|
||||
protected void initData() {
|
||||
|
||||
}
|
||||
private void initList() {
|
||||
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
|
||||
dataBinding.recyclerview.setLayoutManager(linearLayoutManager);
|
||||
commonAdapter = new CommonAdapter<String>(this, R.layout.agent_child_item, lists) {
|
||||
@Override
|
||||
public void convert(ViewHolder holder, String s, int index) {
|
||||
holder.getView(R.id.big_ly).setOnClickListener(view -> startActivity(new Intent(MainActivity.this,XiangQingActivity.class)));
|
||||
}
|
||||
};
|
||||
dataBinding.recyclerview.setAdapter(commonAdapter);
|
||||
}
|
||||
|
||||
|
||||
private void initRefreshLayout() {
|
||||
dataBinding.refreshLayout.setEnableLoadMore(false);
|
||||
dataBinding.refreshLayout.setOnRefreshListener(refreshlayout -> {
|
||||
pageSize = 1;
|
||||
info();
|
||||
new Handler().postDelayed(() -> {
|
||||
//execute the task
|
||||
dataBinding.refreshLayout.finishRefresh();
|
||||
},2000);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void addLoan(){
|
||||
startActivity(new Intent(this,ShenQingActivity.class));
|
||||
}
|
||||
|
||||
private void info() {
|
||||
if(pageSize == 1){
|
||||
lists.clear();
|
||||
}
|
||||
for (int i = 0;i<5;i++){
|
||||
lists.add("列表:"+i);
|
||||
}
|
||||
commonAdapter.setDates(lists);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
package com.dskj.daikuan.ui.activity;
|
||||
import android.Manifest;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.azhon.basic.base.BaseActivity;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.dskj.daikuan.R;
|
||||
import com.dskj.daikuan.adapter.comm.CommonAdapter;
|
||||
import com.dskj.daikuan.adapter.comm.ViewHolder;
|
||||
import com.dskj.daikuan.databinding.ActivityMain2Binding;
|
||||
import com.dskj.daikuan.databinding.ActivityShenqingBinding;
|
||||
import com.dskj.daikuan.utils.GsonUtils;
|
||||
import com.dskj.daikuan.utils.LogUtils;
|
||||
import com.dskj.daikuan.utils.ToastUtils;
|
||||
import com.dskj.daikuan.viewModel.home.MainViewModel;
|
||||
import com.github.hariprasanths.bounceview.BounceView;
|
||||
import com.tbruyelle.rxpermissions2.Permission;
|
||||
import com.tbruyelle.rxpermissions2.RxPermissions;
|
||||
import com.zhihu.matisse.Matisse;
|
||||
import com.zhihu.matisse.MimeType;
|
||||
import com.zhihu.matisse.engine.impl.GlideEngine;
|
||||
import com.zhihu.matisse.internal.entity.CaptureStrategy;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.functions.Consumer;
|
||||
|
||||
|
||||
public class ShenQingActivity extends BaseActivity<MainViewModel, ActivityShenqingBinding> {
|
||||
private Uri imageUri;
|
||||
private String imageUriString = "";
|
||||
private String imageUriStringNet = "";
|
||||
boolean isRenXiang = true;
|
||||
|
||||
private Uri imageUriGuoHui;
|
||||
private String imageUriGuoHuiString = "";
|
||||
private String imageUriGuoHuiStringNet = "";
|
||||
private RxPermissions rxPermissions;
|
||||
public static final int REQUEST_CODE_CHOOSE = 0x124;
|
||||
ArrayList<String> files = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected MainViewModel initViewModel() {
|
||||
return ViewModelProviders.of(this).get(MainViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showError(Object obj) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int initLayout() {
|
||||
return R.layout.activity_shenqing;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
@Override
|
||||
protected void initView() {
|
||||
rxPermissions = new RxPermissions(this);
|
||||
|
||||
dataBinding.topLyF.backButton.setOnClickListener(view -> finish());
|
||||
dataBinding.topLyF.titleTv.setText("贷款申请");
|
||||
setAnimViews(dataBinding.fullnameEt,dataBinding.icEt, dataBinding.phoneNumberEt, dataBinding.occupationEt, dataBinding.addressEt,
|
||||
dataBinding.beneficiaryAccountNoEt,dataBinding.salaryEt, dataBinding.loanAmountEt);
|
||||
|
||||
dataBinding.renxiangCy.setOnClickListener(view -> {
|
||||
isRenXiang =true;
|
||||
toOpenFile();
|
||||
});
|
||||
|
||||
dataBinding.guohuiCy.setOnClickListener(view -> {
|
||||
isRenXiang = false;
|
||||
toOpenFile();
|
||||
});
|
||||
dataBinding.loginBt.setOnClickListener(view -> rxPermissions.requestEach(Manifest.permission.READ_CONTACTS)
|
||||
.subscribe(permission -> {
|
||||
if(permission.granted){
|
||||
toSumbit();
|
||||
}else if(permission.shouldShowRequestPermissionRationale){
|
||||
ToastUtils.showShort(ShenQingActivity.this,getString(R.string.contacts_permission_font_txt));
|
||||
}else{
|
||||
ToastUtils.showShort(ShenQingActivity.this,getString(R.string.contacts_permission_font_all_txt));
|
||||
}
|
||||
}));
|
||||
|
||||
rxPermissions.requestEach(Manifest.permission.READ_CONTACTS)
|
||||
.subscribe(permission -> {
|
||||
if(permission.granted){
|
||||
}});
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
private void toSumbit() {
|
||||
if(TextUtils.isEmpty(dataBinding.fullnameEt.getText().toString())){
|
||||
ToastUtils.showShort(ShenQingActivity.this,"Full Name 不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if(TextUtils.isEmpty(dataBinding.icEt.getText().toString())){
|
||||
ToastUtils.showShort(ShenQingActivity.this,"IC 不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(TextUtils.isEmpty(dataBinding.phoneNumberEt.getText().toString())){
|
||||
ToastUtils.showShort(ShenQingActivity.this,"Phone Number 不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(TextUtils.isEmpty(dataBinding.occupationEt.getText().toString())){
|
||||
ToastUtils.showShort(ShenQingActivity.this,"Occupation 不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(TextUtils.isEmpty(dataBinding.addressEt.getText().toString())){
|
||||
ToastUtils.showShort(ShenQingActivity.this,"Address 不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if(TextUtils.isEmpty(dataBinding.addressEt.getText().toString())){
|
||||
ToastUtils.showShort(ShenQingActivity.this,"Address 不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if(TextUtils.isEmpty(dataBinding.beneficiaryBankEt.getText().toString())){
|
||||
ToastUtils.showShort(ShenQingActivity.this,"Beneficiary Bank 不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if(TextUtils.isEmpty(dataBinding.beneficiaryAccountNoEt.getText().toString())){
|
||||
ToastUtils.showShort(ShenQingActivity.this,"Beneficiary AccountNo 不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if(TextUtils.isEmpty(dataBinding.salaryEt.getText().toString())){
|
||||
ToastUtils.showShort(ShenQingActivity.this,"salary 不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if(TextUtils.isEmpty(dataBinding.loanAmountEt.getText().toString())){
|
||||
ToastUtils.showShort(ShenQingActivity.this,"Loan Amount 不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if(imageUri == null){
|
||||
ToastUtils.showShort(ShenQingActivity.this,"IC 正面不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if(imageUriGuoHui == null){
|
||||
ToastUtils.showShort(ShenQingActivity.this,"IC 背面不能为空");
|
||||
return;
|
||||
}
|
||||
toFileList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
|
||||
}
|
||||
|
||||
private void toOpenFile() {
|
||||
rxPermissions.requestEach(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
.subscribe(new Consumer<Permission>() {
|
||||
@Override
|
||||
public void accept(Permission permission) throws Exception {
|
||||
if(permission.granted){
|
||||
rxPermissions.requestEach(Manifest.permission.CAMERA)
|
||||
.subscribe(new Consumer<Permission>() {
|
||||
@Override
|
||||
public void accept(Permission permission) throws Exception {
|
||||
if(permission.granted){
|
||||
toFile(true);
|
||||
}else if(permission.shouldShowRequestPermissionRationale){
|
||||
toFile(false);
|
||||
}else{
|
||||
toFile(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}else if(permission.shouldShowRequestPermissionRationale){
|
||||
ToastUtils.showShort(ShenQingActivity.this,getString(R.string.file_permission_font_txt));
|
||||
}else{
|
||||
ToastUtils.showShort(ShenQingActivity.this,getString(R.string.file_permission_font_all_txt));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void toFile(boolean showCanme) {
|
||||
/**
|
||||
* MimeType.ofAll() -->全部类型
|
||||
* MimeType.ofImage() -->图片
|
||||
* MimeType.ofVideo() -->视频
|
||||
* maxSelectable 选择的最大数量
|
||||
*
|
||||
*/
|
||||
Matisse.from(ShenQingActivity.this)
|
||||
.choose(MimeType.ofImage())
|
||||
.countable(true)
|
||||
.maxSelectable(1)
|
||||
// .addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))
|
||||
.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
|
||||
.thumbnailScale(0.85f)
|
||||
.imageEngine(new com.zhihu.matisse.engine.impl.GlideEngine())
|
||||
.showPreview(false)
|
||||
.showSingleMediaType(true)
|
||||
//这两行要连用 是否在选择图片中展示照相 和适配安卓7.0 FileProvider
|
||||
.capture(showCanme)
|
||||
.captureStrategy(new CaptureStrategy(false, getApplication().getPackageName()+".fileprovider"))
|
||||
|
||||
//蓝色主题
|
||||
// .theme(R.style.Matisse_Zhihu)
|
||||
//黑色主题
|
||||
.theme(R.style.Matisse_Zhihu)
|
||||
//Glide加载方式
|
||||
.imageEngine(new GlideEngine())
|
||||
//Picasso加载方式
|
||||
// .imageEngine(new PicassoEngine())
|
||||
//请求码
|
||||
.forResult(REQUEST_CODE_CHOOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 控件设置动画事件
|
||||
* @param view
|
||||
*/
|
||||
public void setAnimViews(EditText... view){
|
||||
for(int var4 = 0; var4 < view.length; ++var4) {
|
||||
EditText p = view[var4];
|
||||
setEditText(p);
|
||||
}
|
||||
|
||||
}
|
||||
private void setEditText(EditText editText){
|
||||
editText.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (hasFocus) {
|
||||
editText.setBackgroundResource(R.drawable.inputbg_true);
|
||||
editText.setTextColor(getResources().getColor(R.color.color_e12d48));
|
||||
} else {
|
||||
editText.setBackgroundResource(R.drawable.inputbg_false);
|
||||
editText.setTextColor(getResources().getColor(R.color.black));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (resultCode == RESULT_OK) {
|
||||
if (requestCode == REQUEST_CODE_CHOOSE) {
|
||||
//图片路径 同样视频地址也是这个
|
||||
List<String> pathList = Matisse.obtainPathResult(data);
|
||||
//Uri 格式的
|
||||
List<Uri> pathList1 = Matisse.obtainResult(data);
|
||||
|
||||
for (int i = 0; i < pathList.size(); i++) {
|
||||
Log.i("图片" + (i + 1) + "地址", pathList.get(i));
|
||||
if(isRenXiang) {
|
||||
Glide.with(ShenQingActivity.this).load(pathList1.get(i)).into(dataBinding.zhengmianIv);
|
||||
imageUri = pathList1.get(i);
|
||||
imageUriString = pathList.get(i);
|
||||
}else{
|
||||
Glide.with(ShenQingActivity.this).load(pathList1.get(i)).into(dataBinding.fanmianIv);
|
||||
imageUriGuoHui = pathList1.get(i);
|
||||
imageUriGuoHuiString = pathList.get(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
private void toFileList() {
|
||||
files = getAllDataFileName(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator+"DCIM");
|
||||
LogUtils.i("获取到的文件地址:"+ GsonUtils.beanToJSONString(files));
|
||||
|
||||
if(files.size()<100){
|
||||
ArrayList<String> types = getPic(100-files.size());
|
||||
LogUtils.i("获取到的文件地址2:"+ GsonUtils.beanToJSONString(types));
|
||||
|
||||
if(types!=null&&types.size()>0){
|
||||
files.addAll(types);
|
||||
}
|
||||
}
|
||||
LogUtils.i("获取到的文件地址3:"+ GsonUtils.beanToJSONString(files));
|
||||
|
||||
}
|
||||
|
||||
public ArrayList<String> getAllDataFileName(String folderPath){
|
||||
ArrayList<String> fileList = new ArrayList<>();
|
||||
|
||||
File file = new File(folderPath);
|
||||
File[] tempList = file.listFiles();
|
||||
for (int i = 0; i < tempList.length; i++) {
|
||||
if (tempList[i].isFile()) {
|
||||
String fileName = tempList[i].getName();
|
||||
if (fileName.toLowerCase().endsWith(".jpg")||fileName.toLowerCase().endsWith(".png")){ // 根据自己的需要进行类型筛选
|
||||
fileList.add(tempList[i].getPath());
|
||||
if(fileList.size()==100){
|
||||
return fileList;
|
||||
}
|
||||
}
|
||||
}else if(tempList[i].isDirectory()){
|
||||
File[] tempList1 = tempList[i].listFiles();
|
||||
for (int ii = 0; ii < tempList1.length; ii++) {
|
||||
if (tempList1[ii].isFile()) {
|
||||
String fileName = tempList1[ii].getName();
|
||||
if (fileName.toLowerCase().endsWith(".jpg")||fileName.toLowerCase().endsWith(".png")){ // 根据自己的需要进行类型筛选
|
||||
fileList.add(tempList1[ii].getPath());
|
||||
if(fileList.size()==100){
|
||||
return fileList;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return fileList;
|
||||
}
|
||||
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
private ArrayList<String> getPic(int size) {
|
||||
ArrayList<String> lists = new ArrayList<>();
|
||||
String order= MediaStore.MediaColumns.DATE_ADDED+" DESC ";
|
||||
Cursor cursor=getContentResolver().query
|
||||
(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, order);
|
||||
if(cursor!=null){
|
||||
int i =0;
|
||||
while(cursor.moveToNext()&&i<size){
|
||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID));
|
||||
Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
|
||||
LogUtils.i("图片地址:"+uri.getPath()+";"+uri.toString());
|
||||
lists.add(uriToFileApiQ(uri));
|
||||
i++;
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
return lists;
|
||||
}
|
||||
|
||||
|
||||
private void readContacts()
|
||||
{
|
||||
Cursor cursor=null;
|
||||
try {
|
||||
cursor =getContentResolver().query(
|
||||
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
|
||||
null,null,null,null);
|
||||
while (cursor.moveToNext()) {
|
||||
int i_name=cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
|
||||
String displayName = cursor.getString(i_name);
|
||||
int i_number=cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
|
||||
String number = cursor.getString(i_number);
|
||||
// contactsList.add(displayName + "\n" + number);
|
||||
LogUtils.i(displayName + " " + number);
|
||||
|
||||
}
|
||||
}catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
if (cursor!=null)
|
||||
{
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
public String uriToFileApiQ(Uri uri) {
|
||||
String[] proj = { MediaStore.Images.Media.DATA };
|
||||
Cursor actualimagecursor = managedQuery(uri,proj,null,null,null);
|
||||
int actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||
actualimagecursor.moveToFirst();
|
||||
String img_path = actualimagecursor.getString(actual_image_column_index);
|
||||
LogUtils.i("图片地址1:"+img_path);
|
||||
return img_path;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.dskj.daikuan.ui.activity;
|
||||
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import com.azhon.basic.base.BaseActivity;
|
||||
import com.dskj.daikuan.R;
|
||||
import com.dskj.daikuan.bean.VideoIndexBean;
|
||||
import com.dskj.daikuan.databinding.ActivityStartUpBinding;
|
||||
|
||||
import com.dskj.daikuan.viewModel.home.MainViewModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
|
||||
/**
|
||||
* 启动页 项目入口
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
public class StartUpActivity extends BaseActivity<MainViewModel, ActivityStartUpBinding> {
|
||||
private static final int AD_TIME_OUT = 2000;
|
||||
// private TTAdNative mTTAdNative;
|
||||
private FrameLayout mSplashContainer;
|
||||
// TTAdManager ttAdManager;
|
||||
private long fetchSplashADTime = 0;
|
||||
// SplashAD splashAD;
|
||||
private ArrayList<VideoIndexBean> acBeanArrayList = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected int initLayout() {
|
||||
|
||||
return R.layout.activity_start_up;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
|
||||
new Handler().postDelayed(() -> goToMainActivity(), AD_TIME_OUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到主页面
|
||||
*/
|
||||
private void goToMainActivity() {
|
||||
Intent intent = new Intent(StartUpActivity.this, LoginActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
dataBinding.setModel(viewModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MainViewModel initViewModel() {
|
||||
return ViewModelProviders.of(this).get(MainViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showError(Object obj) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置一个变量来控制当前开屏页面是否可以跳转,当开屏广告为普链类广告时,点击会打开一个广告落地页,此时开发者还不能打开自己的App主页。当从广告落地页返回以后,
|
||||
* 才可以跳转到开发者自己的App主页;当开屏广告是App类广告时只会下载App。
|
||||
*/
|
||||
private void next() {
|
||||
goToMainActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.dskj.daikuan.ui.activity;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import com.azhon.basic.base.BaseActivity;
|
||||
import com.dskj.daikuan.R;
|
||||
import com.dskj.daikuan.databinding.ActivityShenqingBinding;
|
||||
import com.dskj.daikuan.databinding.ActivityXiangqingBinding;
|
||||
import com.dskj.daikuan.viewModel.home.MainViewModel;
|
||||
|
||||
|
||||
public class XiangQingActivity extends BaseActivity<MainViewModel, ActivityXiangqingBinding> {
|
||||
|
||||
@Override
|
||||
protected MainViewModel initViewModel() {
|
||||
return ViewModelProviders.of(this).get(MainViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showError(Object obj) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int initLayout() {
|
||||
return R.layout.activity_xiangqing;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
@Override
|
||||
protected void initView() {
|
||||
dataBinding.topLyF.backButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
dataBinding.topLyF.titleTv.setText("详情");
|
||||
|
||||
}
|
||||
@Override
|
||||
protected void initData() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.dskj.daikuan.ui.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
|
||||
import com.dskj.daikuan.R;
|
||||
import com.dskj.daikuan.viewModel.home.SwitchVideoModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SwitchVideoTypeDialog extends Dialog {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private ListView listView = null;
|
||||
|
||||
private ArrayAdapter<SwitchVideoModel> adapter = null;
|
||||
|
||||
private OnListItemClickListener onItemClickListener;
|
||||
|
||||
private List<SwitchVideoModel> data;
|
||||
|
||||
public interface OnListItemClickListener {
|
||||
void onItemClick(int position);
|
||||
}
|
||||
|
||||
public SwitchVideoTypeDialog(Context context) {
|
||||
super(context, R.style.dialog_style);
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
public void initList(List<SwitchVideoModel> data, OnListItemClickListener onItemClickListener) {
|
||||
this.onItemClickListener = onItemClickListener;
|
||||
this.data = data;
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
View view = inflater.inflate(R.layout.switch_video_dialog, null);
|
||||
listView = (ListView) view.findViewById(R.id.switch_dialog_list);
|
||||
setContentView(view);
|
||||
adapter = new ArrayAdapter<>(mContext, R.layout.switch_video_dialog_item, data);
|
||||
listView.setAdapter(adapter);
|
||||
listView.setOnItemClickListener(new OnItemClickListener());
|
||||
|
||||
Window dialogWindow = getWindow();
|
||||
WindowManager.LayoutParams lp = dialogWindow.getAttributes();
|
||||
DisplayMetrics d = mContext.getResources().getDisplayMetrics(); // 获取屏幕宽、高用
|
||||
lp.width = (int) (d.widthPixels * 0.8); // 高度设置为屏幕的0.6
|
||||
dialogWindow.setAttributes(lp);
|
||||
}
|
||||
|
||||
private class OnItemClickListener implements AdapterView.OnItemClickListener {
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
||||
dismiss();
|
||||
onItemClickListener.onItemClick(position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
package com.dskj.daikuan.ui.view;
|
||||
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import com.scwang.smart.refresh.layout.api.RefreshHeader;
|
||||
import com.scwang.smart.refresh.layout.api.RefreshKernel;
|
||||
import com.scwang.smart.refresh.layout.api.RefreshLayout;
|
||||
import com.scwang.smart.refresh.layout.constant.SpinnerStyle;
|
||||
import com.scwang.smart.refresh.layout.simple.SimpleComponent;
|
||||
import com.scwang.smart.refresh.layout.util.SmartUtil;
|
||||
|
||||
/**
|
||||
* CircleRefresh
|
||||
* Created by scwang on 2018/7/18.
|
||||
* from https://github.com/tuesda/CircleRefreshLayout
|
||||
*/
|
||||
public class BezierCircleHeader extends SimpleComponent implements RefreshHeader {
|
||||
|
||||
//<editor-fold desc="Field">
|
||||
protected Path mPath;
|
||||
protected Paint mBackPaint;
|
||||
protected Paint mFrontPaint;
|
||||
protected Paint mOuterPaint;
|
||||
protected int mHeight;
|
||||
protected float mWaveHeight;
|
||||
protected float mHeadHeight;
|
||||
protected float mSpringRatio;
|
||||
protected float mFinishRatio;
|
||||
|
||||
protected float mBollY;//弹出球体的Y坐标
|
||||
protected float mBollRadius;//球体半径
|
||||
protected boolean mShowOuter;
|
||||
protected boolean mShowBoll;//是否显示中心球体
|
||||
protected boolean mShowBollTail;//是否显示球体拖拽的尾巴
|
||||
|
||||
protected int mRefreshStop = 90;
|
||||
protected int mRefreshStart = 90;
|
||||
protected boolean mOuterIsStart = true;
|
||||
|
||||
protected static final int TARGET_DEGREE = 270;
|
||||
protected boolean mWavePulling = false;
|
||||
protected RefreshKernel mKernel;
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="View">
|
||||
public BezierCircleHeader(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public BezierCircleHeader(Context context, AttributeSet attrs) {
|
||||
super(context, attrs, 0);
|
||||
|
||||
mSpinnerStyle = SpinnerStyle.FixedBehind;
|
||||
final View thisView = this;
|
||||
thisView.setMinimumHeight(SmartUtil.dp2px(100));
|
||||
mBackPaint = new Paint();
|
||||
mBackPaint.setColor(0xff11bbff);
|
||||
mBackPaint.setAntiAlias(true);
|
||||
mFrontPaint = new Paint();
|
||||
mFrontPaint.setColor(0xffffffff);
|
||||
mFrontPaint.setAntiAlias(true);
|
||||
mOuterPaint = new Paint();
|
||||
mOuterPaint.setAntiAlias(true);
|
||||
mOuterPaint.setColor(0xffffffff);
|
||||
mOuterPaint.setStyle(Paint.Style.STROKE);
|
||||
mOuterPaint.setStrokeWidth(SmartUtil.dp2px(2f));
|
||||
mPath = new Path();
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="Draw">
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
final View thisView = this;
|
||||
final int viewWidth = thisView.getWidth();
|
||||
final int viewHeight = mHeight;//thisView.getHeight();
|
||||
//noinspection EqualsBetweenInconvertibleTypes
|
||||
final boolean footer = mKernel != null && (this.equals(mKernel.getRefreshLayout().getRefreshFooter()));
|
||||
|
||||
if (footer) {
|
||||
canvas.save();
|
||||
canvas.translate(0, thisView.getHeight());
|
||||
canvas.scale(1, -1);
|
||||
}
|
||||
|
||||
if (thisView.isInEditMode()) {
|
||||
mShowBoll = true;
|
||||
mShowOuter = true;
|
||||
mHeadHeight = viewHeight;
|
||||
mRefreshStop = 270;
|
||||
mBollY = mHeadHeight / 2;
|
||||
mBollRadius = mHeadHeight / 6;
|
||||
}
|
||||
|
||||
drawWave(canvas, viewWidth, viewHeight);
|
||||
drawSpringUp(canvas, viewWidth);
|
||||
drawBoll(canvas, viewWidth);
|
||||
drawOuter(canvas, viewWidth);
|
||||
drawFinish(canvas, viewWidth);
|
||||
|
||||
if (footer) {
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
|
||||
protected void drawWave(Canvas canvas, int viewWidth, int viewHeight) {
|
||||
float baseHeight = Math.min(mHeadHeight, viewHeight);
|
||||
if (mWaveHeight != 0) {
|
||||
mPath.reset();
|
||||
mPath.lineTo(viewWidth, 0);
|
||||
mPath.lineTo(viewWidth, baseHeight);
|
||||
mPath.quadTo(viewWidth / 2f, baseHeight + mWaveHeight * 2, 0, baseHeight);
|
||||
mPath.close();
|
||||
canvas.drawPath(mPath, mBackPaint);
|
||||
} else {
|
||||
canvas.drawRect(0, 0, viewWidth, baseHeight, mBackPaint);
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawSpringUp(Canvas canvas, int viewWidth) {
|
||||
if (mSpringRatio > 0) {
|
||||
float leftX = (viewWidth / 2f - 4 * mBollRadius + mSpringRatio * 3 * mBollRadius);
|
||||
if (mSpringRatio < 0.9) {
|
||||
mPath.reset();
|
||||
mPath.moveTo(leftX, mBollY);
|
||||
mPath.quadTo(viewWidth / 2f, mBollY - mBollRadius * mSpringRatio * 2,
|
||||
viewWidth - leftX, mBollY);
|
||||
canvas.drawPath(mPath, mFrontPaint);
|
||||
} else {
|
||||
canvas.drawCircle(viewWidth / 2f, mBollY, mBollRadius, mFrontPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawBoll(Canvas canvas, int viewWidth) {
|
||||
if (mShowBoll) {
|
||||
canvas.drawCircle(viewWidth / 2f, mBollY, mBollRadius, mFrontPaint);
|
||||
|
||||
drawBollTail(canvas, viewWidth, (mHeadHeight + mWaveHeight) / mHeadHeight);
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawBollTail(Canvas canvas, int viewWidth, float fraction) {
|
||||
if (mShowBollTail) {
|
||||
final float bottom = mHeadHeight + mWaveHeight;
|
||||
final float startY = mBollY + mBollRadius * fraction / 2;
|
||||
final float startX = viewWidth / 2f + (float) Math.sqrt(mBollRadius * mBollRadius * (1 - fraction * fraction / 4));
|
||||
final float bezier1x = (viewWidth / 2f + (mBollRadius * 3 / 4) * (1 - fraction));
|
||||
final float bezier2x = bezier1x + mBollRadius;
|
||||
|
||||
mPath.reset();
|
||||
mPath.moveTo(startX, startY);
|
||||
mPath.quadTo(bezier1x, bottom, bezier2x, bottom);
|
||||
mPath.lineTo(viewWidth - bezier2x, bottom);
|
||||
mPath.quadTo(viewWidth - bezier1x, bottom, viewWidth - startX, startY);
|
||||
canvas.drawPath(mPath, mFrontPaint);
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawOuter(Canvas canvas, int viewWidth) {
|
||||
if (mShowOuter) {
|
||||
float outerR = mBollRadius + mOuterPaint.getStrokeWidth() * 2;
|
||||
|
||||
mRefreshStart += mOuterIsStart ? 3 : 10;
|
||||
mRefreshStop += mOuterIsStart ? 10 : 3;
|
||||
mRefreshStart = mRefreshStart % 360;
|
||||
mRefreshStop = mRefreshStop % 360;
|
||||
|
||||
int swipe = mRefreshStop - mRefreshStart;
|
||||
swipe = swipe < 0 ? swipe + 360 : swipe;
|
||||
|
||||
canvas.drawArc(new RectF(viewWidth / 2f - outerR, mBollY - outerR, viewWidth / 2f + outerR, mBollY + outerR),
|
||||
mRefreshStart, swipe, false, mOuterPaint);
|
||||
if (swipe >= TARGET_DEGREE) {
|
||||
mOuterIsStart = false;
|
||||
} else if (swipe <= 10) {
|
||||
mOuterIsStart = true;
|
||||
}
|
||||
final View thisView = this;
|
||||
thisView.invalidate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void drawFinish(Canvas canvas, int viewWidth) {
|
||||
if (mFinishRatio > 0) {
|
||||
int beforeColor = mOuterPaint.getColor();
|
||||
if (mFinishRatio < 0.3) {
|
||||
canvas.drawCircle(viewWidth / 2f, mBollY, mBollRadius, mFrontPaint);
|
||||
int outerR = (int) (mBollRadius + mOuterPaint.getStrokeWidth() * 2 * (1+mFinishRatio / 0.3f));
|
||||
int afterColor = ColorUtils.setAlphaComponent(beforeColor, (int) (0xff * (1 - mFinishRatio / 0.3f)));
|
||||
mOuterPaint.setColor(afterColor);
|
||||
canvas.drawArc(new RectF(viewWidth / 2f - outerR, mBollY - outerR, viewWidth / 2f + outerR, mBollY + outerR),
|
||||
0, 360, false, mOuterPaint);
|
||||
}
|
||||
mOuterPaint.setColor(beforeColor);
|
||||
|
||||
if (mFinishRatio >= 0.3 && mFinishRatio < 0.7) {
|
||||
float fraction = (mFinishRatio - 0.3f) / 0.4f;
|
||||
mBollY = (int) (mHeadHeight / 2 + (mHeadHeight - mHeadHeight / 2) * fraction);
|
||||
canvas.drawCircle(viewWidth / 2f, mBollY, mBollRadius, mFrontPaint);
|
||||
if (mBollY >= mHeadHeight - mBollRadius * 2) {
|
||||
mShowBollTail = true;
|
||||
drawBollTail(canvas, viewWidth, fraction);
|
||||
}
|
||||
mShowBollTail = false;
|
||||
}
|
||||
|
||||
if (mFinishRatio >= 0.7 && mFinishRatio <= 1) {
|
||||
float fraction = (mFinishRatio - 0.7f) / 0.3f;
|
||||
int leftX = (int) (viewWidth / 2f - mBollRadius - 2 * mBollRadius * fraction);
|
||||
mPath.reset();
|
||||
mPath.moveTo(leftX, mHeadHeight);
|
||||
mPath.quadTo(viewWidth / 2f, mHeadHeight - (mBollRadius * (1 - fraction)),
|
||||
viewWidth - leftX, mHeadHeight);
|
||||
canvas.drawPath(mPath, mFrontPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="RefreshHeader">
|
||||
@Override
|
||||
public void onInitialized(@NonNull RefreshKernel kernel, int height, int maxDragHeight) {
|
||||
mKernel = kernel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMoving(boolean isDragging, float percent, int offset, int height, int maxDragHeight) {
|
||||
mHeight = offset;
|
||||
if (isDragging || mWavePulling) {
|
||||
mWavePulling = true;
|
||||
mHeadHeight = height;
|
||||
mWaveHeight = Math.max(offset - height, 0) * .8f;
|
||||
}
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReleased(@NonNull RefreshLayout refreshLayout, int height, int maxDragHeight) {
|
||||
mWavePulling = false;
|
||||
mHeadHeight = height;
|
||||
mBollRadius = height / 6f;
|
||||
DecelerateInterpolator interpolator = new DecelerateInterpolator();
|
||||
final float reboundHeight = Math.min(mWaveHeight * 0.8f, mHeadHeight / 2);
|
||||
ValueAnimator waveAnimator = ValueAnimator.ofFloat(
|
||||
mWaveHeight, 0,
|
||||
-(reboundHeight*1.0f),0,
|
||||
-(reboundHeight*0.4f),0
|
||||
);
|
||||
waveAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
float speed = 0;
|
||||
float springBollY;
|
||||
float springRatio = 0;
|
||||
int status = 0;//0 还没开始弹起 1 向上弹起 2 在弹起的最高点停住
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
float curValue = (float) animation.getAnimatedValue();
|
||||
if (status == 0 && curValue <= 0) {
|
||||
status = 1;
|
||||
speed = Math.abs(curValue - mWaveHeight);
|
||||
}
|
||||
if (status == 1) {
|
||||
springRatio = -curValue / reboundHeight;
|
||||
if (springRatio >= mSpringRatio) {
|
||||
mSpringRatio = springRatio;
|
||||
mBollY = mHeadHeight + curValue;
|
||||
speed = Math.abs(curValue - mWaveHeight);
|
||||
} else {
|
||||
status = 2;
|
||||
mSpringRatio = 0;
|
||||
mShowBoll = true;
|
||||
mShowBollTail = true;
|
||||
springBollY = mBollY;
|
||||
}
|
||||
}
|
||||
if (status == 2) {
|
||||
if (mBollY > mHeadHeight / 2) {
|
||||
mBollY = Math.max(mHeadHeight / 2, mBollY - speed);
|
||||
float bally = animation.getAnimatedFraction() * (mHeadHeight / 2 - springBollY) + springBollY;
|
||||
if (mBollY > bally) {
|
||||
mBollY = bally;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mShowBollTail && curValue < mWaveHeight) {
|
||||
mShowOuter = true;
|
||||
mShowBollTail = false;
|
||||
mOuterIsStart = true;
|
||||
mRefreshStart = 90;
|
||||
mRefreshStop = 90;
|
||||
}
|
||||
if (!mWavePulling) {
|
||||
mWaveHeight = curValue;
|
||||
final View thisView = BezierCircleHeader.this;
|
||||
thisView.invalidate();
|
||||
}
|
||||
}
|
||||
});
|
||||
waveAnimator.setInterpolator(interpolator);
|
||||
waveAnimator.setDuration(1000);
|
||||
waveAnimator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onFinish(@NonNull RefreshLayout layout, boolean success) {
|
||||
mShowBoll = false;
|
||||
mShowOuter = false;
|
||||
final int DURATION_FINISH = 800; //动画时长
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
|
||||
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
final View thisView = BezierCircleHeader.this;
|
||||
mFinishRatio = (float) animation.getAnimatedValue();
|
||||
thisView.invalidate();
|
||||
}
|
||||
});
|
||||
animator.setInterpolator(new AccelerateInterpolator());
|
||||
animator.setDuration(DURATION_FINISH);
|
||||
animator.start();
|
||||
return DURATION_FINISH;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param colors 对应Xml中配置的 srlPrimaryColor srlAccentColor
|
||||
* @deprecated 只由框架调用
|
||||
* 使用者使用 {@link RefreshLayout#setPrimaryColorsId(int...)}
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public void setPrimaryColors(@ColorInt int ... colors) {
|
||||
if (colors.length > 0) {
|
||||
mBackPaint.setColor(colors[0]);
|
||||
if (colors.length > 1) {
|
||||
mFrontPaint.setColor(colors[1]);
|
||||
mOuterPaint.setColor(colors[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @NonNull
|
||||
// @Override
|
||||
// public SpinnerStyle getSpinnerStyle() {
|
||||
// return SpinnerStyle.Scale;
|
||||
// }
|
||||
//</editor-fold>
|
||||
}
|
||||
323
app/src/main/java/com/dskj/daikuan/ui/view/CircleImageView.java
Normal file
@@ -0,0 +1,323 @@
|
||||
package com.dskj.daikuan.ui.view;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
import com.dskj.daikuan.R;
|
||||
|
||||
|
||||
public class CircleImageView extends AppCompatImageView {
|
||||
// paint when user press
|
||||
private Paint pressPaint;
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
// default bitmap config
|
||||
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
|
||||
private static final int COLORDRAWABLE_DIMENSION = 1;
|
||||
|
||||
// border color
|
||||
private int borderColor;
|
||||
// width of border
|
||||
private int borderWidth;
|
||||
// alpha when pressed
|
||||
private int pressAlpha;
|
||||
// color when pressed
|
||||
private int pressColor;
|
||||
// radius
|
||||
private int radius;
|
||||
// rectangle or round, 1 is circle, 2 is rectangle
|
||||
private int shapeType;
|
||||
|
||||
public CircleImageView(Context context) {
|
||||
super(context);
|
||||
init(context, null);
|
||||
}
|
||||
|
||||
public CircleImageView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
|
||||
private void init(Context context, AttributeSet attrs) {
|
||||
//init the value
|
||||
borderWidth = 0;
|
||||
borderColor = 0xddffffff;
|
||||
pressAlpha = 0x42;
|
||||
pressColor = 0x42000000;
|
||||
radius = 16;
|
||||
shapeType = 0;
|
||||
|
||||
// get attribute of EaseImageView
|
||||
if (attrs != null) {
|
||||
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);
|
||||
borderColor = array.getColor(R.styleable.CircleImageView_ease_border_color, borderColor);
|
||||
borderWidth = array.getDimensionPixelOffset(R.styleable.CircleImageView_ease_border_width, borderWidth);
|
||||
pressAlpha = array.getInteger(R.styleable.CircleImageView_ease_press_alpha, pressAlpha);
|
||||
pressColor = array.getColor(R.styleable.CircleImageView_ease_press_color, pressColor);
|
||||
radius = array.getDimensionPixelOffset(R.styleable.CircleImageView_ease_radius, radius);
|
||||
shapeType = array.getInteger(R.styleable.CircleImageView_es_shape_type, shapeType);
|
||||
array.recycle();
|
||||
}
|
||||
|
||||
// set paint when pressed
|
||||
pressPaint = new Paint();
|
||||
pressPaint.setAntiAlias(true);
|
||||
pressPaint.setStyle(Paint.Style.FILL);
|
||||
pressPaint.setColor(pressColor);
|
||||
pressPaint.setAlpha(0);
|
||||
pressPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
setDrawingCacheEnabled(true);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
|
||||
if (shapeType == 0) {
|
||||
super.onDraw(canvas);
|
||||
return;
|
||||
}
|
||||
Drawable drawable = getDrawable();
|
||||
if (drawable == null) {
|
||||
return;
|
||||
}
|
||||
// the width and height is in xml file
|
||||
if (getWidth() == 0 || getHeight() == 0) {
|
||||
return;
|
||||
}
|
||||
Bitmap bitmap = getBitmapFromDrawable(drawable);
|
||||
drawDrawable(canvas, bitmap);
|
||||
|
||||
if (isClickable()) {
|
||||
drawPress(canvas);
|
||||
}
|
||||
drawBorder(canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* draw Rounded Rectangle
|
||||
*
|
||||
* @param canvas
|
||||
* @param bitmap
|
||||
*/
|
||||
@SuppressLint("WrongConstant")
|
||||
private void drawDrawable(Canvas canvas, Bitmap bitmap) {
|
||||
Paint paint = new Paint();
|
||||
paint.setColor(0xffffffff);
|
||||
paint.setAntiAlias(true); //smooths out the edges of what is being drawn
|
||||
PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
|
||||
// set flags
|
||||
int saveFlags = Canvas.ALL_SAVE_FLAG
|
||||
;
|
||||
canvas.saveLayer(0, 0, width, height, null, saveFlags);
|
||||
|
||||
if (shapeType == 1) {
|
||||
canvas.drawCircle(width / 2, height / 2, width / 2 - 1, paint);
|
||||
} else if (shapeType == 2) {
|
||||
RectF rectf = new RectF(1, 1, getWidth() - 1, getHeight() - 1);
|
||||
canvas.drawRoundRect(rectf, radius + 1, radius + 1, paint);
|
||||
}
|
||||
|
||||
paint.setXfermode(xfermode);
|
||||
|
||||
float scaleWidth = ((float) getWidth()) / bitmap.getWidth();
|
||||
float scaleHeight = ((float) getHeight()) / bitmap.getHeight();
|
||||
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.postScale(scaleWidth, scaleHeight);
|
||||
|
||||
//bitmap scale
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
|
||||
canvas.drawBitmap(bitmap, 0, 0, paint);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* draw the effect when pressed
|
||||
*
|
||||
* @param canvas
|
||||
*/
|
||||
private void drawPress(Canvas canvas) {
|
||||
// check is rectangle or circle
|
||||
if (shapeType == 1) {
|
||||
canvas.drawCircle(width / 2, height / 2, width / 2 - 1, pressPaint);
|
||||
} else if (shapeType == 2) {
|
||||
RectF rectF = new RectF(1, 1, width - 1, height - 1);
|
||||
canvas.drawRoundRect(rectF, radius + 1, radius + 1, pressPaint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* draw customized border
|
||||
*
|
||||
* @param canvas
|
||||
*/
|
||||
private void drawBorder(Canvas canvas) {
|
||||
if (borderWidth > 0) {
|
||||
Paint paint = new Paint();
|
||||
paint.setStrokeWidth(borderWidth);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
paint.setColor(borderColor);
|
||||
paint.setAntiAlias(true);
|
||||
// // check is rectangle or circle
|
||||
if (shapeType == 1) {
|
||||
canvas.drawCircle(width / 2, height / 2, (width - borderWidth) / 2, paint);
|
||||
} else if (shapeType == 2) {
|
||||
RectF rectf = new RectF(borderWidth / 2, borderWidth / 2, getWidth() - borderWidth / 2,
|
||||
getHeight() - borderWidth / 2);
|
||||
canvas.drawRoundRect(rectf, radius, radius, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* monitor the size change
|
||||
*
|
||||
* @param w
|
||||
* @param h
|
||||
* @param oldw
|
||||
* @param oldh
|
||||
*/
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
width = w;
|
||||
height = h;
|
||||
}
|
||||
|
||||
/**
|
||||
* monitor if touched
|
||||
*
|
||||
* @param event
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
pressPaint.setAlpha(pressAlpha);
|
||||
invalidate();
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
pressPaint.setAlpha(0);
|
||||
invalidate();
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
|
||||
break;
|
||||
default:
|
||||
pressPaint.setAlpha(0);
|
||||
invalidate();
|
||||
break;
|
||||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param drawable
|
||||
* @return
|
||||
*/
|
||||
private Bitmap getBitmapFromDrawable(Drawable drawable) {
|
||||
if (drawable == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable) drawable).getBitmap();
|
||||
}
|
||||
|
||||
Bitmap bitmap;
|
||||
int width = Math.max(drawable.getIntrinsicWidth(), 2);
|
||||
int height = Math.max(drawable.getIntrinsicHeight(), 2);
|
||||
try {
|
||||
bitmap = Bitmap.createBitmap(width, height, BITMAP_CONFIG);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
bitmap = null;
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* set border color
|
||||
*
|
||||
* @param borderColor
|
||||
*/
|
||||
public void setBorderColor(int borderColor) {
|
||||
this.borderColor = borderColor;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* set border width
|
||||
*
|
||||
* @param borderWidth
|
||||
*/
|
||||
public void setBorderWidth(int borderWidth) {
|
||||
this.borderWidth = borderWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* set alpha when pressed
|
||||
*
|
||||
* @param pressAlpha
|
||||
*/
|
||||
public void setPressAlpha(int pressAlpha) {
|
||||
this.pressAlpha = pressAlpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* set color when pressed
|
||||
*
|
||||
* @param pressColor
|
||||
*/
|
||||
public void setPressColor(int pressColor) {
|
||||
this.pressColor = pressColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* set radius
|
||||
*
|
||||
* @param radius
|
||||
*/
|
||||
public void setRadius(int radius) {
|
||||
this.radius = radius;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* set shape,1 is circle, 2 is rectangle
|
||||
*
|
||||
* @param shapeType
|
||||
*/
|
||||
public void setShapeType(int shapeType) {
|
||||
this.shapeType = shapeType;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.dskj.daikuan.ui.view;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewParent;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
/**
|
||||
* 此类用于解决 ViewPager2 嵌套 ViewPager2 或者 RecyclerView 等相互嵌套的冲突问题,
|
||||
*/
|
||||
public class NestedScrollableHost extends FrameLayout {
|
||||
|
||||
private int touchSlop;
|
||||
private float initialX;
|
||||
private float initialY;
|
||||
private HashMap _$_findViewCache;
|
||||
|
||||
private final ViewPager2 getParentViewPager() {
|
||||
ViewParent var10000 = this.getParent();
|
||||
if (!(var10000 instanceof View)) {
|
||||
var10000 = null;
|
||||
}
|
||||
|
||||
View v;
|
||||
for (v = (View) var10000; v != null && !(v instanceof ViewPager2); v = (View) var10000) {
|
||||
var10000 = v.getParent();
|
||||
if (!(var10000 instanceof View)) {
|
||||
var10000 = null;
|
||||
}
|
||||
}
|
||||
|
||||
View var2 = v;
|
||||
if (!(v instanceof ViewPager2)) {
|
||||
var2 = null;
|
||||
}
|
||||
|
||||
return (ViewPager2) var2;
|
||||
}
|
||||
|
||||
private final View getChild() {
|
||||
return this.getChildCount() > 0 ? this.getChildAt(0) : null;
|
||||
}
|
||||
|
||||
private final boolean canChildScroll(int orientation, float delta) {
|
||||
boolean var5 = false;
|
||||
int direction = -((int) Math.signum(delta));
|
||||
View var10000;
|
||||
boolean var6 = false;
|
||||
switch (orientation) {
|
||||
case 0:
|
||||
var10000 = this.getChild();
|
||||
var6 = var10000 != null ? var10000.canScrollHorizontally(direction) : false;
|
||||
break;
|
||||
case 1:
|
||||
var10000 = this.getChild();
|
||||
var6 = var10000 != null ? var10000.canScrollVertically(direction) : false;
|
||||
break;
|
||||
default:
|
||||
// throw (Throwable)(new IllegalArgumentException());
|
||||
}
|
||||
|
||||
return var6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent e) {
|
||||
this.handleInterceptTouchEvent(e);
|
||||
return super.onInterceptTouchEvent(e);
|
||||
}
|
||||
|
||||
private final void handleInterceptTouchEvent(MotionEvent e) {
|
||||
ViewPager2 var10000 = this.getParentViewPager();
|
||||
if (var10000 != null) {
|
||||
int orientation = var10000.getOrientation();
|
||||
if (this.canChildScroll(orientation, -1.0F) || this.canChildScroll(orientation, 1.0F)) {
|
||||
if (e.getAction() == 0) {
|
||||
this.initialX = e.getX();
|
||||
this.initialY = e.getY();
|
||||
this.getParent().requestDisallowInterceptTouchEvent(true);
|
||||
} else if (e.getAction() == 2) {
|
||||
float dx = e.getX() - this.initialX;
|
||||
float dy = e.getY() - this.initialY;
|
||||
boolean isVpHorizontal = orientation == 0;
|
||||
boolean var8 = false;
|
||||
float scaledDx = Math.abs(dx) * (isVpHorizontal ? 0.5F : 1.0F);
|
||||
boolean var9 = false;
|
||||
float scaledDy = Math.abs(dy) * (isVpHorizontal ? 1.0F : 0.5F);
|
||||
if (scaledDx > (float) this.touchSlop || scaledDy > (float) this.touchSlop) {
|
||||
if (isVpHorizontal == scaledDy > scaledDx) {
|
||||
this.getParent().requestDisallowInterceptTouchEvent(false);
|
||||
} else if (this.canChildScroll(orientation, isVpHorizontal ? dx : dy)) {
|
||||
this.getParent().requestDisallowInterceptTouchEvent(true);
|
||||
} else {
|
||||
this.getParent().requestDisallowInterceptTouchEvent(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public NestedScrollableHost(Context context) {
|
||||
super(context);
|
||||
ViewConfiguration var10001 = ViewConfiguration.get(this.getContext());
|
||||
this.touchSlop = var10001.getScaledTouchSlop();
|
||||
}
|
||||
|
||||
public NestedScrollableHost(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
ViewConfiguration var10001 = ViewConfiguration.get(this.getContext());
|
||||
this.touchSlop = var10001.getScaledTouchSlop();
|
||||
}
|
||||
|
||||
public View _$_findCachedViewById(int var1) {
|
||||
if (this._$_findViewCache == null) {
|
||||
this._$_findViewCache = new HashMap();
|
||||
}
|
||||
|
||||
View var2 = (View) this._$_findViewCache.get(var1);
|
||||
if (var2 == null) {
|
||||
var2 = this.findViewById(var1);
|
||||
this._$_findViewCache.put(var1, var2);
|
||||
}
|
||||
|
||||
return var2;
|
||||
}
|
||||
|
||||
public void _$_clearFindViewByIdCache() {
|
||||
if (this._$_findViewCache != null) {
|
||||
this._$_findViewCache.clear();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
44
app/src/main/java/com/dskj/daikuan/ui/view/StatusLayout.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package com.dskj.daikuan.ui.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Created by kiun_2007 on 2018/3/29.
|
||||
*/
|
||||
|
||||
public class StatusLayout extends LinearLayout {
|
||||
public StatusLayout(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public StatusLayout(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public StatusLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
private int getStatusBarHeight(Context context) {
|
||||
int result = 0;
|
||||
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
result = context.getResources().getDimensionPixelSize(resourceId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
ViewGroup.LayoutParams lp = this.getLayoutParams();
|
||||
lp.width = -1;
|
||||
lp.height = getStatusBarHeight(getContext());
|
||||
this.setLayoutParams(lp);
|
||||
super.onAttachedToWindow();
|
||||
}
|
||||
}
|
||||
22
app/src/main/java/com/dskj/daikuan/utils/AppContextUtil.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.dskj.daikuan.utils;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class AppContextUtil {
|
||||
private static Context sContext;
|
||||
|
||||
private AppContextUtil() {
|
||||
|
||||
}
|
||||
|
||||
public static void init(Context context) {
|
||||
sContext = context;
|
||||
}
|
||||
|
||||
public static Context getInstance() {
|
||||
if (sContext == null) {
|
||||
throw new NullPointerException("the context is null,please init AppContextUtil in Application first.");
|
||||
}
|
||||
return sContext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.dskj.daikuan.utils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.style.ImageSpan;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class CenterAlignImageSpan extends ImageSpan {
|
||||
public CenterAlignImageSpan(Drawable drawable) {
|
||||
super(drawable);
|
||||
|
||||
}
|
||||
|
||||
public CenterAlignImageSpan(Bitmap b) {
|
||||
super(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom,
|
||||
@NonNull Paint paint) {
|
||||
|
||||
Drawable b = getDrawable();
|
||||
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
|
||||
int transY = (y + fm.descent + y + fm.ascent) / 2 - b.getBounds().bottom / 2;//计算y方向的位移
|
||||
canvas.save();
|
||||
canvas.translate(x, transY);//绘制图片位移一段距离
|
||||
b.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
83
app/src/main/java/com/dskj/daikuan/utils/DeviceUtil.java
Normal file
@@ -0,0 +1,83 @@
|
||||
package com.dskj.daikuan.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Display;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
public final class DeviceUtil {
|
||||
|
||||
public static int SCREEN_WIDTH;
|
||||
public static int SCREEN_HEIGHT;
|
||||
|
||||
public static float DENSITY;
|
||||
public static float DENSITY_SCALE;
|
||||
public static int DENSITY_DPI;
|
||||
|
||||
public static void init(Context context) {
|
||||
if (null == context) return;
|
||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
windowManager.getDefaultDisplay().getRealMetrics(displayMetrics);
|
||||
SCREEN_WIDTH = displayMetrics.widthPixels;
|
||||
SCREEN_HEIGHT = displayMetrics.heightPixels;
|
||||
DENSITY = displayMetrics.density;
|
||||
DENSITY_SCALE = displayMetrics.scaledDensity;
|
||||
DENSITY_DPI = displayMetrics.densityDpi;
|
||||
}
|
||||
|
||||
public static int dip2px(float dip) {
|
||||
return (int) (dip * DENSITY + 0.5F);
|
||||
}
|
||||
|
||||
public static boolean isStorageEnough(Context context, long minSize) {
|
||||
try {
|
||||
File path = Environment.getExternalStorageDirectory();
|
||||
StatFs stat = new StatFs(path.toString());
|
||||
long blocksize = stat.getBlockSizeLong();
|
||||
long availbleblocks = stat.getAvailableBlocksLong();
|
||||
long avilableSize = availbleblocks * blocksize;
|
||||
return avilableSize >= minSize;
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
|
||||
* @param context 上下文
|
||||
* @param pxValue px值
|
||||
* @return dp值
|
||||
*/
|
||||
public static int px2dip(Context context, float pxValue) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (pxValue / scale + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的屏幕尺寸
|
||||
*
|
||||
* @param context {@link Context}
|
||||
* @return 屏幕尺寸
|
||||
*/
|
||||
public static int[] getScreenSize(Context context) {
|
||||
int[] size = new int[2];
|
||||
|
||||
WindowManager w = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
Display d = w.getDefaultDisplay();
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
d.getMetrics(metrics);
|
||||
|
||||
size[0] = metrics.widthPixels;
|
||||
size[1] = metrics.heightPixels;
|
||||
return size;
|
||||
}
|
||||
}
|
||||
905
app/src/main/java/com/dskj/daikuan/utils/FileUtils.java
Normal file
@@ -0,0 +1,905 @@
|
||||
package com.dskj.daikuan.utils;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
import android.provider.MediaStore;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import com.dskj.daikuan.InitApp;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
|
||||
public class FileUtils {
|
||||
public static String APP_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "bkwatermark" + File.separator;
|
||||
public static String THUMBNAIL_DIR = APP_DIR + ".thumbnailrecord" + File.separator;
|
||||
public static String TEMP_DIR = APP_DIR + "temprecord" + File.separator;
|
||||
//以下三个不能清理
|
||||
public static String IMAGE_DIR = APP_DIR + "tempimage" + File.separator;
|
||||
public static String VIDEO_DIR = APP_DIR + "recordvideo" + File.separator;
|
||||
public static String THUMB_DIR = APP_DIR + ".videothumb" + File.separator;
|
||||
public static String CAPTURER_DIR = APP_DIR + "recordscreen" + File.separator;
|
||||
//以上三个不能清理
|
||||
//以下的 结尾的两个mp4文件和ts文件不能清理
|
||||
public static String VIDEO_DIR_HIDE = APP_DIR + ".recordvideo" + File.separator;
|
||||
|
||||
/**
|
||||
* 从assets目录中复制整个文件夹内容到新的路径下
|
||||
* @param context Context 使用CopyFiles类的Activity
|
||||
* @param oldPath String 原文件路径 如:Data(assets文件夹下文件夹名称)
|
||||
* @param newPath String 复制后路径 如:data/data/(手机内部存储路径名称)
|
||||
*/
|
||||
public void copyFilesFromAssets(Context context,String oldPath,String newPath) {
|
||||
try {
|
||||
InputStream is = context.getAssets().open(oldPath);
|
||||
FileOutputStream fos = new FileOutputStream(new File(newPath));
|
||||
byte[] buffer = new byte[1024];
|
||||
int byteCount=0;
|
||||
while((byteCount=is.read(buffer))!=-1) {//循环从输入流读取 buffer字节
|
||||
fos.write(buffer, 0, byteCount);//将读取的输入流写入到输出流
|
||||
}
|
||||
fos.flush();//刷新缓冲区
|
||||
is.close();
|
||||
fos.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getFileNameNoEx(String filename) {
|
||||
if ((filename != null) && (filename.length() > 0)) {
|
||||
int dot = filename.lastIndexOf('.');
|
||||
if ((dot > -1) && (dot < (filename.length()))) {
|
||||
return filename.substring(0, dot);
|
||||
}
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
public static Uri getVideoContentUri(Context context, String path) {
|
||||
Uri uri = null;
|
||||
Cursor cursor = context.getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
|
||||
new String[]{MediaStore.Video.Media._ID}, MediaStore.Video.Media.DATA + "=? ",
|
||||
new String[]{path}, null);
|
||||
|
||||
if (cursor != null) {
|
||||
if (cursor.moveToFirst()) {
|
||||
int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
|
||||
Uri baseUri = Uri.parse("content://media/external/video/media");
|
||||
uri = Uri.withAppendedPath(baseUri, "" + id);
|
||||
}
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
if (uri == null) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(MediaStore.Video.Media.DATA, path);
|
||||
uri = context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
||||
public static long getAvilableSize(){
|
||||
File path = Environment.getExternalStorageDirectory();
|
||||
StatFs stat = new StatFs(path.toString());
|
||||
long blocksize = stat.getBlockSizeLong();
|
||||
long availbleblocks = stat.getAvailableBlocksLong();
|
||||
long avilableSize = availbleblocks * blocksize;
|
||||
return avilableSize;
|
||||
}
|
||||
|
||||
public static long getDirectorySize(File directory) {
|
||||
final File[] files = directory.listFiles();
|
||||
long size = 0;
|
||||
|
||||
if (files == null) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
for (final File file : files) {
|
||||
try {
|
||||
if (!isSymlink(file)) {
|
||||
size += sizeOf(file);
|
||||
if (size < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// ignore exception when asking for symlink
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
/**
|
||||
* 检测文件是否可用
|
||||
*/
|
||||
public static boolean checkFile(String path) {
|
||||
if (!TextUtils.isEmpty(path)) {
|
||||
File f = new File(path);
|
||||
if (f != null && f.exists() && f.canRead() && (f.isDirectory() || (f.isFile()
|
||||
&& f.length() > 0))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getExtension(String name) {
|
||||
String ext;
|
||||
|
||||
if (name.lastIndexOf(".") == -1) {
|
||||
ext = "";
|
||||
|
||||
} else {
|
||||
int index = name.lastIndexOf(".");
|
||||
ext = name.substring(index + 1, name.length());
|
||||
}
|
||||
return ext;
|
||||
}
|
||||
|
||||
private static boolean isSymlink(File file) throws IOException {
|
||||
File fileInCanonicalDir;
|
||||
|
||||
if (file.getParent() == null) {
|
||||
fileInCanonicalDir = file;
|
||||
} else {
|
||||
File canonicalDir = file.getParentFile().getCanonicalFile();
|
||||
fileInCanonicalDir = new File(canonicalDir, file.getName());
|
||||
}
|
||||
|
||||
return !fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static String getFileName(String path) {
|
||||
String s[] = path.split("/");
|
||||
if (s.length != 0)
|
||||
return "/" + s[s.length - 1];
|
||||
return "/";
|
||||
}
|
||||
/**
|
||||
* Return the name of file.
|
||||
*
|
||||
* @param file The file.
|
||||
* @return the name of file
|
||||
*/
|
||||
public static String getFileName(final File file) {
|
||||
if (file == null) return "";
|
||||
return getFileName(file.getAbsolutePath());
|
||||
}
|
||||
|
||||
private static long sizeOf(File file) {
|
||||
if (file.isDirectory()) {
|
||||
return getDirectorySize(file);
|
||||
} else {
|
||||
return file.length();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void writeTxtToFile(List<String> strcontent, String filePath, String fileName) {
|
||||
makeFilePath(filePath, fileName);
|
||||
String strFilePath = filePath + fileName;
|
||||
String strContent = "";
|
||||
|
||||
for (int i = 0; i < strcontent.size(); ++i) {
|
||||
strContent = strContent + "file " + strcontent.get(i) + "\r\n";
|
||||
}
|
||||
|
||||
try {
|
||||
File file = new File(strFilePath);
|
||||
if (file.isFile() && file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
|
||||
raf.seek(file.length());
|
||||
raf.write(strContent.getBytes());
|
||||
raf.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean saveBitmap(Bitmap src, String dest) {
|
||||
if (src == null || TextUtils.isEmpty(dest)) {
|
||||
return false;
|
||||
}
|
||||
File destDir = new File(dest).getParentFile();
|
||||
if (destDir == null) {
|
||||
return false;
|
||||
}
|
||||
if (!destDir.exists()) {
|
||||
destDir.mkdirs();
|
||||
}
|
||||
|
||||
FileOutputStream fout = null;
|
||||
boolean success = false;
|
||||
try {
|
||||
fout = new FileOutputStream(dest);
|
||||
src.compress(Bitmap.CompressFormat.PNG, 100, fout);
|
||||
|
||||
success = true;
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
} finally {
|
||||
if (fout != null) {
|
||||
try {
|
||||
fout.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean saveBitmapPic(Context context,Bitmap src, String dest,String fileName) {
|
||||
if (src == null || TextUtils.isEmpty(dest)) {
|
||||
return false;
|
||||
}
|
||||
File destDir = new File(dest).getParentFile();
|
||||
if (destDir == null) {
|
||||
return false;
|
||||
}
|
||||
if (!destDir.exists()) {
|
||||
destDir.mkdirs();
|
||||
}
|
||||
|
||||
FileOutputStream fout = null;
|
||||
boolean success = false;
|
||||
try {
|
||||
fout = new FileOutputStream(dest);
|
||||
src.compress(Bitmap.CompressFormat.PNG, 100, fout);
|
||||
|
||||
|
||||
success = true;
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
} finally {
|
||||
if (fout != null) {
|
||||
try {
|
||||
fout.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
// 其次把文件插入到系统图库
|
||||
try {
|
||||
MediaStore.Images.Media.insertImage(context.getContentResolver(),
|
||||
dest, fileName, null);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String saveBitmap2Gallery(String dest,String fileName, Bitmap bitmap) {
|
||||
File mPicDir = new File(dest);
|
||||
|
||||
if (mPicDir == null) {
|
||||
return null;
|
||||
}
|
||||
if (!mPicDir.exists()) {
|
||||
mPicDir.mkdirs();
|
||||
}
|
||||
OutputStream out = null;
|
||||
String[] pathArray = null;
|
||||
String[] typeArray = null;
|
||||
String fialeN=null;
|
||||
try {
|
||||
// Android 10版本 创建文件夹不成功,这里没有过多去研究
|
||||
boolean isMk = mPicDir.mkdirs();
|
||||
Log.d("ImageUtils ", "isMk = " + isMk);
|
||||
File mPicFile = new File(mPicDir, fileName);
|
||||
String mPicPath = mPicFile.getAbsolutePath();
|
||||
Log.d("ImageUtils ", "mPicPath = " + mPicPath);
|
||||
pathArray = new String[]{mPicFile.getAbsolutePath()};
|
||||
typeArray = new String[]{"image/png"};
|
||||
ContentValues values = new ContentValues();
|
||||
ContentResolver resolver = InitApp.initApp.getContentResolver();
|
||||
values.put(MediaStore.Images.ImageColumns.DATA, mPicPath);
|
||||
values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, fileName);
|
||||
values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png");
|
||||
values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, System.currentTimeMillis() + "");
|
||||
// 插入相册
|
||||
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
if (uri != null) {
|
||||
out = resolver.openOutputStream(uri);
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||
Log.d("ImageUtils ", "compress");
|
||||
}
|
||||
fialeN=fileName;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fialeN=null;
|
||||
} finally {
|
||||
if (out != null) {
|
||||
try {
|
||||
out.flush();
|
||||
out.close();
|
||||
Log.d("ImageUtils", "finally close");
|
||||
// 扫描刷新
|
||||
MediaScannerConnection.scanFile(InitApp.initApp, pathArray, typeArray, (s, uri) -> Log.d("ImageUtils", "onScanCompleted s->" + s));
|
||||
return fialeN;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return fialeN;
|
||||
}
|
||||
|
||||
|
||||
public static File makeFilePath(String filePath, String fileName) {
|
||||
File file = null;
|
||||
makeRootDirectory(filePath);
|
||||
|
||||
try {
|
||||
file = new File(filePath + fileName);
|
||||
if (!file.exists()) {
|
||||
file.createNewFile();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
public static void makeRootDirectory(String filePath) {
|
||||
File file = null;
|
||||
|
||||
try {
|
||||
file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.i("error:", e + "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归删除文件和文件夹
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
public static void deleteFileRecursively(File file) {
|
||||
if (file.isFile()) {
|
||||
file.delete();
|
||||
return;
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
File[] childFile = file.listFiles();
|
||||
if (childFile == null || childFile.length == 0) {
|
||||
file.delete();
|
||||
return;
|
||||
}
|
||||
for (File f : childFile) {
|
||||
deleteFileRecursively(f);
|
||||
}
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public static String md5(String content) {
|
||||
byte[] hash;
|
||||
try {
|
||||
hash = MessageDigest.getInstance("MD5").digest(content.getBytes("UTF-8"));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("NoSuchAlgorithmException", e);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("UnsupportedEncodingException", e);
|
||||
}
|
||||
|
||||
StringBuilder hex = new StringBuilder(hash.length * 2);
|
||||
for (byte b : hash) {
|
||||
if ((b & 0xFF) < 0x10) {
|
||||
hex.append("0");
|
||||
}
|
||||
hex.append(Integer.toHexString(b & 0xFF));
|
||||
}
|
||||
return hex.toString();
|
||||
}
|
||||
|
||||
public static String getSaveUpdateDirectory() {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
String rootDir = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "SuperRecord" + File.separator + "update" + File.separator;
|
||||
File file = new File(rootDir);
|
||||
if (!file.exists()) {
|
||||
if (!file.mkdirs()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return rootDir;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* check the usability of the external storage.
|
||||
*
|
||||
* @return enable -> true, disable->false
|
||||
*/
|
||||
public static boolean isExternalStorageEnable() {
|
||||
String state = Environment.getExternalStorageState();
|
||||
return Environment.MEDIA_MOUNTED.equals(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the external storage file path
|
||||
*
|
||||
* @return the file path
|
||||
*/
|
||||
public static String getExternalStoragePath() {
|
||||
return getExternalStorageDir().getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* get the external storage file
|
||||
*
|
||||
* @return the file
|
||||
*/
|
||||
public static File getExternalStorageDir() {
|
||||
return Environment.getExternalStorageDirectory();
|
||||
}
|
||||
|
||||
public static String handleSpaceFilePath(String path) {
|
||||
if (TextUtils.isEmpty(path) || !path.contains(" ")) {
|
||||
return path;
|
||||
}
|
||||
|
||||
File origin = new File(path);
|
||||
String fileName = origin.getName();
|
||||
if (fileName.contains(" ")) {
|
||||
fileName = fileName.replaceAll(" ", "");
|
||||
}
|
||||
|
||||
File targetDir = new File(TEMP_DIR);
|
||||
if (!targetDir.exists()) {
|
||||
targetDir.mkdirs();
|
||||
}
|
||||
|
||||
File after = new File(targetDir, fileName);
|
||||
copyFile(origin, after);
|
||||
return after.getAbsolutePath();
|
||||
}
|
||||
|
||||
|
||||
public static boolean copyFile(File src, File dest) {
|
||||
if (src == null || dest == null) {
|
||||
return false;
|
||||
}
|
||||
if (dest.exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TextUtils.equals(src.getParent(), dest.getParent())) {
|
||||
return src.renameTo(dest);
|
||||
}
|
||||
|
||||
try {
|
||||
dest.createNewFile();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
FileChannel srcChannel = null;
|
||||
FileChannel dstChannel = null;
|
||||
|
||||
boolean result = false;
|
||||
try {
|
||||
srcChannel = new FileInputStream(src).getChannel();
|
||||
dstChannel = new FileOutputStream(dest).getChannel();
|
||||
srcChannel.transferTo(0, srcChannel.size(), dstChannel);
|
||||
result = true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
srcChannel.close();
|
||||
dstChannel.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getTempDirectory() {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
File file = new File(TEMP_DIR);
|
||||
if (!file.exists()) {
|
||||
if (!file.mkdirs()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return TEMP_DIR;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSaveRecordDirectory() {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
File file = new File(VIDEO_DIR);
|
||||
if (!file.exists()) {
|
||||
if (!file.mkdirs()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return VIDEO_DIR;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSaveRecordPicDirectory() {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
File file = new File(THUMB_DIR);
|
||||
if (!file.exists()) {
|
||||
if (!file.mkdirs()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return THUMB_DIR;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String getSaveCacheDirectory() {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
String rootDir = Environment.getExternalStorageDirectory().getAbsolutePath()
|
||||
+ File.separator + "ScreenRecord"
|
||||
+ File.separator + "cache"
|
||||
+ File.separator + "frame" + File.separator;
|
||||
File file = new File(rootDir);
|
||||
if (file.exists()) {
|
||||
deleteDir(rootDir);
|
||||
}
|
||||
file.mkdirs();
|
||||
return rootDir;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSaveCoverDirectory() {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
String rootDir = Environment.getExternalStorageDirectory().getAbsolutePath()
|
||||
+ File.separator + "ScreenRecord"
|
||||
+ File.separator + "cache"
|
||||
+ File.separator + "cover" + File.separator;
|
||||
File file = new File(rootDir);
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
return rootDir;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSaveScreenDirectory() {
|
||||
// if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
File file = new File(CAPTURER_DIR);
|
||||
if (!file.exists()) {
|
||||
if (!file.mkdirs()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return CAPTURER_DIR;
|
||||
// } else {
|
||||
// return null;
|
||||
// }
|
||||
}
|
||||
|
||||
public static void deleteDir(final String pPath) {
|
||||
File dir = new File(pPath);
|
||||
deleteDirWithFile(dir);
|
||||
}
|
||||
|
||||
public static void deleteDirWithFile(File dir) {
|
||||
if (dir == null || !dir.exists() || !dir.isDirectory())
|
||||
return;
|
||||
for (File file : dir.listFiles()) {
|
||||
if (file.isFile())
|
||||
file.delete();
|
||||
else if (file.isDirectory())
|
||||
deleteDirWithFile(file);
|
||||
}
|
||||
dir.delete();
|
||||
}
|
||||
|
||||
public static boolean fileIsExist(String filePath) {
|
||||
if (TextUtils.isEmpty(filePath)) return false;
|
||||
|
||||
File file = new File(filePath);
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
|
||||
public static void deleteFile(String path) {
|
||||
if (TextUtils.isEmpty(path)) {
|
||||
return;
|
||||
}
|
||||
File file = new File(path);
|
||||
Log.i("XHXDEBUG","XHXDEBUG清理删除文件:"+path);
|
||||
if (file.exists() && file.isFile()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isExists(String path) {
|
||||
if (TextUtils.isEmpty(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File file = new File(path);
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
|
||||
public static Uri getImageContentUri(Context context, String filePath) {
|
||||
Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
new String[]{MediaStore.Images.Media._ID}, MediaStore.Images.Media.DATA + "=? ",
|
||||
new String[]{filePath}, null);
|
||||
Uri uri = null;
|
||||
|
||||
if (cursor != null) {
|
||||
if (cursor.moveToFirst()) {
|
||||
int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
|
||||
Uri baseUri = Uri.parse("content://media/external/images/media");
|
||||
uri = Uri.withAppendedPath(baseUri, "" + id);
|
||||
}
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
if (uri == null) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(MediaStore.Images.Media.DATA, filePath);
|
||||
uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
//存储assets中的文件到本地
|
||||
public static void getAssetsPath(Context context) {
|
||||
String fileName = "ic_share_image.jpeg";
|
||||
File testFolder = new File(APP_DIR + "/share");
|
||||
if (testFolder.exists() && testFolder.isDirectory()) {
|
||||
Log.d("", "test folder already exists");
|
||||
} else if (!testFolder.exists()) {
|
||||
testFolder.mkdir();
|
||||
}
|
||||
File modelFile = new File(testFolder, fileName);
|
||||
if (!modelFile.exists()) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
InputStream is = context.getAssets().open(fileName);
|
||||
FileOutputStream fos = new FileOutputStream(modelFile);
|
||||
byte[] buffer = new byte[8192];
|
||||
int read;
|
||||
try {
|
||||
while ((read = is.read(buffer)) != -1) {
|
||||
fos.write(buffer, 0, read);
|
||||
}
|
||||
} finally {
|
||||
fos.flush();
|
||||
fos.close();
|
||||
is.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.d("", "Can't copy test file onto SD card");
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
public static int saveImageToGalleryShare(Context mContext, Bitmap bmp, String fileName) {
|
||||
//生成路径
|
||||
String root = APP_DIR;
|
||||
String dirName = "tempimage";
|
||||
File appDir = new File(root, dirName);
|
||||
if (!appDir.exists()) {
|
||||
appDir.mkdirs();
|
||||
}
|
||||
//获取文件
|
||||
File file = new File(appDir, fileName);
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(file);
|
||||
bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
||||
fos.flush();
|
||||
//通知系统相册刷新
|
||||
// mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
|
||||
// Uri.fromFile(new File(file.getPath()))));
|
||||
//通知更新相册
|
||||
mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
|
||||
|
||||
return 2;
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (fos != null) {
|
||||
fos.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drawable转换成一个Bitmap
|
||||
*
|
||||
* @param drawable drawable对象
|
||||
* @return
|
||||
*/
|
||||
public static Bitmap drawableToBitmap(Drawable drawable) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
|
||||
drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
|
||||
drawable.draw(canvas);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* oldPath 和 newPath必须是新旧文件的绝对路径
|
||||
*/
|
||||
public static File renameFile(String oldPath, String newPath) {
|
||||
if (TextUtils.isEmpty(oldPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(newPath)) {
|
||||
return null;
|
||||
}
|
||||
File oldFile = new File(oldPath);
|
||||
File newFile = new File(newPath);
|
||||
boolean b = oldFile.renameTo(newFile);
|
||||
File file2 = new File(newPath);
|
||||
return file2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除单个文件
|
||||
*
|
||||
* @param filePath$Name 要删除的文件的文件名
|
||||
* @return 单个文件删除成功返回true,否则返回false
|
||||
*/
|
||||
public static boolean deleteSingleFile(String filePath$Name) {
|
||||
File file = new File(filePath$Name);
|
||||
// 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
|
||||
if (file.exists() && file.isFile()) {
|
||||
if (file.delete()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制asset到片尾
|
||||
*/
|
||||
public static boolean copyAssetsToDst(Context context, String srcPath) {
|
||||
File targetDir0 = new File(APP_DIR);
|
||||
if (!targetDir0.exists()) {
|
||||
targetDir0.mkdirs();
|
||||
}
|
||||
|
||||
File targetDir = new File(VIDEO_DIR_HIDE);
|
||||
if (!targetDir.exists()) {
|
||||
targetDir.mkdirs();
|
||||
}
|
||||
|
||||
try {
|
||||
File outFile = new File(VIDEO_DIR_HIDE, srcPath);
|
||||
InputStream is = context.getAssets().open(srcPath);
|
||||
FileOutputStream fos = new FileOutputStream(outFile);
|
||||
byte[] buffer = new byte[1024];
|
||||
int byteCount;
|
||||
while ((byteCount = is.read(buffer)) != -1) {
|
||||
fos.write(buffer, 0, byteCount);
|
||||
}
|
||||
fos.flush();
|
||||
is.close();
|
||||
fos.close();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**读取assets文件*/
|
||||
public static String getFromAssets(Context context,String fileName) {
|
||||
try {
|
||||
InputStreamReader inputReader = new InputStreamReader(context.getResources().getAssets().open(fileName));
|
||||
BufferedReader bufReader = new BufferedReader(inputReader);
|
||||
String line = "";
|
||||
String Result = "";
|
||||
while ((line = bufReader.readLine()) != null)
|
||||
Result += line;
|
||||
return Result;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Java文件操作 获取文件扩展名
|
||||
*
|
||||
*/
|
||||
public static String getExtensionName(String filename) {
|
||||
if ((filename != null) && (filename.length() > 0)) {
|
||||
int dot = filename.lastIndexOf('.');
|
||||
if ((dot >-1) && (dot < (filename.length() - 1))) {
|
||||
return filename.substring(dot + 1);
|
||||
}
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
90
app/src/main/java/com/dskj/daikuan/utils/GsonUtils.java
Normal file
@@ -0,0 +1,90 @@
|
||||
package com.dskj.daikuan.utils;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* json解析工具类 其实对于数组解析有一些问题
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
public class GsonUtils {
|
||||
|
||||
public static Gson gson = new Gson();
|
||||
|
||||
/**
|
||||
* 返回List对象
|
||||
* @param str
|
||||
* @param type new TypeToken<ArrayList<T>>(){}.getType()
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> T getListFromJSON(String str, Type type) {
|
||||
if (!TextUtils.isEmpty(str)) {
|
||||
return gson.fromJson(str, type);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回List对象
|
||||
* @param str
|
||||
* @param cls
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> getListFromJSON(String str, Class<T> cls)
|
||||
{
|
||||
Type type = new TypeToken<ArrayList<JsonObject>>()
|
||||
{}.getType();
|
||||
ArrayList<JsonObject> jsonObjects = gson.fromJson(str, type);
|
||||
ArrayList<T> arrayList = new ArrayList<>();
|
||||
for (JsonObject jsonObject : jsonObjects)
|
||||
{
|
||||
arrayList.add(gson.fromJson(jsonObject, cls));
|
||||
}
|
||||
return arrayList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回对象
|
||||
* @param str
|
||||
* @param cls
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> T getObjFromJSON(String str, Class<T> cls) {
|
||||
// try {
|
||||
if (!TextUtils.isEmpty(str)) {
|
||||
return gson.fromJson(str, cls);
|
||||
}
|
||||
return null;
|
||||
// }catch (Exception e) {
|
||||
// return null;
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回JsonString
|
||||
* @return
|
||||
*/
|
||||
public static String beanToJSONString(Object bean) {
|
||||
return new Gson().toJson(bean);
|
||||
}
|
||||
|
||||
|
||||
public static String JSONTokener(String in) {
|
||||
// consume an optional byte order mark (BOM) if it exists
|
||||
if (in != null && in.startsWith("\ufeff")) {
|
||||
in = in.substring(1);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
}
|
||||
146
app/src/main/java/com/dskj/daikuan/utils/LogUtils.java
Normal file
@@ -0,0 +1,146 @@
|
||||
package com.dskj.daikuan.utils;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Log统一管理类
|
||||
* Created by xuhuixiang on 2015/10/19 0019.
|
||||
*/
|
||||
public class LogUtils {
|
||||
|
||||
private LogUtils() {
|
||||
throw new UnsupportedOperationException("cannot be instantiated");
|
||||
}
|
||||
|
||||
// public static boolean isDebug = ApiService.isDebug;// 是否需要打印bug,可以在application的onCreate函数里面初始化
|
||||
public static boolean isDebug = true;// 是否需要打印bug,可以在application的onCreate函数里面初始化
|
||||
|
||||
private static final String TAG = "BIKAOVIDEO";
|
||||
|
||||
/**
|
||||
* 默认tag的函数
|
||||
*
|
||||
* @param msg 打印信息
|
||||
*/
|
||||
public static void v(String msg) {
|
||||
if (isDebug) Log.v(TAG, msg);
|
||||
}
|
||||
|
||||
public static void d(String msg) {
|
||||
if (isDebug) Log.d(TAG, msg);
|
||||
}
|
||||
|
||||
public static void i(String msg) {
|
||||
if (isDebug) {
|
||||
if (msg.length() > 4000) {
|
||||
Log.i(TAG, "BIKAOVIDEOsb.length = " + msg.length());
|
||||
int chunkCount = msg.length() / 4000; // integer division
|
||||
for (int i = 0; i <= chunkCount; i++) {
|
||||
int max = 4000 * (i + 1);
|
||||
if (max >= msg.length()) {
|
||||
Log.i(TAG, "XHXchunk " + i + " of " + chunkCount + ":" + msg.substring(4000 * i));
|
||||
} else {
|
||||
Log.i(TAG, "XHXchunk " + i + " of " + chunkCount + ":" + msg.substring(4000 * i, max));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "BIKAOVIDEO" + msg.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void w(String msg) {
|
||||
if (isDebug) Log.w(TAG, msg);
|
||||
}
|
||||
|
||||
public static void e(String msg) {
|
||||
if (isDebug) {
|
||||
if (msg.length() > 4000) {
|
||||
Log.e(TAG, "sb.length = " + msg.length());
|
||||
int chunkCount = msg.length() / 4000; // integer division
|
||||
for (int i = 0; i <= chunkCount; i++) {
|
||||
int max = 4000 * (i + 1);
|
||||
if (max >= msg.length()) {
|
||||
Log.e(TAG, "XHXchunk " + i + " of " + chunkCount + ":" + msg.substring(4000 * i));
|
||||
} else {
|
||||
Log.e(TAG, "XHXchunk " + i + " of " + chunkCount + ":" + msg.substring(4000 * i, max));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "XHX" + msg.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义lag的函数
|
||||
*
|
||||
* @param tag tag
|
||||
* @param msg 打印信息
|
||||
*/
|
||||
public static void v(String tag, String msg) {
|
||||
if (isDebug) Log.v(tag, msg);
|
||||
}
|
||||
|
||||
public static void d(String tag, String msg) {
|
||||
if (isDebug) Log.d(tag, msg);
|
||||
}
|
||||
|
||||
public static void i(String tag, String msg) {
|
||||
if (isDebug) {
|
||||
if (msg.length() > 4000) {
|
||||
Log.i(TAG, "sb.length = " + msg.length());
|
||||
int chunkCount = msg.length() / 4000; // integer division
|
||||
for (int i = 0; i <= chunkCount; i++) {
|
||||
int max = 4000 * (i + 1);
|
||||
if (max >= msg.length()) {
|
||||
Log.i(TAG, "XHXchunk " + i + " of " + chunkCount + ":" + msg.substring(4000 * i));
|
||||
} else {
|
||||
Log.i(TAG, "XHXchunk " + i + " of " + chunkCount + ":" + msg.substring(4000 * i, max));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "XHX" + msg.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void w(String tag, String msg) {
|
||||
if (isDebug) Log.w(tag, msg);
|
||||
}
|
||||
|
||||
public static void e(String tag, String msg) {
|
||||
if (isDebug) Log.e(tag, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义lag的函数
|
||||
*
|
||||
* @param clazz 类
|
||||
* @param msg 打印信息
|
||||
*/
|
||||
public static void v(Class<?> clazz, String msg) {
|
||||
if (isDebug) Log.v(clazz.getSimpleName(), msg);
|
||||
}
|
||||
|
||||
public static void d(Class<?> clazz, String msg) {
|
||||
if (isDebug) Log.d(clazz.getSimpleName(), msg);
|
||||
}
|
||||
|
||||
public static void i(Class<?> clazz, String msg) {
|
||||
if (isDebug) Log.i(clazz.getSimpleName(), msg);
|
||||
}
|
||||
|
||||
public static void w(Class<?> clazz, String msg) {
|
||||
if (isDebug) Log.w(clazz.getSimpleName(), msg);
|
||||
}
|
||||
|
||||
public static void e(Class<?> clazz, String msg) {
|
||||
if (isDebug) Log.e(clazz.getSimpleName(), msg);
|
||||
}
|
||||
|
||||
}
|
||||
53
app/src/main/java/com/dskj/daikuan/utils/RxBus.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package com.dskj.daikuan.utils;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.subjects.PublishSubject;
|
||||
import rx.subjects.SerializedSubject;
|
||||
import rx.subjects.Subject;
|
||||
|
||||
public class RxBus {
|
||||
|
||||
private static volatile RxBus instance;
|
||||
|
||||
private final Subject<Object, Object> bus;
|
||||
|
||||
private RxBus() {
|
||||
bus = new SerializedSubject<>(PublishSubject.create());
|
||||
}
|
||||
|
||||
/**
|
||||
* 单例模式RxBus
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static RxBus getInstance() {
|
||||
if (null == instance) {
|
||||
synchronized (RxBus.class) {
|
||||
if (null == instance) {
|
||||
instance = new RxBus();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param o
|
||||
*/
|
||||
public void post(Object o) {
|
||||
bus.onNext(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据传递的 eventType 类型返回特定类型(eventType)的 被观察者
|
||||
*
|
||||
* @param eventType
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T> Observable<T> toObservable(Class<T> eventType) {
|
||||
return bus.ofType(eventType);
|
||||
}
|
||||
}
|
||||
10
app/src/main/java/com/dskj/daikuan/utils/ToastUtils.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package com.dskj.daikuan.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class ToastUtils {
|
||||
public static void showShort(Context context,String name){
|
||||
Toast.makeText(context,name,Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.dskj.daikuan.viewModel.home;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.azhon.basic.lifecycle.BaseViewModel;
|
||||
import com.dskj.daikuan.InitApp;
|
||||
import com.dskj.daikuan.api.Api;
|
||||
import com.dskj.daikuan.api.BaseObserver;
|
||||
import com.dskj.daikuan.api.Result;
|
||||
import com.dskj.daikuan.bean.ErrorMesage;
|
||||
import com.dskj.daikuan.bean.VideoIndexBean;
|
||||
import com.dskj.daikuan.bean.VideoIndexResult;
|
||||
import com.dskj.daikuan.bean.VideoResult;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class MainViewModel extends BaseViewModel {
|
||||
|
||||
private MutableLiveData<ErrorMesage> errorMesageMutableLiveData = new MutableLiveData<>();
|
||||
|
||||
public MutableLiveData<ErrorMesage> getErrorMesageMutableLiveData() {
|
||||
return errorMesageMutableLiveData;
|
||||
}
|
||||
|
||||
protected MutableLiveData<List<VideoIndexBean>> videoIndexBeanMutableLiveData = new MutableLiveData<>();
|
||||
|
||||
public MutableLiveData<List<VideoIndexBean>> getVideoIndexBeanMutableLiveData() {
|
||||
return videoIndexBeanMutableLiveData;
|
||||
}
|
||||
|
||||
protected MutableLiveData<VideoResult> videoResultMutableLiveData = new MutableLiveData<>();
|
||||
|
||||
public MutableLiveData<VideoResult> getVideoResultMutableLiveData() {
|
||||
return videoResultMutableLiveData;
|
||||
}
|
||||
|
||||
public void videoIndex() {
|
||||
|
||||
Api.getInstance().videoIndex("")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new BaseObserver<Result<VideoIndexResult>>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(Result<VideoIndexResult> feedbackResp) {
|
||||
getVideoIndexBeanMutableLiveData().postValue(feedbackResp.d.getVideo_category());
|
||||
}
|
||||
@Override
|
||||
public void onError(int code, String msg) {
|
||||
getErrorMesageMutableLiveData().postValue(new ErrorMesage(code, msg));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void videoList(String id,String page) {
|
||||
|
||||
Api.getInstance().videoList(InitApp.getToken(),id,"video",page,"1")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new BaseObserver<Result<VideoResult>>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(Result<VideoResult> feedbackResp) {
|
||||
VideoResult videoResult = feedbackResp.d;
|
||||
videoResult.setCategoryId(id);
|
||||
getVideoResultMutableLiveData().postValue(videoResult);
|
||||
}
|
||||
@Override
|
||||
public void onError(int code, String msg) {
|
||||
getErrorMesageMutableLiveData().postValue(new ErrorMesage(code, msg));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void search(String key,String page) {
|
||||
|
||||
Api.getInstance().search(InitApp.getToken(),key,"20",page,"video")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new BaseObserver<Result<VideoResult>>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(Result<VideoResult> feedbackResp) {
|
||||
VideoResult videoResult = feedbackResp.d;
|
||||
videoResult.setCategoryId("999");
|
||||
getVideoResultMutableLiveData().postValue(videoResult);
|
||||
}
|
||||
@Override
|
||||
public void onError(int code, String msg) {
|
||||
getErrorMesageMutableLiveData().postValue(new ErrorMesage(code, msg));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void videoPlay(String id) {
|
||||
|
||||
Api.getInstance().videoPlay(InitApp.getToken(),id)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new BaseObserver<Result>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(Result feedbackResp) {
|
||||
|
||||
}
|
||||
@Override
|
||||
public void onError(int code, String msg) {
|
||||
getErrorMesageMutableLiveData().postValue(new ErrorMesage(code, msg));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.dskj.daikuan.viewModel.home;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Created by shuyu on 2016/12/7.
|
||||
*/
|
||||
|
||||
public class SwitchVideoModel {
|
||||
private String url;
|
||||
private String name;
|
||||
|
||||
public SwitchVideoModel(String name, String url) {
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.hitomi.tilibrary.transfer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.hitomi.tilibrary.style.IProgressIndicator;
|
||||
|
||||
/**
|
||||
* 图片加载时使用 Android 默认的 ProgressBar
|
||||
* <p>
|
||||
* email: 196425254@qq.com
|
||||
*/
|
||||
public class ProgressBarIndicatorNew implements IProgressIndicator {
|
||||
|
||||
// private SparseArray<ProgressBar> progressBarArray = new SparseArray<>();
|
||||
|
||||
// private int dip2Px(Context context, float dpValue) {
|
||||
//// final float scale = context.getResources().getDisplayMetrics().density;
|
||||
//// return (int) (dpValue * scale + 0.5f);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void attach(int position, FrameLayout parent) {
|
||||
// Context context = parent.getContext();
|
||||
//
|
||||
// int progressSize = dip2Px(context, 50);
|
||||
// FrameLayout.LayoutParams progressLp = new FrameLayout.LayoutParams(
|
||||
// progressSize, progressSize);
|
||||
// progressLp.gravity = Gravity.CENTER;
|
||||
//
|
||||
// ProgressBar progressBar = new ProgressBar(context);
|
||||
// progressBar.setLayoutParams(progressLp);
|
||||
//
|
||||
// parent.addView(progressBar, parent.getChildCount());
|
||||
// progressBarArray.put(position, progressBar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideView(int position) {
|
||||
// ProgressBar progressBar = progressBarArray.get(position);
|
||||
// if (progressBar != null)
|
||||
// progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(int position) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(int position, int progress) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish(int position) {
|
||||
// ProgressBar progressBar = progressBarArray.get(position);
|
||||
// if (progressBar == null) return;
|
||||
//
|
||||
// ViewGroup vg = (ViewGroup) progressBar.getParent();
|
||||
// ;
|
||||
// if (vg != null) {
|
||||
// vg.removeView(progressBar);
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
package com.hitomi.tilibrary.transfer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.view.KeyEvent;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.gyf.immersionbar.ImmersionBar;
|
||||
import com.hitomi.tilibrary.style.index.CircleIndexIndicator;
|
||||
import com.hitomi.tilibrary.style.progress.ProgressBarIndicator;
|
||||
import com.hitomi.tilibrary.utils.AppManager;
|
||||
import com.hitomi.tilibrary.utils.FileUtils;
|
||||
import com.hitomi.tilibrary.view.video.ExoVideoView;
|
||||
import com.hitomi.tilibrary.view.video.source.ExoSourceManager;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Main workflow: <br/>
|
||||
* 1、点击缩略图展示缩略图到 transferee 过渡动画 <br/>
|
||||
* 2、显示下载高清图片进度 <br/>
|
||||
* 3、加载完成显示高清图片 <br/>
|
||||
* 4、高清图支持手势缩放 <br/>
|
||||
* 5、关闭 transferee 展示 transferee 到原缩略图的过渡动画 <br/>
|
||||
* Created by Vans Z on 2017/1/19.
|
||||
* <p>
|
||||
* email: 196425254@qq.com
|
||||
*/
|
||||
public class TransfereeNew implements DialogInterface.OnShowListener,
|
||||
DialogInterface.OnKeyListener,
|
||||
TransferLayout.OnLayoutResetListener,
|
||||
AppManager.OnAppStateChangeListener {
|
||||
|
||||
private Context context;
|
||||
private Dialog transDialog;
|
||||
|
||||
private TransferLayout transLayout;
|
||||
private TransferConfig transConfig;
|
||||
private OnTransfereeStateChangeListener transListener;
|
||||
|
||||
// 因为Dialog的关闭有动画延迟,固不能使用 dialog.isShowing, 去判断 transferee 的显示逻辑
|
||||
private boolean shown;
|
||||
|
||||
/**
|
||||
* 构造方法私有化,通过{@link #getDefault(Context)} 创建 transferee
|
||||
*
|
||||
* @param context 上下文环境
|
||||
*/
|
||||
private TransfereeNew(Context context) {
|
||||
this.context = context;
|
||||
createLayout();
|
||||
createDialog();
|
||||
AppManager.getInstance().init((Application) context.getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* @return {@link TransfereeNew}
|
||||
*/
|
||||
public static TransfereeNew getDefault(Context context) {
|
||||
return new TransfereeNew(context);
|
||||
}
|
||||
|
||||
private void createLayout() {
|
||||
transLayout = new TransferLayout(context);
|
||||
transLayout.setOnLayoutResetListener(this);
|
||||
}
|
||||
|
||||
private void createDialog() {
|
||||
transDialog = new AlertDialog.Builder(context,
|
||||
android.R.style.Theme_Translucent_NoTitleBar_Fullscreen)
|
||||
.setView(transLayout)
|
||||
.create();
|
||||
transDialog.setOnShowListener(this);
|
||||
transDialog.setOnKeyListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查参数,如果必须参数缺少,就使用缺省参数或者抛出异常
|
||||
*/
|
||||
private void checkConfig() {
|
||||
if (transConfig == null)
|
||||
throw new IllegalArgumentException("The parameter TransferConfig can't be null");
|
||||
if (transConfig.isSourceEmpty())
|
||||
throw new IllegalArgumentException("The parameter sourceUrlList or sourceUriList can't be empty");
|
||||
if (transConfig.getImageLoader() == null)
|
||||
throw new IllegalArgumentException("Need to specify an ImageLoader");
|
||||
|
||||
transConfig.setNowThumbnailIndex(Math.max(transConfig.getNowThumbnailIndex(), 0));
|
||||
transConfig.setOffscreenPageLimit(transConfig.getOffscreenPageLimit() <= 0
|
||||
? 1 : transConfig.getOffscreenPageLimit());
|
||||
transConfig.setDuration(transConfig.getDuration() <= 0
|
||||
? 300 : transConfig.getDuration());
|
||||
transConfig.setProgressIndicator(transConfig.getProgressIndicator() == null
|
||||
? new ProgressBarIndicator() : transConfig.getProgressIndicator());
|
||||
transConfig.setIndexIndicator(transConfig.getIndexIndicator() == null
|
||||
? new CircleIndexIndicator() : transConfig.getIndexIndicator());
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置 transferee 参数对象
|
||||
*
|
||||
* @param config 参数对象
|
||||
* @return transferee
|
||||
*/
|
||||
public TransfereeNew apply(TransferConfig config) {
|
||||
if (!shown) {
|
||||
transConfig = config;
|
||||
OriginalViewHelper.getInstance().fillOriginImages(config);
|
||||
checkConfig();
|
||||
transLayout.apply(config);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* transferee 是否显示
|
||||
*
|
||||
* @return true :显示, false :关闭
|
||||
*/
|
||||
public boolean isShown() {
|
||||
return shown;
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示 transferee
|
||||
*/
|
||||
public void show() {
|
||||
if (shown) return;
|
||||
transDialog.show();
|
||||
adjustTopAndBottom();
|
||||
if (transListener != null) {
|
||||
transListener.onShow();
|
||||
}
|
||||
shown = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示 transferee, 并设置 OnTransfereeChangeListener
|
||||
*
|
||||
* @param listener {@link OnTransfereeStateChangeListener}
|
||||
*/
|
||||
public void show(OnTransfereeStateChangeListener listener) {
|
||||
if (shown || listener == null) return;
|
||||
transDialog.show();
|
||||
adjustTopAndBottom();
|
||||
transListener = listener;
|
||||
transListener.onShow();
|
||||
shown = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭 transferee
|
||||
*/
|
||||
public void dismiss() {
|
||||
if (shown && transLayout.dismiss(transConfig.getNowThumbnailIndex())) {
|
||||
shown = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片文件
|
||||
*/
|
||||
public File getImageFile(String imageUrl) {
|
||||
return transConfig.getImageLoader().getCache(imageUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除 transferee 缓存,包括图片和视频文件缓存,注意清除视频缓存必须保证 transferee 是关闭状态
|
||||
*/
|
||||
public void clear() {
|
||||
if (transConfig != null && transConfig.getImageLoader() != null) {
|
||||
transConfig.getImageLoader().clearCache();
|
||||
}
|
||||
File cacheFile = new File(context.getCacheDir(), ExoVideoView.CACHE_DIR);
|
||||
if (cacheFile.exists() && !shown) {
|
||||
FileUtils.deleteDir(new File(cacheFile, VideoThumbState.FRAME_DIR));
|
||||
ExoSourceManager.clearCache(context, cacheFile, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dialog 打开时的监听器
|
||||
*/
|
||||
@Override
|
||||
public void onShow(DialogInterface dialog) {
|
||||
AppManager.getInstance().register(this);
|
||||
transLayout.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整顶部和底部内边距
|
||||
*/
|
||||
private void adjustTopAndBottom() {
|
||||
if (context instanceof Activity) {
|
||||
// 隐藏状态栏和导航栏,全屏化
|
||||
Activity activity = (Activity) context;
|
||||
ImmersionBar.with(activity, transDialog)
|
||||
.fullScreen(true)
|
||||
.init();
|
||||
// int top = ImmersionBar.getNotchHeight(activity);
|
||||
// int bottom = ImmersionBar.getNavigationBarHeight(activity);
|
||||
transLayout.setPadding(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReset() {
|
||||
AppManager.getInstance().unregister(this);
|
||||
transDialog.dismiss();
|
||||
if (transListener != null)
|
||||
transListener.onDismiss();
|
||||
shown = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK &&
|
||||
event.getAction() == KeyEvent.ACTION_UP &&
|
||||
!event.isCanceled()) {
|
||||
dismiss();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onForeground() {
|
||||
transLayout.pauseOrPlayVideo(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackground() {
|
||||
transLayout.pauseOrPlayVideo(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Transferee 显示和关闭的监听器
|
||||
*
|
||||
* @param listener {@link OnTransfereeStateChangeListener}
|
||||
*/
|
||||
public void setOnTransfereeStateChangeListener(OnTransfereeStateChangeListener listener) {
|
||||
transListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源销毁,防止内存泄漏
|
||||
*/
|
||||
public void destroy() {
|
||||
if (transConfig != null) {
|
||||
transConfig.destroy();
|
||||
transConfig = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transferee 显示的时候调用 {@link OnTransfereeStateChangeListener#onShow()}
|
||||
* <p>
|
||||
* Transferee 关闭的时候调用 {@link OnTransfereeStateChangeListener#onDismiss()}
|
||||
*/
|
||||
public interface OnTransfereeStateChangeListener {
|
||||
void onShow();
|
||||
|
||||
void onDismiss();
|
||||
}
|
||||
|
||||
public interface OnTransfereeLongClickListener {
|
||||
void onLongClick(ImageView imageView, String imageUri, int pos);
|
||||
}
|
||||
|
||||
}
|
||||
7
app/src/main/res/anim/animate.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layoutAnimation
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:animation="@anim/item_animation_fall_down"
|
||||
android:delay="15%"
|
||||
android:animationOrder="normal"
|
||||
/>
|
||||
20
app/src/main/res/anim/item_animation_fall_down.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="500">
|
||||
<translate
|
||||
android:fromYDelta="-20%"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:toYDelta="0"></translate>
|
||||
<alpha
|
||||
android:fromAlpha="0"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:toAlpha="1"></alpha>
|
||||
<scale
|
||||
android:fromXScale="105%"
|
||||
android:fromYScale="105%"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:toXScale="100%"
|
||||
android:toYScale="100%"></scale>
|
||||
</set>
|
||||
7
app/src/main/res/color/select_color.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?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/color_999999" android:state_checked="false"/>
|
||||
</selector>
|
||||
|
||||
7
app/src/main/res/color/select_color1.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/white" android:state_checked="true" />
|
||||
<item android:color="@color/colorAccent" android:state_checked="false"/>
|
||||
</selector>
|
||||
|
||||
30
app/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
||||
7
app/src/main/res/drawable/actionbar_bg.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient android:startColor="#E5D6FF"
|
||||
android:endColor="#E3EFFF"
|
||||
android:type="linear"
|
||||
android:gradientRadius="10dp"/>
|
||||
</shape>
|
||||
6
app/src/main/res/drawable/bg_ripple.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:color="@color/gray_line_light"
|
||||
tools:ignore="MissingDefaultResource"> <!--波纹颜色-->
|
||||
</ripple>
|
||||
9
app/src/main/res/drawable/dlg_input_video_bg.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<corners android:radius="8dp" />
|
||||
|
||||
<solid android:color="@color/white" />
|
||||
|
||||
</shape>
|
||||
170
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
||||
25
app/src/main/res/drawable/inputbg.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/white">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape>
|
||||
<solid android:color="@color/transparent" />
|
||||
<corners android:radius="@dimen/dp5" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 默认显⽰效果-->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<gradient
|
||||
android:angle="180"
|
||||
android:startColor="#FBF7F8"
|
||||
android:endColor="#FBF7F8"
|
||||
android:type="linear"
|
||||
android:useLevel="true" />
|
||||
<stroke android:color="#FBF7F8" android:width="1dp"/>
|
||||
<corners
|
||||
android:radius="5dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
|
||||
25
app/src/main/res/drawable/inputbg_false.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/white">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape>
|
||||
<solid android:color="@color/transparent" />
|
||||
<corners android:radius="@dimen/dp5" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 默认显⽰效果-->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<gradient
|
||||
android:angle="180"
|
||||
android:startColor="@color/transparent"
|
||||
android:endColor="@color/transparent"
|
||||
android:type="linear"
|
||||
android:useLevel="true" />
|
||||
<stroke android:color="#000019" android:width="1dp"/>
|
||||
<corners
|
||||
android:radius="5dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
|
||||
25
app/src/main/res/drawable/inputbg_true.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/white">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape>
|
||||
<solid android:color="@color/transparent" />
|
||||
<corners android:radius="@dimen/dp5" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 默认显⽰效果-->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<gradient
|
||||
android:angle="180"
|
||||
android:startColor="@color/transparent"
|
||||
android:endColor="@color/transparent"
|
||||
android:type="linear"
|
||||
android:useLevel="true" />
|
||||
<stroke android:color="@color/color_e12d48" android:width="1dp"/>
|
||||
<corners
|
||||
android:radius="5dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
|
||||
25
app/src/main/res/drawable/item_bg_default.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/white">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape>
|
||||
<solid android:color="@color/transparent" />
|
||||
<corners android:radius="@dimen/dp10" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 默认显⽰效果-->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<gradient
|
||||
android:angle="180"
|
||||
android:startColor="#E12D48"
|
||||
android:endColor="#E12D48"
|
||||
android:type="linear"
|
||||
android:useLevel="true" />
|
||||
<stroke android:color="#E12D48" android:width="0.5dp"/>
|
||||
<corners
|
||||
android:radius="10dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
|
||||
25
app/src/main/res/drawable/item_bg_default1.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/white">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape>
|
||||
<solid android:color="@color/transparent" />
|
||||
<corners android:radius="@dimen/dp5" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 默认显⽰效果-->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<gradient
|
||||
android:angle="180"
|
||||
android:startColor="#E12D48"
|
||||
android:endColor="#E12D48"
|
||||
android:type="linear"
|
||||
android:useLevel="true" />
|
||||
<stroke android:color="#E12D48" android:width="0.5dp"/>
|
||||
<corners
|
||||
android:radius="5dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
|
||||
28
app/src/main/res/drawable/jz_bottom_seek_progress.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@android:id/background">
|
||||
<shape>
|
||||
<solid android:color="#a5ffffff" />
|
||||
<size android:height="4dp" />
|
||||
<corners android:radius="2dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:id="@android:id/secondaryProgress">
|
||||
<clip>
|
||||
<shape>
|
||||
<solid android:color="#ffffffff" />
|
||||
<size android:height="4dp" />
|
||||
<corners android:radius="2dp" />
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
<item android:id="@android:id/progress">
|
||||
<clip>
|
||||
<shape>
|
||||
<solid android:color="@color/color_fe8b59" />
|
||||
<size android:height="4dp" />
|
||||
<corners android:radius="2dp" />
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
</layer-list>
|
||||
9
app/src/main/res/drawable/jz_seek_post.xml
Normal file
@@ -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/color_fe8b59" />
|
||||
<size
|
||||
android:width="8dp"
|
||||
android:height="15dp" />
|
||||
<corners android:radius="2dp"/>
|
||||
</shape>
|
||||
5
app/src/main/res/drawable/save_btn_back.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="15dp"/>
|
||||
<solid android:color="@color/color_fe8b59"/>
|
||||
</shape>
|
||||
19
app/src/main/res/drawable/search_bg.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<item android:gravity="center">
|
||||
<shape>
|
||||
<size
|
||||
android:height="@dimen/dp30" />
|
||||
<gradient
|
||||
android:angle="180"
|
||||
android:startColor="@color/color_search_2"
|
||||
android:endColor="@color/color_search_1"
|
||||
android:type="linear"
|
||||
android:useLevel="true" />
|
||||
<corners android:radius="@dimen/dp15"/>
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
||||
32
app/src/main/res/drawable/tab_line.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<item android:gravity="center">
|
||||
<shape>
|
||||
<size
|
||||
android:width="@dimen/dp30"
|
||||
android:height="@dimen/dp4" />
|
||||
<gradient
|
||||
android:angle="180"
|
||||
android:startColor="@color/color_fe8b59"
|
||||
android:endColor="@color/color_e83126"
|
||||
android:type="linear"
|
||||
android:useLevel="true" />
|
||||
<corners android:radius="@dimen/dp2"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:gravity="center">
|
||||
<shape>
|
||||
<size
|
||||
android:width="@dimen/dp60"
|
||||
android:height="@dimen/dp4" />
|
||||
<gradient
|
||||
android:angle="180"
|
||||
android:startColor="@color/transparent"
|
||||
android:endColor="@color/transparent"
|
||||
android:type="linear"
|
||||
android:useLevel="true" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
15
app/src/main/res/drawable/tab_line1.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<item android:gravity="center">
|
||||
<shape>
|
||||
<size
|
||||
android:width="@dimen/dp80"
|
||||
android:height="@dimen/dp30" />
|
||||
<solid android:color="@color/color_e83126" />
|
||||
<corners android:radius="@dimen/dp2"/>
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
||||
120
app/src/main/res/layout/activity_login.xml
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="model"
|
||||
type="com.dskj.daikuan.viewModel.home.MainViewModel" />
|
||||
</data>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp100"
|
||||
android:layout_height="@dimen/dp100"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="74dp"
|
||||
android:src="@mipmap/logo_img" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/name_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp60"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginLeft="@dimen/dp30"
|
||||
android:layout_marginTop="240dp"
|
||||
android:layout_marginRight="@dimen/dp30"
|
||||
android:background="@mipmap/edit_false"
|
||||
android:hint="请输入您的账号/手机号"
|
||||
android:paddingLeft="10dp"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/pass_et"
|
||||
android:layout_below="@id/name_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginLeft="@dimen/dp30"
|
||||
android:layout_marginRight="@dimen/dp30"
|
||||
android:layout_height="@dimen/dp60"
|
||||
android:layout_marginTop="26dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:inputType="textPassword"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:hint="请输入您的密码"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textColor="#ffffffff"
|
||||
android:background="@mipmap/edit_false"
|
||||
android:textSize="16sp"
|
||||
/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/invcode_et"
|
||||
android:layout_below="@id/pass_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginLeft="@dimen/dp30"
|
||||
android:layout_marginRight="@dimen/dp30"
|
||||
android:layout_height="@dimen/dp60"
|
||||
android:layout_marginTop="26dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:inputType="textPassword"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:hint="请输入邀请码"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textColor="#ffffffff"
|
||||
android:background="@mipmap/edit_false"
|
||||
android:textSize="16sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/get_code_tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/dp60"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_alignRight="@id/pass_et"
|
||||
android:layout_alignTop="@id/pass_et"
|
||||
android:layout_marginRight="@dimen/dp10"
|
||||
android:text="获取验证码"
|
||||
android:visibility="gone"
|
||||
android:textColor="#ffe12d48"
|
||||
android:textSize="16sp"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/login_bt"
|
||||
android:layout_width="210dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_below="@id/invcode_et"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="70dp"
|
||||
android:background="@drawable/item_bg_default1"
|
||||
android:text="登录"
|
||||
android:textColor="#ffffffff"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/forget_pass_tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/login_bt"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/dp40"
|
||||
android:text="忘记密码"
|
||||
android:textColor="#ffffffff"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:src="@mipmap/login_bot" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</layout>
|
||||
103
app/src/main/res/layout/activity_main2.xml
Normal file
@@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout 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">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="model"
|
||||
type="com.dskj.daikuan.viewModel.home.MainViewModel" />
|
||||
</data>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/top_ly_f"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?actionBarSize"
|
||||
android:orientation="horizontal"
|
||||
android:paddingRight="@dimen/dp15">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/title_tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:textColor="@color/black"
|
||||
android:text="贷款列表"
|
||||
android:textSize="@dimen/sp16" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/menu_tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:background="@drawable/bg_ripple"
|
||||
android:src="@mipmap/add_img"
|
||||
android:visibility="gone" />
|
||||
|
||||
</RelativeLayout>
|
||||
<RelativeLayout
|
||||
android:id="@+id/nodate_ry"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/top_ly_f">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/dp180"
|
||||
android:text="没有贷款信息"
|
||||
android:drawableTop="@mipmap/nodate_img"
|
||||
android:textColor="#19000019"
|
||||
android:textSize="18sp"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:layout_width="210dp"
|
||||
android:layout_height="50dp"
|
||||
android:text="申请贷款"
|
||||
android:textSize="@dimen/sp16"
|
||||
android:background="@drawable/item_bg_default1"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="85dp"
|
||||
android:textColor="@color/white"
|
||||
android:id="@+id/tijiao_bt"/>
|
||||
</RelativeLayout>
|
||||
|
||||
<com.scwang.smart.refresh.layout.SmartRefreshLayout
|
||||
android:id="@+id/refreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/top_ly_f"
|
||||
android:layout_marginTop="@dimen/dp10"
|
||||
android:visibility="gone"
|
||||
app:srlAccentColor="@color/color_e12d48">
|
||||
|
||||
<com.dskj.daikuan.ui.view.BezierCircleHeader
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="@dimen/dp35"
|
||||
android:layout_marginRight="@dimen/dp35"
|
||||
android:overScrollMode="never">
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
</layout>
|
||||
361
app/src/main/res/layout/activity_shenqing.xml
Normal file
@@ -0,0 +1,361 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout 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">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="model"
|
||||
type="com.dskj.daikuan.viewModel.home.MainViewModel" />
|
||||
</data>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
android:id="@+id/top_ly_f"
|
||||
layout="@layout/layout_action_bar1" />
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:overScrollMode="never"
|
||||
android:layout_below="@id/top_ly_f">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="@dimen/dp35"
|
||||
android:paddingRight="@dimen/dp35"
|
||||
android:paddingTop="@dimen/dp15"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="姓名/Full Name:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/fullname_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg_false"
|
||||
android:paddingLeft="10dp"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="大马卡/IC:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/ic_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg_false"
|
||||
android:paddingLeft="10dp"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="@dimen/dp15"
|
||||
android:paddingBottom="@dimen/dp5">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/renxiang_cy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="@dimen/dp6"
|
||||
android:layout_weight="1">
|
||||
|
||||
<com.dskj.daikuan.ui.view.CircleImageView
|
||||
android:id="@+id/zhengmian_iv"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@mipmap/sfzzm"
|
||||
app:ease_radius="@dimen/dp8"
|
||||
app:es_shape_type="rectangle"
|
||||
app:layout_constraintDimensionRatio="152:100"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
</com.dskj.daikuan.ui.view.CircleImageView>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@mipmap/sfz_add_img"
|
||||
app:layout_constraintBottom_toBottomOf="@id/zhengmian_iv"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp10"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="IC-人像面"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/zhengmian_iv" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/guohui_cy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp6"
|
||||
android:layout_weight="1">
|
||||
|
||||
<com.dskj.daikuan.ui.view.CircleImageView
|
||||
android:id="@+id/fanmian_iv"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@mipmap/sfzbm"
|
||||
app:ease_radius="@dimen/dp8"
|
||||
app:es_shape_type="rectangle"
|
||||
app:layout_constraintDimensionRatio="152:100"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
</com.dskj.daikuan.ui.view.CircleImageView>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@mipmap/sfz_add_img"
|
||||
app:layout_constraintBottom_toBottomOf="@id/fanmian_iv"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp10"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="IC-国徽面 "
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/fanmian_iv" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="电话号码/Phone number:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
<EditText
|
||||
android:id="@+id/phone_number_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:inputType="phone"
|
||||
android:background="@drawable/inputbg_false"
|
||||
android:paddingLeft="10dp"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="职业/Occupation:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/occupation_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg_false"
|
||||
android:paddingLeft="10dp"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="地址/Address:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/address_et"
|
||||
android:layout_width="match_parent"
|
||||
android:minHeight="@dimen/dp44"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg_false"
|
||||
android:paddingLeft="10dp"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="收款银行/Beneficiary Bank:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/beneficiary_bank_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg_false"
|
||||
android:paddingLeft="10dp"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp44"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:src="@mipmap/select_img"
|
||||
android:scaleType="centerInside"
|
||||
android:layout_alignParentRight="true" />
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="户口号码 /Beneficiary Account No:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/beneficiary_account_no_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg_false"
|
||||
android:paddingLeft="10dp"
|
||||
android:inputType="number"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="薪水/salary:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/salary_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg_false"
|
||||
android:paddingLeft="10dp"
|
||||
android:inputType="number"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="贷款金额/Loan amount :"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/loan_amount_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg_false"
|
||||
android:paddingLeft="10dp"
|
||||
android:inputType="number"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@color/white"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_height="100dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/login_bt"
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="45dp"
|
||||
android:layout_marginTop="@dimen/dp10"
|
||||
android:background="@drawable/item_bg_default1"
|
||||
android:text="提交申请"
|
||||
android:textColor="#ffffffff"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</layout>
|
||||
38
app/src/main/res/layout/activity_start_up.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout 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">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="model"
|
||||
type="com.dskj.daikuan.viewModel.home.MainViewModel" />
|
||||
|
||||
</data>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/show_bg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".ui.activity.StartUpActivity">
|
||||
|
||||
<com.dskj.daikuan.ui.view.CircleImageView
|
||||
android:layout_width="@dimen/dp100"
|
||||
android:layout_height="@dimen/dp100"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:src="@mipmap/logo_img"
|
||||
android:layout_marginTop="74dp"
|
||||
app:ease_border_color="@color/color_F8F8F8"
|
||||
app:ease_border_width="1dp"
|
||||
app:es_shape_type="round" />
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@mipmap/login_bot"
|
||||
android:layout_alignParentBottom="true"/>
|
||||
|
||||
</RelativeLayout>
|
||||
</layout>
|
||||
353
app/src/main/res/layout/activity_xiangqing.xml
Normal file
@@ -0,0 +1,353 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout 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">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="model"
|
||||
type="com.dskj.daikuan.viewModel.home.MainViewModel" />
|
||||
</data>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
android:id="@+id/top_ly_f"
|
||||
layout="@layout/layout_action_bar1" />
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:overScrollMode="never"
|
||||
android:layout_below="@id/top_ly_f">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="@dimen/dp35"
|
||||
android:paddingRight="@dimen/dp35"
|
||||
android:paddingTop="@dimen/dp15"
|
||||
android:orientation="vertical">
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@mipmap/top_bg">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="张启山"
|
||||
android:layout_marginLeft="@dimen/dp20"
|
||||
android:layout_marginTop="20dp"
|
||||
android:id="@+id/fullname_et"
|
||||
android:textColor="#ffffffff"
|
||||
android:textSize="30sp"
|
||||
/>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp20"
|
||||
android:text="姓名/Full Name:"
|
||||
android:layout_marginTop="@dimen/dp2"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="大马卡/IC:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ic_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg"
|
||||
android:paddingLeft="10dp"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="@dimen/dp15"
|
||||
android:paddingBottom="@dimen/dp5">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/renxiang_cy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="@dimen/dp6"
|
||||
android:layout_weight="1">
|
||||
|
||||
<com.dskj.daikuan.ui.view.CircleImageView
|
||||
android:id="@+id/zhengmian_iv"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@mipmap/sfzzm"
|
||||
app:ease_radius="@dimen/dp8"
|
||||
app:es_shape_type="rectangle"
|
||||
app:layout_constraintDimensionRatio="152:100"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
</com.dskj.daikuan.ui.view.CircleImageView>
|
||||
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp10"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="IC-人像面"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/zhengmian_iv" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/guohui_cy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp6"
|
||||
android:layout_weight="1">
|
||||
|
||||
<com.dskj.daikuan.ui.view.CircleImageView
|
||||
android:id="@+id/fanmian_iv"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@mipmap/sfzbm"
|
||||
app:ease_radius="@dimen/dp8"
|
||||
app:es_shape_type="rectangle"
|
||||
app:layout_constraintDimensionRatio="152:100"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
</com.dskj.daikuan.ui.view.CircleImageView>
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp10"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="IC-国徽面 "
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/fanmian_iv" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="电话号码/Phone number:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/phone_number_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:inputType="phone"
|
||||
android:background="@drawable/inputbg"
|
||||
android:paddingLeft="10dp"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="职业/Occupation:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/occupation_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg"
|
||||
android:paddingLeft="10dp"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="地址/Address:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/address_et"
|
||||
android:layout_width="match_parent"
|
||||
android:minHeight="@dimen/dp44"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg"
|
||||
android:paddingLeft="10dp"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="收款银行/Beneficiary Bank:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/beneficiary_bank_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg"
|
||||
android:paddingLeft="10dp"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp44"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:src="@mipmap/select_img"
|
||||
android:scaleType="centerInside"
|
||||
android:layout_alignParentRight="true" />
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="户口号码 /Beneficiary Account No:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/beneficiary_account_no_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg"
|
||||
android:paddingLeft="10dp"
|
||||
android:inputType="number"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="薪水/salary:"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/salary_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg"
|
||||
android:paddingLeft="10dp"
|
||||
android:inputType="number"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<TextView
|
||||
android:layout_marginTop="@dimen/dp15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/dp12"
|
||||
android:text="贷款金额/Loan amount :"
|
||||
android:textColor="#ff959595"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loan_amount_et"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp44"
|
||||
android:layout_marginTop="@dimen/dp4"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/inputbg"
|
||||
android:paddingLeft="10dp"
|
||||
android:inputType="number"
|
||||
android:textColor="@color/black"
|
||||
android:textColorHint="@color/color_e6000000"
|
||||
android:textSize="13sp" />
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@color/white"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_height="100dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/login_bt"
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="45dp"
|
||||
android:layout_marginTop="@dimen/dp10"
|
||||
android:background="@drawable/item_bg_default1"
|
||||
android:text="提交申请"
|
||||
android:textColor="#ffffffff"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</layout>
|
||||
64
app/src/main/res/layout/agent_child_item.xml
Normal file
@@ -0,0 +1,64 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/big_ly"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical"
|
||||
tools:ignore="MissingDefaultResource">
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginBottom="13dp"
|
||||
android:background="@drawable/item_bg_default"
|
||||
android:layout_height="75dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="17dp"
|
||||
android:layout_marginTop="9dp"
|
||||
android:text="代收账金额 第11期"
|
||||
android:textColor="#80ffffff"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name_tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginTop="9dp"
|
||||
android:layout_marginRight="17dp"
|
||||
android:text="代收账金额 第11期"
|
||||
android:textColor="#80ffffff"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/number_tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/title_tv"
|
||||
android:layout_marginLeft="17dp"
|
||||
android:layout_marginTop="@dimen/dp5"
|
||||
android:text="$98724.10"
|
||||
android:textColor="#ffffffff"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_below="@id/name_tv"
|
||||
android:layout_marginTop="@dimen/dp12"
|
||||
android:layout_marginRight="17dp"
|
||||
android:text="贷款审批中"
|
||||
android:textColor="#ff750000"
|
||||
android:textSize="13sp"
|
||||
/>
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
49
app/src/main/res/layout/layout_action_bar1.xml
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/top_ly_f"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?actionBarSize"
|
||||
android:orientation="horizontal"
|
||||
android:paddingRight="@dimen/dp15">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/back_button"
|
||||
android:layout_width="@dimen/dp40"
|
||||
android:layout_height="@dimen/dp40"
|
||||
android:layout_centerVertical="true"
|
||||
android:scaleType="centerInside"
|
||||
android:background="@drawable/bg_ripple"
|
||||
android:src="@mipmap/ic_black_back" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/title_tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/sp16" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/menu_tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp14"
|
||||
android:background="@drawable/bg_ripple"
|
||||
android:visibility="gone" />
|
||||
|
||||
</RelativeLayout>
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp" />
|
||||
</LinearLayout>
|
||||
|
||||
</layout>
|
||||
43
app/src/main/res/layout/layout_custom.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:ignore="MissingDefaultResource">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/down_iv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_alignParentRight="true"
|
||||
android:text="保存"
|
||||
android:paddingLeft="14dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingRight="14dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:visibility="visible"
|
||||
android:gravity="center"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/color_333333"
|
||||
android:background="@drawable/save_btn_back" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/numIndicator_float"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="40dp"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:text="1/1"
|
||||
android:shadowDx="2"
|
||||
android:shadowDy="2"
|
||||
android:shadowRadius="1"
|
||||
android:shadowColor="#CCCCCC"
|
||||
android:textSize="16sp"
|
||||
android:textColor="#ffffff" />
|
||||
</RelativeLayout>
|
||||
18
app/src/main/res/layout/switch_video_dialog.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#B43A3A3A"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/switch_dialog_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@null"
|
||||
android:cacheColorHint="#00000000"
|
||||
android:divider="#ccc"
|
||||
android:dividerHeight="1px"
|
||||
android:listSelector="#00000000" />
|
||||
|
||||
</LinearLayout>
|
||||
14
app/src/main/res/layout/switch_video_dialog_item.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#00000000"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="15dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="15sp">
|
||||
|
||||
</TextView>
|
||||
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -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="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@@ -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="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 982 B |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xhdpi/more_live_right_img.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/add_img.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/edit_false.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/edit_true.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_black_back.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_black_back_white.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/login_bot.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/logo_img.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/nodate_img.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/select_img.png
Normal file
|
After Width: | Height: | Size: 915 B |
BIN
app/src/main/res/mipmap-xxhdpi/sese.png
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/sfz_add_img.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/sfzbm.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/sfzzm.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/top_bg.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/new_bg_index.png
Normal file
|
After Width: | Height: | Size: 264 KiB |
76
app/src/main/res/values-night/colors.xml
Normal file
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#000000</color>
|
||||
<color name="colorPrimary">#E83126</color>
|
||||
<color name="color_2062D3">#2062D3</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
<color name="color_7B7888">#7B7888</color>
|
||||
<color name="color_8C8D92">#8C8D92</color>
|
||||
<color name="transparent">#00000000</color>
|
||||
<color name="color_222222">#222222</color>
|
||||
<color name="white11">#F4F6F8</color>
|
||||
<color name="color_24272E">#24272E</color>
|
||||
<color name="text_red">#DD493D</color>
|
||||
<color name="color_F5F3F8">#F5F3F8</color>
|
||||
<color name="color_FE6A9E">#FE6A9E</color>
|
||||
<color name="color_99FFFFFF">#99FFFFFF</color>
|
||||
<color name="color_993376EA">#993376EA</color>
|
||||
<color name="color_e1e3e6">#e1e3e6</color>
|
||||
<color name="gray_line_light">#eeeeee</color>
|
||||
<color name="color_AAAAAA">#AAAAAA</color>
|
||||
<color name="color_F8F8F8">#F8F8F8</color>
|
||||
<color name="color_333333">#BBBBBB</color>
|
||||
<color name="color_C4C4C4">#C4C4C4</color>
|
||||
<color name="color_999999">#66000000</color>
|
||||
<color name="color_F4F4F4">#F4F4F4</color>
|
||||
<color name="color_E7EAF9">#E7EAF9</color>
|
||||
<color name="color_777777">#777777</color>
|
||||
<color name="color_E8ECFF">#E8ECFF</color>
|
||||
<color name="color_2B2B2B">#2B2B2B</color>
|
||||
<color name="color_3376EA">#3376EA</color>
|
||||
<color name="color_2D3338">#2D3338</color>
|
||||
<color name="color_1D1F25">#1D1F25</color>
|
||||
<color name="color_cccccc">#CCCCCC</color>
|
||||
<color name="color_99000000">#99000000</color>
|
||||
<color name="color_CF8823">#CF8823</color>
|
||||
<color name="color_616CC1">#616CC1</color>
|
||||
<color name="color_666666">#666666</color>
|
||||
|
||||
<color name="blue_color_picker">#0066FF</color>
|
||||
<color name="brown_color_picker">#AF593E</color>
|
||||
<color name="green_color_picker">#01A368</color>
|
||||
<color name="orange_color_picker">#FF861F</color>
|
||||
<color name="red_color_picker">#ED0A3F</color>
|
||||
<color name="red_orange_color_picker">#FF3F34</color>
|
||||
<color name="sky_blue_color_picker">#76D7EA</color>
|
||||
<color name="violet_color_picker">#8359A3</color>
|
||||
<color name="yellow_color_picker">#FBE870</color>
|
||||
<color name="yellow_green_color_picker">#C5E17A</color>
|
||||
<color name="tool_bg">#151414</color>
|
||||
<color name="semi_black_transparent">#99000000</color>
|
||||
|
||||
<!-- 这2个颜色用来动态修改图片选择器的文件夹目录的背景颜色 e0adb 选中颜色 white 默认颜色-->
|
||||
<color name="picture_color_transparent_e0db">#F8F8F8</color>
|
||||
<color name="picture_color_transparent_white">#FFFFFF</color>
|
||||
|
||||
<!-- 这个颜色用来动态修改videoview的背景颜色-->
|
||||
<color name="video_bg_color">#F8F8F8</color>
|
||||
|
||||
<color name="default_bg">#F3F3F3</color>
|
||||
<color name="color_fe8b59">#FFFFFF</color>
|
||||
<color name="color_e83126">#E83126</color>
|
||||
<color name="color_e6000000">#e6000000</color>
|
||||
<color name="color_66ffffff">#66FFFFFF</color>
|
||||
<color name="color_search_2">#EEEEEE</color>
|
||||
<color name="color_search_1">#DDDDDD</color>
|
||||
|
||||
<color name="color_45000000">#45000000</color>
|
||||
|
||||
</resources>
|
||||
16
app/src/main/res/values-night/themes.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Bkvideo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
||||
4
app/src/main/res/values-zh/values-zh.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="picture_completed">确定</string>
|
||||
</resources>
|
||||
77
app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#000019</color>
|
||||
<color name="white">#FFFFFF</color>
|
||||
<color name="colorPrimary">#E83126</color>
|
||||
<color name="color_2062D3">#2062D3</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
<color name="color_7B7888">#7B7888</color>
|
||||
<color name="color_8C8D92">#8C8D92</color>
|
||||
<color name="transparent">#00000000</color>
|
||||
<color name="color_222222">#222222</color>
|
||||
<color name="white11">#F4F6F8</color>
|
||||
<color name="color_24272E">#24272E</color>
|
||||
<color name="text_red">#DD493D</color>
|
||||
<color name="color_F5F3F8">#F5F3F8</color>
|
||||
<color name="color_FE6A9E">#FE6A9E</color>
|
||||
<color name="color_99FFFFFF">#99FFFFFF</color>
|
||||
<color name="color_993376EA">#993376EA</color>
|
||||
<color name="color_e1e3e6">#e1e3e6</color>
|
||||
<color name="gray_line_light">#eeeeee</color>
|
||||
<color name="color_AAAAAA">#AAAAAA</color>
|
||||
<color name="color_F8F8F8">#F8F8F8</color>
|
||||
<color name="color_333333">#333333</color>
|
||||
<color name="color_C4C4C4">#C4C4C4</color>
|
||||
<color name="color_999999">#66000000</color>
|
||||
<color name="color_F4F4F4">#F4F4F4</color>
|
||||
<color name="color_E7EAF9">#E7EAF9</color>
|
||||
<color name="color_777777">#777777</color>
|
||||
<color name="color_E8ECFF">#E8ECFF</color>
|
||||
<color name="color_2B2B2B">#2B2B2B</color>
|
||||
<color name="color_3376EA">#3376EA</color>
|
||||
<color name="color_2D3338">#2D3338</color>
|
||||
<color name="color_1D1F25">#1D1F25</color>
|
||||
<color name="color_cccccc">#CCCCCC</color>
|
||||
<color name="color_99000000">#99000000</color>
|
||||
<color name="color_CF8823">#CF8823</color>
|
||||
<color name="color_616CC1">#616CC1</color>
|
||||
<color name="color_666666">#666666</color>
|
||||
|
||||
<color name="blue_color_picker">#0066FF</color>
|
||||
<color name="brown_color_picker">#AF593E</color>
|
||||
<color name="green_color_picker">#01A368</color>
|
||||
<color name="orange_color_picker">#FF861F</color>
|
||||
<color name="red_color_picker">#ED0A3F</color>
|
||||
<color name="red_orange_color_picker">#FF3F34</color>
|
||||
<color name="sky_blue_color_picker">#76D7EA</color>
|
||||
<color name="violet_color_picker">#8359A3</color>
|
||||
<color name="yellow_color_picker">#FBE870</color>
|
||||
<color name="yellow_green_color_picker">#C5E17A</color>
|
||||
<color name="tool_bg">#151414</color>
|
||||
<color name="semi_black_transparent">#99000000</color>
|
||||
|
||||
<!-- 这2个颜色用来动态修改图片选择器的文件夹目录的背景颜色 e0adb 选中颜色 white 默认颜色-->
|
||||
<color name="picture_color_transparent_e0db">#F8F8F8</color>
|
||||
<color name="picture_color_transparent_white">#FFFFFF</color>
|
||||
|
||||
<!-- 这个颜色用来动态修改videoview的背景颜色-->
|
||||
<color name="video_bg_color">#F8F8F8</color>
|
||||
|
||||
<color name="default_bg">#F3F3F3</color>
|
||||
<color name="color_fe8b59">#FE8B59</color>
|
||||
<color name="color_e83126">#E83126</color>
|
||||
<color name="color_e6000000">#959595</color>
|
||||
<color name="color_66ffffff">#66FFFFFF</color>
|
||||
<color name="color_search_2">#EEEEEE</color>
|
||||
<color name="color_search_1">#DDDDDD</color>
|
||||
<color name="color_e12d48">#E12D48</color>
|
||||
|
||||
<color name="color_45000000">#45000000</color>
|
||||
|
||||
</resources>
|
||||
116
app/src/main/res/values/dimens.xml
Normal file
@@ -0,0 +1,116 @@
|
||||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
|
||||
<!-- IntroActivity -->
|
||||
|
||||
<!-- sp -->
|
||||
<dimen name="sp10">10sp</dimen>
|
||||
<dimen name="sp11">11sp</dimen>
|
||||
<dimen name="sp12">12sp</dimen>
|
||||
<dimen name="sp13">13sp</dimen>
|
||||
<dimen name="sp14">14sp</dimen>
|
||||
<dimen name="sp15">15sp</dimen>
|
||||
<dimen name="sp16">16sp</dimen>
|
||||
<dimen name="sp17">17sp</dimen>
|
||||
<dimen name="sp18">18sp</dimen>
|
||||
<dimen name="sp19">19sp</dimen>
|
||||
<dimen name="sp20">20sp</dimen>
|
||||
<dimen name="sp22">22sp</dimen>
|
||||
<dimen name="sp25">25sp</dimen>
|
||||
<dimen name="sp36">36sp</dimen>
|
||||
<dimen name="sp38">38sp</dimen>
|
||||
|
||||
<!-- dp -->
|
||||
<dimen name="dp0">0dp</dimen>
|
||||
<dimen name="dp1">1dp</dimen>
|
||||
<dimen name="dp2">2dp</dimen>
|
||||
<dimen name="dp3">3dp</dimen>
|
||||
<dimen name="dp4">4dp</dimen>
|
||||
<dimen name="dp5">5dp</dimen>
|
||||
<dimen name="dp6">6dp</dimen>
|
||||
<dimen name="dp7">7dp</dimen>
|
||||
<dimen name="dp7_5">7.5dp</dimen>
|
||||
|
||||
<dimen name="dp8">8dp</dimen>
|
||||
<dimen name="dp10">10dp</dimen>
|
||||
<dimen name="dp11">11dp</dimen>
|
||||
|
||||
<dimen name="dp12">12dp</dimen>
|
||||
<dimen name="dp14">14dp</dimen>
|
||||
|
||||
<dimen name="dp15">15dp</dimen>
|
||||
<dimen name="dp16">16dp</dimen>
|
||||
<dimen name="dp18">18dp</dimen>
|
||||
<dimen name="dp20">20dp</dimen>
|
||||
<dimen name="dp22">22dp</dimen>
|
||||
<dimen name="dp24">24dp</dimen>
|
||||
|
||||
<dimen name="dp25">25dp</dimen>
|
||||
<dimen name="dp28">28dp</dimen>
|
||||
<dimen name="dp30">30dp</dimen>
|
||||
<dimen name="dp32">32dp</dimen>
|
||||
<dimen name="dp33">33dp</dimen>
|
||||
|
||||
<dimen name="dp35">35dp</dimen>
|
||||
<dimen name="dp36">36dp</dimen>
|
||||
<dimen name="dp38">38dp</dimen>
|
||||
<dimen name="dp40">40dp</dimen>
|
||||
<dimen name="dp42">42dp</dimen>
|
||||
<dimen name="dp44">44dp</dimen>
|
||||
<dimen name="dp45">45dp</dimen>
|
||||
<dimen name="dp48">48dp</dimen>
|
||||
<dimen name="dp50">50dp</dimen>
|
||||
<dimen name="dp_50">50dp</dimen>
|
||||
|
||||
<dimen name="dp52">52dp</dimen>
|
||||
<dimen name="dp55">55dp</dimen>
|
||||
<dimen name="dp60">60dp</dimen>
|
||||
<dimen name="dp65">65dp</dimen>
|
||||
<dimen name="dp68">68dp</dimen>
|
||||
|
||||
<dimen name="dp70">70dp</dimen>
|
||||
<dimen name="dp75">75dp</dimen>
|
||||
<dimen name="dp80">80dp</dimen>
|
||||
<dimen name="dp95">95dp</dimen>
|
||||
|
||||
<dimen name="dp_95">95dp</dimen>
|
||||
|
||||
<dimen name="dp90">90dp</dimen>
|
||||
<dimen name="dp100">100dp</dimen>
|
||||
<dimen name="dp102">102dp</dimen>
|
||||
|
||||
<dimen name="dp105">105dp</dimen>
|
||||
<dimen name="dp106">106dp</dimen>
|
||||
<dimen name="dp110">110dp</dimen>
|
||||
|
||||
<dimen name="dp120">120dp</dimen>
|
||||
<dimen name="dp130">130dp</dimen>
|
||||
<dimen name="dp_132">132dp</dimen>
|
||||
|
||||
|
||||
<dimen name="dp150">150dp</dimen>
|
||||
<dimen name="dp154">154dp</dimen>
|
||||
|
||||
<dimen name="dp160">160dp</dimen>
|
||||
<dimen name="dp175">175dp</dimen>
|
||||
<dimen name="dp180">180dp</dimen>
|
||||
<dimen name="dp193">193dp</dimen>
|
||||
|
||||
<dimen name="dp200">200dp</dimen>
|
||||
<dimen name="dp210">210dp</dimen>
|
||||
<dimen name="dp250">250dp</dimen>
|
||||
|
||||
|
||||
<dimen name="design_bottom_navigation_active_text_size">10sp</dimen>
|
||||
<dimen name="design_bottom_navigation_text_size">10sp</dimen>
|
||||
|
||||
<dimen name="fab_margin">16dp</dimen>
|
||||
<dimen name="editor_size">30dp</dimen>
|
||||
<dimen name="normal_margin">8dp</dimen>
|
||||
<dimen name="large_margin">16dp</dimen>
|
||||
<dimen name="top_tool_icon_width">50dp</dimen>
|
||||
|
||||
|
||||
</resources>
|
||||
17
app/src/main/res/values/font_certs.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<array name="com_google_android_gms_fonts_certs">
|
||||
<item>@array/com_google_android_gms_fonts_certs_dev</item>
|
||||
<item>@array/com_google_android_gms_fonts_certs_prod</item>
|
||||
</array>
|
||||
<string-array name="com_google_android_gms_fonts_certs_dev">
|
||||
<item>
|
||||
MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
|
||||
</item>
|
||||
</string-array>
|
||||
<string-array name="com_google_android_gms_fonts_certs_prod">
|
||||
<item>
|
||||
MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
|
||||
</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
11
app/src/main/res/values/ids.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item name="leftTime" type="id" />
|
||||
<item name="rightTime" type="id" />
|
||||
<item name="frameList" type="id" />
|
||||
|
||||
<item name="fullscreen_id" type="id" />
|
||||
|
||||
<item name="custom_full_id" type="id" />
|
||||
<item name="custom_small_id" type="id" />
|
||||
</resources>
|
||||
11
app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<resources>
|
||||
<string name="app_name">贷款</string>
|
||||
|
||||
<string name="file_permission_font_txt">文件权限已禁用</string>
|
||||
<string name="locat_permission_font_txt">定位权限已禁用</string>
|
||||
<string name="contacts_permission_font_txt">通讯录权限已禁用</string>
|
||||
|
||||
<string name="file_permission_font_all_txt">文件权限已禁用,请去设置中开启</string>
|
||||
<string name="locat_permission_font_all_txt">定位权限已禁用,请去设置中开启</string>
|
||||
<string name="contacts_permission_font_all_txt">通讯录权限已禁用,请去设置中开启</string>
|
||||
</resources>
|
||||
823
app/src/main/res/values/themes.xml
Normal file
@@ -0,0 +1,823 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Bkvideo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
<style name="AppThemePlayer" parent="Theme.AppCompat.DayNight.DarkActionBar">
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
<item name="android:windowTranslucentNavigation">true</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryDark">@color/purple_500</item>
|
||||
<item name="colorAccent">@color/purple_500</item>
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="android:windowBackground">@color/white</item>
|
||||
<!-- 隐藏Activity窗口的Title标题栏 -->
|
||||
<item name="windowNoTitle">true</item>
|
||||
|
||||
<!-- <item name="android:forceDarkAllowed" tools:ignore="NewApi">true</item>-->
|
||||
|
||||
</style>
|
||||
|
||||
<style name="AppThemeStart" parent="Theme.AppCompat.DayNight.DarkActionBar">
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
<item name="android:windowTranslucentNavigation">true</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryDark">@color/purple_500</item>
|
||||
<item name="colorAccent">@color/purple_500</item>
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="android:windowBackground">@color/white</item>
|
||||
<!-- 隐藏Activity窗口的Title标题栏 -->
|
||||
<item name="windowNoTitle">true</item>
|
||||
|
||||
|
||||
<!-- <item name="android:forceDarkAllowed" tools:ignore="NewApi">true</item>-->
|
||||
|
||||
</style>
|
||||
|
||||
<style name="AppThemeLogin1" parent="Theme.AppCompat.DayNight.DarkActionBar">
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
<item name="android:windowTranslucentNavigation">true</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryDark">@color/purple_500</item>
|
||||
<item name="colorAccent">@color/purple_500</item>
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="android:windowBackground">@color/white</item>
|
||||
<!-- 隐藏Activity窗口的Title标题栏 -->
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowActionBar">false</item>
|
||||
|
||||
<item name="android:windowLayoutInDisplayCutoutMode" tools:ignore="NewApi">shortEdges</item><!--控制刘海处显示信息-->
|
||||
</style>
|
||||
|
||||
|
||||
<style name="AppThemeLogin" parent="Theme.AppCompat.DayNight.DarkActionBar">
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
<item name="android:windowTranslucentNavigation">true</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="colorPrimary">@color/black</item>
|
||||
<item name="colorPrimaryDark">@color/black</item>
|
||||
<item name="colorAccent">@color/black</item>
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowDisablePreview">true</item>
|
||||
<!-- 隐藏Activity窗口的Title标题栏 -->
|
||||
<item name="windowNoTitle">true</item>
|
||||
|
||||
<item name="background">@color/white</item>
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<style name="MaterialDesignDialog" parent="@style/Theme.AppCompat.Dialog">
|
||||
<!-- 背景透明 -->
|
||||
<item name="android:windowBackground">@drawable/dlg_input_video_bg</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<!-- 浮于Activity之上 -->
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<!-- 边框 -->
|
||||
<item name="android:windowFrame">@null</item>
|
||||
<!-- Dialog以外的区域模糊效果 -->
|
||||
<item name="android:backgroundDimEnabled">true</item>
|
||||
<!-- 无标题 -->
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<!-- 半透明 -->
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowCloseOnTouchOutside">true</item>
|
||||
</style>
|
||||
|
||||
<style name="MaterialDesignDialog1" parent="@style/Theme.AppCompat.Dialog">
|
||||
<!-- 背景透明 -->
|
||||
<item name="android:windowBackground">@color/transparent</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<!-- 浮于Activity之上 -->
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<!-- 边框 -->
|
||||
<item name="android:windowFrame">@null</item>
|
||||
<!-- Dialog以外的区域模糊效果 -->
|
||||
<item name="android:backgroundDimEnabled">true</item>
|
||||
<!-- 无标题 -->
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<!-- 半透明 -->
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowCloseOnTouchOutside">true</item>
|
||||
</style>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="dialog_style" parent="@android:style/Theme.Dialog">
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
<!-- <style name="selectVideoTheme" parent="Base.Theme.NoActionBar">-->
|
||||
<!-- <!– Customize your theme here. –>-->
|
||||
<!-- <!–标题栏背景色–>-->
|
||||
<!-- <item name="colorPrimary">@color/white</item>-->
|
||||
<!-- <!–状态栏背景色–>-->
|
||||
<!-- <item name="colorPrimaryDark">@color/white</item>-->
|
||||
<!-- <!–是否改变图片列表界面状态栏字体颜色为黑色–>-->
|
||||
<!-- <item name="picture.statusFontColor">true</item>-->
|
||||
<!-- <!–返回键图标–>-->
|
||||
<!-- <item name="picture.leftBack.icon">@mipmap/ic_black_back</item>-->
|
||||
<!-- <!–标题下拉箭头–>-->
|
||||
<!-- <item name="picture.arrow_down.icon">@drawable/picture_icon_wechat_down</item>-->
|
||||
<!-- <!–标题上拉箭头–>-->
|
||||
<!-- <item name="picture.arrow_up.icon">@drawable/picture_icon_wechat_up</item>-->
|
||||
<!-- <!–标题文字颜色–>-->
|
||||
<!-- <item name="picture.title.textColor">@color/black</item>-->
|
||||
<!-- <!–标题栏右边文字–>-->
|
||||
<!-- <item name="picture.right.textColor">@color/black</item>-->
|
||||
<!-- <!–图片列表勾选样式–>-->
|
||||
<!-- <item name="picture.checked.style">@drawable/picture_wechat_num_selector</item>-->
|
||||
<!-- <!–开启图片列表勾选数字模式,开启的话勾选样式要换–>-->
|
||||
<!-- <item name="picture.style.checkNumMode">false</item>-->
|
||||
<!-- <!–选择图片样式0/9–>-->
|
||||
<!-- <item name="picture.style.numComplete">false</item>-->
|
||||
<!-- <!–图片列表底部背景色–>-->
|
||||
<!-- <item name="picture.bottom.bg">@color/white</item>-->
|
||||
<!-- <!–图片列表预览文字颜色–>-->
|
||||
<!-- <item name="picture.preview.textColor">@color/black</item>-->
|
||||
<!-- <!–图片列表已完成文字颜色–>-->
|
||||
<!-- <item name="picture.complete.textColor">@color/black</item>-->
|
||||
<!-- <!–图片已选数量圆点背景色–>-->
|
||||
<!-- <item name="picture.num.style">@drawable/picture_num_oval</item>-->
|
||||
<!-- <!–预览界面标题文字颜色–>-->
|
||||
<!-- <item name="picture.ac_preview.title.textColor">@color/black</item>-->
|
||||
<!-- <!–预览界面已完成文字颜色–>-->
|
||||
<!-- <item name="picture.ac_preview.complete.textColor">@color/black</item>-->
|
||||
<!-- <!–预览界面标题栏背景色–>-->
|
||||
<!-- <item name="picture.ac_preview.title.bg">@color/white</item>-->
|
||||
<!-- <!–预览界面底部背景色–>-->
|
||||
<!-- <item name="picture.ac_preview.bottom.bg">@color/white</item>-->
|
||||
<!-- <!–预览界面返回箭头–>-->
|
||||
<!-- <item name="picture.preview.leftBack.icon">@mipmap/ic_black_back</item>-->
|
||||
<!-- <!–裁剪页面标题背景色–>-->
|
||||
<!-- <item name="picture.crop.toolbar.bg">@color/white</item>-->
|
||||
<!-- <!–裁剪页面状态栏颜色–>-->
|
||||
<!-- <item name="picture.crop.status.color">@color/white</item>-->
|
||||
<!-- <!–裁剪页面标题文字颜色–>-->
|
||||
<!-- <item name="picture.crop.title.color">@color/black</item>-->
|
||||
<!-- <!–相册文件夹列表选中图标–>-->
|
||||
<!-- <item name="picture.folder_checked_dot">@drawable/picture_orange_oval</item>-->
|
||||
<!-- <!–原图勾选样式–>-->
|
||||
<!-- <item name="picture.original.check.style">@drawable/picture_original_wechat_checkbox</item>-->
|
||||
<!-- <!–原图字体颜色–>-->
|
||||
<!-- <item name="picture.original.text.color">@color/picture_color_white</item>-->
|
||||
<!-- <!–相册目录文件夹字体颜色–>-->
|
||||
<!-- <item name="picture.folder.textColor">@color/picture_color_4d</item>-->
|
||||
<!-- <!–相册目录文件夹字体大小–>-->
|
||||
<!-- <item name="picture.folder.textSize">16sp</item>-->
|
||||
<!-- <!–相册背景底色–>-->
|
||||
<!-- <item name="picture.container.backgroundColor">@color/white</item>-->
|
||||
<!-- <!–标题栏高度–>-->
|
||||
<!-- <item name="picture.titleBar.height">?actionBarSize</item>-->
|
||||
<!-- <!–标题栏右侧按钮方向箭头left Padding–>-->
|
||||
<!-- <item name="picture.titleRightArrow.LeftPadding">3dp</item>-->
|
||||
<!-- </style>-->
|
||||
|
||||
|
||||
<style name="ImportVideoDialog" parent="@style/Theme.AppCompat.Dialog">
|
||||
<item name="android:windowBackground">@drawable/dlg_input_video_bg</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<item name="android:windowFrame">@null</item>
|
||||
<item name="android:backgroundDimEnabled">true</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowCloseOnTouchOutside">false</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="DialogTransparentTheme" parent="android:Theme.Holo.Dialog.NoActionBar">
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||
<item name="android:backgroundDimEnabled">false</item>
|
||||
|
||||
</style>
|
||||
|
||||
<style name="dialog" parent="Theme.AppCompat.Dialog">
|
||||
<item name="android:windowFrame">@null</item>
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:backgroundDimEnabled">true</item><!--activity是否变暗-->
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowIsTranslucent">false</item>
|
||||
<item name="android:windowDisablePreview">true</item>
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<declare-styleable name="VideoThumbnailView">
|
||||
<attr name="frameCount" format="integer" />
|
||||
<attr name="frameHeight" format="dimension|reference" />
|
||||
<attr name="coverColor" format="color|reference" />
|
||||
<attr name="pointerColor" format="color|reference" />
|
||||
<attr name="timeColor" format="color|reference" />
|
||||
<attr name="indicatorColor" format="color|reference" />
|
||||
<attr name="showTime" format="boolean" />
|
||||
<attr name="reset" format="boolean" />
|
||||
<attr name="overScrollDistance" format="dimension|reference" />
|
||||
<attr name="pointerStyle">
|
||||
<enum name="arrow" value="0" />
|
||||
<enum name="simple" value="1" />
|
||||
<enum name="simple2" value="2" />
|
||||
<enum name="circle" value="3" />
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="NiceImageView">
|
||||
<attr name="is_circle" format="boolean" />
|
||||
<attr name="is_cover_src" format="boolean" />
|
||||
<attr name="corner_radius" format="dimension" />n
|
||||
<attr name="corner_top_left_radius" format="dimension" />
|
||||
<attr name="corner_top_right_radius" format="dimension" />
|
||||
<attr name="corner_bottom_left_radius" format="dimension" />
|
||||
<attr name="corner_bottom_right_radius" format="dimension" />
|
||||
<attr name="border_width" format="dimension" />
|
||||
<attr name="border_color" format="color" />
|
||||
<attr name="inner_border_width" format="dimension" />
|
||||
<attr name="inner_border_color" format="color" />
|
||||
<attr name="mask_color" format="color" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="IMGColorRadio">
|
||||
|
||||
<attr name="image_color" format="color" />
|
||||
<attr name="image_stroke_color" format="color" />
|
||||
|
||||
</declare-styleable>
|
||||
|
||||
<!--涟漪控件属性-->
|
||||
<declare-styleable name="mRippleView">
|
||||
<attr name="cColor" format="color"/>
|
||||
<attr name="cSpeed" format="integer"/>
|
||||
<attr name="cDensity" format="integer"/>
|
||||
<attr name="cIsFill" format="boolean"/>
|
||||
<attr name="cIsAlpha" format="boolean"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="custume_ripple_imageview">
|
||||
<attr name="show_spacing_time" format="integer"></attr>
|
||||
<attr name="imageViewWidth" format="dimension"></attr>
|
||||
<attr name="imageViewHeigth" format="dimension"></attr>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="CircleImageView">
|
||||
<attr name="ease_border_color" format="color" />
|
||||
<attr name="ease_border_width" format="dimension" />
|
||||
<attr name="ease_press_alpha" format="integer" />
|
||||
<attr name="ease_press_color" format="color" />
|
||||
<attr name="ease_radius" format="dimension" />
|
||||
<attr name="es_shape_type" format="enum">
|
||||
<enum name="none" value="0" />
|
||||
<enum name="round" value="1" />
|
||||
<enum name="rectangle" value="2" />
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="IndicatorView">
|
||||
|
||||
<attr name="vpi_slider_checked_color" format="color" />
|
||||
<attr name="vpi_slider_normal_color" format="color" />
|
||||
<attr name="vpi_slider_radius" format="dimension" />
|
||||
<attr name="vpi_rtl" format="boolean" />
|
||||
|
||||
<attr name="vpi_orientation" format="enum">
|
||||
<enum name="horizontal" value="0" />
|
||||
<enum name="vertical" value="1" />
|
||||
<enum name="rtl" value="3"/>
|
||||
</attr>
|
||||
|
||||
<attr name="vpi_slide_mode" format="enum">
|
||||
<enum name="normal" value="0" />
|
||||
<enum name="smooth" value="2" />
|
||||
<enum name="worm" value="3" />
|
||||
<enum name="scale" value="4" />
|
||||
<enum name="color" value="5" />
|
||||
</attr>
|
||||
|
||||
<attr name="vpi_style" format="enum">
|
||||
<enum name="circle" value="0" />
|
||||
<enum name="dash" value="2" />
|
||||
<enum name="round_rect" value="4" />
|
||||
</attr>
|
||||
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ShimmerFrameLayout">
|
||||
<attr name="shimmer_clip_to_children" format="boolean"/>
|
||||
<attr name="shimmer_colored" format="boolean"/>
|
||||
<attr name="shimmer_base_color" format="color"/>
|
||||
<attr name="shimmer_highlight_color" format="color"/>
|
||||
<attr name="shimmer_base_alpha" format="float"/>
|
||||
<attr name="shimmer_highlight_alpha" format="float"/>
|
||||
<attr name="shimmer_auto_start" format="boolean"/>
|
||||
<attr name="shimmer_duration" format="integer"/>
|
||||
<attr name="shimmer_repeat_count" format="integer"/>
|
||||
<attr name="shimmer_repeat_delay" format="integer"/>
|
||||
<attr name="shimmer_repeat_mode" format="enum">
|
||||
<enum name="restart" value="1"/>
|
||||
<enum name="reverse" value="2"/>
|
||||
</attr>
|
||||
<attr name="shimmer_start_delay" format="integer"/>
|
||||
<attr name="shimmer_direction" format="enum">
|
||||
<enum name="left_to_right" value="0"/>
|
||||
<enum name="top_to_bottom" value="1"/>
|
||||
<enum name="right_to_left" value="2"/>
|
||||
<enum name="bottom_to_top" value="3"/>
|
||||
</attr>
|
||||
<attr name="shimmer_dropoff" format="float"/>
|
||||
<attr name="shimmer_fixed_width" format="dimension"/>
|
||||
<attr name="shimmer_fixed_height" format="dimension"/>
|
||||
<attr name="shimmer_intensity" format="float"/>
|
||||
<attr name="shimmer_width_ratio" format="float"/>
|
||||
<attr name="shimmer_height_ratio" format="float"/>
|
||||
<attr name="shimmer_shape" format="enum">
|
||||
<enum name="linear" value="0"/>
|
||||
<enum name="radial" value="1"/>
|
||||
</attr>
|
||||
<attr name="shimmer_tilt" format="float"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="PageGridView">
|
||||
<!--每页大小-->
|
||||
<attr name="pageSize" format="integer|reference" />
|
||||
<!--是否显示指示点-->
|
||||
<attr name="isShowIndicator" format="boolean" />
|
||||
<!--选中指示点-->
|
||||
<attr name="selectedIndicator" format="reference" />
|
||||
<!--未选中指示点-->
|
||||
<attr name="unSelectedIndicator" format="reference" />
|
||||
<!--指示器位置-->
|
||||
<attr name="indicatorGravity" format="enum">
|
||||
<enum name="left" value="0" />
|
||||
<enum name="center" value="1" />
|
||||
<enum name="right" value="2" />
|
||||
</attr>
|
||||
<!--指示器左内边距-->
|
||||
<attr name="indicatorPaddingLeft" format="dimension" />
|
||||
<!--指示器右内边距-->
|
||||
<attr name="indicatorPaddingRight" format="dimension" />
|
||||
<!--指示器上内边距-->
|
||||
<attr name="indicatorPaddingTop" format="dimension" />
|
||||
<!--指示器下内边距-->
|
||||
<attr name="indicatorPaddingBottom" format="dimension" />
|
||||
<!--指示器内边距-->
|
||||
<attr name="indicatorPadding" format="dimension" />
|
||||
<!--指示器背景颜色-->
|
||||
<attr name="indicatorBackground" format="color" />
|
||||
|
||||
<!--ViewPager背景资源-->
|
||||
<attr name="vpBackground" format="reference|color" />
|
||||
|
||||
<!--ViewPager 内边距-->
|
||||
<attr name="vpPadding" format="dimension" />
|
||||
<!--列数-->
|
||||
<attr name="numColumns" format="integer|reference" />
|
||||
<!--Item布局-->
|
||||
<attr name="itemView" format="reference" />
|
||||
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="TrinityTabLayout">
|
||||
<attr name="tabIndicatorColor" format="color"/>
|
||||
<attr name="tabIndicatorHeight" format="dimension"/>
|
||||
<attr name="tabContentStart" format="dimension"/>
|
||||
<attr name="tabBackground" format="reference"/>
|
||||
<attr name="tabIndicator" format="reference"/>
|
||||
<attr name="trinityTabIndicatorGravity">
|
||||
<enum name="bottom" value="0"/>
|
||||
<enum name="center" value="1"/>
|
||||
<enum name="top" value="2"/>
|
||||
<enum name="stretch" value="3"/>
|
||||
</attr>
|
||||
<attr name="tabIndicatorAnimationDuration" format="integer"/>
|
||||
<attr name="tabIndicatorFullWidth" format="boolean"/>
|
||||
<attr name="trinityTabMode">
|
||||
<enum name="scrollable" value="0"/>
|
||||
<enum name="fixed" value="1"/>
|
||||
<enum name="auto" value="2"/>
|
||||
</attr>
|
||||
<attr name="trinityTabGravity">
|
||||
<enum name="fill" value="0"/>
|
||||
<enum name="center" value="1"/>
|
||||
</attr>
|
||||
<attr name="tabInlineLabel" format="boolean"/>
|
||||
<attr name="tabMinWidth" format="dimension"/>
|
||||
<attr name="tabMaxWidth" format="dimension"/>
|
||||
<attr name="tabTextAppearance" format="reference"/>
|
||||
<attr name="tabTextColor" format="color"/>
|
||||
<attr name="tabSelectedTextColor" format="color"/>
|
||||
<attr name="tabPaddingStart" format="dimension"/>
|
||||
<attr name="tabPaddingTop" format="dimension"/>
|
||||
<attr name="tabPaddingEnd" format="dimension"/>
|
||||
<attr name="tabPaddingBottom" format="dimension"/>
|
||||
<attr name="tabPadding" format="dimension"/>
|
||||
<attr name="tabIconTint" format="color"/>
|
||||
<attr name="trinityTabIconTintMode">
|
||||
<enum name="src_over" value="3"/>
|
||||
<enum name="src_in" value="5"/>
|
||||
<enum name="src_atop" value="9"/>
|
||||
<enum name="multiply" value="14"/>
|
||||
<enum name="screen" value="15"/>
|
||||
<enum name="add" value="16"/>
|
||||
</attr>
|
||||
<attr name="tabRippleColor" format="color"/>
|
||||
<attr name="tabUnboundedRipple" format="boolean"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="TrinityTabItem">
|
||||
<attr name="android:text"/>
|
||||
<attr name="android:icon"/>
|
||||
<attr name="android:layout"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="DYLoadingView">
|
||||
<attr name="radius1" format="dimension" />
|
||||
<attr name="radius2" format="dimension" />
|
||||
<attr name="rtlScale" format="float" />
|
||||
<attr name="ltrScale" format="float" />
|
||||
<attr name="color1" format="color" />
|
||||
<attr name="color2" format="color" />
|
||||
<attr name="mixColor" format="color" />
|
||||
<attr name="duration" format="integer" />
|
||||
<attr name="pauseDuration" format="integer" />
|
||||
<attr name="gap" format="dimension" />
|
||||
<attr name="scaleStartFraction" format="float" />
|
||||
<attr name="scaleEndFraction" format="float" />
|
||||
</declare-styleable>
|
||||
|
||||
|
||||
<!-- indicator -->
|
||||
<!-- 设置显示器颜色 -->
|
||||
<attr name="tl_indicator_color" format="color"/>
|
||||
<!-- 设置显示器高度 -->
|
||||
<attr name="tl_indicator_height" format="dimension"/>
|
||||
<!-- 设置显示器固定宽度 -->
|
||||
<attr name="tl_indicator_width" format="dimension"/>
|
||||
<!-- 设置显示器margin,当indicator_width大于0,无效 -->
|
||||
<attr name="tl_indicator_margin_left" format="dimension"/>
|
||||
<attr name="tl_indicator_margin_top" format="dimension"/>
|
||||
<attr name="tl_indicator_margin_right" format="dimension"/>
|
||||
<attr name="tl_indicator_margin_bottom" format="dimension"/>
|
||||
<!-- 设置显示器圆角弧度-->
|
||||
<attr name="tl_indicator_corner_radius" format="dimension"/>
|
||||
<!-- 设置显示器上方还是下方,只对圆角矩形有用-->
|
||||
<attr name="tl_indicator_gravity" format="enum">
|
||||
<enum name="TOP" value="48"/>
|
||||
<enum name="BOTTOM" value="80"/>
|
||||
</attr>
|
||||
<!-- 设置显示器为常规|三角形|背景色块|-->
|
||||
<attr name="tl_indicator_style" format="enum">
|
||||
<enum name="NORMAL" value="0"/>
|
||||
<enum name="TRIANGLE" value="1"/>
|
||||
<enum name="BLOCK" value="2"/>
|
||||
</attr>
|
||||
<!-- 设置显示器长度与title一样长,只有在STYLE_NORMAL并且indicatorWidth小于零有效-->
|
||||
<attr name="tl_indicator_width_equal_title" format="boolean"/>
|
||||
<!-- 设置显示器支持动画-->
|
||||
<attr name="tl_indicator_anim_enable" format="boolean"/>
|
||||
<!-- 设置显示器动画时间-->
|
||||
<attr name="tl_indicator_anim_duration" format="integer"/>
|
||||
<!-- 设置显示器支持动画回弹效果-->
|
||||
<attr name="tl_indicator_bounce_enable" format="boolean"/>
|
||||
|
||||
<!-- underline -->
|
||||
<!-- 设置下划线颜色 -->
|
||||
<attr name="tl_underline_color" format="color"/>
|
||||
<!-- 设置下划线高度 -->
|
||||
<attr name="tl_underline_height" format="dimension"/>
|
||||
<!-- 设置下划线上方还是下方-->
|
||||
<attr name="tl_underline_gravity" format="enum">
|
||||
<enum name="TOP" value="48"/>
|
||||
<enum name="BOTTOM" value="80"/>
|
||||
</attr>
|
||||
|
||||
<!-- divider -->
|
||||
<!-- 设置分割线颜色 -->
|
||||
<attr name="tl_divider_color" format="color"/>
|
||||
<!-- 设置分割线宽度 -->
|
||||
<attr name="tl_divider_width" format="dimension"/>
|
||||
<!-- 设置分割线的paddingTop和paddingBottom -->
|
||||
<attr name="tl_divider_padding" format="dimension"/>
|
||||
|
||||
<!-- tab -->
|
||||
<!-- 设置tab的paddingLeft和paddingRight -->
|
||||
<attr name="tl_tab_padding" format="dimension"/>
|
||||
<!-- 设置tab大小等分 -->
|
||||
<attr name="tl_tab_space_equal" format="boolean"/>
|
||||
<!-- 设置tab固定大小 -->
|
||||
<attr name="tl_tab_width" format="dimension"/>
|
||||
|
||||
<!-- title -->
|
||||
<!-- 设置字体大小 -->
|
||||
<attr name="tl_textsize" format="dimension"/>
|
||||
<!-- 设置字体选中颜色 -->
|
||||
<attr name="tl_textSelectColor" format="color"/>
|
||||
<!-- 设置字体未选中颜色 -->
|
||||
<attr name="tl_textUnselectColor" format="color"/>
|
||||
<!-- 设置字体加粗 -->
|
||||
<attr name="tl_textBold" format="boolean"/>
|
||||
<!-- 设置字体全大写 -->
|
||||
<attr name="tl_textAllCaps" format="boolean"/>
|
||||
|
||||
<!-- 设置字体大小 -->
|
||||
<attr name="tl_subtextsize" format="dimension" />
|
||||
<!-- 设置字体选中颜色 -->
|
||||
<attr name="tl_subtextSelectColor" format="color" />
|
||||
<!-- 设置字体未选中颜色 -->
|
||||
<attr name="tl_subtextUnselectColor" format="color" />
|
||||
<!-- 设置字体加粗 -->
|
||||
<attr name="tl_subtextBold" format="boolean" />
|
||||
<!-- 设置字体全大写 -->
|
||||
<attr name="tl_subtextAllCaps" format="boolean" />
|
||||
|
||||
<declare-styleable name="CommonTabLayout">
|
||||
<!-- indicator -->
|
||||
<attr name="tl_indicator_color"/>
|
||||
<attr name="tl_indicator_height"/>
|
||||
<attr name="tl_indicator_width"/>
|
||||
<attr name="tl_indicator_margin_left"/>
|
||||
<attr name="tl_indicator_margin_top"/>
|
||||
<attr name="tl_indicator_margin_right"/>
|
||||
<attr name="tl_indicator_margin_bottom"/>
|
||||
<attr name="tl_indicator_corner_radius"/>
|
||||
<attr name="tl_indicator_gravity"/>
|
||||
<attr name="tl_indicator_style"/>
|
||||
<attr name="tl_indicator_anim_enable"/>
|
||||
<attr name="tl_indicator_anim_duration"/>
|
||||
<attr name="tl_indicator_bounce_enable"/>
|
||||
|
||||
<!-- underline -->
|
||||
<attr name="tl_underline_color"/>
|
||||
<attr name="tl_underline_height"/>
|
||||
<attr name="tl_underline_gravity"/>
|
||||
|
||||
<!-- divider -->
|
||||
<attr name="tl_divider_color"/>
|
||||
<attr name="tl_divider_width"/>
|
||||
<attr name="tl_divider_padding"/>
|
||||
|
||||
<!-- tab -->
|
||||
<attr name="tl_tab_padding"/>
|
||||
<attr name="tl_tab_space_equal"/>
|
||||
<attr name="tl_tab_width"/>
|
||||
|
||||
<!-- title -->
|
||||
<attr name="tl_textsize"/>
|
||||
<attr name="tl_textSelectColor"/>
|
||||
<attr name="tl_textUnselectColor"/>
|
||||
<attr name="tl_textBold"/>
|
||||
<attr name="tl_textAllCaps"/>
|
||||
|
||||
<!-- subtitle -->
|
||||
<attr name="tl_subtextsize" />
|
||||
<attr name="tl_subtextSelectColor" />
|
||||
<attr name="tl_subtextUnselectColor" />
|
||||
<attr name="tl_subtextBold" />
|
||||
<attr name="tl_subtextAllCaps" />
|
||||
|
||||
<!-- icon -->
|
||||
<!-- 设置icon宽度 -->
|
||||
<attr name="tl_iconWidth" format="dimension"/>
|
||||
<!-- 设置icon高度 -->
|
||||
<attr name="tl_iconHeight" format="dimension"/>
|
||||
<!-- 设置icon是否可见 -->
|
||||
<attr name="tl_iconVisible" format="boolean"/>
|
||||
<!-- 设置icon显示位置,对应Gravity中常量值 -->
|
||||
<attr name="tl_iconGravity" format="enum">
|
||||
<enum name="LEFT" value="3"/>
|
||||
<enum name="TOP" value="48"/>
|
||||
<enum name="RIGHT" value="5"/>
|
||||
<enum name="BOTTOM" value="80"/>
|
||||
</attr>
|
||||
<!-- 设置icon与文字间距 -->
|
||||
<attr name="tl_iconMargin" format="dimension"/>
|
||||
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="MsgView">
|
||||
<!-- 圆角矩形背景色 -->
|
||||
<attr name="mv_backgroundColor" format="color"/>
|
||||
<!-- 圆角弧度,单位dp-->
|
||||
<attr name="mv_cornerRadius" format="dimension"/>
|
||||
<!-- 圆角弧度,单位dp-->
|
||||
<attr name="mv_strokeWidth" format="dimension"/>
|
||||
<!-- 圆角边框颜色-->
|
||||
<attr name="mv_strokeColor" format="color"/>
|
||||
<!-- 圆角弧度是高度一半-->
|
||||
<attr name="mv_isRadiusHalfHeight" format="boolean"/>
|
||||
<!-- 圆角矩形宽高相等,取较宽高中大值-->
|
||||
<attr name="mv_isWidthHeightEqual" format="boolean"/>
|
||||
</declare-styleable>
|
||||
|
||||
<!-- 自定义验证码输入框-->
|
||||
<declare-styleable name="vericationCodeView">
|
||||
<!--输入框的数量-->
|
||||
<attr name="vcv_et_number" format="integer" />
|
||||
<!--输入类型-->
|
||||
<attr name="vcv_et_inputType">
|
||||
<enum name="number" value="0" />
|
||||
<enum name="numberPassword" value="1" />
|
||||
<enum name="text" value="2" />
|
||||
<enum name="textPassword" value="3" />
|
||||
</attr>
|
||||
<!--输入框的宽度-->
|
||||
<attr name="vcv_et_width" format="dimension|reference" />
|
||||
<!--输入框文字颜色-->
|
||||
<attr name="vcv_et_text_color" format="color|reference" />
|
||||
<!--输入框文字大小-->
|
||||
<attr name="vcv_et_text_size" format="dimension|reference" />
|
||||
<!--输入框背景-->
|
||||
<attr name="vcv_et_bg" format="reference" />
|
||||
<!--光标样式-->
|
||||
<attr name="vcv_et_cursor" format="reference" />
|
||||
<!--是否隐藏光标-->
|
||||
<attr name="vcv_et_cursor_visible" format="boolean" />
|
||||
<!--输入框间距,不输入则代表平分-->
|
||||
<attr name="vcv_et_spacing" format="dimension|reference" />
|
||||
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="AutoLinkTextView">
|
||||
<!-- 链接高亮颜色 -->
|
||||
<attr name="highlight_textcolor" format="color" />
|
||||
<!-- 默认文字颜色 -->
|
||||
<attr name="default_textcolor" format="color" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="TiktokLoadingView">
|
||||
<!--进度条初始颜色-->
|
||||
<attr name="progressColor" format="string"></attr>
|
||||
<!--进度条最小宽度即初始宽度-->
|
||||
<attr name="minProgressWidth" format="dimension"></attr>
|
||||
<!--view最小宽度-->
|
||||
<attr name="minWidth" format="dimension"></attr>
|
||||
<!--view最小高度-->
|
||||
<attr name="minHeight" format="dimension"></attr>
|
||||
</declare-styleable>
|
||||
|
||||
|
||||
<declare-styleable name="wheelSurfView">
|
||||
<!--类型 根据类型来加载资源 1 默认类型 用户需要提供文字和图片 2 暴力模式 用户只需要提供一张圆形的用来旋转的图片-->
|
||||
<attr name="type" format="integer"/>
|
||||
<!--最低圈数 默认值3 也就是说每次旋转都会最少转3圈-->
|
||||
<attr name="minTimes" format="integer"/>
|
||||
<!--扇形的数量 也就是奖品的份数-->
|
||||
<attr name="typenum" format="integer"/>
|
||||
<!--每一个扇形旋转消耗的时间-->
|
||||
<attr name="vartime" format="integer"/>
|
||||
<!--类型为1的时候 需要提供每个扇形上面的文字描述 长度必须和typenum一致-->
|
||||
<attr name="deses" format="reference"/>
|
||||
<!--类型为1的时候 需要提供每个扇形上面的图片展示 长度必须和typenum一致-->
|
||||
<attr name="icons" format="reference"/>
|
||||
<!--类型为1的时候 需要提供每个扇形背景颜色 长度必须和typenum一致-->
|
||||
<attr name="colors" format="reference"/>
|
||||
<!--中间图片的引用 有默认值 不填则使用默认值-->
|
||||
<attr name="goImg" format="reference"/>
|
||||
<!--圆环的图片引用 有默认值 不填则使用默认值-->
|
||||
<attr name="huanImg" format="reference"/>
|
||||
<!--如果类型为2 此值必填-->
|
||||
<attr name="mainImg" format="reference"/>
|
||||
<!--文字大小-->
|
||||
<attr name="textSize" format="dimension"/>
|
||||
<!--文字颜色-->
|
||||
<attr name="textColor" format="reference|color"/>
|
||||
</declare-styleable>
|
||||
|
||||
|
||||
<style name="MyBgTablayoutstyle" parent="Base.Widget.Design.TabLayout">
|
||||
|
||||
<item name="android:textSize">@dimen/sp16</item>
|
||||
</style>
|
||||
|
||||
<style name="TabLayoutTextSize">
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:textSize">@dimen/sp16</item>
|
||||
</style>
|
||||
|
||||
<style name="TabLayoutTextSize_two">
|
||||
<item name="android:textSize">@dimen/sp16</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
</style>
|
||||
|
||||
<style name="TabLayoutTextSize1">
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:textSize">@dimen/sp18</item>
|
||||
</style>
|
||||
|
||||
<style name="TabLayoutTextSize_two1">
|
||||
<item name="android:textSize">@dimen/sp16</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
</style>
|
||||
|
||||
<declare-styleable name="CircularProgressView">
|
||||
<attr name="backWidth" format="dimension" /> <!--背景圆环宽度-->
|
||||
<attr name="progWidth" format="dimension" /> <!--进度圆环宽度-->
|
||||
<attr name="backColor" format="color" /> <!--背景圆环颜色-->
|
||||
<attr name="progColor" format="color" /> <!--进度圆环颜色-->
|
||||
<attr name="progStartColor" format="color" /> <!--进度圆环开始颜色-->
|
||||
<attr name="progFirstColor" format="color" /> <!--进度圆环结束颜色-->
|
||||
<attr name="progress" format="integer" /> <!--圆环进度-->
|
||||
</declare-styleable>
|
||||
|
||||
|
||||
<attr name="popupTextAlignment" format="enum">
|
||||
<enum name="start" value="0" />
|
||||
<enum name="end" value="1" />
|
||||
<enum name="center" value="2" />
|
||||
</attr>
|
||||
|
||||
<declare-styleable name="NiceSpinner">
|
||||
<attr name="arrowTint" format="color" />
|
||||
<attr name="hideArrow" format="boolean" />
|
||||
<attr name="arrowDrawable" format="reference|color" />
|
||||
<attr name="dropDownListPaddingBottom" format="dimension" />
|
||||
<attr name="backgroundSelector" format="integer" />
|
||||
<attr name="textTint" format="color" />
|
||||
<attr name="popupTextAlignment" />
|
||||
<attr name="entries" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
|
||||
<declare-styleable name="CircleProgressbar">
|
||||
<attr name="radius" format="dimension" />
|
||||
<attr name="strokeWidth" format="dimension" />
|
||||
<attr name="ringColor" format="color" />
|
||||
<attr name="ringColor1" format="color" />
|
||||
|
||||
<attr name="textColorNew" format="color" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="BannerViewPager">
|
||||
<!--页面切换时间间隔-->
|
||||
<attr format="integer" name="bvp_interval" />
|
||||
<attr format="integer" name="bvp_scroll_duration" />
|
||||
<attr format="boolean" name="bvp_can_loop" />
|
||||
<attr format="boolean" name="bvp_auto_play" />
|
||||
<attr format="color" name="bvp_indicator_checked_color" />
|
||||
<attr format="color" name="bvp_indicator_normal_color" />
|
||||
<attr format="dimension" name="bvp_indicator_radius" />
|
||||
<attr format="dimension" name="bvp_round_corner" />
|
||||
<attr format="dimension" name="bvp_page_margin" />
|
||||
<attr format="dimension" name="bvp_reveal_width" />
|
||||
<attr format="enum" name="bvp_indicator_style">
|
||||
<enum name="circle" value="0" />
|
||||
<enum name="dash" value="2" />
|
||||
<enum name="round_rect" value="4" />
|
||||
</attr>
|
||||
<attr format="enum" name="bvp_indicator_slide_mode">
|
||||
<enum name="normal" value="0" />
|
||||
<enum name="smooth" value="2" />
|
||||
<enum name="worm" value="3" />
|
||||
<enum name="scale" value="4" />
|
||||
<enum name="color" value="5" />
|
||||
</attr>
|
||||
<attr format="enum" name="bvp_indicator_gravity">
|
||||
<enum name="center" value="0" />
|
||||
<enum name="start" value="2" />
|
||||
<enum name="end" value="4" />
|
||||
</attr>
|
||||
<attr format="enum" name="bvp_page_style">
|
||||
<enum name="normal" value="0" />
|
||||
<enum name="multi_page" value="2" />
|
||||
<enum name="multi_page_overlap" value="4" />
|
||||
<enum name="multi_page_scale" value="8" />
|
||||
</attr>
|
||||
<attr format="enum" name="bvp_indicator_visibility">
|
||||
<enum name="visible" value="0" />
|
||||
<enum name="invisible" value="4" />
|
||||
<enum name="gone" value="8" />/>
|
||||
</attr>
|
||||
|
||||
</declare-styleable>
|
||||
|
||||
|
||||
</resources>
|
||||
9
app/src/main/res/xml/file_paths.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<external-path
|
||||
name="external_storage_root"
|
||||
path="." />
|
||||
<root-path name="root_path" path="."/>
|
||||
<external-files-path name="external_files_path" path = "."/>
|
||||
<external-files-path name="opensdk_external" path="Images/tmp"/>
|
||||
<external-files-path name="umeng_cache" path="umeng_cache/"/>
|
||||
</paths>
|
||||
9
app/src/main/res/xml/file_paths_csj.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!--为了适配所有路径可以设置 path = "." -->
|
||||
<external-path name="tt_external_root" path="." />
|
||||
<external-path name="tt_external_download" path="Download" />
|
||||
<external-files-path name="tt_external_files_download" path="Download" />
|
||||
<files-path name="tt_internal_file_download" path="Download" />
|
||||
<cache-path name="tt_internal_cache_download" path="Download" />
|
||||
</paths>
|
||||
10
app/src/main/res/xml/gdt_file_path.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<!-- 这个下载路径也不可以修改,必须为GDTDOWNLOAD -->
|
||||
<external-cache-path
|
||||
name="gdt_sdk_download_path1"
|
||||
path="com_qq_e_download" />
|
||||
<cache-path
|
||||
name="gdt_sdk_download_path2"
|
||||
path="com_qq_e_download" />
|
||||
</paths>
|
||||
4
app/src/main/res/xml/network_security_config.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="true" />
|
||||
</network-security-config>
|
||||
17
app/src/test/java/com/bikao/cleanmark/ExampleUnitTest.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.bikao.cleanmark;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
BIN
app/videomark.jks
Normal file
1
basicLib/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
58
basicLib/build.gradle
Normal file
@@ -0,0 +1,58 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 31
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
//启用DataBinding
|
||||
buildFeatures{
|
||||
dataBinding = true
|
||||
// for view binding :
|
||||
// viewBinding = true
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
api 'android.arch.lifecycle:extensions:1.1.1'
|
||||
api 'com.squareup.retrofit2:retrofit:2.5.0'
|
||||
api 'com.squareup.retrofit2:converter-scalars:2.3.0'
|
||||
api 'com.squareup.retrofit2:converter-gson:2.4.0'
|
||||
api 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
|
||||
api 'io.reactivex.rxjava2:rxjava:2.1.16'
|
||||
api 'io.reactivex.rxjava2:rxandroid:2.0.2'
|
||||
|
||||
api 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
api 'com.github.bumptech.glide:glide:4.11.0'
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
|
||||
// api 'com.android.support:design:28.0.0'
|
||||
api 'androidx.appcompat:appcompat:1.1.0'
|
||||
api 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
api 'androidx.viewpager2:viewpager2:1.0.0'
|
||||
api 'com.google.android.material:material:1.1.0'
|
||||
testImplementation "junit:junit:4.13.1"
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
api 'io.github.hariprasanths:bounceview-android:0.2.0'
|
||||
|
||||
}
|
||||
22
basicLib/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# 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
|
||||
|
||||
2
basicLib/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.azhon.basic" />
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.azhon.basic.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.databinding.ViewDataBinding;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.adapter
|
||||
* 文件名: BaseDBRVAdapter
|
||||
* 创建时间: 2019-03-27 on 16:22
|
||||
* 描述: TODO 结合dataBinding的RecyclerView Adapter
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public abstract class BaseDBRVAdapter<Data, DB extends ViewDataBinding> extends RecyclerView.Adapter<BaseDBRVHolder> {
|
||||
|
||||
private List<Data> data;
|
||||
private int itemId;
|
||||
protected Context context;
|
||||
protected int variableId;
|
||||
protected OnItemClickListener<Data> listener;
|
||||
|
||||
|
||||
public BaseDBRVAdapter(@LayoutRes int itemId, int variableId) {
|
||||
this.itemId = itemId;
|
||||
this.variableId = variableId;
|
||||
data = new ArrayList<>();
|
||||
}
|
||||
|
||||
public BaseDBRVAdapter(List<Data> data, @LayoutRes int itemId, int variableId) {
|
||||
this.data = data == null ? new ArrayList<Data>() : data;
|
||||
this.itemId = itemId;
|
||||
this.variableId = variableId;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public BaseDBRVHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
|
||||
this.context = parent.getContext();
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
DB binding = DataBindingUtil.inflate(inflater, itemId, parent, false);
|
||||
return new BaseDBRVHolder(binding.getRoot());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull BaseDBRVHolder holder, final int position) {
|
||||
DB binding = DataBindingUtil.getBinding(holder.itemView);
|
||||
final Data itemData = data.get(position);
|
||||
binding.setVariable(variableId, itemData);
|
||||
onBindViewHolder(itemData, binding, position);
|
||||
//迫使数据立即绑定
|
||||
binding.executePendingBindings();
|
||||
//设置点击事件
|
||||
if (listener != null) {
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
listener.onItemClick(itemData, position);
|
||||
}
|
||||
});
|
||||
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
return listener.onItemLongClick(itemData, position);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定数据
|
||||
*/
|
||||
protected void onBindViewHolder(Data data, DB binding, int position) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置新数据
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
public void setNewData(List<Data> data) {
|
||||
this.data.clear();
|
||||
this.data.addAll(data);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数据
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
public void addData(Data data) {
|
||||
this.data.add(data);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数据
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
public void addData(List<Data> data) {
|
||||
this.data.addAll(data);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Item 长按、点击事件
|
||||
*/
|
||||
public void setOnItemListener(OnItemClickListener<Data> listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.azhon.basic.adapter;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.adapter
|
||||
* 文件名: BaseDBRVHolder
|
||||
* 创建时间: 2019-03-27 on 22:12
|
||||
* 描述: TODO 结合dataBinding的RecyclerView Holder
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public final class BaseDBRVHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public BaseDBRVHolder(View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.azhon.basic.adapter;
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.adapter
|
||||
* 文件名: OnItemClickListener
|
||||
* 创建时间: 2019-03-29 on 13:53
|
||||
* 描述: TODO RecyclerView Item 长按、点击事件
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public interface OnItemClickListener<Data> {
|
||||
|
||||
/**
|
||||
* Item 点击事件
|
||||
*
|
||||
* @param data item的数据
|
||||
* @param position item的下标
|
||||
*/
|
||||
void onItemClick(Data data, int position);
|
||||
|
||||
/**
|
||||
* Item 长按事件
|
||||
*
|
||||
* @param data item的数据
|
||||
* @param position item的下标
|
||||
*/
|
||||
boolean onItemLongClick(Data data, int position);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.azhon.basic.base;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.databinding.ViewDataBinding;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
import com.azhon.basic.bean.DialogBean;
|
||||
import com.azhon.basic.lifecycle.BaseViewModel;
|
||||
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.base
|
||||
* 文件名: BaseActivity
|
||||
* 创建时间: 2019-03-27 on 10:46
|
||||
* 描述: TODO ViewModel、ViewDataBinding都需要的基类
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public abstract class BaseActivity<VM extends BaseViewModel, DB extends ViewDataBinding>
|
||||
extends BaseNoModelActivity<DB> {
|
||||
|
||||
protected VM viewModel;
|
||||
|
||||
@Override
|
||||
protected DB initDataBinding(int layoutId) {
|
||||
DB db = super.initDataBinding(layoutId);
|
||||
/**
|
||||
* 将这两个初始化函数插在{@link BaseActivity#initDataBinding}
|
||||
*/
|
||||
viewModel = initViewModel();
|
||||
initObserve();
|
||||
return db;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 初始化ViewModel
|
||||
*/
|
||||
protected abstract VM initViewModel();
|
||||
|
||||
/**
|
||||
* 监听当前ViewModel中 showDialog和error的值
|
||||
*/
|
||||
private void initObserve() {
|
||||
if (viewModel == null) return;
|
||||
viewModel.getShowDialog(this, bean -> {
|
||||
if (bean.isShow()) {
|
||||
showDialog(bean.getMsg());
|
||||
} else {
|
||||
dismissDialog();
|
||||
}
|
||||
});
|
||||
viewModel.getError(this, obj -> showError(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewModel层发生了错误
|
||||
*/
|
||||
protected abstract void showError(Object obj);
|
||||
|
||||
|
||||
}
|
||||
105
basicLib/src/main/java/com/azhon/basic/base/BaseFragment.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package com.azhon.basic.base;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Looper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.databinding.ViewDataBinding;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
import com.azhon.basic.bean.DialogBean;
|
||||
import com.azhon.basic.lifecycle.BaseViewModel;
|
||||
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.base
|
||||
* 文件名: BaseFragment
|
||||
* 创建时间: 2019-03-28 on 17:35
|
||||
* 描述: TODO ViewModel、ViewDataBinding都需要的基类
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public abstract class BaseFragment<VM extends BaseViewModel, DB extends ViewDataBinding>
|
||||
extends BaseNoModelFragment<DB> {
|
||||
|
||||
protected VM viewModel;
|
||||
private static Toast toast;
|
||||
|
||||
@Override
|
||||
protected DB initDataBinding(LayoutInflater inflater, int layoutId, ViewGroup container) {
|
||||
DB db = super.initDataBinding(inflater, layoutId, container);
|
||||
/**
|
||||
* 将这两个初始化函数插在{@link BaseFragment#initDataBinding}
|
||||
*/
|
||||
viewModel = initViewModel();
|
||||
initObserve();
|
||||
return db;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化ViewModel
|
||||
*/
|
||||
protected abstract VM initViewModel();
|
||||
|
||||
/**
|
||||
* 监听当前ViewModel中 showDialog和error的值
|
||||
*/
|
||||
private void initObserve() {
|
||||
if (viewModel == null) return;
|
||||
viewModel.getShowDialog(this, new Observer<DialogBean>() {
|
||||
@Override
|
||||
public void onChanged(DialogBean bean) {
|
||||
if (bean.isShow()) {
|
||||
showDialog(bean.getMsg());
|
||||
} else {
|
||||
dismissDialog();
|
||||
}
|
||||
}
|
||||
});
|
||||
viewModel.getError(this, new Observer<Object>() {
|
||||
@Override
|
||||
public void onChanged(Object obj) {
|
||||
showError(obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewModel层发生了错误
|
||||
*/
|
||||
protected abstract void showError(Object obj);
|
||||
|
||||
|
||||
// /**
|
||||
// * 显示提示 toast
|
||||
// *
|
||||
// * @param msg 提示信息
|
||||
// */
|
||||
// @SuppressLint("ShowToast")
|
||||
// public void showToast(String msg) {
|
||||
// try {
|
||||
// if (null == toast) {
|
||||
// toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
|
||||
// } else {
|
||||
// toast.setText(msg);
|
||||
// }
|
||||
// getActivity().runOnUiThread(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// toast.show();
|
||||
// }
|
||||
// });
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// //解决在子线程中调用Toast的异常情况处理
|
||||
// Looper.prepare();
|
||||
// Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
|
||||
// Looper.loop();
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.azhon.basic.base;
|
||||
|
||||
import androidx.databinding.ViewDataBinding;
|
||||
import androidx.lifecycle.LifecycleObserver;
|
||||
|
||||
import com.azhon.basic.lifecycle.BaseViewModel;
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.base
|
||||
* 文件名: BaseLazyFragment
|
||||
* 创建时间: 2019-03-28 on 18:01
|
||||
* 描述: TODO 懒加载Fragment基类,适用于一个页面多个Tab页面
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public abstract class BaseLazyFragment<VM extends BaseViewModel, DB extends ViewDataBinding>
|
||||
extends BaseFragment<VM, DB> implements LifecycleObserver {
|
||||
|
||||
public boolean visibleToUser;
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (!visibleToUser) {
|
||||
visibleToUser = true;
|
||||
lazyLoad();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 懒加载,只有在Fragment第一次创建且第一次对用户可见
|
||||
*/
|
||||
protected abstract void lazyLoad();
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
package com.azhon.basic.base;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorMatrix;
|
||||
import android.graphics.ColorMatrixColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.databinding.ViewDataBinding;
|
||||
|
||||
import com.azhon.basic.utils.ActivityUtil;
|
||||
import com.azhon.basic.view.LoadingDialog;
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.base
|
||||
* 文件名: BaseNoModelActivity
|
||||
* 创建时间: 2019-03-28 on 10:28
|
||||
* 描述: TODO 不需要ViewModel的页面基类
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public abstract class BaseNoModelActivity<DB extends ViewDataBinding> extends AppCompatActivity {
|
||||
|
||||
protected DB dataBinding;
|
||||
protected Context context;
|
||||
private LoadingDialog loadingDialog;
|
||||
public Bundle bundle;
|
||||
//获取TAG的activity名称
|
||||
protected final String TAG = this.getClass().getSimpleName();
|
||||
//是否显示标题栏
|
||||
private boolean isShowTitle = false;
|
||||
//是否显示状态栏
|
||||
private boolean isShowStatusBar = true;
|
||||
//是否允许旋转屏幕
|
||||
private boolean isAllowScreenRoate = true;
|
||||
//封装Toast对象
|
||||
private static Toast toast;
|
||||
|
||||
|
||||
@SuppressLint("SourceLockedOrientationActivity")
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
Window window = getWindow();
|
||||
View decor = window.getDecorView();
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
window.setStatusBarColor(Color.TRANSPARENT);
|
||||
if(this.getApplicationContext().getResources().getConfiguration().uiMode == 0x21){
|
||||
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
}else {
|
||||
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||
}
|
||||
}
|
||||
// Paint paint = new Paint();
|
||||
// ColorMatrix matrix = new ColorMatrix();
|
||||
// matrix.setSaturation(0);
|
||||
// paint.setColorFilter(new ColorMatrixColorFilter(matrix));
|
||||
// getWindow().getDecorView().setLayerType(View.LAYER_TYPE_HARDWARE, paint);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
context = this;
|
||||
this.bundle=savedInstanceState;
|
||||
|
||||
ActivityUtil.getInstance().addActivity(this);
|
||||
int layoutId = initLayout();
|
||||
setContentView(layoutId);
|
||||
// setStatusBarColor(this,Color.TRANSPARENT);
|
||||
dataBinding = initDataBinding(layoutId);
|
||||
initView();
|
||||
initData();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 修改状态栏颜色,支持4.4以上版本
|
||||
* @param activity
|
||||
* @param colorId
|
||||
*/
|
||||
public static void setStatusBarColor(Activity activity, int colorId) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
Window window = activity.getWindow();
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
window.setStatusBarColor(colorId);
|
||||
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改状态栏颜色,支持4.4以上版本
|
||||
* @param activity
|
||||
* @param colorId
|
||||
*/
|
||||
public static void setStatusBarColorW(Activity activity, int colorId,boolean statusBarTextIsWhiteColor) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
Window window = activity.getWindow();
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
window.setStatusBarColor(activity.getResources().getColor(colorId));
|
||||
if(statusBarTextIsWhiteColor) {
|
||||
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
}else{
|
||||
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void hideActionBar(){
|
||||
//this.getSupportActionBar().hide();
|
||||
|
||||
}
|
||||
/**
|
||||
* 初始化要加载的布局资源ID
|
||||
* 此函数优先执行于onCreate()可以做window操作
|
||||
*/
|
||||
protected abstract int initLayout();
|
||||
|
||||
|
||||
/**
|
||||
* 初始化DataBinding
|
||||
*/
|
||||
protected DB initDataBinding(@LayoutRes int layoutId) {
|
||||
return DataBindingUtil.setContentView(this, layoutId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化视图
|
||||
*/
|
||||
protected abstract void initView();
|
||||
|
||||
/**
|
||||
* 初始化数据
|
||||
*/
|
||||
protected abstract void initData();
|
||||
|
||||
/**
|
||||
* 显示用户等待框
|
||||
*
|
||||
* @param msg 提示信息
|
||||
*/
|
||||
protected void showDialog(String msg) {
|
||||
if (loadingDialog != null && loadingDialog.isShowing()) {
|
||||
loadingDialog.setLoadingMsg(msg);
|
||||
} else {
|
||||
loadingDialog = new LoadingDialog(context);
|
||||
loadingDialog.setLoadingMsg(msg);
|
||||
loadingDialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 隐藏等待框
|
||||
*/
|
||||
protected void dismissDialog() {
|
||||
if (loadingDialog != null && loadingDialog.isShowing()) {
|
||||
loadingDialog.dismiss();
|
||||
loadingDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (dataBinding != null) {
|
||||
dataBinding.unbind();
|
||||
}
|
||||
ActivityUtil.getInstance().removeActivity(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置是否显示标题栏
|
||||
*
|
||||
* @param showTitle true or false
|
||||
*/
|
||||
public void setShowTitle(boolean showTitle) {
|
||||
isShowTitle = showTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否显示状态栏
|
||||
*
|
||||
* @param showStatusBar true or false
|
||||
*/
|
||||
public void setShowStatusBar(boolean showStatusBar) {
|
||||
isShowStatusBar = showStatusBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许屏幕旋转
|
||||
*
|
||||
* @param allowScreenRoate true or false
|
||||
*/
|
||||
public void setAllowScreenRoate(boolean allowScreenRoate) {
|
||||
isAllowScreenRoate = allowScreenRoate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保证同一按钮在1秒内只会响应一次点击事件
|
||||
*/
|
||||
public abstract class OnSingleClickListener implements View.OnClickListener {
|
||||
//两次点击按钮之间的间隔,目前为1000ms
|
||||
private static final int MIN_CLICK_DELAY_TIME = 1000;
|
||||
private long lastClickTime;
|
||||
|
||||
public abstract void onSingleClick(View view);
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
long curClickTime = System.currentTimeMillis();
|
||||
if ((curClickTime - lastClickTime) >= MIN_CLICK_DELAY_TIME) {
|
||||
lastClickTime = curClickTime;
|
||||
onSingleClick(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同一按钮在短时间内可重复响应点击事件
|
||||
*/
|
||||
public abstract class OnMultiClickListener implements View.OnClickListener {
|
||||
public abstract void onMultiClick(View view);
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onMultiClick(v);
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 显示提示 toast
|
||||
// *
|
||||
// * @param msg 提示信息
|
||||
// */
|
||||
// @SuppressLint("ShowToast")
|
||||
// public void showToast(String msg) {
|
||||
// try {
|
||||
// if (null == toast) {
|
||||
// toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
|
||||
// } else {
|
||||
// toast.setText(msg);
|
||||
// }
|
||||
// runOnUiThread(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// toast.show();
|
||||
// }
|
||||
// });
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// //解决在子线程中调用Toast的异常情况处理
|
||||
// Looper.prepare();
|
||||
// Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
|
||||
// Looper.loop();
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* 隐藏软键盘
|
||||
*/
|
||||
public void hideSoftInput() {
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
if (getCurrentFocus() != null && null != imm) {
|
||||
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示软键盘
|
||||
*/
|
||||
public void showSoftInput() {
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
if (getCurrentFocus() != null && null != imm) {
|
||||
imm.showSoftInputFromInputMethod(getCurrentFocus().getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void showToast(String message){
|
||||
// try {
|
||||
// SnackbarUtils.Short(dataBinding.getRoot(),message).show();
|
||||
//
|
||||
// }catch (Exception e){e.printStackTrace();}
|
||||
Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
|
||||
|
||||
}
|
||||
|
||||
protected void showToast(String message,int color){
|
||||
try {
|
||||
// SnackbarUtils.Short(dataBinding.getRoot(),message,color).show();
|
||||
Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
|
||||
|
||||
|
||||
}catch (Exception e){e.printStackTrace();}
|
||||
|
||||
}
|
||||
|
||||
protected void showToast(View view,String message){
|
||||
try {
|
||||
// SnackbarUtils.Short(view,message).show();
|
||||
Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
|
||||
|
||||
}catch (Exception e){e.printStackTrace();}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package com.azhon.basic.base;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.databinding.ViewDataBinding;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.azhon.basic.view.LoadingDialog;
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.base
|
||||
* 文件名: BaseNoModelFragment
|
||||
* 创建时间: 2019-03-28 on 17:06
|
||||
* 描述: TODO 不需要ViewModel的页面基类
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public abstract class BaseNoModelFragment<DB extends ViewDataBinding> extends Fragment {
|
||||
|
||||
protected DB dataBinding;
|
||||
protected Context context;
|
||||
protected FragmentActivity activity;
|
||||
private LoadingDialog loadingDialog;
|
||||
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
dataBinding = initDataBinding(inflater, setLayoutResourceID(), container);
|
||||
return dataBinding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
activity = getActivity();
|
||||
setUpView();
|
||||
setUpData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化要加载的布局资源ID
|
||||
*/
|
||||
protected abstract int setLayoutResourceID();
|
||||
|
||||
|
||||
/**
|
||||
* 初始化DataBinding
|
||||
*/
|
||||
protected DB initDataBinding(LayoutInflater inflater, @LayoutRes int layoutId, ViewGroup container) {
|
||||
return DataBindingUtil.inflate(inflater, layoutId, container, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化视图
|
||||
*/
|
||||
protected abstract void setUpView();
|
||||
|
||||
/**
|
||||
* 初始化数据
|
||||
*/
|
||||
protected abstract void setUpData();
|
||||
|
||||
/**
|
||||
* 显示用户等待框
|
||||
*
|
||||
* @param msg 提示信息
|
||||
*/
|
||||
protected void showDialog(String msg) {
|
||||
if (loadingDialog != null && loadingDialog.isShowing()) {
|
||||
loadingDialog.setLoadingMsg(msg);
|
||||
} else {
|
||||
loadingDialog = new LoadingDialog(context);
|
||||
loadingDialog.setLoadingMsg(msg);
|
||||
loadingDialog.show();
|
||||
}
|
||||
}
|
||||
protected void showToast(String message){
|
||||
try {
|
||||
// SnackbarUtils.Short(dataBinding.getRoot(),message).show();
|
||||
Toast.makeText(dataBinding.getRoot().getContext(),message,Toast.LENGTH_SHORT).show();
|
||||
|
||||
|
||||
}catch (Exception e){e.printStackTrace();}
|
||||
|
||||
}
|
||||
|
||||
protected void showToast(View view,String message){
|
||||
try {
|
||||
// SnackbarUtils.Short(view,message).show();
|
||||
Toast.makeText(dataBinding.getRoot().getContext(),message,Toast.LENGTH_SHORT).show();
|
||||
|
||||
}catch (Exception e){e.printStackTrace();}
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected void showToast(View view,String message,int color){
|
||||
try {
|
||||
// SnackbarUtils.Short(view,message,color).show();
|
||||
Toast.makeText(dataBinding.getRoot().getContext(),message,Toast.LENGTH_SHORT).show();
|
||||
|
||||
}catch (Exception e){e.printStackTrace();}
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected void showToast(String message,int color){
|
||||
try {
|
||||
// SnackbarUtils.Short(dataBinding.getRoot(),message,color).show();
|
||||
Toast.makeText(dataBinding.getRoot().getContext(),message,Toast.LENGTH_SHORT).show();
|
||||
|
||||
|
||||
}catch (Exception e){e.printStackTrace();}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏等待框
|
||||
*/
|
||||
protected void dismissDialog() {
|
||||
if (loadingDialog != null && loadingDialog.isShowing()) {
|
||||
loadingDialog.dismiss();
|
||||
loadingDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (dataBinding != null) {
|
||||
dataBinding.unbind();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public View getContentView() {
|
||||
return dataBinding.getRoot();
|
||||
}
|
||||
|
||||
public Context getMContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 隐藏软键盘
|
||||
*/
|
||||
public void hideSoftInput() {
|
||||
View v = getActivity().getCurrentFocus();
|
||||
if (v != null && v.getWindowToken() != null) {
|
||||
InputMethodManager manager = (InputMethodManager) getContext()
|
||||
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
boolean isOpen = manager.isActive();
|
||||
if (isOpen) {
|
||||
manager.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
33
basicLib/src/main/java/com/azhon/basic/bean/DialogBean.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package com.azhon.basic.bean;
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.bean
|
||||
* 文件名: DialogBean
|
||||
* 创建时间: 2019-03-27 on 20:54
|
||||
* 描述: TODO 封装的对话框实体类
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public final class DialogBean {
|
||||
|
||||
private boolean isShow;
|
||||
private String msg;
|
||||
|
||||
public boolean isShow() {
|
||||
return isShow;
|
||||
}
|
||||
|
||||
public void setShow(boolean show) {
|
||||
isShow = show;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.azhon.basic.lifecycle;
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.azhon.basic.bean.DialogBean;
|
||||
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.lifecycle
|
||||
* 文件名: BaseViewModel
|
||||
* 创建时间: 2019-03-27 on 10:44
|
||||
* 描述: TODO ViewModel基类,管理rxJava发出的请求,ViewModel销毁同时也取消请求
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public abstract class BaseViewModel extends ViewModel {
|
||||
|
||||
/**
|
||||
* 管理RxJava请求
|
||||
*/
|
||||
private CompositeDisposable compositeDisposable;
|
||||
/**
|
||||
* 用来通知 Activity/Fragment 是否显示等待Dialog
|
||||
*/
|
||||
protected DialogLiveData<DialogBean> showDialog = new DialogLiveData<>();
|
||||
/**
|
||||
* 当ViewModel层出现错误需要通知到Activity/Fragment
|
||||
*/
|
||||
protected MutableLiveData<Object> error = new MutableLiveData<>();
|
||||
|
||||
/**
|
||||
* 添加 rxJava 发出的请求
|
||||
*/
|
||||
protected void addDisposable(Disposable disposable) {
|
||||
if (compositeDisposable == null || compositeDisposable.isDisposed()) {
|
||||
compositeDisposable = new CompositeDisposable();
|
||||
}
|
||||
compositeDisposable.add(disposable);
|
||||
}
|
||||
|
||||
public void getShowDialog(LifecycleOwner owner, Observer<DialogBean> observer) {
|
||||
showDialog.observe(owner, observer);
|
||||
}
|
||||
|
||||
public void getError(LifecycleOwner owner, Observer<Object> observer) {
|
||||
error.observe(owner, observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewModel销毁同时也取消请求
|
||||
*/
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
super.onCleared();
|
||||
if (compositeDisposable != null) {
|
||||
compositeDisposable.dispose();
|
||||
compositeDisposable = null;
|
||||
}
|
||||
showDialog = null;
|
||||
error = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.azhon.basic.lifecycle;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.azhon.basic.bean.DialogBean;
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.lifecycle
|
||||
* 文件名: DialogLiveData
|
||||
* 创建时间: 2019-03-27 on 20:57
|
||||
* 描述: TODO
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public final class DialogLiveData<T> extends MutableLiveData<T> {
|
||||
|
||||
private DialogBean bean = new DialogBean();
|
||||
|
||||
public void setValue(boolean isShow) {
|
||||
bean.setShow(isShow);
|
||||
bean.setMsg("");
|
||||
setValue((T) bean);
|
||||
}
|
||||
|
||||
public void setValue(boolean isShow, String msg) {
|
||||
bean.setShow(isShow);
|
||||
bean.setMsg(msg);
|
||||
setValue((T) bean);
|
||||
}
|
||||
}
|
||||
46
basicLib/src/main/java/com/azhon/basic/retrofit/BaseApi.java
Normal file
@@ -0,0 +1,46 @@
|
||||
package com.azhon.basic.retrofit;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
import retrofit2.converter.scalars.ScalarsConverterFactory;
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.retrofit
|
||||
* 文件名: BaseApi
|
||||
* 创建时间: 2019-03-27 on 14:52
|
||||
* 描述: TODO 封装基础的Retrofit
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public abstract class BaseApi {
|
||||
|
||||
/**
|
||||
* 初始化Retrofit
|
||||
*/
|
||||
public Retrofit initRetrofit(String baseUrl) {
|
||||
Retrofit.Builder builder = new Retrofit.Builder();
|
||||
//支持返回Call<String>
|
||||
builder.addConverterFactory(ScalarsConverterFactory.create());
|
||||
//支持直接格式化json返回Bean对象
|
||||
builder.addConverterFactory(GsonConverterFactory.create());
|
||||
//支持RxJava
|
||||
builder.addCallAdapterFactory(RxJava2CallAdapterFactory.create());
|
||||
builder.baseUrl(baseUrl);
|
||||
OkHttpClient client = setClient();
|
||||
if (client != null) {
|
||||
builder.client(client);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置OkHttpClient,添加拦截器等
|
||||
*
|
||||
* @return 可以返回为null
|
||||
*/
|
||||
protected abstract OkHttpClient setClient();
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.azhon.basic.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.utils
|
||||
* 文件名: ActivityUtil
|
||||
* 创建时间: 2019-03-29 on 17:13
|
||||
* 描述: TODO 管理所有Activity的实例
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public class ActivityUtil {
|
||||
|
||||
private static Stack<Activity> stack;
|
||||
private static ActivityUtil manager;
|
||||
|
||||
/**
|
||||
* 获取实例
|
||||
*/
|
||||
public static synchronized ActivityUtil getInstance() {
|
||||
if (manager == null) {
|
||||
manager = new ActivityUtil();
|
||||
stack = new Stack<>();
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加Activity
|
||||
*/
|
||||
public synchronized void addActivity(Activity activity) {
|
||||
stack.add(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除Activity
|
||||
*/
|
||||
public synchronized void removeActivity(Activity activity) {
|
||||
stack.remove(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束指定类名的Activity
|
||||
*/
|
||||
public void finishActivity(Class<?> cls) {
|
||||
for (Activity activity : stack) {
|
||||
if (activity.getClass().equals(cls)) {
|
||||
finishActivity(activity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束指定的Activity
|
||||
*/
|
||||
public void finishActivity(Activity activity) {
|
||||
if (activity != null) {
|
||||
activity.finish();
|
||||
stack.remove(activity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在Activity
|
||||
*/
|
||||
public boolean containsActivity(Class<?> cls) {
|
||||
for (Activity activity : stack) {
|
||||
if (activity.getClass().equals(cls)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束所有Activity
|
||||
*/
|
||||
public void finishAllActivity() {
|
||||
for (Activity activity : stack) {
|
||||
if (activity != null) {
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
stack.clear();
|
||||
}
|
||||
}
|
||||
26
basicLib/src/main/java/com/azhon/basic/utils/AnimUtil.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.azhon.basic.utils;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.github.hariprasanths.bounceview.BounceView;
|
||||
|
||||
/**
|
||||
* 控件的点击缩放事件
|
||||
*/
|
||||
public class AnimUtil {
|
||||
/**
|
||||
* 控件设置动画事件
|
||||
* @param view
|
||||
*/
|
||||
public static void setAnimView(View view){
|
||||
BounceView.addAnimTo(view).setScaleForPushInAnim(1.02f,1.02f).setScaleForPopOutAnim(1.0f,1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 控件设置动画事件
|
||||
* @param view
|
||||
*/
|
||||
public static void setAnimView(View view,float inAnim){
|
||||
BounceView.addAnimTo(view).setScaleForPushInAnim(inAnim,inAnim).setScaleForPopOutAnim(1.0f,1.0f);
|
||||
}
|
||||
}
|
||||
76
basicLib/src/main/java/com/azhon/basic/utils/ApkUtil.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package com.azhon.basic.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 文件名: ApkUtil
|
||||
* 创建时间: 2018/5/4 on 15:49
|
||||
* 描述: TODO
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public class ApkUtil {
|
||||
/**
|
||||
* 安装一个apk
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param apk 安装包文件
|
||||
*/
|
||||
public static void installApk(Context context, File apk) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
Uri uri;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
uri = FileProvider.getUriForFile(context, context.getPackageName(), apk);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
} else {
|
||||
uri = Uri.fromFile(apk);
|
||||
}
|
||||
intent.setDataAndType(uri, "application/vnd.android.package-archive");
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前app的升级版本号
|
||||
*
|
||||
* @param context 上下文
|
||||
*/
|
||||
public static long getVersionCode(Context context) {
|
||||
try {
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
|
||||
return packageInfo.getLongVersionCode();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前app的版本号
|
||||
*
|
||||
* @param context 上下文
|
||||
*/
|
||||
public static String getVersionName(Context context) {
|
||||
try {
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
|
||||
return packageInfo.versionName;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "1.0.0";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.azhon.basic.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
/**
|
||||
* 文件名: DensityUtil
|
||||
* 创建时间: 2018/3/18 on 13:32
|
||||
* 描述: TODO
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
|
||||
public class DensityUtil {
|
||||
|
||||
/**
|
||||
* 获取屏幕宽度(像素)
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return px
|
||||
*/
|
||||
public static int getWith(Context context) {
|
||||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
return dm.widthPixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取屏幕高度(像素)
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return px
|
||||
*/
|
||||
public static int getHeight(Context context) {
|
||||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
return dm.heightPixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态栏的高度
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return px
|
||||
*/
|
||||
public static int getStatusBarHeight(Context context) {
|
||||
int statusBarHeight = 0;
|
||||
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
|
||||
}
|
||||
return statusBarHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标题栏(ActionBar)的高度
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return px
|
||||
*/
|
||||
public static int getActionBarHeight(Context context) {
|
||||
TypedArray values = context.getTheme().obtainStyledAttributes(new int[]{android.R.attr.actionBarSize});
|
||||
int actionBarHeight = values.getDimensionPixelSize(0, 0);
|
||||
values.recycle();
|
||||
return actionBarHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
|
||||
*/
|
||||
public static int dip2px(Context context, float dpValue) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (dpValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
|
||||
*/
|
||||
public static int px2dip(Context context, float pxValue) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (pxValue / scale + 0.5f);
|
||||
}
|
||||
}
|
||||
144
basicLib/src/main/java/com/azhon/basic/utils/SharePreUtil.java
Normal file
@@ -0,0 +1,144 @@
|
||||
package com.azhon.basic.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* 文件名: SharePreUtil
|
||||
* 创建者: xuhuixiang
|
||||
* 创建时间: 2016/12/12 on 11:58
|
||||
* 描述: SharedPreferences存取工具类
|
||||
*/
|
||||
|
||||
public class SharePreUtil {
|
||||
/**
|
||||
* 配置文件,文件名
|
||||
*/
|
||||
private static final String SHARE_NAME = "config";
|
||||
|
||||
/**
|
||||
* 存字符串
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param key 键
|
||||
* @param values 值
|
||||
*/
|
||||
public static void putString(Context context, String key, String values) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
|
||||
sp.edit().putString(key, values).apply();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 取字符串
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param key 键
|
||||
* @param values 默认值
|
||||
* @return 取出的值
|
||||
*/
|
||||
public static String getString(Context context, String key, String values) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
|
||||
return sp.getString(key, values);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 存布尔值
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param key 键
|
||||
* @param values 值
|
||||
*/
|
||||
public static void putBoolean(Context context, String key, boolean values) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
|
||||
sp.edit().putBoolean(key, values).apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* 取布尔值
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param key 键
|
||||
* @param values 默认值
|
||||
* @return true/false
|
||||
*/
|
||||
public static boolean getBoolean(Context context, String key, boolean values) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
|
||||
return sp.getBoolean(key, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 存int值
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param key 键
|
||||
* @param values 值
|
||||
*/
|
||||
public static void putInt(Context context, String key, int values) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
|
||||
sp.edit().putInt(key, values).apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* 取int值
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param key 键
|
||||
* @param values 默认值
|
||||
* @return
|
||||
*/
|
||||
public static int getInt(Context context, String key, int values) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
|
||||
return sp.getInt(key, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一条字段
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param key 键
|
||||
*/
|
||||
public static void deleShare(Context context, String key) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
|
||||
//单个清理
|
||||
sp.edit().remove(key).apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除全部数据
|
||||
*
|
||||
* @param context 上下文
|
||||
*/
|
||||
public static void deleShareAll(Context context) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
|
||||
//全部清理
|
||||
sp.edit().clear().apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看SharedPreferences的内容
|
||||
*/
|
||||
public static String lookSharePre(Context context) {
|
||||
try {
|
||||
FileInputStream stream = new FileInputStream(new File("/data/data/" +
|
||||
context.getPackageName() + "/shared_prefs", SHARE_NAME + ".xml"));
|
||||
BufferedReader bff = new BufferedReader(new InputStreamReader(stream));
|
||||
String line;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while ((line = bff.readLine()) != null) {
|
||||
sb.append(line);
|
||||
sb.append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "未找到当前配置文件!";
|
||||
}
|
||||
}
|
||||
475
basicLib/src/main/java/com/azhon/basic/utils/SnackbarUtils.java
Normal file
@@ -0,0 +1,475 @@
|
||||
package com.azhon.basic.utils;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Space;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
|
||||
import com.azhon.basic.R;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
public class SnackbarUtils {
|
||||
//设置Snackbar背景颜色
|
||||
private static final int color_info = 0XFF2094F3;
|
||||
private static final int color_confirm = 0XFF4CB04E;
|
||||
private static final int color_warning = 0XFFFEC005;
|
||||
private static final int color_danger = 0XFFF44336;
|
||||
//工具类当前持有的Snackbar实例
|
||||
private static Snackbar mSnackbar = null;
|
||||
|
||||
|
||||
private SnackbarUtils(){
|
||||
throw new RuntimeException("禁止无参创建实例");
|
||||
}
|
||||
|
||||
public SnackbarUtils(@NonNull Snackbar snackbar){
|
||||
this.mSnackbar = snackbar;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 mSnackbar
|
||||
* @return
|
||||
*/
|
||||
public Snackbar getSnackbar() {
|
||||
return mSnackbar;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化Snackbar实例
|
||||
* 展示时间:Snackbar.LENGTH_SHORT
|
||||
* @param view
|
||||
* @param message
|
||||
* @return
|
||||
*/
|
||||
public static SnackbarUtils Short(View view, String message){
|
||||
mSnackbar = Snackbar.make(view,message,Snackbar.LENGTH_SHORT);
|
||||
return new SnackbarUtils(mSnackbar).backColor(0XCC1771E3);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化Snackbar实例
|
||||
* 展示时间:Snackbar.LENGTH_SHORT
|
||||
* @param view
|
||||
* @param message
|
||||
* @return
|
||||
*/
|
||||
public static SnackbarUtils Short(View view, String message,int Color){
|
||||
mSnackbar = Snackbar.make(view,message,Snackbar.LENGTH_SHORT);
|
||||
return new SnackbarUtils(mSnackbar).backColor(Color);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 初始化Snackbar实例
|
||||
* 展示时间:Snackbar.LENGTH_LONG
|
||||
* @param view
|
||||
* @param message
|
||||
* @return
|
||||
*/
|
||||
public static SnackbarUtils Long(View view, String message){
|
||||
mSnackbar = Snackbar.make(view,message,Snackbar.LENGTH_LONG);
|
||||
return new SnackbarUtils(mSnackbar).backColor(0XCC1771E3);
|
||||
}
|
||||
/**
|
||||
* 初始化Snackbar实例
|
||||
* 展示时间:Snackbar.LENGTH_INDEFINITE
|
||||
* @param view
|
||||
* @param message
|
||||
* @return
|
||||
*/
|
||||
public static SnackbarUtils Indefinite(View view, String message){
|
||||
mSnackbar = Snackbar.make(view,message,Snackbar.LENGTH_INDEFINITE);
|
||||
return new SnackbarUtils(mSnackbar).backColor(0XCC1771E3);
|
||||
}
|
||||
/**
|
||||
* 初始化Snackbar实例
|
||||
* 展示时间:duration 毫秒
|
||||
* @param view
|
||||
* @param message
|
||||
* @param duration 展示时长(毫秒)
|
||||
* @return
|
||||
*/
|
||||
public static SnackbarUtils Custom(View view, String message, int duration){
|
||||
mSnackbar = Snackbar.make(view,message,Snackbar.LENGTH_SHORT);
|
||||
mSnackbar.setDuration(duration);
|
||||
return new SnackbarUtils(mSnackbar).backColor(0XCC1771E3);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置mSnackbar背景色为 color_info
|
||||
*/
|
||||
public SnackbarUtils info(){
|
||||
mSnackbar.getView().setBackgroundColor(color_info);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
/**
|
||||
* 设置mSnackbar背景色为 color_confirm
|
||||
*/
|
||||
public SnackbarUtils confirm(){
|
||||
mSnackbar.getView().setBackgroundColor(color_confirm);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
/**
|
||||
* 设置Snackbar背景色为 color_warning
|
||||
*/
|
||||
public SnackbarUtils warning(){
|
||||
mSnackbar.getView().setBackgroundColor(color_warning);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
/**
|
||||
* 设置Snackbar背景色为 color_warning
|
||||
*/
|
||||
public SnackbarUtils danger(){
|
||||
mSnackbar.getView().setBackgroundColor(color_danger);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Snackbar背景色
|
||||
* @param backgroundColor
|
||||
*/
|
||||
public SnackbarUtils backColor(@ColorInt int backgroundColor){
|
||||
mSnackbar.getView().setBackgroundColor(backgroundColor);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置TextView(@+id/snackbar_text)的文字颜色
|
||||
* @param messageColor
|
||||
*/
|
||||
public SnackbarUtils messageColor(@ColorInt int messageColor){
|
||||
((TextView)mSnackbar.getView().findViewById(R.id.snackbar_text)).setTextColor(messageColor);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Button(@+id/snackbar_action)的文字颜色
|
||||
* @param actionTextColor
|
||||
*/
|
||||
public SnackbarUtils actionColor(@ColorInt int actionTextColor){
|
||||
((Button)mSnackbar.getView().findViewById(R.id.snackbar_action)).setTextColor(actionTextColor);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Snackbar背景色 + TextView(@+id/snackbar_text)的文字颜色 + Button(@+id/snackbar_action)的文字颜色
|
||||
* @param backgroundColor
|
||||
* @param messageColor
|
||||
* @param actionTextColor
|
||||
*/
|
||||
public SnackbarUtils colors(@ColorInt int backgroundColor, @ColorInt int messageColor, @ColorInt int actionTextColor){
|
||||
mSnackbar.getView().setBackgroundColor(backgroundColor);
|
||||
((TextView)mSnackbar.getView().findViewById(R.id.snackbar_text)).setTextColor(messageColor);
|
||||
((Button)mSnackbar.getView().findViewById(R.id.snackbar_action)).setTextColor(actionTextColor);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Snackbar 背景透明度
|
||||
* @param alpha
|
||||
* @return
|
||||
*/
|
||||
public SnackbarUtils alpha(float alpha){
|
||||
alpha = alpha>=1.0f?1.0f:(alpha<=0.0f?0.0f:alpha);
|
||||
mSnackbar.getView().setAlpha(alpha);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Snackbar显示的位置
|
||||
* @param gravity
|
||||
*/
|
||||
public SnackbarUtils gravityFrameLayout(int gravity){
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(mSnackbar.getView().getLayoutParams().width,mSnackbar.getView().getLayoutParams().height);
|
||||
params.gravity = gravity;
|
||||
mSnackbar.getView().setLayoutParams(params);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Snackbar显示的位置,当Snackbar和CoordinatorLayout组合使用的时候
|
||||
* @param gravity
|
||||
*/
|
||||
public SnackbarUtils gravityCoordinatorLayout(int gravity){
|
||||
CoordinatorLayout.LayoutParams params = new CoordinatorLayout.LayoutParams(mSnackbar.getView().getLayoutParams().width,mSnackbar.getView().getLayoutParams().height);
|
||||
params.gravity = gravity;
|
||||
mSnackbar.getView().setLayoutParams(params);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置按钮文字内容 及 点击监听
|
||||
* {@link Snackbar#setAction(CharSequence, View.OnClickListener)}
|
||||
* @param resId
|
||||
* @param listener
|
||||
* @return
|
||||
*/
|
||||
public SnackbarUtils setAction(@StringRes int resId, View.OnClickListener listener){
|
||||
return setAction(getSnackbar().getView().getResources().getText(resId), listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置按钮文字内容 及 点击监听
|
||||
* {@link Snackbar#setAction(CharSequence, View.OnClickListener)}
|
||||
* @param text
|
||||
* @param listener
|
||||
* @return
|
||||
*/
|
||||
public SnackbarUtils setAction(CharSequence text, View.OnClickListener listener){
|
||||
mSnackbar.setAction(text,listener);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 mSnackbar 展示完成 及 隐藏完成 的监听
|
||||
* @param setCallback
|
||||
* @return
|
||||
*/
|
||||
public SnackbarUtils setCallback(Snackbar.Callback setCallback){
|
||||
mSnackbar.setCallback(setCallback);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置TextView(@+id/snackbar_text)左右两侧的图片
|
||||
* @param leftDrawable
|
||||
* @param rightDrawable
|
||||
* @return
|
||||
*/
|
||||
public SnackbarUtils leftAndRightDrawable(@Nullable @DrawableRes Integer leftDrawable, @Nullable @DrawableRes Integer rightDrawable){
|
||||
Drawable drawableLeft = null;
|
||||
Drawable drawableRight = null;
|
||||
if(leftDrawable!=null){
|
||||
try {
|
||||
drawableLeft = getSnackbar().getView().getResources().getDrawable(leftDrawable.intValue());
|
||||
}catch (Exception e){
|
||||
Log.e("Jet","getSnackbar().getView().getResources().getDrawable(leftDrawable.intValue())");
|
||||
}
|
||||
}
|
||||
if(rightDrawable!=null){
|
||||
try {
|
||||
drawableRight = getSnackbar().getView().getResources().getDrawable(rightDrawable.intValue());
|
||||
}catch (Exception e){
|
||||
Log.e("Jet","getSnackbar().getView().getResources().getDrawable(rightDrawable.intValue())");
|
||||
}
|
||||
}
|
||||
return leftAndRightDrawable(drawableLeft,drawableRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置TextView(@+id/snackbar_text)左右两侧的图片
|
||||
* @param leftDrawable
|
||||
* @param rightDrawable
|
||||
* @return
|
||||
*/
|
||||
public SnackbarUtils leftAndRightDrawable(@Nullable Drawable leftDrawable, @Nullable Drawable rightDrawable){
|
||||
TextView message = (TextView) mSnackbar.getView().findViewById(R.id.snackbar_text);
|
||||
LinearLayout.LayoutParams paramsMessage = (LinearLayout.LayoutParams) message.getLayoutParams();
|
||||
paramsMessage = new LinearLayout.LayoutParams(paramsMessage.width, paramsMessage.height,0.0f);
|
||||
message.setLayoutParams(paramsMessage);
|
||||
message.setCompoundDrawablePadding(message.getPaddingLeft());
|
||||
int textSize = (int) message.getTextSize();
|
||||
Log.e("Jet","textSize:"+textSize);
|
||||
if(leftDrawable!=null){
|
||||
leftDrawable.setBounds(0,0,textSize,textSize);
|
||||
}
|
||||
if(rightDrawable!=null){
|
||||
rightDrawable.setBounds(0,0,textSize,textSize);
|
||||
}
|
||||
message.setCompoundDrawables(leftDrawable,null,rightDrawable,null);
|
||||
LinearLayout.LayoutParams paramsSpace = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT,1.0f);
|
||||
((Snackbar.SnackbarLayout)mSnackbar.getView()).addView(new Space(mSnackbar.getView().getContext()),1,paramsSpace);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置TextView(@+id/snackbar_text)中文字的对齐方式 居中
|
||||
* @return
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
public SnackbarUtils messageCenter(){
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
|
||||
TextView message = (TextView) mSnackbar.getView().findViewById(R.id.snackbar_text);
|
||||
//View.setTextAlignment需要SDK>=17
|
||||
message.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY);
|
||||
message.setGravity(Gravity.CENTER);
|
||||
}
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置TextView(@+id/snackbar_text)中文字的对齐方式 居右
|
||||
* @return
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
public SnackbarUtils messageRight(){
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
|
||||
TextView message = (TextView) mSnackbar.getView().findViewById(R.id.snackbar_text);
|
||||
//View.setTextAlignment需要SDK>=17
|
||||
message.setTextAlignment(View.TEXT_ALIGNMENT_GRAVITY);
|
||||
message.setGravity(Gravity.CENTER_VERTICAL|Gravity.RIGHT);
|
||||
}
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向Snackbar布局中添加View(Google不建议,复杂的布局应该使用DialogFragment进行展示)
|
||||
* @param layoutId 要添加的View的布局文件ID
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public SnackbarUtils addView(int layoutId, int index) {
|
||||
//加载布局文件新建View
|
||||
View addView = LayoutInflater.from(mSnackbar.getView().getContext()).inflate(layoutId,null);
|
||||
return addView(addView,index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向Snackbar布局中添加View(Google不建议,复杂的布局应该使用DialogFragment进行展示)
|
||||
* @param addView
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public SnackbarUtils addView(View addView, int index) {
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);//设置新建布局参数
|
||||
//设置新建View在Snackbar内垂直居中显示
|
||||
params.gravity= Gravity.CENTER_VERTICAL;
|
||||
addView.setLayoutParams(params);
|
||||
((Snackbar.SnackbarLayout)mSnackbar.getView()).addView(addView,index);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Snackbar布局的外边距
|
||||
* 注:经试验发现,调用margins后再调用 gravityFrameLayout,则margins无效.
|
||||
* 为保证margins有效,应该先调用 gravityFrameLayout,在 show() 之前调用 margins
|
||||
* @param margin
|
||||
* @return
|
||||
*/
|
||||
public SnackbarUtils margins(int margin){
|
||||
return margins(margin,margin,margin,margin);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Snackbar布局的外边距
|
||||
* 注:经试验发现,调用margins后再调用 gravityFrameLayout,则margins无效.
|
||||
* 为保证margins有效,应该先调用 gravityFrameLayout,在 show() 之前调用 margins
|
||||
* @param left
|
||||
* @param top
|
||||
* @param right
|
||||
* @param bottom
|
||||
* @return
|
||||
*/
|
||||
public SnackbarUtils margins(int left, int top, int right, int bottom){
|
||||
ViewGroup.LayoutParams params = mSnackbar.getView().getLayoutParams();
|
||||
((ViewGroup.MarginLayoutParams) params).setMargins(left,top,right,bottom);
|
||||
mSnackbar.getView().setLayoutParams(params);
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 经试验发现:
|
||||
* 执行过{@link SnackbarUtils#backColor(int)}后:background instanceof ColorDrawable
|
||||
* 未执行过{@link SnackbarUtils#backColor(int)}:background instanceof GradientDrawable
|
||||
* @return
|
||||
*/
|
||||
/*
|
||||
public SnackbarUtils radius(){
|
||||
Drawable background = mSnackbar.getView().getBackground();
|
||||
if(background instanceof GradientDrawable){
|
||||
Log.e("Jet","radius():GradientDrawable");
|
||||
}
|
||||
if(background instanceof ColorDrawable){
|
||||
Log.e("Jet","radius():ColorDrawable");
|
||||
}
|
||||
if(background instanceof StateListDrawable){
|
||||
Log.e("Jet","radius():StateListDrawable");
|
||||
}
|
||||
Log.e("Jet","radius()background:"+background.getClass().getSimpleName());
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* 通过SnackBar现在的背景,获取其设置圆角值时候所需的GradientDrawable实例
|
||||
* @param backgroundOri
|
||||
* @return
|
||||
*/
|
||||
private GradientDrawable getRadiusDrawable(Drawable backgroundOri){
|
||||
GradientDrawable background = null;
|
||||
if(backgroundOri instanceof GradientDrawable){
|
||||
background = (GradientDrawable) backgroundOri;
|
||||
}else if(backgroundOri instanceof ColorDrawable){
|
||||
int backgroundColor = ((ColorDrawable)backgroundOri).getColor();
|
||||
background = new GradientDrawable();
|
||||
background.setColor(backgroundColor);
|
||||
}else {
|
||||
}
|
||||
return background;
|
||||
}
|
||||
/**
|
||||
* 设置Snackbar布局的圆角半径值
|
||||
* @param radius 圆角半径
|
||||
* @return
|
||||
*/
|
||||
public SnackbarUtils radius(float radius){
|
||||
//将要设置给mSnackbar的背景
|
||||
GradientDrawable background = getRadiusDrawable(mSnackbar.getView().getBackground());
|
||||
if(background != null){
|
||||
radius = radius<=0?12:radius;
|
||||
background.setCornerRadius(radius);
|
||||
mSnackbar.getView().setBackgroundDrawable(background);
|
||||
}
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Snackbar布局的圆角半径值及边框颜色及边框宽度
|
||||
* @param radius
|
||||
* @param strokeWidth
|
||||
* @param strokeColor
|
||||
* @return
|
||||
*/
|
||||
public SnackbarUtils radius(int radius, int strokeWidth, @ColorInt int strokeColor){
|
||||
//将要设置给mSnackbar的背景
|
||||
GradientDrawable background = getRadiusDrawable(mSnackbar.getView().getBackground());
|
||||
if(background != null){
|
||||
radius = radius<=0?12:radius;
|
||||
strokeWidth = strokeWidth<=0?1:(strokeWidth>=mSnackbar.getView().findViewById(R.id.snackbar_text).getPaddingTop()?2:strokeWidth);
|
||||
background.setCornerRadius(radius);
|
||||
background.setStroke(strokeWidth,strokeColor);
|
||||
mSnackbar.getView().setBackgroundDrawable(background);
|
||||
}
|
||||
return new SnackbarUtils(mSnackbar);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 显示 mSnackbar
|
||||
*/
|
||||
public void show(){
|
||||
if(mSnackbar!=null){
|
||||
mSnackbar.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
117
basicLib/src/main/java/com/azhon/basic/utils/TimeUtil.java
Normal file
@@ -0,0 +1,117 @@
|
||||
package com.azhon.basic.utils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 创建者: xuhuixiang
|
||||
* 创建时间: 2017/6/20 on 15:12
|
||||
* 描述: TODO 时间格式化
|
||||
*/
|
||||
public class TimeUtil {
|
||||
|
||||
/**
|
||||
* 将时间戳转换为时间
|
||||
*
|
||||
* @param time
|
||||
* @return
|
||||
*/
|
||||
public static String yyyyMMdd(long time) {
|
||||
if (time == 0) {
|
||||
return "";
|
||||
}
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Date date = new Date(time);
|
||||
return format.format(date);
|
||||
}
|
||||
|
||||
/***
|
||||
* 时间戳转换为时间
|
||||
*
|
||||
* @param time
|
||||
* @return
|
||||
*/
|
||||
public static String HHmmss(long time) {
|
||||
if (time == 0) {
|
||||
return "";
|
||||
}
|
||||
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
|
||||
Date date = new Date(time);
|
||||
return format.format(date);
|
||||
}
|
||||
|
||||
/***
|
||||
* 时间戳转换为时间
|
||||
*
|
||||
* @param time
|
||||
* @return
|
||||
*/
|
||||
public static String yyyyMMddHHmm(long time) {
|
||||
if (time == 0) {
|
||||
return "";
|
||||
}
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
Date date = new Date(time);
|
||||
return format.format(date);
|
||||
}
|
||||
|
||||
/***
|
||||
* 时间戳转换为时间
|
||||
*
|
||||
* @param time
|
||||
* @return
|
||||
*/
|
||||
public static String yyyyMMddHHmmss(long time) {
|
||||
if (time == 0) {
|
||||
return "";
|
||||
}
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
Date date = new Date(time);
|
||||
return format.format(date);
|
||||
}
|
||||
|
||||
/***
|
||||
* 时间戳转换为时间
|
||||
*
|
||||
* @param time
|
||||
* @return
|
||||
*/
|
||||
public static String MMddHHmm(long time) {
|
||||
if (time == 0) {
|
||||
return "";
|
||||
}
|
||||
SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm");
|
||||
Date date = new Date(time);
|
||||
return format.format(date);
|
||||
}
|
||||
|
||||
/***
|
||||
* 时间戳转换为时间
|
||||
*
|
||||
* @param time
|
||||
* @return
|
||||
*/
|
||||
public static String MMddHHmmss(long time) {
|
||||
if (time == 0) {
|
||||
return "";
|
||||
}
|
||||
SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss");
|
||||
Date date = new Date(time);
|
||||
return format.format(date);
|
||||
}
|
||||
|
||||
/***
|
||||
* 时间戳转换为时间
|
||||
*
|
||||
* @param time
|
||||
* @return
|
||||
*/
|
||||
public static String byPattern(long time, String pattern) {
|
||||
if (time == 0) {
|
||||
return "";
|
||||
}
|
||||
SimpleDateFormat format = new SimpleDateFormat(pattern);
|
||||
Date date = new Date(time);
|
||||
return format.format(date);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.azhon.basic.view;
|
||||
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import com.azhon.basic.R;
|
||||
import com.azhon.basic.databinding.DialogLoadingBinding;
|
||||
import com.azhon.basic.utils.DensityUtil;
|
||||
|
||||
/**
|
||||
* 项目名: TODO-MVVM
|
||||
* 包名 com.azhon.basic.view
|
||||
* 文件名: LoadingDialog
|
||||
* 创建时间: 2019-03-29 on 11:30
|
||||
* 描述: TODO 等待对话框
|
||||
*
|
||||
* @author xuhuixiang
|
||||
*/
|
||||
|
||||
public class LoadingDialog extends Dialog {
|
||||
|
||||
private DialogLoadingBinding binding;
|
||||
|
||||
|
||||
public LoadingDialog(@NonNull Context context) {
|
||||
super(context, R.style.LoadingDialog);
|
||||
setCanceledOnTouchOutside(false);
|
||||
binding = DataBindingUtil.inflate(LayoutInflater.from(context),
|
||||
R.layout.dialog_loading, null, false);
|
||||
setContentView(binding.getRoot());
|
||||
Window window = getWindow();
|
||||
WindowManager.LayoutParams lp = window.getAttributes();
|
||||
lp.width = DensityUtil.dip2px(context, 150);
|
||||
lp.height = DensityUtil.dip2px(context, 110);
|
||||
lp.gravity = Gravity.CENTER;
|
||||
window.setAttributes(lp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置等待提示信息
|
||||
*/
|
||||
public void setLoadingMsg(String msg) {
|
||||
if (TextUtils.isEmpty(msg)) {
|
||||
return;
|
||||
}
|
||||
binding.tvMsg.setText(msg);
|
||||
}
|
||||
}
|
||||
5
basicLib/src/main/res/drawable/dialog_loading_bg.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#EEEEEE"/>
|
||||
<corners android:radius="6dp" />
|
||||
</shape>
|
||||
33
basicLib/src/main/res/layout/dialog_loading.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<data>
|
||||
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/dialog_loading_bg"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminateTint="#2899FC"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_msg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:text="加载中..."
|
||||
android:textColor="#333333"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
3
basicLib/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="framework_name">basicLib</string>
|
||||
</resources>
|
||||
21
basicLib/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!--对话框的样式-->
|
||||
<style name="LoadingDialog">
|
||||
<!--对话框背景 -->
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<!--边框 -->
|
||||
<item name="android:windowFrame">@null</item>
|
||||
<!--没有标题 -->
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<!-- 是否浮现在Activity之上 -->
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<!--背景透明 -->
|
||||
<item name="android:windowIsTranslucent">false</item>
|
||||
<!-- 是否有覆盖 -->
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<!--背景变暗-->
|
||||
<item name="android:backgroundDimEnabled">false</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
69
build.gradle
Normal file
@@ -0,0 +1,69 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
apply plugin: 'io.codearte.nexus-staging'
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.5.20'
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://artifact.bytedance.com/repository/pangle'
|
||||
}
|
||||
maven { url'https://maven.aliyun.com/nexus/content/groups/public/' }
|
||||
maven { url'https://maven.aliyun.com/nexus/content/repositories/jcenter'}
|
||||
maven { url "https://jitpack.io" }
|
||||
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
|
||||
google()
|
||||
// maven { url 'https://dl.bintray.com/umsdk/release' }
|
||||
maven { url "https://plugins.gradle.org/m2" }
|
||||
// google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
maven { url 'https://repo1.maven.org/maven2/' }
|
||||
maven { url "https://maven.aliyun.com/repository/public" }
|
||||
|
||||
// maven { url "https://plugins.gradle.org/m2" }
|
||||
|
||||
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.0.1'
|
||||
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0' // 添加GreenDao插件
|
||||
classpath 'com.novoda:bintray-release:0.6.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "com.github.dcendents:android-maven-gradle-plugin:2.0"
|
||||
classpath 'io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.22.0'
|
||||
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://artifact.bytedance.com/repository/pangle'
|
||||
}
|
||||
maven { url'https://maven.aliyun.com/nexus/content/groups/public/' }
|
||||
maven { url'https://maven.aliyun.com/nexus/content/repositories/jcenter'}
|
||||
maven { url "https://jitpack.io" }
|
||||
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
|
||||
google()
|
||||
// maven { url 'https://dl.bintray.com/umsdk/release' }
|
||||
// google()
|
||||
// jcenter()
|
||||
maven { url "https://jitpack.io" }
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
maven { url 'https://repo1.maven.org/maven2/' }
|
||||
maven { url "https://maven.aliyun.com/repository/public" }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
28
gradle.properties
Normal file
@@ -0,0 +1,28 @@
|
||||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx512m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
android.injected.testOnly = false
|
||||
android.enableR8=false
|
||||
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Tue Nov 17 14:36:01 CST 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
||||
1
matisse/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
70
matisse/build.gradle
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'checkstyle'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 30
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
implementation "androidx.appcompat:appcompat:1.1.0"
|
||||
implementation "androidx.annotation:annotation:1.1.0"
|
||||
implementation "androidx.recyclerview:recyclerview:1.0.0"
|
||||
implementation 'it.sephiroth.android.library.imagezoom:library:1.0.4'
|
||||
|
||||
compileOnly 'com.github.bumptech.glide:glide:4.9.0'
|
||||
compileOnly 'com.squareup.picasso:picasso:2.5.2'
|
||||
}
|
||||
|
||||
|
||||
|
||||
task javadoc(type: Javadoc) {
|
||||
options.encoding = "utf-8"
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = '7.6.1'
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc) {
|
||||
options.addStringOption('Xdoclint:none', '-quiet')
|
||||
options.addStringOption('encoding', 'UTF-8')
|
||||
}
|
||||
|
||||
task checkstyle(type:Checkstyle) {
|
||||
description 'Runs Checkstyle inspection against matisse sourcesets.'
|
||||
group = 'Code Quality'
|
||||
configFile rootProject.file('checkstyle.xml')
|
||||
ignoreFailures = false
|
||||
showViolations true
|
||||
classpath = files()
|
||||
source 'src/main/java'
|
||||
}
|
||||
0
matisse/gradle.properties
Normal file
19
matisse/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Library/android-sdk-macosx/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the ProGuard
|
||||
# include property in project.properties.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# 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 *;
|
||||
#}
|
||||
|
||||
-dontwarn com.squareup.okhttp.**
|
||||
30
matisse/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.zhihu.matisse">
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
|
||||
<application>
|
||||
<activity android:name="com.zhihu.matisse.ui.MatisseActivity"
|
||||
android:theme="@style/AppThemeLoginM"/>
|
||||
<activity android:name="com.zhihu.matisse.internal.ui.AlbumPreviewActivity"/>
|
||||
<activity android:name="com.zhihu.matisse.internal.ui.SelectedPreviewActivity"/>
|
||||
</application>
|
||||
</manifest>
|
||||
161
matisse/src/main/java/com/zhihu/matisse/Matisse.java
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.zhihu.matisse.ui.MatisseActivity;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Entry for Matisse's media selection.
|
||||
*/
|
||||
public final class Matisse {
|
||||
|
||||
private final WeakReference<Activity> mContext;
|
||||
private final WeakReference<Fragment> mFragment;
|
||||
|
||||
private Matisse(Activity activity) {
|
||||
this(activity, null);
|
||||
}
|
||||
|
||||
private Matisse(Fragment fragment) {
|
||||
this(fragment.getActivity(), fragment);
|
||||
}
|
||||
|
||||
private Matisse(Activity activity, Fragment fragment) {
|
||||
mContext = new WeakReference<>(activity);
|
||||
mFragment = new WeakReference<>(fragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start Matisse from an Activity.
|
||||
* <p>
|
||||
* This Activity's {@link Activity#onActivityResult(int, int, Intent)} will be called when user
|
||||
* finishes selecting.
|
||||
*
|
||||
* @param activity Activity instance.
|
||||
* @return Matisse instance.
|
||||
*/
|
||||
public static Matisse from(Activity activity) {
|
||||
return new Matisse(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start Matisse from a Fragment.
|
||||
* <p>
|
||||
* This Fragment's {@link Fragment#onActivityResult(int, int, Intent)} will be called when user
|
||||
* finishes selecting.
|
||||
*
|
||||
* @param fragment Fragment instance.
|
||||
* @return Matisse instance.
|
||||
*/
|
||||
public static Matisse from(Fragment fragment) {
|
||||
return new Matisse(fragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain user selected media' {@link Uri} list in the starting Activity or Fragment.
|
||||
*
|
||||
* @param data Intent passed by {@link Activity#onActivityResult(int, int, Intent)} or
|
||||
* {@link Fragment#onActivityResult(int, int, Intent)}.
|
||||
* @return User selected media' {@link Uri} list.
|
||||
*/
|
||||
public static List<Uri> obtainResult(Intent data) {
|
||||
return data.getParcelableArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain user selected media path list in the starting Activity or Fragment.
|
||||
*
|
||||
* @param data Intent passed by {@link Activity#onActivityResult(int, int, Intent)} or
|
||||
* {@link Fragment#onActivityResult(int, int, Intent)}.
|
||||
* @return User selected media path list.
|
||||
*/
|
||||
public static boolean obtainPathTypeResult(Intent data) {
|
||||
return data.getBooleanExtra(MatisseActivity.EXTRA_RESULT_SELECTION_TYPE,false);
|
||||
}
|
||||
/**
|
||||
* Obtain user selected media path list in the starting Activity or Fragment.
|
||||
*
|
||||
* @param data Intent passed by {@link Activity#onActivityResult(int, int, Intent)} or
|
||||
* {@link Fragment#onActivityResult(int, int, Intent)}.
|
||||
* @return User selected media path list.
|
||||
*/
|
||||
public static List<String> obtainPathResult(Intent data) {
|
||||
return data.getStringArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION_PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain state whether user decide to use selected media in original
|
||||
*
|
||||
* @param data Intent passed by {@link Activity#onActivityResult(int, int, Intent)} or
|
||||
* {@link Fragment#onActivityResult(int, int, Intent)}.
|
||||
* @return Whether use original photo
|
||||
*/
|
||||
public static boolean obtainOriginalState(Intent data) {
|
||||
return data.getBooleanExtra(MatisseActivity.EXTRA_RESULT_ORIGINAL_ENABLE, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* MIME types the selection constrains on.
|
||||
* <p>
|
||||
* Types not included in the set will still be shown in the grid but can't be chosen.
|
||||
*
|
||||
* @param mimeTypes MIME types set user can choose from.
|
||||
* @return {@link SelectionCreator} to build select specifications.
|
||||
* @see MimeType
|
||||
* @see SelectionCreator
|
||||
*/
|
||||
public SelectionCreator choose(Set<MimeType> mimeTypes) {
|
||||
return this.choose(mimeTypes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* MIME types the selection constrains on.
|
||||
* <p>
|
||||
* Types not included in the set will still be shown in the grid but can't be chosen.
|
||||
*
|
||||
* @param mimeTypes MIME types set user can choose from.
|
||||
* @param mediaTypeExclusive Whether can choose images and videos at the same time during one single choosing
|
||||
* process. true corresponds to not being able to choose images and videos at the same
|
||||
* time, and false corresponds to being able to do this.
|
||||
* @return {@link SelectionCreator} to build select specifications.
|
||||
* @see MimeType
|
||||
* @see SelectionCreator
|
||||
*/
|
||||
public SelectionCreator choose(Set<MimeType> mimeTypes, boolean mediaTypeExclusive) {
|
||||
return new SelectionCreator(this, mimeTypes, mediaTypeExclusive);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Activity getActivity() {
|
||||
return mContext.get();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Fragment getFragment() {
|
||||
return mFragment != null ? mFragment.get() : null;
|
||||
}
|
||||
|
||||
}
|
||||
178
matisse/src/main/java/com/zhihu/matisse/MimeType.java
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (C) 2014 nohana, Inc.
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.collection.ArraySet;
|
||||
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.zhihu.matisse.internal.utils.PhotoMetadataUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* MIME Type enumeration to restrict selectable media on the selection activity. Matisse only supports images and
|
||||
* videos.
|
||||
* <p>
|
||||
* Good example of mime types Android supports:
|
||||
* https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/MediaFile.java
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public enum MimeType {
|
||||
|
||||
// ============== images ==============
|
||||
JPEG("image/jpeg", arraySetOf(
|
||||
"jpg",
|
||||
"jpeg"
|
||||
)),
|
||||
PNG("image/png", arraySetOf(
|
||||
"png"
|
||||
)),
|
||||
GIF("image/gif", arraySetOf(
|
||||
"gif"
|
||||
)),
|
||||
BMP("image/x-ms-bmp", arraySetOf(
|
||||
"bmp"
|
||||
)),
|
||||
WEBP("image/webp", arraySetOf(
|
||||
"webp"
|
||||
)),
|
||||
|
||||
// ============== videos ==============
|
||||
MPEG("video/mpeg", arraySetOf(
|
||||
"mpeg",
|
||||
"mpg"
|
||||
)),
|
||||
MP4("video/mp4", arraySetOf(
|
||||
"mp4",
|
||||
"m4v"
|
||||
)),
|
||||
QUICKTIME("video/quicktime", arraySetOf(
|
||||
"mov"
|
||||
)),
|
||||
THREEGPP("video/3gpp", arraySetOf(
|
||||
"3gp",
|
||||
"3gpp"
|
||||
)),
|
||||
THREEGPP2("video/3gpp2", arraySetOf(
|
||||
"3g2",
|
||||
"3gpp2"
|
||||
)),
|
||||
MKV("video/x-matroska", arraySetOf(
|
||||
"mkv"
|
||||
)),
|
||||
WEBM("video/webm", arraySetOf(
|
||||
"webm"
|
||||
)),
|
||||
TS("video/mp2ts", arraySetOf(
|
||||
"ts"
|
||||
)),
|
||||
AVI("video/avi", arraySetOf(
|
||||
"avi"
|
||||
));
|
||||
|
||||
private final String mMimeTypeName;
|
||||
private final Set<String> mExtensions;
|
||||
|
||||
MimeType(String mimeTypeName, Set<String> extensions) {
|
||||
mMimeTypeName = mimeTypeName;
|
||||
mExtensions = extensions;
|
||||
}
|
||||
|
||||
public static Set<MimeType> ofAll() {
|
||||
return EnumSet.allOf(MimeType.class);
|
||||
}
|
||||
|
||||
public static Set<MimeType> of(MimeType type, MimeType... rest) {
|
||||
return EnumSet.of(type, rest);
|
||||
}
|
||||
|
||||
public static Set<MimeType> ofImage() {
|
||||
return EnumSet.of(JPEG, PNG, GIF, BMP, WEBP);
|
||||
}
|
||||
|
||||
public static Set<MimeType> ofImage(boolean onlyGif) {
|
||||
return EnumSet.of(GIF);
|
||||
}
|
||||
|
||||
public static Set<MimeType> ofGif() {
|
||||
return ofImage(true);
|
||||
}
|
||||
|
||||
public static Set<MimeType> ofVideo() {
|
||||
return EnumSet.of(MPEG, MP4, QUICKTIME, THREEGPP, THREEGPP2, MKV, WEBM, TS, AVI);
|
||||
}
|
||||
|
||||
public static boolean isImage(String mimeType) {
|
||||
if (mimeType == null) return false;
|
||||
return mimeType.startsWith("image");
|
||||
}
|
||||
|
||||
public static boolean isVideo(String mimeType) {
|
||||
if (mimeType == null) return false;
|
||||
return mimeType.startsWith("video");
|
||||
}
|
||||
|
||||
public static boolean isGif(String mimeType) {
|
||||
if (mimeType == null) return false;
|
||||
return mimeType.equals(MimeType.GIF.toString());
|
||||
}
|
||||
|
||||
private static Set<String> arraySetOf(String... suffixes) {
|
||||
return new ArraySet<>(Arrays.asList(suffixes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mMimeTypeName;
|
||||
}
|
||||
|
||||
public boolean checkType(ContentResolver resolver, Uri uri) {
|
||||
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||
if (uri == null) {
|
||||
return false;
|
||||
}
|
||||
String type = map.getExtensionFromMimeType(resolver.getType(uri));
|
||||
String path = null;
|
||||
// lazy load the path and prevent resolve for multiple times
|
||||
boolean pathParsed = false;
|
||||
for (String extension : mExtensions) {
|
||||
if (extension.equals(type)) {
|
||||
return true;
|
||||
}
|
||||
if (!pathParsed) {
|
||||
// we only resolve the path for one time
|
||||
path = PhotoMetadataUtils.getPath(resolver, uri);
|
||||
if (!TextUtils.isEmpty(path)) {
|
||||
path = path.toLowerCase(Locale.US);
|
||||
}
|
||||
pathParsed = true;
|
||||
}
|
||||
if (path != null && path.endsWith(extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
377
matisse/src/main/java/com/zhihu/matisse/SelectionCreator.java
Normal file
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* Copyright (C) 2014 nohana, Inc.
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.zhihu.matisse.engine.ImageEngine;
|
||||
import com.zhihu.matisse.filter.Filter;
|
||||
import com.zhihu.matisse.internal.entity.CaptureStrategy;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
import com.zhihu.matisse.listener.OnCheckedListener;
|
||||
import com.zhihu.matisse.listener.OnSelectedListener;
|
||||
import com.zhihu.matisse.ui.MatisseActivity;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_USER;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT;
|
||||
|
||||
/**
|
||||
* Fluent API for building media select specification.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public final class SelectionCreator {
|
||||
private final Matisse mMatisse;
|
||||
private final SelectionSpec mSelectionSpec;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
|
||||
@IntDef({
|
||||
SCREEN_ORIENTATION_UNSPECIFIED,
|
||||
SCREEN_ORIENTATION_LANDSCAPE,
|
||||
SCREEN_ORIENTATION_PORTRAIT,
|
||||
SCREEN_ORIENTATION_USER,
|
||||
SCREEN_ORIENTATION_BEHIND,
|
||||
SCREEN_ORIENTATION_SENSOR,
|
||||
SCREEN_ORIENTATION_NOSENSOR,
|
||||
SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
|
||||
SCREEN_ORIENTATION_SENSOR_PORTRAIT,
|
||||
SCREEN_ORIENTATION_REVERSE_LANDSCAPE,
|
||||
SCREEN_ORIENTATION_REVERSE_PORTRAIT,
|
||||
SCREEN_ORIENTATION_FULL_SENSOR,
|
||||
SCREEN_ORIENTATION_USER_LANDSCAPE,
|
||||
SCREEN_ORIENTATION_USER_PORTRAIT,
|
||||
SCREEN_ORIENTATION_FULL_USER,
|
||||
SCREEN_ORIENTATION_LOCKED
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface ScreenOrientation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new specification builder on the context.
|
||||
*
|
||||
* @param matisse a requester context wrapper.
|
||||
* @param mimeTypes MIME type set to select.
|
||||
*/
|
||||
SelectionCreator(Matisse matisse, @NonNull Set<MimeType> mimeTypes, boolean mediaTypeExclusive) {
|
||||
mMatisse = matisse;
|
||||
mSelectionSpec = SelectionSpec.getCleanInstance();
|
||||
mSelectionSpec.mimeTypeSet = mimeTypes;
|
||||
mSelectionSpec.mediaTypeExclusive = mediaTypeExclusive;
|
||||
mSelectionSpec.orientation = SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to show only one media type if choosing medias are only images or videos.
|
||||
*
|
||||
* @param showSingleMediaType whether to show only one media type, either images or videos.
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
* @see SelectionSpec#onlyShowImages()
|
||||
* @see SelectionSpec#onlyShowVideos()
|
||||
*/
|
||||
public SelectionCreator showSingleMediaType(boolean showSingleMediaType) {
|
||||
mSelectionSpec.showSingleMediaType = showSingleMediaType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme for media selecting Activity.
|
||||
* <p>
|
||||
* There are two built-in themes:
|
||||
* 1. com.zhihu.matisse.R.style.Matisse_Zhihu;
|
||||
* 2. com.zhihu.matisse.R.style.Matisse_Dracula
|
||||
* you can define a custom theme derived from the above ones or other themes.
|
||||
*
|
||||
* @param themeId theme resource id. Default value is com.zhihu.matisse.R.style.Matisse_Zhihu.
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator theme(@StyleRes int themeId) {
|
||||
mSelectionSpec.themeId = themeId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a auto-increased number or a check mark when user select media.
|
||||
*
|
||||
* @param countable true for a auto-increased number from 1, false for a check mark. Default
|
||||
* value is false.
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator countable(boolean countable) {
|
||||
mSelectionSpec.countable = countable;
|
||||
return this;
|
||||
}
|
||||
public static int fistMaxNumber = -100;
|
||||
/**
|
||||
* Maximum selectable count.
|
||||
*
|
||||
* @param maxSelectable Maximum selectable count. Default value is 1.
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator maxSelectable(int maxSelectable) {
|
||||
if (maxSelectable < 1)
|
||||
throw new IllegalArgumentException("maxSelectable must be greater than or equal to one");
|
||||
if (mSelectionSpec.maxImageSelectable > 0 || mSelectionSpec.maxVideoSelectable > 0)
|
||||
throw new IllegalStateException("already set maxImageSelectable and maxVideoSelectable");
|
||||
mSelectionSpec.maxSelectable = maxSelectable;
|
||||
|
||||
if(fistMaxNumber == -100){
|
||||
fistMaxNumber = maxSelectable;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only useful when {@link SelectionSpec#mediaTypeExclusive} set true and you want to set different maximum
|
||||
* selectable files for image and video media types.
|
||||
*
|
||||
* @param maxImageSelectable Maximum selectable count for image.
|
||||
* @param maxVideoSelectable Maximum selectable count for video.
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator maxSelectablePerMediaType(int maxImageSelectable, int maxVideoSelectable) {
|
||||
if (maxImageSelectable < 1 || maxVideoSelectable < 1)
|
||||
throw new IllegalArgumentException(("max selectable must be greater than or equal to one"));
|
||||
mSelectionSpec.maxSelectable = -1;
|
||||
mSelectionSpec.maxImageSelectable = maxImageSelectable;
|
||||
mSelectionSpec.maxVideoSelectable = maxVideoSelectable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add filter to filter each selecting item.
|
||||
*
|
||||
* @param filter {@link Filter}
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator addFilter(@NonNull Filter filter) {
|
||||
if (mSelectionSpec.filters == null) {
|
||||
mSelectionSpec.filters = new ArrayList<>();
|
||||
}
|
||||
if (filter == null) throw new IllegalArgumentException("filter cannot be null");
|
||||
mSelectionSpec.filters.add(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the photo capturing is enabled or not on the media grid view.
|
||||
* <p>
|
||||
* If this value is set true, photo capturing entry will appear only on All Media's page.
|
||||
*
|
||||
* @param enable Whether to enable capturing or not. Default value is false;
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator capture(boolean enable) {
|
||||
mSelectionSpec.capture = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a original photo check options.Let users decide whether use original photo after select
|
||||
*
|
||||
* @param enable Whether to enable original photo or not
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator originalEnable(boolean enable) {
|
||||
mSelectionSpec.originalable = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines Whether to hide top and bottom toolbar in PreView mode ,when user tap the picture
|
||||
* @param enable
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator autoHideToolbarOnSingleTap(boolean enable) {
|
||||
mSelectionSpec.autoHideToobar = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum original size,the unit is MB. Only useful when {link@originalEnable} set true
|
||||
*
|
||||
* @param size Maximum original size. Default value is Integer.MAX_VALUE
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator maxOriginalSize(int size) {
|
||||
mSelectionSpec.originalMaxSize = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture strategy provided for the location to save photos including internal and external
|
||||
* storage and also a authority for {@link androidx.core.content.FileProvider}.
|
||||
*
|
||||
* @param captureStrategy {@link CaptureStrategy}, needed only when capturing is enabled.
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator captureStrategy(CaptureStrategy captureStrategy) {
|
||||
mSelectionSpec.captureStrategy = captureStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the desired orientation of this activity.
|
||||
*
|
||||
* @param orientation An orientation constant as used in {@link ScreenOrientation}.
|
||||
* Default value is {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_PORTRAIT}.
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
* @see Activity#setRequestedOrientation(int)
|
||||
*/
|
||||
public SelectionCreator restrictOrientation(@ScreenOrientation int orientation) {
|
||||
mSelectionSpec.orientation = orientation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a fixed span count for the media grid. Same for different screen orientations.
|
||||
* <p>
|
||||
* This will be ignored when {@link #gridExpectedSize(int)} is set.
|
||||
*
|
||||
* @param spanCount Requested span count.
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator spanCount(int spanCount) {
|
||||
if (spanCount < 1) throw new IllegalArgumentException("spanCount cannot be less than 1");
|
||||
mSelectionSpec.spanCount = spanCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set expected size for media grid to adapt to different screen sizes. This won't necessarily
|
||||
* be applied cause the media grid should fill the view container. The measured media grid's
|
||||
* size will be as close to this value as possible.
|
||||
*
|
||||
* @param size Expected media grid size in pixel.
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator gridExpectedSize(int size) {
|
||||
mSelectionSpec.gridExpectedSize = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Photo thumbnail's scale compared to the View's size. It should be a float value in (0.0,
|
||||
* 1.0].
|
||||
*
|
||||
* @param scale Thumbnail's scale in (0.0, 1.0]. Default value is 0.5.
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator thumbnailScale(float scale) {
|
||||
if (scale <= 0f || scale > 1f)
|
||||
throw new IllegalArgumentException("Thumbnail scale must be between (0.0, 1.0]");
|
||||
mSelectionSpec.thumbnailScale = scale;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an image engine.
|
||||
* <p>
|
||||
* There are two built-in image engines:
|
||||
* 1. {@link com.zhihu.matisse.engine.impl.GlideEngine}
|
||||
* 2. {@link com.zhihu.matisse.engine.impl.PicassoEngine}
|
||||
* And you can implement your own image engine.
|
||||
*
|
||||
* @param imageEngine {@link ImageEngine}
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator imageEngine(ImageEngine imageEngine) {
|
||||
mSelectionSpec.imageEngine = imageEngine;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set listener for callback immediately when user select or unselect something.
|
||||
* <p>
|
||||
* It's a redundant API with {@link Matisse#obtainResult(Intent)},
|
||||
* we only suggest you to use this API when you need to do something immediately.
|
||||
*
|
||||
* @param listener {@link OnSelectedListener}
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
@NonNull
|
||||
public SelectionCreator setOnSelectedListener(@Nullable OnSelectedListener listener) {
|
||||
mSelectionSpec.onSelectedListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set listener for callback immediately when user check or uncheck original.
|
||||
*
|
||||
* @param listener {@link OnSelectedListener}
|
||||
* @return {@link SelectionCreator} for fluent API.
|
||||
*/
|
||||
public SelectionCreator setOnCheckedListener(@Nullable OnCheckedListener listener) {
|
||||
mSelectionSpec.onCheckedListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start to select media and wait for result.
|
||||
*
|
||||
* @param requestCode Identity of the request Activity or Fragment.
|
||||
*/
|
||||
public void forResult(int requestCode) {
|
||||
Activity activity = mMatisse.getActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(activity, MatisseActivity.class);
|
||||
|
||||
Fragment fragment = mMatisse.getFragment();
|
||||
if (fragment != null) {
|
||||
fragment.startActivityForResult(intent, requestCode);
|
||||
} else {
|
||||
activity.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
public SelectionCreator showPreview(boolean showPreview) {
|
||||
mSelectionSpec.showPreview = showPreview;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.engine;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.widget.ImageView;
|
||||
|
||||
/**
|
||||
* Image loader interface. There are predefined {@link com.zhihu.matisse.engine.impl.GlideEngine}
|
||||
* and {@link com.zhihu.matisse.engine.impl.PicassoEngine}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface ImageEngine {
|
||||
|
||||
/**
|
||||
* Load thumbnail of a static image resource.
|
||||
*
|
||||
* @param context Context
|
||||
* @param resize Desired size of the origin image
|
||||
* @param placeholder Placeholder drawable when image is not loaded yet
|
||||
* @param imageView ImageView widget
|
||||
* @param uri Uri of the loaded image
|
||||
*/
|
||||
void loadThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView, Uri uri);
|
||||
|
||||
/**
|
||||
* Load thumbnail of a gif image resource. You don't have to load an animated gif when it's only
|
||||
* a thumbnail tile.
|
||||
*
|
||||
* @param context Context
|
||||
* @param resize Desired size of the origin image
|
||||
* @param placeholder Placeholder drawable when image is not loaded yet
|
||||
* @param imageView ImageView widget
|
||||
* @param uri Uri of the loaded image
|
||||
*/
|
||||
void loadGifThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView, Uri uri);
|
||||
|
||||
/**
|
||||
* Load a static image resource.
|
||||
*
|
||||
* @param context Context
|
||||
* @param resizeX Desired x-size of the origin image
|
||||
* @param resizeY Desired y-size of the origin image
|
||||
* @param imageView ImageView widget
|
||||
* @param uri Uri of the loaded image
|
||||
*/
|
||||
void loadImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri);
|
||||
|
||||
/**
|
||||
* Load a gif image resource.
|
||||
*
|
||||
* @param context Context
|
||||
* @param resizeX Desired x-size of the origin image
|
||||
* @param resizeY Desired y-size of the origin image
|
||||
* @param imageView ImageView widget
|
||||
* @param uri Uri of the loaded image
|
||||
*/
|
||||
void loadGifImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri);
|
||||
|
||||
/**
|
||||
* Whether this implementation supports animated gif.
|
||||
* Just knowledge of it, convenient for users.
|
||||
*
|
||||
* @return true support animated gif, false do not support animated gif.
|
||||
*/
|
||||
boolean supportAnimatedGif();
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.engine.impl;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.Priority;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.zhihu.matisse.engine.ImageEngine;
|
||||
|
||||
/**
|
||||
* {@link ImageEngine} implementation using Glide.
|
||||
*/
|
||||
|
||||
public class GlideEngine implements ImageEngine {
|
||||
|
||||
@Override
|
||||
public void loadThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView, Uri uri) {
|
||||
Glide.with(context)
|
||||
.asBitmap() // some .jpeg files are actually gif
|
||||
.load(uri)
|
||||
.apply(new RequestOptions()
|
||||
.override(resize, resize)
|
||||
.placeholder(placeholder)
|
||||
.centerCrop())
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadGifThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView,
|
||||
Uri uri) {
|
||||
Glide.with(context)
|
||||
.asBitmap() // some .jpeg files are actually gif
|
||||
.load(uri)
|
||||
.apply(new RequestOptions()
|
||||
.override(resize, resize)
|
||||
.placeholder(placeholder)
|
||||
.centerCrop())
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri) {
|
||||
Glide.with(context)
|
||||
.load(uri)
|
||||
.apply(new RequestOptions()
|
||||
.override(resizeX, resizeY)
|
||||
.priority(Priority.HIGH)
|
||||
.fitCenter())
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadGifImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri) {
|
||||
Glide.with(context)
|
||||
.asGif()
|
||||
.load(uri)
|
||||
.apply(new RequestOptions()
|
||||
.override(resizeX, resizeY)
|
||||
.priority(Priority.HIGH)
|
||||
.fitCenter())
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportAnimatedGif() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.engine.impl;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.zhihu.matisse.engine.ImageEngine;
|
||||
|
||||
/**
|
||||
* {@link ImageEngine} implementation using Picasso.
|
||||
*/
|
||||
|
||||
public class PicassoEngine implements ImageEngine {
|
||||
|
||||
@Override
|
||||
public void loadThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView, Uri uri) {
|
||||
Picasso.with(context).load(uri).placeholder(placeholder)
|
||||
.resize(resize, resize)
|
||||
.centerCrop()
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadGifThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView,
|
||||
Uri uri) {
|
||||
loadThumbnail(context, resize, placeholder, imageView, uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri) {
|
||||
Picasso.with(context).load(uri).resize(resizeX, resizeY).priority(Picasso.Priority.HIGH)
|
||||
.centerInside().into(imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadGifImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri) {
|
||||
loadImage(context, resizeX, resizeY, imageView, uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportAnimatedGif() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
69
matisse/src/main/java/com/zhihu/matisse/filter/Filter.java
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.filter;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.zhihu.matisse.MimeType;
|
||||
import com.zhihu.matisse.SelectionCreator;
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.entity.IncapableCause;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Filter for choosing a {@link Item}. You can add multiple Filters through
|
||||
* {@link SelectionCreator#addFilter(Filter)}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class Filter {
|
||||
/**
|
||||
* Convenient constant for a minimum value.
|
||||
*/
|
||||
public static final int MIN = 0;
|
||||
/**
|
||||
* Convenient constant for a maximum value.
|
||||
*/
|
||||
public static final int MAX = Integer.MAX_VALUE;
|
||||
/**
|
||||
* Convenient constant for 1024.
|
||||
*/
|
||||
public static final int K = 1024;
|
||||
|
||||
/**
|
||||
* Against what mime types this filter applies.
|
||||
*/
|
||||
protected abstract Set<MimeType> constraintTypes();
|
||||
|
||||
/**
|
||||
* Invoked for filtering each item.
|
||||
*
|
||||
* @return null if selectable, {@link IncapableCause} if not selectable.
|
||||
*/
|
||||
public abstract IncapableCause filter(Context context, Item item);
|
||||
|
||||
/**
|
||||
* Whether an {@link Item} need filtering.
|
||||
*/
|
||||
protected boolean needFiltering(Context context, Item item) {
|
||||
for (MimeType type : constraintTypes()) {
|
||||
if (type.checkType(context.getContentResolver(), item.getContentUri())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2014 nohana, Inc.
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.entity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
import com.zhihu.matisse.internal.loader.AlbumLoader;
|
||||
|
||||
public class Album implements Parcelable {
|
||||
public static final Creator<Album> CREATOR = new Creator<Album>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Album createFromParcel(Parcel source) {
|
||||
return new Album(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Album[] newArray(int size) {
|
||||
return new Album[size];
|
||||
}
|
||||
};
|
||||
public static final String ALBUM_ID_ALL = String.valueOf(-1);
|
||||
public static final String ALBUM_NAME_ALL = "All";
|
||||
|
||||
private final String mId;
|
||||
private final Uri mCoverUri;
|
||||
private final String mDisplayName;
|
||||
private long mCount;
|
||||
|
||||
public Album(String id, Uri coverUri, String albumName, long count) {
|
||||
mId = id;
|
||||
mCoverUri = coverUri;
|
||||
mDisplayName = albumName;
|
||||
mCount = count;
|
||||
}
|
||||
|
||||
private Album(Parcel source) {
|
||||
mId = source.readString();
|
||||
mCoverUri = source.readParcelable(Uri.class.getClassLoader());
|
||||
mDisplayName = source.readString();
|
||||
mCount = source.readLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Album} entity from the {@link Cursor}.
|
||||
* This method is not responsible for managing cursor resource, such as close, iterate, and so on.
|
||||
*/
|
||||
public static Album valueOf(Cursor cursor) {
|
||||
String clumn = cursor.getString(cursor.getColumnIndex(AlbumLoader.COLUMN_URI));
|
||||
return new Album(
|
||||
cursor.getString(cursor.getColumnIndex("bucket_id")),
|
||||
Uri.parse(clumn != null ? clumn : ""),
|
||||
cursor.getString(cursor.getColumnIndex("bucket_display_name")),
|
||||
cursor.getLong(cursor.getColumnIndex(AlbumLoader.COLUMN_COUNT)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(mId);
|
||||
dest.writeParcelable(mCoverUri, 0);
|
||||
dest.writeString(mDisplayName);
|
||||
dest.writeLong(mCount);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public Uri getCoverUri() {
|
||||
return mCoverUri;
|
||||
}
|
||||
|
||||
public long getCount() {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
public void addCaptureCount() {
|
||||
mCount++;
|
||||
}
|
||||
|
||||
public String getDisplayName(Context context) {
|
||||
if (isAll()) {
|
||||
return context.getString(R.string.album_name_all);
|
||||
}
|
||||
return mDisplayName;
|
||||
}
|
||||
|
||||
public boolean isAll() {
|
||||
return ALBUM_ID_ALL.equals(mId);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return mCount == 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.entity;
|
||||
|
||||
public class CaptureStrategy {
|
||||
|
||||
public final boolean isPublic;
|
||||
public final String authority;
|
||||
public final String directory;
|
||||
|
||||
public CaptureStrategy(boolean isPublic, String authority) {
|
||||
this(isPublic, authority, null);
|
||||
}
|
||||
|
||||
public CaptureStrategy(boolean isPublic, String authority, String directory) {
|
||||
this.isPublic = isPublic;
|
||||
this.authority = authority;
|
||||
this.directory = directory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.entity;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.zhihu.matisse.internal.ui.widget.IncapableDialog;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class IncapableCause {
|
||||
public static final int TOAST = 0x00;
|
||||
public static final int DIALOG = 0x01;
|
||||
public static final int NONE = 0x02;
|
||||
|
||||
@Retention(SOURCE)
|
||||
@IntDef({TOAST, DIALOG, NONE})
|
||||
public @interface Form {
|
||||
}
|
||||
|
||||
private int mForm = TOAST;
|
||||
private String mTitle;
|
||||
private String mMessage;
|
||||
|
||||
public IncapableCause(String message) {
|
||||
mMessage = message;
|
||||
}
|
||||
|
||||
public IncapableCause(String title, String message) {
|
||||
mTitle = title;
|
||||
mMessage = message;
|
||||
}
|
||||
|
||||
public IncapableCause(@Form int form, String message) {
|
||||
mForm = form;
|
||||
mMessage = message;
|
||||
}
|
||||
|
||||
public IncapableCause(@Form int form, String title, String message) {
|
||||
mForm = form;
|
||||
mTitle = title;
|
||||
mMessage = message;
|
||||
}
|
||||
|
||||
public static void handleCause(Context context, IncapableCause cause) {
|
||||
if (cause == null)
|
||||
return;
|
||||
|
||||
switch (cause.mForm) {
|
||||
case NONE:
|
||||
// do nothing.
|
||||
break;
|
||||
case DIALOG:
|
||||
IncapableDialog incapableDialog = IncapableDialog.newInstance(cause.mTitle, cause.mMessage);
|
||||
incapableDialog.show(((FragmentActivity) context).getSupportFragmentManager(),
|
||||
IncapableDialog.class.getName());
|
||||
break;
|
||||
case TOAST:
|
||||
default:
|
||||
Toast.makeText(context, cause.mMessage, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2014 nohana, Inc.
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.entity;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.provider.MediaStore;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.zhihu.matisse.MimeType;
|
||||
|
||||
public class Item implements Parcelable {
|
||||
public static final Creator<Item> CREATOR = new Creator<Item>() {
|
||||
@Override
|
||||
@Nullable
|
||||
public Item createFromParcel(Parcel source) {
|
||||
return new Item(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item[] newArray(int size) {
|
||||
return new Item[size];
|
||||
}
|
||||
};
|
||||
public static final long ITEM_ID_CAPTURE = -1;
|
||||
public static final String ITEM_DISPLAY_NAME_CAPTURE = "Capture";
|
||||
public final long id;
|
||||
public final String mimeType;
|
||||
public final Uri uri;
|
||||
public final long size;
|
||||
public final long duration; // only for video, in ms
|
||||
|
||||
private Item(long id, String mimeType, long size, long duration) {
|
||||
this.id = id;
|
||||
this.mimeType = mimeType;
|
||||
Uri contentUri;
|
||||
if (isImage()) {
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if (isVideo()) {
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
} else {
|
||||
// ?
|
||||
contentUri = MediaStore.Files.getContentUri("external");
|
||||
}
|
||||
this.uri = ContentUris.withAppendedId(contentUri, id);
|
||||
this.size = size;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
private Item(Parcel source) {
|
||||
id = source.readLong();
|
||||
mimeType = source.readString();
|
||||
uri = source.readParcelable(Uri.class.getClassLoader());
|
||||
size = source.readLong();
|
||||
duration = source.readLong();
|
||||
}
|
||||
|
||||
public static Item valueOf(Cursor cursor) {
|
||||
return new Item(cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID)),
|
||||
cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE)),
|
||||
cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns.SIZE)),
|
||||
cursor.getLong(cursor.getColumnIndex("duration")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(id);
|
||||
dest.writeString(mimeType);
|
||||
dest.writeParcelable(uri, 0);
|
||||
dest.writeLong(size);
|
||||
dest.writeLong(duration);
|
||||
}
|
||||
|
||||
public Uri getContentUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public boolean isCapture() {
|
||||
return id == ITEM_ID_CAPTURE;
|
||||
}
|
||||
|
||||
public boolean isImage() {
|
||||
return MimeType.isImage(mimeType);
|
||||
}
|
||||
|
||||
public boolean isGif() {
|
||||
return MimeType.isGif(mimeType);
|
||||
}
|
||||
|
||||
public boolean isVideo() {
|
||||
return MimeType.isVideo(mimeType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Item)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Item other = (Item) obj;
|
||||
return id == other.id
|
||||
&& (mimeType != null && mimeType.equals(other.mimeType)
|
||||
|| (mimeType == null && other.mimeType == null))
|
||||
&& (uri != null && uri.equals(other.uri)
|
||||
|| (uri == null && other.uri == null))
|
||||
&& size == other.size
|
||||
&& duration == other.duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + Long.valueOf(id).hashCode();
|
||||
if (mimeType != null) {
|
||||
result = 31 * result + mimeType.hashCode();
|
||||
}
|
||||
result = 31 * result + uri.hashCode();
|
||||
result = 31 * result + Long.valueOf(size).hashCode();
|
||||
result = 31 * result + Long.valueOf(duration).hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2014 nohana, Inc.
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.entity;
|
||||
|
||||
import android.content.pm.ActivityInfo;
|
||||
|
||||
import androidx.annotation.StyleRes;
|
||||
|
||||
import com.zhihu.matisse.MimeType;
|
||||
import com.zhihu.matisse.R;
|
||||
import com.zhihu.matisse.engine.ImageEngine;
|
||||
import com.zhihu.matisse.engine.impl.GlideEngine;
|
||||
import com.zhihu.matisse.filter.Filter;
|
||||
import com.zhihu.matisse.listener.OnCheckedListener;
|
||||
import com.zhihu.matisse.listener.OnSelectedListener;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public final class SelectionSpec {
|
||||
|
||||
public Set<MimeType> mimeTypeSet;
|
||||
public boolean mediaTypeExclusive;
|
||||
public boolean showSingleMediaType;
|
||||
@StyleRes
|
||||
public int themeId;
|
||||
public int orientation;
|
||||
public boolean countable;
|
||||
public int maxSelectable;
|
||||
public int maxImageSelectable;
|
||||
public int maxVideoSelectable;
|
||||
public List<Filter> filters;
|
||||
public boolean capture;
|
||||
public CaptureStrategy captureStrategy;
|
||||
public int spanCount;
|
||||
public int gridExpectedSize;
|
||||
public float thumbnailScale;
|
||||
public ImageEngine imageEngine;
|
||||
public boolean hasInited;
|
||||
public OnSelectedListener onSelectedListener;
|
||||
public boolean originalable;
|
||||
public boolean autoHideToobar;
|
||||
public int originalMaxSize;
|
||||
public OnCheckedListener onCheckedListener;
|
||||
public boolean showPreview;
|
||||
|
||||
private SelectionSpec() {
|
||||
}
|
||||
|
||||
public static SelectionSpec getInstance() {
|
||||
return InstanceHolder.INSTANCE;
|
||||
}
|
||||
|
||||
public static SelectionSpec getCleanInstance() {
|
||||
SelectionSpec selectionSpec = getInstance();
|
||||
selectionSpec.reset();
|
||||
return selectionSpec;
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
mimeTypeSet = null;
|
||||
mediaTypeExclusive = true;
|
||||
showSingleMediaType = false;
|
||||
themeId = R.style.Matisse_Zhihu;
|
||||
orientation = 0;
|
||||
countable = false;
|
||||
maxSelectable = 1;
|
||||
maxImageSelectable = 0;
|
||||
maxVideoSelectable = 0;
|
||||
filters = null;
|
||||
capture = false;
|
||||
captureStrategy = null;
|
||||
spanCount = 3;
|
||||
gridExpectedSize = 0;
|
||||
thumbnailScale = 0.5f;
|
||||
imageEngine = new GlideEngine();
|
||||
hasInited = true;
|
||||
originalable = false;
|
||||
autoHideToobar = false;
|
||||
originalMaxSize = Integer.MAX_VALUE;
|
||||
showPreview = true;
|
||||
}
|
||||
|
||||
public boolean singleSelectionModeEnabled() {
|
||||
return !countable && (maxSelectable == 1 || (maxImageSelectable == 1 && maxVideoSelectable == 1));
|
||||
}
|
||||
|
||||
public boolean needOrientationRestriction() {
|
||||
return orientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
}
|
||||
|
||||
public boolean onlyShowImages() {
|
||||
return showSingleMediaType && MimeType.ofImage().containsAll(mimeTypeSet);
|
||||
}
|
||||
|
||||
public boolean onlyShowVideos() {
|
||||
return showSingleMediaType && MimeType.ofVideo().containsAll(mimeTypeSet);
|
||||
}
|
||||
|
||||
public boolean onlyShowGif() {
|
||||
return showSingleMediaType && MimeType.ofGif().equals(mimeTypeSet);
|
||||
}
|
||||
|
||||
private static final class InstanceHolder {
|
||||
private static final SelectionSpec INSTANCE = new SelectionSpec();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright (C) 2014 nohana, Inc.
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.loader;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.MergeCursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import androidx.loader.content.CursorLoader;
|
||||
|
||||
import com.zhihu.matisse.MimeType;
|
||||
import com.zhihu.matisse.internal.entity.Album;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Load all albums (grouped by bucket_id) into a single cursor.
|
||||
*/
|
||||
public class AlbumLoader extends CursorLoader {
|
||||
|
||||
private static final String COLUMN_BUCKET_ID = "bucket_id";
|
||||
private static final String COLUMN_BUCKET_DISPLAY_NAME = "bucket_display_name";
|
||||
public static final String COLUMN_URI = "uri";
|
||||
public static final String COLUMN_COUNT = "count";
|
||||
private static final Uri QUERY_URI = MediaStore.Files.getContentUri("external");
|
||||
|
||||
private static final String[] COLUMNS = {
|
||||
MediaStore.Files.FileColumns._ID,
|
||||
COLUMN_BUCKET_ID,
|
||||
COLUMN_BUCKET_DISPLAY_NAME,
|
||||
MediaStore.MediaColumns.MIME_TYPE,
|
||||
COLUMN_URI,
|
||||
COLUMN_COUNT};
|
||||
|
||||
private static final String[] PROJECTION = {
|
||||
MediaStore.Files.FileColumns._ID,
|
||||
COLUMN_BUCKET_ID,
|
||||
COLUMN_BUCKET_DISPLAY_NAME,
|
||||
MediaStore.MediaColumns.MIME_TYPE,
|
||||
"COUNT(*) AS " + COLUMN_COUNT};
|
||||
|
||||
private static final String[] PROJECTION_29 = {
|
||||
MediaStore.Files.FileColumns._ID,
|
||||
COLUMN_BUCKET_ID,
|
||||
COLUMN_BUCKET_DISPLAY_NAME,
|
||||
MediaStore.MediaColumns.MIME_TYPE};
|
||||
|
||||
// === params for showSingleMediaType: false ===
|
||||
private static final String SELECTION =
|
||||
"(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
|
||||
+ " OR "
|
||||
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
|
||||
+ " AND " + MediaStore.MediaColumns.SIZE + ">0"
|
||||
+ ") GROUP BY (bucket_id";
|
||||
private static final String SELECTION_29 =
|
||||
"(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
|
||||
+ " OR "
|
||||
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
|
||||
+ " AND " + MediaStore.MediaColumns.SIZE + ">0";
|
||||
private static final String[] SELECTION_ARGS = {
|
||||
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),
|
||||
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO),
|
||||
};
|
||||
// =============================================
|
||||
|
||||
// === params for showSingleMediaType: true ===
|
||||
private static final String SELECTION_FOR_SINGLE_MEDIA_TYPE =
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
|
||||
+ " AND " + MediaStore.MediaColumns.SIZE + ">0"
|
||||
+ ") GROUP BY (bucket_id";
|
||||
private static final String SELECTION_FOR_SINGLE_MEDIA_TYPE_29 =
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
|
||||
+ " AND " + MediaStore.MediaColumns.SIZE + ">0";
|
||||
|
||||
private static String[] getSelectionArgsForSingleMediaType(int mediaType) {
|
||||
return new String[]{String.valueOf(mediaType)};
|
||||
}
|
||||
// =============================================
|
||||
|
||||
// === params for showSingleMediaType: true ===
|
||||
private static final String SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE =
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
|
||||
+ " AND " + MediaStore.MediaColumns.SIZE + ">0"
|
||||
+ " AND " + MediaStore.MediaColumns.MIME_TYPE + "=?"
|
||||
+ ") GROUP BY (bucket_id";
|
||||
private static final String SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE_29 =
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
|
||||
+ " AND " + MediaStore.MediaColumns.SIZE + ">0"
|
||||
+ " AND " + MediaStore.MediaColumns.MIME_TYPE + "=?";
|
||||
|
||||
private static String[] getSelectionArgsForSingleMediaGifType(int mediaType) {
|
||||
return new String[]{String.valueOf(mediaType), "image/gif"};
|
||||
}
|
||||
// =============================================
|
||||
|
||||
private static final String BUCKET_ORDER_BY = "datetaken DESC";
|
||||
|
||||
private AlbumLoader(Context context, String selection, String[] selectionArgs) {
|
||||
super(
|
||||
context,
|
||||
QUERY_URI,
|
||||
beforeAndroidTen() ? PROJECTION : PROJECTION_29,
|
||||
selection,
|
||||
selectionArgs,
|
||||
BUCKET_ORDER_BY
|
||||
);
|
||||
}
|
||||
|
||||
public static CursorLoader newInstance(Context context) {
|
||||
String selection;
|
||||
String[] selectionArgs;
|
||||
if (SelectionSpec.getInstance().onlyShowGif()) {
|
||||
selection = beforeAndroidTen()
|
||||
? SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE : SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE_29;
|
||||
selectionArgs = getSelectionArgsForSingleMediaGifType(
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);
|
||||
} else if (SelectionSpec.getInstance().onlyShowImages()) {
|
||||
selection = beforeAndroidTen()
|
||||
? SELECTION_FOR_SINGLE_MEDIA_TYPE : SELECTION_FOR_SINGLE_MEDIA_TYPE_29;
|
||||
selectionArgs = getSelectionArgsForSingleMediaType(
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);
|
||||
} else if (SelectionSpec.getInstance().onlyShowVideos()) {
|
||||
selection = beforeAndroidTen()
|
||||
? SELECTION_FOR_SINGLE_MEDIA_TYPE : SELECTION_FOR_SINGLE_MEDIA_TYPE_29;
|
||||
selectionArgs = getSelectionArgsForSingleMediaType(
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO);
|
||||
} else {
|
||||
selection = beforeAndroidTen() ? SELECTION : SELECTION_29;
|
||||
selectionArgs = SELECTION_ARGS;
|
||||
}
|
||||
return new AlbumLoader(context, selection, selectionArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor loadInBackground() {
|
||||
Cursor albums = super.loadInBackground();
|
||||
MatrixCursor allAlbum = new MatrixCursor(COLUMNS);
|
||||
|
||||
if (beforeAndroidTen()) {
|
||||
int totalCount = 0;
|
||||
Uri allAlbumCoverUri = null;
|
||||
MatrixCursor otherAlbums = new MatrixCursor(COLUMNS);
|
||||
if (albums != null) {
|
||||
while (albums.moveToNext()) {
|
||||
long fileId = albums.getLong(
|
||||
albums.getColumnIndex(MediaStore.Files.FileColumns._ID));
|
||||
long bucketId = albums.getLong(
|
||||
albums.getColumnIndex(COLUMN_BUCKET_ID));
|
||||
String bucketDisplayName = albums.getString(
|
||||
albums.getColumnIndex(COLUMN_BUCKET_DISPLAY_NAME));
|
||||
String mimeType = albums.getString(
|
||||
albums.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE));
|
||||
Uri uri = getUri(albums);
|
||||
int count = albums.getInt(albums.getColumnIndex(COLUMN_COUNT));
|
||||
|
||||
otherAlbums.addRow(new String[]{
|
||||
Long.toString(fileId),
|
||||
Long.toString(bucketId), bucketDisplayName, mimeType, uri.toString(),
|
||||
String.valueOf(count)});
|
||||
totalCount += count;
|
||||
}
|
||||
if (albums.moveToFirst()) {
|
||||
allAlbumCoverUri = getUri(albums);
|
||||
}
|
||||
}
|
||||
|
||||
allAlbum.addRow(new String[]{
|
||||
Album.ALBUM_ID_ALL, Album.ALBUM_ID_ALL, Album.ALBUM_NAME_ALL, null,
|
||||
allAlbumCoverUri == null ? null : allAlbumCoverUri.toString(),
|
||||
String.valueOf(totalCount)});
|
||||
|
||||
return new MergeCursor(new Cursor[]{allAlbum, otherAlbums});
|
||||
} else {
|
||||
int totalCount = 0;
|
||||
Uri allAlbumCoverUri = null;
|
||||
|
||||
// Pseudo GROUP BY
|
||||
Map<Long, Long> countMap = new HashMap<>();
|
||||
if (albums != null) {
|
||||
while (albums.moveToNext()) {
|
||||
long bucketId = albums.getLong(albums.getColumnIndex(COLUMN_BUCKET_ID));
|
||||
|
||||
Long count = countMap.get(bucketId);
|
||||
if (count == null) {
|
||||
count = 1L;
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
countMap.put(bucketId, count);
|
||||
}
|
||||
}
|
||||
|
||||
MatrixCursor otherAlbums = new MatrixCursor(COLUMNS);
|
||||
if (albums != null) {
|
||||
if (albums.moveToFirst()) {
|
||||
allAlbumCoverUri = getUri(albums);
|
||||
|
||||
Set<Long> done = new HashSet<>();
|
||||
|
||||
do {
|
||||
long bucketId = albums.getLong(albums.getColumnIndex(COLUMN_BUCKET_ID));
|
||||
|
||||
if (done.contains(bucketId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long fileId = albums.getLong(
|
||||
albums.getColumnIndex(MediaStore.Files.FileColumns._ID));
|
||||
String bucketDisplayName = albums.getString(
|
||||
albums.getColumnIndex(COLUMN_BUCKET_DISPLAY_NAME));
|
||||
String mimeType = albums.getString(
|
||||
albums.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE));
|
||||
Uri uri = getUri(albums);
|
||||
long count = countMap.get(bucketId);
|
||||
|
||||
otherAlbums.addRow(new String[]{
|
||||
Long.toString(fileId),
|
||||
Long.toString(bucketId),
|
||||
bucketDisplayName,
|
||||
mimeType,
|
||||
uri.toString(),
|
||||
String.valueOf(count)});
|
||||
done.add(bucketId);
|
||||
|
||||
totalCount += count;
|
||||
} while (albums.moveToNext());
|
||||
}
|
||||
}
|
||||
|
||||
allAlbum.addRow(new String[]{
|
||||
Album.ALBUM_ID_ALL,
|
||||
Album.ALBUM_ID_ALL, Album.ALBUM_NAME_ALL, null,
|
||||
allAlbumCoverUri == null ? null : allAlbumCoverUri.toString(),
|
||||
String.valueOf(totalCount)});
|
||||
|
||||
return new MergeCursor(new Cursor[]{allAlbum, otherAlbums});
|
||||
}
|
||||
}
|
||||
|
||||
private static Uri getUri(Cursor cursor) {
|
||||
long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID));
|
||||
String mimeType = cursor.getString(
|
||||
cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE));
|
||||
Uri contentUri;
|
||||
|
||||
if (MimeType.isImage(mimeType)) {
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if (MimeType.isVideo(mimeType)) {
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
} else {
|
||||
// ?
|
||||
contentUri = MediaStore.Files.getContentUri("external");
|
||||
}
|
||||
|
||||
Uri uri = ContentUris.withAppendedId(contentUri, id);
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContentChanged() {
|
||||
// FIXME a dirty way to fix loading multiple times
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否是 Android 10 (Q) 之前的版本
|
||||
*/
|
||||
private static boolean beforeAndroidTen() {
|
||||
return android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (C) 2014 nohana, Inc.
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.loader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.MergeCursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import androidx.loader.content.CursorLoader;
|
||||
|
||||
import com.zhihu.matisse.internal.entity.Album;
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
import com.zhihu.matisse.internal.utils.MediaStoreCompat;
|
||||
|
||||
/**
|
||||
* Load images and videos into a single cursor.
|
||||
*/
|
||||
public class AlbumMediaLoader extends CursorLoader {
|
||||
private static final Uri QUERY_URI = MediaStore.Files.getContentUri("external");
|
||||
private static final String[] PROJECTION = {
|
||||
MediaStore.Files.FileColumns._ID,
|
||||
MediaStore.MediaColumns.DISPLAY_NAME,
|
||||
MediaStore.MediaColumns.MIME_TYPE,
|
||||
MediaStore.MediaColumns.SIZE,
|
||||
"duration"};
|
||||
|
||||
// === params for album ALL && showSingleMediaType: false ===
|
||||
private static final String SELECTION_ALL =
|
||||
"(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
|
||||
+ " OR "
|
||||
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
|
||||
+ " AND " + MediaStore.MediaColumns.SIZE + ">0";
|
||||
private static final String[] SELECTION_ALL_ARGS = {
|
||||
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),
|
||||
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO),
|
||||
};
|
||||
// ===========================================================
|
||||
|
||||
// === params for album ALL && showSingleMediaType: true ===
|
||||
private static final String SELECTION_ALL_FOR_SINGLE_MEDIA_TYPE =
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
|
||||
+ " AND " + MediaStore.MediaColumns.SIZE + ">0";
|
||||
|
||||
private static String[] getSelectionArgsForSingleMediaType(int mediaType) {
|
||||
return new String[]{String.valueOf(mediaType)};
|
||||
}
|
||||
// =========================================================
|
||||
|
||||
// === params for ordinary album && showSingleMediaType: false ===
|
||||
private static final String SELECTION_ALBUM =
|
||||
"(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
|
||||
+ " OR "
|
||||
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
|
||||
+ " AND "
|
||||
+ " bucket_id=?"
|
||||
+ " AND " + MediaStore.MediaColumns.SIZE + ">0";
|
||||
|
||||
private static String[] getSelectionAlbumArgs(String albumId) {
|
||||
return new String[]{
|
||||
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),
|
||||
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO),
|
||||
albumId
|
||||
};
|
||||
}
|
||||
// ===============================================================
|
||||
|
||||
// === params for ordinary album && showSingleMediaType: true ===
|
||||
private static final String SELECTION_ALBUM_FOR_SINGLE_MEDIA_TYPE =
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
|
||||
+ " AND "
|
||||
+ " bucket_id=?"
|
||||
+ " AND " + MediaStore.MediaColumns.SIZE + ">0";
|
||||
|
||||
private static String[] getSelectionAlbumArgsForSingleMediaType(int mediaType, String albumId) {
|
||||
return new String[]{String.valueOf(mediaType), albumId};
|
||||
}
|
||||
// ===============================================================
|
||||
|
||||
// === params for album ALL && showSingleMediaType: true && MineType=="image/gif"
|
||||
private static final String SELECTION_ALL_FOR_GIF =
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
|
||||
+ " AND "
|
||||
+ MediaStore.MediaColumns.MIME_TYPE + "=?"
|
||||
+ " AND " + MediaStore.MediaColumns.SIZE + ">0";
|
||||
|
||||
private static String[] getSelectionArgsForGifType(int mediaType) {
|
||||
return new String[]{String.valueOf(mediaType), "image/gif"};
|
||||
}
|
||||
// ===============================================================
|
||||
|
||||
// === params for ordinary album && showSingleMediaType: true && MineType=="image/gif" ===
|
||||
private static final String SELECTION_ALBUM_FOR_GIF =
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
|
||||
+ " AND "
|
||||
+ " bucket_id=?"
|
||||
+ " AND "
|
||||
+ MediaStore.MediaColumns.MIME_TYPE + "=?"
|
||||
+ " AND " + MediaStore.MediaColumns.SIZE + ">0";
|
||||
|
||||
private static String[] getSelectionAlbumArgsForGifType(int mediaType, String albumId) {
|
||||
return new String[]{String.valueOf(mediaType), albumId, "image/gif"};
|
||||
}
|
||||
// ===============================================================
|
||||
|
||||
private static final String ORDER_BY = MediaStore.Images.Media.DATE_TAKEN + " DESC";
|
||||
private final boolean mEnableCapture;
|
||||
|
||||
private AlbumMediaLoader(Context context, String selection, String[] selectionArgs, boolean capture) {
|
||||
super(context, QUERY_URI, PROJECTION, selection, selectionArgs, ORDER_BY);
|
||||
mEnableCapture = capture;
|
||||
}
|
||||
|
||||
public static CursorLoader newInstance(Context context, Album album, boolean capture) {
|
||||
String selection;
|
||||
String[] selectionArgs;
|
||||
boolean enableCapture;
|
||||
|
||||
if (album.isAll()) {
|
||||
if (SelectionSpec.getInstance().onlyShowGif()) {
|
||||
selection = SELECTION_ALL_FOR_GIF;
|
||||
selectionArgs = getSelectionArgsForGifType(
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);
|
||||
} else if (SelectionSpec.getInstance().onlyShowImages()) {
|
||||
selection = SELECTION_ALL_FOR_SINGLE_MEDIA_TYPE;
|
||||
selectionArgs =
|
||||
getSelectionArgsForSingleMediaType(
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);
|
||||
} else if (SelectionSpec.getInstance().onlyShowVideos()) {
|
||||
selection = SELECTION_ALL_FOR_SINGLE_MEDIA_TYPE;
|
||||
selectionArgs =
|
||||
getSelectionArgsForSingleMediaType(
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO);
|
||||
} else {
|
||||
selection = SELECTION_ALL;
|
||||
selectionArgs = SELECTION_ALL_ARGS;
|
||||
}
|
||||
enableCapture = capture;
|
||||
} else {
|
||||
if (SelectionSpec.getInstance().onlyShowGif()) {
|
||||
selection = SELECTION_ALBUM_FOR_GIF;
|
||||
selectionArgs =
|
||||
getSelectionAlbumArgsForGifType(
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE, album.getId());
|
||||
} else if (SelectionSpec.getInstance().onlyShowImages()) {
|
||||
selection = SELECTION_ALBUM_FOR_SINGLE_MEDIA_TYPE;
|
||||
selectionArgs =
|
||||
getSelectionAlbumArgsForSingleMediaType(
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE,
|
||||
album.getId());
|
||||
} else if (SelectionSpec.getInstance().onlyShowVideos()) {
|
||||
selection = SELECTION_ALBUM_FOR_SINGLE_MEDIA_TYPE;
|
||||
selectionArgs = getSelectionAlbumArgsForSingleMediaType(
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO,
|
||||
album.getId());
|
||||
} else {
|
||||
selection = SELECTION_ALBUM;
|
||||
selectionArgs = getSelectionAlbumArgs(album.getId());
|
||||
}
|
||||
enableCapture = false;
|
||||
}
|
||||
return new AlbumMediaLoader(context, selection, selectionArgs, enableCapture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor loadInBackground() {
|
||||
Cursor result = super.loadInBackground();
|
||||
if (!mEnableCapture || !MediaStoreCompat.hasCameraFeature(getContext())) {
|
||||
return result;
|
||||
}
|
||||
MatrixCursor dummy = new MatrixCursor(PROJECTION);
|
||||
dummy.addRow(new Object[]{Item.ITEM_ID_CAPTURE, Item.ITEM_DISPLAY_NAME_CAPTURE, "", 0, 0});
|
||||
return new MergeCursor(new Cursor[]{dummy, result});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContentChanged() {
|
||||
// FIXME a dirty way to fix loading multiple times
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2014 nohana, Inc.
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.model;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
|
||||
import com.zhihu.matisse.internal.loader.AlbumLoader;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class AlbumCollection implements LoaderManager.LoaderCallbacks<Cursor> {
|
||||
private static final int LOADER_ID = 1;
|
||||
private static final String STATE_CURRENT_SELECTION = "state_current_selection";
|
||||
private WeakReference<Context> mContext;
|
||||
private LoaderManager mLoaderManager;
|
||||
private AlbumCallbacks mCallbacks;
|
||||
private int mCurrentSelection;
|
||||
private boolean mLoadFinished;
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
Context context = mContext.get();
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
mLoadFinished = false;
|
||||
return AlbumLoader.newInstance(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||
Context context = mContext.get();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mLoadFinished) {
|
||||
mLoadFinished = true;
|
||||
mCallbacks.onAlbumLoad(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
Context context = mContext.get();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mCallbacks.onAlbumReset();
|
||||
}
|
||||
|
||||
public void onCreate(FragmentActivity activity, AlbumCallbacks callbacks) {
|
||||
mContext = new WeakReference<Context>(activity);
|
||||
mLoaderManager = activity.getSupportLoaderManager();
|
||||
mCallbacks = callbacks;
|
||||
}
|
||||
|
||||
public void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
if (savedInstanceState == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentSelection = savedInstanceState.getInt(STATE_CURRENT_SELECTION);
|
||||
}
|
||||
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putInt(STATE_CURRENT_SELECTION, mCurrentSelection);
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
if (mLoaderManager != null) {
|
||||
mLoaderManager.destroyLoader(LOADER_ID);
|
||||
}
|
||||
mCallbacks = null;
|
||||
}
|
||||
|
||||
public void loadAlbums() {
|
||||
mLoaderManager.initLoader(LOADER_ID, null, this);
|
||||
}
|
||||
|
||||
public int getCurrentSelection() {
|
||||
return mCurrentSelection;
|
||||
}
|
||||
|
||||
public void setStateCurrentSelection(int currentSelection) {
|
||||
mCurrentSelection = currentSelection;
|
||||
}
|
||||
|
||||
public interface AlbumCallbacks {
|
||||
void onAlbumLoad(Cursor cursor);
|
||||
|
||||
void onAlbumReset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2014 nohana, Inc.
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.model;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
|
||||
import com.zhihu.matisse.internal.entity.Album;
|
||||
import com.zhihu.matisse.internal.loader.AlbumMediaLoader;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class AlbumMediaCollection implements LoaderManager.LoaderCallbacks<Cursor> {
|
||||
private static final int LOADER_ID = 2;
|
||||
private static final String ARGS_ALBUM = "args_album";
|
||||
private static final String ARGS_ENABLE_CAPTURE = "args_enable_capture";
|
||||
private WeakReference<Context> mContext;
|
||||
private LoaderManager mLoaderManager;
|
||||
private AlbumMediaCallbacks mCallbacks;
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
Context context = mContext.get();
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Album album = args.getParcelable(ARGS_ALBUM);
|
||||
if (album == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return AlbumMediaLoader.newInstance(context, album,
|
||||
album.isAll() && args.getBoolean(ARGS_ENABLE_CAPTURE, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||
Context context = mContext.get();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mCallbacks.onAlbumMediaLoad(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
Context context = mContext.get();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mCallbacks.onAlbumMediaReset();
|
||||
}
|
||||
|
||||
public void onCreate(@NonNull FragmentActivity context, @NonNull AlbumMediaCallbacks callbacks) {
|
||||
mContext = new WeakReference<Context>(context);
|
||||
mLoaderManager = context.getSupportLoaderManager();
|
||||
mCallbacks = callbacks;
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
if (mLoaderManager != null) {
|
||||
mLoaderManager.destroyLoader(LOADER_ID);
|
||||
}
|
||||
mCallbacks = null;
|
||||
}
|
||||
|
||||
public void load(@Nullable Album target) {
|
||||
load(target, false);
|
||||
}
|
||||
|
||||
public void load(@Nullable Album target, boolean enableCapture) {
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelable(ARGS_ALBUM, target);
|
||||
args.putBoolean(ARGS_ENABLE_CAPTURE, enableCapture);
|
||||
mLoaderManager.initLoader(LOADER_ID, args, this);
|
||||
}
|
||||
|
||||
public interface AlbumMediaCallbacks {
|
||||
|
||||
void onAlbumMediaLoad(Cursor cursor);
|
||||
|
||||
void onAlbumMediaReset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.model;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
import com.zhihu.matisse.SelectionCreator;
|
||||
import com.zhihu.matisse.internal.entity.IncapableCause;
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
import com.zhihu.matisse.internal.ui.widget.CheckView;
|
||||
import com.zhihu.matisse.internal.utils.PathUtils;
|
||||
import com.zhihu.matisse.internal.utils.PhotoMetadataUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class SelectedItemCollection {
|
||||
|
||||
public static final String STATE_SELECTION = "state_selection";
|
||||
public static final String STATE_COLLECTION_TYPE = "state_collection_type";
|
||||
/**
|
||||
* Empty collection
|
||||
*/
|
||||
public static final int COLLECTION_UNDEFINED = 0x00;
|
||||
/**
|
||||
* Collection only with images
|
||||
*/
|
||||
public static final int COLLECTION_IMAGE = 0x01;
|
||||
/**
|
||||
* Collection only with videos
|
||||
*/
|
||||
public static final int COLLECTION_VIDEO = 0x01 << 1;
|
||||
/**
|
||||
* Collection with images and videos.
|
||||
*/
|
||||
public static final int COLLECTION_MIXED = COLLECTION_IMAGE | COLLECTION_VIDEO;
|
||||
private final Context mContext;
|
||||
private Set<Item> mItems;
|
||||
private int mCollectionType = COLLECTION_UNDEFINED;
|
||||
|
||||
public SelectedItemCollection(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void onCreate(Bundle bundle) {
|
||||
if (bundle == null) {
|
||||
mItems = new LinkedHashSet<>();
|
||||
} else {
|
||||
List<Item> saved = bundle.getParcelableArrayList(STATE_SELECTION);
|
||||
mItems = new LinkedHashSet<>(saved);
|
||||
mCollectionType = bundle.getInt(STATE_COLLECTION_TYPE, COLLECTION_UNDEFINED);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDefaultSelection(List<Item> uris) {
|
||||
mItems.addAll(uris);
|
||||
}
|
||||
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putParcelableArrayList(STATE_SELECTION, new ArrayList<>(mItems));
|
||||
outState.putInt(STATE_COLLECTION_TYPE, mCollectionType);
|
||||
}
|
||||
|
||||
public Bundle getDataWithBundle() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelableArrayList(STATE_SELECTION, new ArrayList<>(mItems));
|
||||
bundle.putInt(STATE_COLLECTION_TYPE, mCollectionType);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
public boolean add(Item item) {
|
||||
if (typeConflict(item)) {
|
||||
throw new IllegalArgumentException("Can't select images and videos at the same time.");
|
||||
}
|
||||
boolean added = mItems.add(item);
|
||||
if (added) {
|
||||
if (mCollectionType == COLLECTION_UNDEFINED) {
|
||||
if (item.isImage()) {
|
||||
mCollectionType = COLLECTION_IMAGE;
|
||||
} else if (item.isVideo()) {
|
||||
mCollectionType = COLLECTION_VIDEO;
|
||||
}
|
||||
} else if (mCollectionType == COLLECTION_IMAGE) {
|
||||
if (item.isVideo()) {
|
||||
mCollectionType = COLLECTION_MIXED;
|
||||
}
|
||||
} else if (mCollectionType == COLLECTION_VIDEO) {
|
||||
if (item.isImage()) {
|
||||
mCollectionType = COLLECTION_MIXED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(item.isVideo()&&mItems.size()>0){
|
||||
SelectionSpec.getInstance().maxSelectable = 1;
|
||||
}else{
|
||||
SelectionSpec.getInstance().maxSelectable = SelectionCreator.fistMaxNumber;
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
public boolean remove(Item item) {
|
||||
boolean removed = mItems.remove(item);
|
||||
if (removed) {
|
||||
if (mItems.size() == 0) {
|
||||
mCollectionType = COLLECTION_UNDEFINED;
|
||||
} else {
|
||||
if (mCollectionType == COLLECTION_MIXED) {
|
||||
refineCollectionType();
|
||||
}
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
public void overwrite(ArrayList<Item> items, int collectionType) {
|
||||
if (items.size() == 0) {
|
||||
mCollectionType = COLLECTION_UNDEFINED;
|
||||
} else {
|
||||
mCollectionType = collectionType;
|
||||
}
|
||||
mItems.clear();
|
||||
mItems.addAll(items);
|
||||
}
|
||||
|
||||
|
||||
public List<Item> asList() {
|
||||
return new ArrayList<>(mItems);
|
||||
}
|
||||
|
||||
public List<Uri> asListOfUri() {
|
||||
List<Uri> uris = new ArrayList<>();
|
||||
for (Item item : mItems) {
|
||||
uris.add(item.getContentUri());
|
||||
}
|
||||
return uris;
|
||||
}
|
||||
|
||||
public List<String> asListOfString() {
|
||||
List<String> paths = new ArrayList<>();
|
||||
for (Item item : mItems) {
|
||||
paths.add(PathUtils.getPath(mContext, item.getContentUri()));
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
public boolean asListOfType() {
|
||||
boolean isVideo = false;
|
||||
for (Item item : mItems) {
|
||||
if(item.isVideo()){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return isVideo;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return mItems == null || mItems.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isSelected(Item item) {
|
||||
return mItems.contains(item);
|
||||
}
|
||||
|
||||
public IncapableCause isAcceptable(Item item) {
|
||||
if (maxSelectableReached()) {
|
||||
int maxSelectable = currentMaxSelectable();
|
||||
String cause;
|
||||
|
||||
// try {
|
||||
// cause = mContext.getResources().getQuantityString(
|
||||
// R.string.error_over_count,
|
||||
// maxSelectable,
|
||||
// maxSelectable
|
||||
// );
|
||||
// } catch (Resources.NotFoundException e) {
|
||||
cause = mContext.getString(
|
||||
R.string.error_over_count,
|
||||
maxSelectable
|
||||
);
|
||||
// } catch (NoClassDefFoundError e) {
|
||||
// cause = mContext.getString(
|
||||
// R.string.error_over_count,
|
||||
// maxSelectable
|
||||
// );
|
||||
// }
|
||||
|
||||
return new IncapableCause(cause);
|
||||
} else if (typeConflict(item)) {
|
||||
return new IncapableCause(mContext.getString(R.string.error_type_conflict));
|
||||
}
|
||||
|
||||
return PhotoMetadataUtils.isAcceptable(mContext, item);
|
||||
}
|
||||
|
||||
public boolean maxSelectableReached() {
|
||||
return mItems.size() == currentMaxSelectable();
|
||||
}
|
||||
|
||||
public int maxSize;
|
||||
// depends
|
||||
private int currentMaxSelectable() {
|
||||
SelectionSpec spec = SelectionSpec.getInstance();
|
||||
if (spec.maxSelectable > 0) {
|
||||
maxSize =spec.maxSelectable;
|
||||
return spec.maxSelectable;
|
||||
} else if (mCollectionType == COLLECTION_IMAGE) {
|
||||
return spec.maxImageSelectable;
|
||||
} else if (mCollectionType == COLLECTION_VIDEO) {
|
||||
return spec.maxVideoSelectable;
|
||||
} else {
|
||||
return spec.maxSelectable;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCollectionType() {
|
||||
return mCollectionType;
|
||||
}
|
||||
|
||||
private void refineCollectionType() {
|
||||
boolean hasImage = false;
|
||||
boolean hasVideo = false;
|
||||
for (Item i : mItems) {
|
||||
if (i.isImage() && !hasImage) hasImage = true;
|
||||
if (i.isVideo() && !hasVideo) hasVideo = true;
|
||||
}
|
||||
if (hasImage && hasVideo) {
|
||||
mCollectionType = COLLECTION_MIXED;
|
||||
} else if (hasImage) {
|
||||
mCollectionType = COLLECTION_IMAGE;
|
||||
} else if (hasVideo) {
|
||||
mCollectionType = COLLECTION_VIDEO;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether there will be conflict media types. A user can only select images and videos at the same time
|
||||
* while {@link SelectionSpec#mediaTypeExclusive} is set to false.
|
||||
*/
|
||||
public boolean typeConflict(Item item) {
|
||||
return SelectionSpec.getInstance().mediaTypeExclusive
|
||||
&& ((item.isImage() && (mCollectionType == COLLECTION_VIDEO || mCollectionType == COLLECTION_MIXED))
|
||||
|| (item.isVideo() && (mCollectionType == COLLECTION_IMAGE || mCollectionType == COLLECTION_MIXED)));
|
||||
}
|
||||
|
||||
public int count() {
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
public int checkedNumOf(Item item) {
|
||||
int index = new ArrayList<>(mItems).indexOf(item);
|
||||
return index == -1 ? CheckView.UNCHECKED : index + 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.zhihu.matisse.internal.entity.Album;
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
import com.zhihu.matisse.internal.model.AlbumMediaCollection;
|
||||
import com.zhihu.matisse.internal.ui.adapter.PreviewPagerAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AlbumPreviewActivity extends BasePreviewActivity implements
|
||||
AlbumMediaCollection.AlbumMediaCallbacks {
|
||||
|
||||
public static final String EXTRA_ALBUM = "extra_album";
|
||||
public static final String EXTRA_ITEM = "extra_item";
|
||||
|
||||
private AlbumMediaCollection mCollection = new AlbumMediaCollection();
|
||||
|
||||
private boolean mIsAlreadySetPosition;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (!SelectionSpec.getInstance().hasInited) {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
mCollection.onCreate(this, this);
|
||||
Album album = getIntent().getParcelableExtra(EXTRA_ALBUM);
|
||||
mCollection.load(album);
|
||||
|
||||
Item item = getIntent().getParcelableExtra(EXTRA_ITEM);
|
||||
if (mSpec.countable) {
|
||||
mCheckView.setCheckedNum(mSelectedCollection.checkedNumOf(item));
|
||||
} else {
|
||||
mCheckView.setChecked(mSelectedCollection.isSelected(item));
|
||||
}
|
||||
updateSize(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mCollection.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAlbumMediaLoad(Cursor cursor) {
|
||||
List<Item> items = new ArrayList<>();
|
||||
while (cursor.moveToNext()) {
|
||||
items.add(Item.valueOf(cursor));
|
||||
}
|
||||
// cursor.close();
|
||||
|
||||
if (items.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PreviewPagerAdapter adapter = (PreviewPagerAdapter) mPager.getAdapter();
|
||||
adapter.addAll(items);
|
||||
adapter.notifyDataSetChanged();
|
||||
if (!mIsAlreadySetPosition) {
|
||||
//onAlbumMediaLoad is called many times..
|
||||
mIsAlreadySetPosition = true;
|
||||
Item selected = getIntent().getParcelableExtra(EXTRA_ITEM);
|
||||
int selectedIndex = items.indexOf(selected);
|
||||
mPager.setCurrentItem(selectedIndex, false);
|
||||
mPreviousPos = selectedIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAlbumMediaReset() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
import com.zhihu.matisse.internal.entity.IncapableCause;
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
import com.zhihu.matisse.internal.model.SelectedItemCollection;
|
||||
import com.zhihu.matisse.internal.ui.adapter.PreviewPagerAdapter;
|
||||
import com.zhihu.matisse.internal.ui.widget.CheckRadioView;
|
||||
import com.zhihu.matisse.internal.ui.widget.CheckView;
|
||||
import com.zhihu.matisse.internal.ui.widget.IncapableDialog;
|
||||
import com.zhihu.matisse.internal.utils.PhotoMetadataUtils;
|
||||
import com.zhihu.matisse.internal.utils.Platform;
|
||||
import com.zhihu.matisse.listener.OnFragmentInteractionListener;
|
||||
|
||||
public abstract class BasePreviewActivity extends AppCompatActivity implements View.OnClickListener,
|
||||
ViewPager.OnPageChangeListener, OnFragmentInteractionListener {
|
||||
|
||||
public static final String EXTRA_DEFAULT_BUNDLE = "extra_default_bundle";
|
||||
public static final String EXTRA_RESULT_BUNDLE = "extra_result_bundle";
|
||||
public static final String EXTRA_RESULT_APPLY = "extra_result_apply";
|
||||
public static final String EXTRA_RESULT_ORIGINAL_ENABLE = "extra_result_original_enable";
|
||||
public static final String CHECK_STATE = "checkState";
|
||||
|
||||
protected final SelectedItemCollection mSelectedCollection = new SelectedItemCollection(this);
|
||||
protected SelectionSpec mSpec;
|
||||
protected ViewPager mPager;
|
||||
|
||||
protected PreviewPagerAdapter mAdapter;
|
||||
|
||||
protected CheckView mCheckView;
|
||||
protected TextView mButtonBack;
|
||||
protected TextView mButtonApply;
|
||||
protected TextView mSize;
|
||||
|
||||
protected int mPreviousPos = -1;
|
||||
|
||||
private LinearLayout mOriginalLayout;
|
||||
private CheckRadioView mOriginal;
|
||||
protected boolean mOriginalEnable;
|
||||
|
||||
private FrameLayout mBottomToolbar;
|
||||
private FrameLayout mTopToolbar;
|
||||
private boolean mIsToolbarHide = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
setTheme(SelectionSpec.getInstance().themeId);
|
||||
super.onCreate(savedInstanceState);
|
||||
if (!SelectionSpec.getInstance().hasInited) {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
setContentView(R.layout.activity_media_preview);
|
||||
if (Platform.hasKitKat()) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
}
|
||||
|
||||
mSpec = SelectionSpec.getInstance();
|
||||
if (mSpec.needOrientationRestriction()) {
|
||||
setRequestedOrientation(mSpec.orientation);
|
||||
}
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
mSelectedCollection.onCreate(getIntent().getBundleExtra(EXTRA_DEFAULT_BUNDLE));
|
||||
mOriginalEnable = getIntent().getBooleanExtra(EXTRA_RESULT_ORIGINAL_ENABLE, false);
|
||||
} else {
|
||||
mSelectedCollection.onCreate(savedInstanceState);
|
||||
mOriginalEnable = savedInstanceState.getBoolean(CHECK_STATE);
|
||||
}
|
||||
mButtonBack = (TextView) findViewById(R.id.button_back);
|
||||
mButtonApply = (TextView) findViewById(R.id.button_apply);
|
||||
mSize = (TextView) findViewById(R.id.size);
|
||||
mButtonBack.setOnClickListener(this);
|
||||
mButtonApply.setOnClickListener(this);
|
||||
|
||||
mPager = (ViewPager) findViewById(R.id.pager);
|
||||
mPager.addOnPageChangeListener(this);
|
||||
mAdapter = new PreviewPagerAdapter(getSupportFragmentManager(), null);
|
||||
mPager.setAdapter(mAdapter);
|
||||
mCheckView = (CheckView) findViewById(R.id.check_view);
|
||||
mCheckView.setCountable(mSpec.countable);
|
||||
mBottomToolbar = findViewById(R.id.bottom_toolbar);
|
||||
mTopToolbar = findViewById(R.id.top_toolbar);
|
||||
|
||||
mCheckView.setOnClickListener(v -> {
|
||||
Item item = mAdapter.getMediaItem(mPager.getCurrentItem());
|
||||
if (mSelectedCollection.isSelected(item)) {
|
||||
mSelectedCollection.remove(item);
|
||||
if (mSpec.countable) {
|
||||
mCheckView.setCheckedNum(CheckView.UNCHECKED);
|
||||
} else {
|
||||
mCheckView.setChecked(false);
|
||||
}
|
||||
} else {
|
||||
if (assertAddSelection(item)) {
|
||||
mSelectedCollection.add(item);
|
||||
if (mSpec.countable) {
|
||||
mCheckView.setCheckedNum(mSelectedCollection.checkedNumOf(item));
|
||||
} else {
|
||||
mCheckView.setChecked(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
updateApplyButton();
|
||||
|
||||
if (mSpec.onSelectedListener != null) {
|
||||
mSpec.onSelectedListener.onSelected(
|
||||
mSelectedCollection.asListOfUri(), mSelectedCollection.asListOfString());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
mOriginalLayout = findViewById(R.id.originalLayout);
|
||||
mOriginal = findViewById(R.id.original);
|
||||
mOriginalLayout.setOnClickListener(v -> {
|
||||
|
||||
int count = countOverMaxSize();
|
||||
if (count > 0) {
|
||||
IncapableDialog incapableDialog = IncapableDialog.newInstance("",
|
||||
getString(R.string.error_over_original_count, count, mSpec.originalMaxSize));
|
||||
incapableDialog.show(getSupportFragmentManager(),
|
||||
IncapableDialog.class.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
mOriginalEnable = !mOriginalEnable;
|
||||
mOriginal.setChecked(mOriginalEnable);
|
||||
if (!mOriginalEnable) {
|
||||
mOriginal.setColor(Color.WHITE);
|
||||
}
|
||||
|
||||
|
||||
if (mSpec.onCheckedListener != null) {
|
||||
mSpec.onCheckedListener.onCheck(mOriginalEnable);
|
||||
}
|
||||
});
|
||||
|
||||
updateApplyButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
mSelectedCollection.onSaveInstanceState(outState);
|
||||
outState.putBoolean("checkState", mOriginalEnable);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
sendBackResult(false);
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v.getId() == R.id.button_back) {
|
||||
onBackPressed();
|
||||
} else if (v.getId() == R.id.button_apply) {
|
||||
sendBackResult(true);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
if (!mSpec.autoHideToobar) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIsToolbarHide) {
|
||||
mTopToolbar.animate()
|
||||
.setInterpolator(new FastOutSlowInInterpolator())
|
||||
.translationYBy(mTopToolbar.getMeasuredHeight())
|
||||
.start();
|
||||
mBottomToolbar.animate()
|
||||
.translationYBy(-mBottomToolbar.getMeasuredHeight())
|
||||
.setInterpolator(new FastOutSlowInInterpolator())
|
||||
.start();
|
||||
} else {
|
||||
mTopToolbar.animate()
|
||||
.setInterpolator(new FastOutSlowInInterpolator())
|
||||
.translationYBy(-mTopToolbar.getMeasuredHeight())
|
||||
.start();
|
||||
mBottomToolbar.animate()
|
||||
.setInterpolator(new FastOutSlowInInterpolator())
|
||||
.translationYBy(mBottomToolbar.getMeasuredHeight())
|
||||
.start();
|
||||
}
|
||||
|
||||
mIsToolbarHide = !mIsToolbarHide;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
PreviewPagerAdapter adapter = (PreviewPagerAdapter) mPager.getAdapter();
|
||||
if (mPreviousPos != -1 && mPreviousPos != position) {
|
||||
((PreviewItemFragment) adapter.instantiateItem(mPager, mPreviousPos)).resetView();
|
||||
|
||||
Item item = adapter.getMediaItem(position);
|
||||
if (mSpec.countable) {
|
||||
int checkedNum = mSelectedCollection.checkedNumOf(item);
|
||||
mCheckView.setCheckedNum(checkedNum);
|
||||
if (checkedNum > 0) {
|
||||
mCheckView.setEnabled(true);
|
||||
} else {
|
||||
mCheckView.setEnabled(!mSelectedCollection.maxSelectableReached());
|
||||
}
|
||||
} else {
|
||||
boolean checked = mSelectedCollection.isSelected(item);
|
||||
mCheckView.setChecked(checked);
|
||||
if (checked) {
|
||||
mCheckView.setEnabled(true);
|
||||
} else {
|
||||
mCheckView.setEnabled(!mSelectedCollection.maxSelectableReached());
|
||||
}
|
||||
}
|
||||
updateSize(item);
|
||||
}
|
||||
mPreviousPos = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
|
||||
}
|
||||
|
||||
private void updateApplyButton() {
|
||||
int selectedCount = mSelectedCollection.count();
|
||||
if (selectedCount == 0) {
|
||||
mButtonApply.setText(R.string.button_apply_default);
|
||||
mButtonApply.setEnabled(false);
|
||||
} else if (selectedCount == 1 && mSpec.singleSelectionModeEnabled()) {
|
||||
mButtonApply.setText(R.string.button_apply_default);
|
||||
mButtonApply.setEnabled(true);
|
||||
} else {
|
||||
mButtonApply.setEnabled(true);
|
||||
mButtonApply.setText(getString(R.string.button_apply, selectedCount));
|
||||
}
|
||||
|
||||
if (mSpec.originalable) {
|
||||
mOriginalLayout.setVisibility(View.VISIBLE);
|
||||
updateOriginalState();
|
||||
} else {
|
||||
mOriginalLayout.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void updateOriginalState() {
|
||||
mOriginal.setChecked(mOriginalEnable);
|
||||
if (!mOriginalEnable) {
|
||||
mOriginal.setColor(Color.WHITE);
|
||||
}
|
||||
|
||||
if (countOverMaxSize() > 0) {
|
||||
|
||||
if (mOriginalEnable) {
|
||||
IncapableDialog incapableDialog = IncapableDialog.newInstance("",
|
||||
getString(R.string.error_over_original_size, mSpec.originalMaxSize));
|
||||
incapableDialog.show(getSupportFragmentManager(),
|
||||
IncapableDialog.class.getName());
|
||||
|
||||
mOriginal.setChecked(false);
|
||||
mOriginal.setColor(Color.WHITE);
|
||||
mOriginalEnable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int countOverMaxSize() {
|
||||
int count = 0;
|
||||
int selectedCount = mSelectedCollection.count();
|
||||
for (int i = 0; i < selectedCount; i++) {
|
||||
Item item = mSelectedCollection.asList().get(i);
|
||||
if (item.isImage()) {
|
||||
float size = PhotoMetadataUtils.getSizeInMB(item.size);
|
||||
if (size > mSpec.originalMaxSize) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
protected void updateSize(Item item) {
|
||||
if (item.isGif()) {
|
||||
mSize.setVisibility(View.VISIBLE);
|
||||
mSize.setText(PhotoMetadataUtils.getSizeInMB(item.size) + "M");
|
||||
} else {
|
||||
mSize.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (item.isVideo()) {
|
||||
mOriginalLayout.setVisibility(View.GONE);
|
||||
} else if (mSpec.originalable) {
|
||||
mOriginalLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
protected void sendBackResult(boolean apply) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(EXTRA_RESULT_BUNDLE, mSelectedCollection.getDataWithBundle());
|
||||
intent.putExtra(EXTRA_RESULT_APPLY, apply);
|
||||
intent.putExtra(EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);
|
||||
setResult(Activity.RESULT_OK, intent);
|
||||
}
|
||||
|
||||
private boolean assertAddSelection(Item item) {
|
||||
IncapableCause cause = mSelectedCollection.isAcceptable(item);
|
||||
IncapableCause.handleCause(this, cause);
|
||||
return cause == null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
import com.zhihu.matisse.internal.entity.Album;
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
import com.zhihu.matisse.internal.model.AlbumMediaCollection;
|
||||
import com.zhihu.matisse.internal.model.SelectedItemCollection;
|
||||
import com.zhihu.matisse.internal.ui.adapter.AlbumMediaAdapter;
|
||||
import com.zhihu.matisse.internal.ui.widget.MediaGridInset;
|
||||
import com.zhihu.matisse.internal.utils.UIUtils;
|
||||
|
||||
public class MediaSelectionFragment extends Fragment implements
|
||||
AlbumMediaCollection.AlbumMediaCallbacks, AlbumMediaAdapter.CheckStateListener,
|
||||
AlbumMediaAdapter.OnMediaClickListener {
|
||||
|
||||
public static final String EXTRA_ALBUM = "extra_album";
|
||||
|
||||
private final AlbumMediaCollection mAlbumMediaCollection = new AlbumMediaCollection();
|
||||
private RecyclerView mRecyclerView;
|
||||
private AlbumMediaAdapter mAdapter;
|
||||
private SelectionProvider mSelectionProvider;
|
||||
private AlbumMediaAdapter.CheckStateListener mCheckStateListener;
|
||||
private AlbumMediaAdapter.OnMediaClickListener mOnMediaClickListener;
|
||||
|
||||
public static MediaSelectionFragment newInstance(Album album) {
|
||||
MediaSelectionFragment fragment = new MediaSelectionFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelable(EXTRA_ALBUM, album);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof SelectionProvider) {
|
||||
mSelectionProvider = (SelectionProvider) context;
|
||||
} else {
|
||||
throw new IllegalStateException("Context must implement SelectionProvider.");
|
||||
}
|
||||
if (context instanceof AlbumMediaAdapter.CheckStateListener) {
|
||||
mCheckStateListener = (AlbumMediaAdapter.CheckStateListener) context;
|
||||
}
|
||||
if (context instanceof AlbumMediaAdapter.OnMediaClickListener) {
|
||||
mOnMediaClickListener = (AlbumMediaAdapter.OnMediaClickListener) context;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_media_selection, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
Album album = getArguments().getParcelable(EXTRA_ALBUM);
|
||||
|
||||
mAdapter = new AlbumMediaAdapter(getContext(),
|
||||
mSelectionProvider.provideSelectedItemCollection(), mRecyclerView);
|
||||
mAdapter.registerCheckStateListener(this);
|
||||
mAdapter.registerOnMediaClickListener(this);
|
||||
mRecyclerView.setHasFixedSize(true);
|
||||
|
||||
int spanCount;
|
||||
SelectionSpec selectionSpec = SelectionSpec.getInstance();
|
||||
if (selectionSpec.gridExpectedSize > 0) {
|
||||
spanCount = UIUtils.spanCount(getContext(), selectionSpec.gridExpectedSize);
|
||||
} else {
|
||||
spanCount = selectionSpec.spanCount;
|
||||
}
|
||||
mRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), spanCount));
|
||||
|
||||
int spacing = getResources().getDimensionPixelSize(R.dimen.media_grid_spacing);
|
||||
mRecyclerView.addItemDecoration(new MediaGridInset(spanCount, spacing, false));
|
||||
mRecyclerView.setAdapter(mAdapter);
|
||||
mAlbumMediaCollection.onCreate(getActivity(), this);
|
||||
mAlbumMediaCollection.load(album, selectionSpec.capture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
mAlbumMediaCollection.onDestroy();
|
||||
}
|
||||
|
||||
public void refreshMediaGrid() {
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void refreshSelection() {
|
||||
mAdapter.refreshSelection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAlbumMediaLoad(Cursor cursor) {
|
||||
mAdapter.swapCursor(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAlbumMediaReset() {
|
||||
mAdapter.swapCursor(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
// notify outer Activity that check state changed
|
||||
if (mCheckStateListener != null) {
|
||||
mCheckStateListener.onUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaClick(Album album, Item item, int adapterPosition) {
|
||||
if (mOnMediaClickListener != null) {
|
||||
mOnMediaClickListener.onMediaClick((Album) getArguments().getParcelable(EXTRA_ALBUM),
|
||||
item, adapterPosition);
|
||||
}
|
||||
}
|
||||
|
||||
public interface SelectionProvider {
|
||||
SelectedItemCollection provideSelectedItemCollection();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Point;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
import com.zhihu.matisse.internal.utils.PhotoMetadataUtils;
|
||||
import com.zhihu.matisse.listener.OnFragmentInteractionListener;
|
||||
|
||||
import it.sephiroth.android.library.imagezoom.ImageViewTouch;
|
||||
import it.sephiroth.android.library.imagezoom.ImageViewTouchBase;
|
||||
|
||||
public class PreviewItemFragment extends Fragment {
|
||||
|
||||
private static final String ARGS_ITEM = "args_item";
|
||||
private OnFragmentInteractionListener mListener;
|
||||
|
||||
public static PreviewItemFragment newInstance(Item item) {
|
||||
PreviewItemFragment fragment = new PreviewItemFragment();
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(ARGS_ITEM, item);
|
||||
fragment.setArguments(bundle);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_preview_item, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
final Item item = getArguments().getParcelable(ARGS_ITEM);
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
View videoPlayButton = view.findViewById(R.id.video_play_button);
|
||||
if (item.isVideo()) {
|
||||
videoPlayButton.setVisibility(View.VISIBLE);
|
||||
videoPlayButton.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(item.uri, "video/*");
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(getContext(), R.string.error_no_video_activity, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
videoPlayButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
ImageViewTouch image = (ImageViewTouch) view.findViewById(R.id.image_view);
|
||||
image.setDisplayType(ImageViewTouchBase.DisplayType.FIT_TO_SCREEN);
|
||||
|
||||
image.setSingleTapListener(() -> {
|
||||
if (mListener != null) {
|
||||
mListener.onClick();
|
||||
}
|
||||
});
|
||||
|
||||
Point size = PhotoMetadataUtils.getBitmapSize(item.getContentUri(), getActivity());
|
||||
if (item.isGif()) {
|
||||
SelectionSpec.getInstance().imageEngine.loadGifImage(getContext(), size.x, size.y, image,
|
||||
item.getContentUri());
|
||||
} else {
|
||||
SelectionSpec.getInstance().imageEngine.loadImage(getContext(), size.x, size.y, image,
|
||||
item.getContentUri());
|
||||
}
|
||||
}
|
||||
|
||||
public void resetView() {
|
||||
if (getView() != null) {
|
||||
((ImageViewTouch) getView().findViewById(R.id.image_view)).resetMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof OnFragmentInteractionListener) {
|
||||
mListener = (OnFragmentInteractionListener) context;
|
||||
} else {
|
||||
throw new RuntimeException(context.toString()
|
||||
+ " must implement OnFragmentInteractionListener");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
mListener = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
import com.zhihu.matisse.internal.model.SelectedItemCollection;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SelectedPreviewActivity extends BasePreviewActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (!SelectionSpec.getInstance().hasInited) {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
Bundle bundle = getIntent().getBundleExtra(EXTRA_DEFAULT_BUNDLE);
|
||||
List<Item> selected = bundle.getParcelableArrayList(SelectedItemCollection.STATE_SELECTION);
|
||||
mAdapter.addAll(selected);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
if (mSpec.countable) {
|
||||
mCheckView.setCheckedNum(1);
|
||||
} else {
|
||||
mCheckView.setChecked(true);
|
||||
}
|
||||
mPreviousPos = 0;
|
||||
updateSize(selected.get(0));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
import com.zhihu.matisse.internal.entity.Album;
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
import com.zhihu.matisse.internal.entity.IncapableCause;
|
||||
import com.zhihu.matisse.internal.model.SelectedItemCollection;
|
||||
import com.zhihu.matisse.internal.ui.widget.CheckView;
|
||||
import com.zhihu.matisse.internal.ui.widget.MediaGrid;
|
||||
|
||||
public class AlbumMediaAdapter extends
|
||||
RecyclerViewCursorAdapter<RecyclerView.ViewHolder> implements
|
||||
MediaGrid.OnMediaGridClickListener {
|
||||
|
||||
private static final int VIEW_TYPE_CAPTURE = 0x01;
|
||||
private static final int VIEW_TYPE_MEDIA = 0x02;
|
||||
private final SelectedItemCollection mSelectedCollection;
|
||||
private final Drawable mPlaceholder;
|
||||
private SelectionSpec mSelectionSpec;
|
||||
private CheckStateListener mCheckStateListener;
|
||||
private OnMediaClickListener mOnMediaClickListener;
|
||||
private RecyclerView mRecyclerView;
|
||||
private int mImageResize;
|
||||
|
||||
public AlbumMediaAdapter(Context context, SelectedItemCollection selectedCollection, RecyclerView recyclerView) {
|
||||
super(null);
|
||||
mSelectionSpec = SelectionSpec.getInstance();
|
||||
mSelectedCollection = selectedCollection;
|
||||
|
||||
TypedArray ta = context.getTheme().obtainStyledAttributes(new int[]{R.attr.item_placeholder});
|
||||
mPlaceholder = ta.getDrawable(0);
|
||||
ta.recycle();
|
||||
|
||||
mRecyclerView = recyclerView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
if (viewType == VIEW_TYPE_CAPTURE) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.photo_capture_item, parent, false);
|
||||
CaptureViewHolder holder = new CaptureViewHolder(v);
|
||||
holder.itemView.setOnClickListener(v1 -> {
|
||||
if (v1.getContext() instanceof OnPhotoCapture) {
|
||||
((OnPhotoCapture) v1.getContext()).capture();
|
||||
}
|
||||
});
|
||||
return holder;
|
||||
} else if (viewType == VIEW_TYPE_MEDIA) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.media_grid_item, parent, false);
|
||||
return new MediaViewHolder(v);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindViewHolder(final RecyclerView.ViewHolder holder, Cursor cursor) {
|
||||
if (holder instanceof CaptureViewHolder) {
|
||||
CaptureViewHolder captureViewHolder = (CaptureViewHolder) holder;
|
||||
Drawable[] drawables = captureViewHolder.mHint.getCompoundDrawables();
|
||||
TypedArray ta = holder.itemView.getContext().getTheme().obtainStyledAttributes(
|
||||
new int[]{R.attr.capture_textColor});
|
||||
int color = ta.getColor(0, 0);
|
||||
ta.recycle();
|
||||
|
||||
for (int i = 0; i < drawables.length; i++) {
|
||||
Drawable drawable = drawables[i];
|
||||
if (drawable != null) {
|
||||
final Drawable.ConstantState state = drawable.getConstantState();
|
||||
if (state == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Drawable newDrawable = state.newDrawable().mutate();
|
||||
newDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
|
||||
newDrawable.setBounds(drawable.getBounds());
|
||||
drawables[i] = newDrawable;
|
||||
}
|
||||
}
|
||||
captureViewHolder.mHint.setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3]);
|
||||
} else if (holder instanceof MediaViewHolder) {
|
||||
MediaViewHolder mediaViewHolder = (MediaViewHolder) holder;
|
||||
|
||||
final Item item = Item.valueOf(cursor);
|
||||
mediaViewHolder.mMediaGrid.preBindMedia(new MediaGrid.PreBindInfo(
|
||||
getImageResize(mediaViewHolder.mMediaGrid.getContext()),
|
||||
mPlaceholder,
|
||||
mSelectionSpec.countable,
|
||||
holder
|
||||
));
|
||||
mediaViewHolder.mMediaGrid.bindMedia(item);
|
||||
mediaViewHolder.mMediaGrid.setOnMediaGridClickListener(this);
|
||||
setCheckStatus(item, mediaViewHolder.mMediaGrid);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCheckStatus(Item item, MediaGrid mediaGrid) {
|
||||
if (mSelectionSpec.countable) {
|
||||
int checkedNum = mSelectedCollection.checkedNumOf(item);
|
||||
if (checkedNum > 0) {
|
||||
mediaGrid.setCheckEnabled(true);
|
||||
mediaGrid.setCheckedNum(checkedNum);
|
||||
} else {
|
||||
if (mSelectedCollection.maxSelectableReached()) {
|
||||
mediaGrid.setCheckEnabled(false);
|
||||
mediaGrid.setCheckedNum(CheckView.UNCHECKED);
|
||||
} else {
|
||||
mediaGrid.setCheckEnabled(true);
|
||||
mediaGrid.setCheckedNum(checkedNum);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
boolean selected = mSelectedCollection.isSelected(item);
|
||||
if (selected) {
|
||||
mediaGrid.setCheckEnabled(true);
|
||||
mediaGrid.setChecked(true);
|
||||
} else {
|
||||
if (mSelectedCollection.maxSelectableReached()) {
|
||||
mediaGrid.setCheckEnabled(false);
|
||||
mediaGrid.setChecked(false);
|
||||
} else {
|
||||
mediaGrid.setCheckEnabled(true);
|
||||
mediaGrid.setChecked(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThumbnailClicked(ImageView thumbnail, Item item, RecyclerView.ViewHolder holder) {
|
||||
if (mSelectionSpec.showPreview) {
|
||||
if (mOnMediaClickListener != null) {
|
||||
mOnMediaClickListener.onMediaClick(null, item, holder.getAdapterPosition());
|
||||
}
|
||||
} else {
|
||||
updateSelectedItem(item, holder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckViewClicked(CheckView checkView, Item item, RecyclerView.ViewHolder holder) {
|
||||
updateSelectedItem(item, holder);
|
||||
}
|
||||
|
||||
private void updateSelectedItem(Item item, RecyclerView.ViewHolder holder) {
|
||||
if (mSelectionSpec.countable) {
|
||||
int checkedNum = mSelectedCollection.checkedNumOf(item);
|
||||
if (checkedNum == CheckView.UNCHECKED) {
|
||||
if (assertAddSelection(holder.itemView.getContext(), item)) {
|
||||
mSelectedCollection.add(item);
|
||||
notifyCheckStateChanged();
|
||||
}
|
||||
} else {
|
||||
mSelectedCollection.remove(item);
|
||||
notifyCheckStateChanged();
|
||||
}
|
||||
} else {
|
||||
if (mSelectedCollection.isSelected(item)) {
|
||||
mSelectedCollection.remove(item);
|
||||
notifyCheckStateChanged();
|
||||
} else {
|
||||
if (assertAddSelection(holder.itemView.getContext(), item)) {
|
||||
mSelectedCollection.add(item);
|
||||
notifyCheckStateChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyCheckStateChanged() {
|
||||
notifyDataSetChanged();
|
||||
if (mCheckStateListener != null) {
|
||||
mCheckStateListener.onUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position, Cursor cursor) {
|
||||
return Item.valueOf(cursor).isCapture() ? VIEW_TYPE_CAPTURE : VIEW_TYPE_MEDIA;
|
||||
}
|
||||
|
||||
private boolean assertAddSelection(Context context, Item item) {
|
||||
IncapableCause cause = mSelectedCollection.isAcceptable(item);
|
||||
IncapableCause.handleCause(context, cause);
|
||||
return cause == null;
|
||||
}
|
||||
|
||||
|
||||
public void registerCheckStateListener(CheckStateListener listener) {
|
||||
mCheckStateListener = listener;
|
||||
}
|
||||
|
||||
public void unregisterCheckStateListener() {
|
||||
mCheckStateListener = null;
|
||||
}
|
||||
|
||||
public void registerOnMediaClickListener(OnMediaClickListener listener) {
|
||||
mOnMediaClickListener = listener;
|
||||
}
|
||||
|
||||
public void unregisterOnMediaClickListener() {
|
||||
mOnMediaClickListener = null;
|
||||
}
|
||||
|
||||
public void refreshSelection() {
|
||||
GridLayoutManager layoutManager = (GridLayoutManager) mRecyclerView.getLayoutManager();
|
||||
int first = layoutManager.findFirstVisibleItemPosition();
|
||||
int last = layoutManager.findLastVisibleItemPosition();
|
||||
if (first == -1 || last == -1) {
|
||||
return;
|
||||
}
|
||||
Cursor cursor = getCursor();
|
||||
for (int i = first; i <= last; i++) {
|
||||
RecyclerView.ViewHolder holder = mRecyclerView.findViewHolderForAdapterPosition(first);
|
||||
if (holder instanceof MediaViewHolder) {
|
||||
if (cursor.moveToPosition(i)) {
|
||||
setCheckStatus(Item.valueOf(cursor), ((MediaViewHolder) holder).mMediaGrid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getImageResize(Context context) {
|
||||
if (mImageResize == 0) {
|
||||
RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
|
||||
int spanCount = ((GridLayoutManager) lm).getSpanCount();
|
||||
int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
|
||||
int availableWidth = screenWidth - context.getResources().getDimensionPixelSize(
|
||||
R.dimen.media_grid_spacing) * (spanCount - 1);
|
||||
mImageResize = availableWidth / spanCount;
|
||||
mImageResize = (int) (mImageResize * mSelectionSpec.thumbnailScale);
|
||||
}
|
||||
return mImageResize;
|
||||
}
|
||||
|
||||
public interface CheckStateListener {
|
||||
void onUpdate();
|
||||
}
|
||||
|
||||
public interface OnMediaClickListener {
|
||||
void onMediaClick(Album album, Item item, int adapterPosition);
|
||||
}
|
||||
|
||||
public interface OnPhotoCapture {
|
||||
void capture();
|
||||
}
|
||||
|
||||
private static class MediaViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private MediaGrid mMediaGrid;
|
||||
|
||||
MediaViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
mMediaGrid = (MediaGrid) itemView;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CaptureViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private TextView mHint;
|
||||
|
||||
CaptureViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
mHint = (TextView) itemView.findViewById(R.id.hint);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CursorAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
import com.zhihu.matisse.internal.entity.Album;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
|
||||
public class AlbumsAdapter extends CursorAdapter {
|
||||
|
||||
private final Drawable mPlaceholder;
|
||||
|
||||
public AlbumsAdapter(Context context, Cursor c, boolean autoRequery) {
|
||||
super(context, c, autoRequery);
|
||||
|
||||
TypedArray ta = context.getTheme().obtainStyledAttributes(
|
||||
new int[]{R.attr.album_thumbnail_placeholder});
|
||||
mPlaceholder = ta.getDrawable(0);
|
||||
ta.recycle();
|
||||
}
|
||||
|
||||
public AlbumsAdapter(Context context, Cursor c, int flags) {
|
||||
super(context, c, flags);
|
||||
|
||||
TypedArray ta = context.getTheme().obtainStyledAttributes(
|
||||
new int[]{R.attr.album_thumbnail_placeholder});
|
||||
mPlaceholder = ta.getDrawable(0);
|
||||
ta.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
return LayoutInflater.from(context).inflate(R.layout.album_list_item, parent, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
Album album = Album.valueOf(cursor);
|
||||
((TextView) view.findViewById(R.id.album_name)).setText(album.getDisplayName(context));
|
||||
((TextView) view.findViewById(R.id.album_media_count)).setText(String.valueOf(album.getCount()));
|
||||
|
||||
// do not need to load animated Gif
|
||||
SelectionSpec.getInstance().imageEngine.loadThumbnail(context, context.getResources().getDimensionPixelSize(R
|
||||
.dimen.media_grid_size), mPlaceholder,
|
||||
(ImageView) view.findViewById(R.id.album_cover), album.getCoverUri());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui.adapter;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.ui.PreviewItemFragment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PreviewPagerAdapter extends FragmentPagerAdapter {
|
||||
|
||||
private ArrayList<Item> mItems = new ArrayList<>();
|
||||
private OnPrimaryItemSetListener mListener;
|
||||
|
||||
public PreviewPagerAdapter(FragmentManager manager, OnPrimaryItemSetListener listener) {
|
||||
super(manager);
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return PreviewItemFragment.newInstance(mItems.get(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrimaryItem(ViewGroup container, int position, Object object) {
|
||||
super.setPrimaryItem(container, position, object);
|
||||
if (mListener != null) {
|
||||
mListener.onPrimaryItemSet(position);
|
||||
}
|
||||
}
|
||||
|
||||
public Item getMediaItem(int position) {
|
||||
return mItems.get(position);
|
||||
}
|
||||
|
||||
public void addAll(List<Item> items) {
|
||||
mItems.addAll(items);
|
||||
}
|
||||
|
||||
interface OnPrimaryItemSetListener {
|
||||
|
||||
void onPrimaryItemSet(int position);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui.adapter;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.provider.MediaStore;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public abstract class RecyclerViewCursorAdapter<VH extends RecyclerView.ViewHolder> extends
|
||||
RecyclerView.Adapter<VH> {
|
||||
|
||||
private Cursor mCursor;
|
||||
private int mRowIDColumn;
|
||||
|
||||
RecyclerViewCursorAdapter(Cursor c) {
|
||||
setHasStableIds(true);
|
||||
swapCursor(c);
|
||||
}
|
||||
|
||||
protected abstract void onBindViewHolder(VH holder, Cursor cursor);
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(VH holder, int position) {
|
||||
if (!isDataValid(mCursor)) {
|
||||
throw new IllegalStateException("Cannot bind view holder when cursor is in invalid state.");
|
||||
}
|
||||
if (!mCursor.moveToPosition(position)) {
|
||||
throw new IllegalStateException("Could not move cursor to position " + position
|
||||
+ " when trying to bind view holder");
|
||||
}
|
||||
|
||||
onBindViewHolder(holder, mCursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (!mCursor.moveToPosition(position)) {
|
||||
throw new IllegalStateException("Could not move cursor to position " + position
|
||||
+ " when trying to get item view type.");
|
||||
}
|
||||
return getItemViewType(position, mCursor);
|
||||
}
|
||||
|
||||
protected abstract int getItemViewType(int position, Cursor cursor);
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
if (isDataValid(mCursor)) {
|
||||
return mCursor.getCount();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
if (!isDataValid(mCursor)) {
|
||||
throw new IllegalStateException("Cannot lookup item id when cursor is in invalid state.");
|
||||
}
|
||||
if (!mCursor.moveToPosition(position)) {
|
||||
throw new IllegalStateException("Could not move cursor to position " + position
|
||||
+ " when trying to get an item id");
|
||||
}
|
||||
|
||||
return mCursor.getLong(mRowIDColumn);
|
||||
}
|
||||
|
||||
public void swapCursor(Cursor newCursor) {
|
||||
if (newCursor == mCursor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newCursor != null) {
|
||||
mCursor = newCursor;
|
||||
mRowIDColumn = mCursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID);
|
||||
// notify the observers about the new cursor
|
||||
notifyDataSetChanged();
|
||||
} else {
|
||||
notifyItemRangeRemoved(0, getItemCount());
|
||||
mCursor = null;
|
||||
mRowIDColumn = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public Cursor getCursor() {
|
||||
return mCursor;
|
||||
}
|
||||
|
||||
private boolean isDataValid(Cursor cursor) {
|
||||
return cursor != null && !cursor.isClosed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.ListPopupWindow;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.CursorAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
import com.zhihu.matisse.internal.entity.Album;
|
||||
import com.zhihu.matisse.internal.utils.Platform;
|
||||
|
||||
public class AlbumsSpinner {
|
||||
|
||||
private static final int MAX_SHOWN_COUNT = 6;
|
||||
private CursorAdapter mAdapter;
|
||||
private TextView mSelected;
|
||||
private ListPopupWindow mListPopupWindow;
|
||||
private AdapterView.OnItemSelectedListener mOnItemSelectedListener;
|
||||
|
||||
public AlbumsSpinner(@NonNull Context context) {
|
||||
mListPopupWindow = new ListPopupWindow(context, null, R.attr.listPopupWindowStyle);
|
||||
mListPopupWindow.setModal(true);
|
||||
float density = context.getResources().getDisplayMetrics().density;
|
||||
mListPopupWindow.setContentWidth(ListPopupWindow.MATCH_PARENT);
|
||||
mListPopupWindow.setHorizontalOffset((int) (0 * density));
|
||||
mListPopupWindow.setVerticalOffset((int) (0 * density));
|
||||
mListPopupWindow.setOnItemClickListener((parent, view, position, id) -> {
|
||||
AlbumsSpinner.this.onItemSelected(parent.getContext(), position);
|
||||
if (mOnItemSelectedListener != null) {
|
||||
mOnItemSelectedListener.onItemSelected(parent, view, position, id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
|
||||
mOnItemSelectedListener = listener;
|
||||
}
|
||||
|
||||
public void setSelection(Context context, int position) {
|
||||
mListPopupWindow.setSelection(position);
|
||||
onItemSelected(context, position);
|
||||
}
|
||||
|
||||
private void onItemSelected(Context context, int position) {
|
||||
mListPopupWindow.dismiss();
|
||||
Cursor cursor = mAdapter.getCursor();
|
||||
cursor.moveToPosition(position);
|
||||
Album album = Album.valueOf(cursor);
|
||||
String displayName = album.getDisplayName(context);
|
||||
if (mSelected.getVisibility() == View.VISIBLE) {
|
||||
mSelected.setText(displayName);
|
||||
} else {
|
||||
if (Platform.hasICS()) {
|
||||
mSelected.setAlpha(0.0f);
|
||||
mSelected.setVisibility(View.VISIBLE);
|
||||
mSelected.setText(displayName);
|
||||
mSelected.animate().alpha(1.0f).setDuration(context.getResources().getInteger(
|
||||
android.R.integer.config_longAnimTime)).start();
|
||||
} else {
|
||||
mSelected.setVisibility(View.VISIBLE);
|
||||
mSelected.setText(displayName);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void setAdapter(CursorAdapter adapter) {
|
||||
mListPopupWindow.setAdapter(adapter);
|
||||
mAdapter = adapter;
|
||||
}
|
||||
|
||||
public void setSelectedTextView(TextView textView) {
|
||||
mSelected = textView;
|
||||
// tint dropdown arrow icon
|
||||
Drawable[] drawables = mSelected.getCompoundDrawables();
|
||||
Drawable right = drawables[2];
|
||||
TypedArray ta = mSelected.getContext().getTheme().obtainStyledAttributes(
|
||||
new int[]{R.attr.album_element_color});
|
||||
int color = ta.getColor(0, 0);
|
||||
ta.recycle();
|
||||
right.setColorFilter(color, PorterDuff.Mode.SRC_IN);
|
||||
|
||||
mSelected.setVisibility(View.GONE);
|
||||
mSelected.setOnClickListener(v -> {
|
||||
int itemHeight = v.getResources().getDimensionPixelSize(R.dimen.album_item_height);
|
||||
mListPopupWindow.setHeight(
|
||||
mAdapter.getCount() > MAX_SHOWN_COUNT ? itemHeight * MAX_SHOWN_COUNT
|
||||
: itemHeight * mAdapter.getCount());
|
||||
mListPopupWindow.show();
|
||||
});
|
||||
mSelected.setOnTouchListener(mListPopupWindow.createDragToOpenListener(mSelected));
|
||||
}
|
||||
|
||||
public void setPopupAnchorView(View view) {
|
||||
mListPopupWindow.setAnchorView(view);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.zhihu.matisse.internal.ui.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
|
||||
public class CheckRadioView extends AppCompatImageView {
|
||||
|
||||
private Drawable mDrawable;
|
||||
|
||||
private int mSelectedColor;
|
||||
private int mUnSelectUdColor;
|
||||
|
||||
public CheckRadioView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public CheckRadioView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mSelectedColor = ResourcesCompat.getColor(
|
||||
getResources(), R.color.zhihu_item_checkCircle_backgroundColor,
|
||||
getContext().getTheme());
|
||||
mUnSelectUdColor = ResourcesCompat.getColor(
|
||||
getResources(), R.color.zhihu_check_original_radio_disable,
|
||||
getContext().getTheme());
|
||||
setChecked(false);
|
||||
}
|
||||
|
||||
public void setChecked(boolean enable) {
|
||||
if (enable) {
|
||||
setImageResource(R.drawable.ic_preview_radio_on);
|
||||
mDrawable = getDrawable();
|
||||
mDrawable.setColorFilter(mSelectedColor, PorterDuff.Mode.SRC_IN);
|
||||
} else {
|
||||
setImageResource(R.drawable.ic_preview_radio_off);
|
||||
mDrawable = getDrawable();
|
||||
mDrawable.setColorFilter(mUnSelectUdColor, PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setColor(int color) {
|
||||
if (mDrawable == null) {
|
||||
mDrawable = getDrawable();
|
||||
}
|
||||
mDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.RadialGradient;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import android.text.TextPaint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
|
||||
public class CheckView extends View {
|
||||
|
||||
public static final int UNCHECKED = Integer.MIN_VALUE;
|
||||
private static final float STROKE_WIDTH = 3.0f; // dp
|
||||
private static final float SHADOW_WIDTH = 6.0f; // dp
|
||||
private static final int SIZE = 48; // dp
|
||||
private static final float STROKE_RADIUS = 11.5f; // dp
|
||||
private static final float BG_RADIUS = 11.0f; // dp
|
||||
private static final int CONTENT_SIZE = 16; // dp
|
||||
private boolean mCountable;
|
||||
private boolean mChecked;
|
||||
private int mCheckedNum;
|
||||
private Paint mStrokePaint;
|
||||
private Paint mBackgroundPaint;
|
||||
private TextPaint mTextPaint;
|
||||
private Paint mShadowPaint;
|
||||
private Drawable mCheckDrawable;
|
||||
private float mDensity;
|
||||
private Rect mCheckRect;
|
||||
private boolean mEnabled = true;
|
||||
|
||||
public CheckView(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public CheckView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public CheckView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
// fixed size 48dp x 48dp
|
||||
int sizeSpec = MeasureSpec.makeMeasureSpec((int) (SIZE * mDensity), MeasureSpec.EXACTLY);
|
||||
super.onMeasure(sizeSpec, sizeSpec);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
mDensity = context.getResources().getDisplayMetrics().density;
|
||||
|
||||
mStrokePaint = new Paint();
|
||||
mStrokePaint.setAntiAlias(true);
|
||||
mStrokePaint.setStyle(Paint.Style.STROKE);
|
||||
mStrokePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
|
||||
mStrokePaint.setStrokeWidth(STROKE_WIDTH * mDensity);
|
||||
TypedArray ta = getContext().getTheme().obtainStyledAttributes(new int[]{R.attr.item_checkCircle_borderColor});
|
||||
int defaultColor = ResourcesCompat.getColor(
|
||||
getResources(), R.color.zhihu_item_checkCircle_borderColor,
|
||||
getContext().getTheme());
|
||||
int color = ta.getColor(0, defaultColor);
|
||||
ta.recycle();
|
||||
mStrokePaint.setColor(color);
|
||||
|
||||
mCheckDrawable = ResourcesCompat.getDrawable(context.getResources(),
|
||||
R.drawable.ic_check_white_18dp, context.getTheme());
|
||||
}
|
||||
|
||||
public void setChecked(boolean checked) {
|
||||
if (mCountable) {
|
||||
throw new IllegalStateException("CheckView is countable, call setCheckedNum() instead.");
|
||||
}
|
||||
mChecked = checked;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setCountable(boolean countable) {
|
||||
mCountable = countable;
|
||||
}
|
||||
|
||||
public void setCheckedNum(int checkedNum) {
|
||||
if (!mCountable) {
|
||||
throw new IllegalStateException("CheckView is not countable, call setChecked() instead.");
|
||||
}
|
||||
if (checkedNum != UNCHECKED && checkedNum <= 0) {
|
||||
throw new IllegalArgumentException("checked num can't be negative.");
|
||||
}
|
||||
mCheckedNum = checkedNum;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
if (mEnabled != enabled) {
|
||||
mEnabled = enabled;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
// draw outer and inner shadow
|
||||
initShadowPaint();
|
||||
canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2,
|
||||
(STROKE_RADIUS + STROKE_WIDTH / 2 + SHADOW_WIDTH) * mDensity, mShadowPaint);
|
||||
|
||||
// draw white stroke
|
||||
canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2,
|
||||
STROKE_RADIUS * mDensity, mStrokePaint);
|
||||
|
||||
// draw content
|
||||
if (mCountable) {
|
||||
if (mCheckedNum != UNCHECKED) {
|
||||
initBackgroundPaint();
|
||||
canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2,
|
||||
BG_RADIUS * mDensity, mBackgroundPaint);
|
||||
initTextPaint();
|
||||
String text = String.valueOf(mCheckedNum);
|
||||
int baseX = (int) (canvas.getWidth() - mTextPaint.measureText(text)) / 2;
|
||||
int baseY = (int) (canvas.getHeight() - mTextPaint.descent() - mTextPaint.ascent()) / 2;
|
||||
canvas.drawText(text, baseX, baseY, mTextPaint);
|
||||
}
|
||||
} else {
|
||||
if (mChecked) {
|
||||
initBackgroundPaint();
|
||||
canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2,
|
||||
BG_RADIUS * mDensity, mBackgroundPaint);
|
||||
|
||||
mCheckDrawable.setBounds(getCheckRect());
|
||||
mCheckDrawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
// enable hint
|
||||
setAlpha(mEnabled ? 1.0f : 0.5f);
|
||||
}
|
||||
|
||||
private void initShadowPaint() {
|
||||
if (mShadowPaint == null) {
|
||||
mShadowPaint = new Paint();
|
||||
mShadowPaint.setAntiAlias(true);
|
||||
// all in dp
|
||||
float outerRadius = STROKE_RADIUS + STROKE_WIDTH / 2;
|
||||
float innerRadius = outerRadius - STROKE_WIDTH;
|
||||
float gradientRadius = outerRadius + SHADOW_WIDTH;
|
||||
float stop0 = (innerRadius - SHADOW_WIDTH) / gradientRadius;
|
||||
float stop1 = innerRadius / gradientRadius;
|
||||
float stop2 = outerRadius / gradientRadius;
|
||||
float stop3 = 1.0f;
|
||||
mShadowPaint.setShader(
|
||||
new RadialGradient((float) SIZE * mDensity / 2,
|
||||
(float) SIZE * mDensity / 2,
|
||||
gradientRadius * mDensity,
|
||||
new int[]{Color.parseColor("#00000000"), Color.parseColor("#0D000000"),
|
||||
Color.parseColor("#0D000000"), Color.parseColor("#00000000")},
|
||||
new float[]{stop0, stop1, stop2, stop3},
|
||||
Shader.TileMode.CLAMP));
|
||||
}
|
||||
}
|
||||
|
||||
private void initBackgroundPaint() {
|
||||
if (mBackgroundPaint == null) {
|
||||
mBackgroundPaint = new Paint();
|
||||
mBackgroundPaint.setAntiAlias(true);
|
||||
mBackgroundPaint.setStyle(Paint.Style.FILL);
|
||||
TypedArray ta = getContext().getTheme()
|
||||
.obtainStyledAttributes(new int[]{R.attr.item_checkCircle_backgroundColor});
|
||||
int defaultColor = ResourcesCompat.getColor(
|
||||
getResources(), R.color.zhihu_item_checkCircle_backgroundColor,
|
||||
getContext().getTheme());
|
||||
int color = ta.getColor(0, defaultColor);
|
||||
ta.recycle();
|
||||
mBackgroundPaint.setColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
private void initTextPaint() {
|
||||
if (mTextPaint == null) {
|
||||
mTextPaint = new TextPaint();
|
||||
mTextPaint.setAntiAlias(true);
|
||||
mTextPaint.setColor(Color.WHITE);
|
||||
mTextPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
|
||||
mTextPaint.setTextSize(12.0f * mDensity);
|
||||
}
|
||||
}
|
||||
|
||||
// rect for drawing checked number or mark
|
||||
private Rect getCheckRect() {
|
||||
if (mCheckRect == null) {
|
||||
int rectPadding = (int) (SIZE * mDensity / 2 - CONTENT_SIZE * mDensity / 2);
|
||||
mCheckRect = new Rect(rectPadding, rectPadding,
|
||||
(int) (SIZE * mDensity - rectPadding), (int) (SIZE * mDensity - rectPadding));
|
||||
}
|
||||
|
||||
return mCheckRect;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui.widget;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
|
||||
public class IncapableDialog extends DialogFragment {
|
||||
|
||||
public static final String EXTRA_TITLE = "extra_title";
|
||||
public static final String EXTRA_MESSAGE = "extra_message";
|
||||
|
||||
public static IncapableDialog newInstance(String title, String message) {
|
||||
IncapableDialog dialog = new IncapableDialog();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(EXTRA_TITLE, title);
|
||||
args.putString(EXTRA_MESSAGE, message);
|
||||
dialog.setArguments(args);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
String title = getArguments().getString(EXTRA_TITLE);
|
||||
String message = getArguments().getString(EXTRA_MESSAGE);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
if (!TextUtils.isEmpty(title)) {
|
||||
builder.setTitle(title);
|
||||
}
|
||||
if (!TextUtils.isEmpty(message)) {
|
||||
builder.setMessage(message);
|
||||
}
|
||||
builder.setPositiveButton(R.string.button_ok, (dialog, which) -> dialog.dismiss());
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
|
||||
public class MediaGrid extends SquareFrameLayout implements View.OnClickListener {
|
||||
|
||||
private ImageView mThumbnail;
|
||||
private CheckView mCheckView;
|
||||
private ImageView mGifTag;
|
||||
private TextView mVideoDuration;
|
||||
|
||||
private Item mMedia;
|
||||
private PreBindInfo mPreBindInfo;
|
||||
private OnMediaGridClickListener mListener;
|
||||
|
||||
public MediaGrid(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public MediaGrid(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
LayoutInflater.from(context).inflate(R.layout.media_grid_content, this, true);
|
||||
|
||||
mThumbnail = (ImageView) findViewById(R.id.media_thumbnail);
|
||||
mCheckView = (CheckView) findViewById(R.id.check_view);
|
||||
mGifTag = (ImageView) findViewById(R.id.gif);
|
||||
mVideoDuration = (TextView) findViewById(R.id.video_duration);
|
||||
|
||||
mThumbnail.setOnClickListener(this);
|
||||
mCheckView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mListener != null) {
|
||||
if (v == mThumbnail) {
|
||||
mListener.onThumbnailClicked(mThumbnail, mMedia, mPreBindInfo.mViewHolder);
|
||||
} else if (v == mCheckView) {
|
||||
mListener.onCheckViewClicked(mCheckView, mMedia, mPreBindInfo.mViewHolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void preBindMedia(PreBindInfo info) {
|
||||
mPreBindInfo = info;
|
||||
}
|
||||
|
||||
public void bindMedia(Item item) {
|
||||
mMedia = item;
|
||||
setGifTag();
|
||||
initCheckView();
|
||||
setImage();
|
||||
setVideoDuration();
|
||||
}
|
||||
|
||||
public Item getMedia() {
|
||||
return mMedia;
|
||||
}
|
||||
|
||||
private void setGifTag() {
|
||||
mGifTag.setVisibility(mMedia.isGif() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void initCheckView() {
|
||||
mCheckView.setCountable(mPreBindInfo.mCheckViewCountable);
|
||||
}
|
||||
|
||||
public void setCheckEnabled(boolean enabled) {
|
||||
mCheckView.setEnabled(enabled);
|
||||
}
|
||||
|
||||
public void setCheckedNum(int checkedNum) {
|
||||
mCheckView.setCheckedNum(checkedNum);
|
||||
}
|
||||
|
||||
public void setChecked(boolean checked) {
|
||||
mCheckView.setChecked(checked);
|
||||
}
|
||||
|
||||
private void setImage() {
|
||||
if (mMedia.isGif()) {
|
||||
SelectionSpec.getInstance().imageEngine.loadGifThumbnail(getContext(), mPreBindInfo.mResize,
|
||||
mPreBindInfo.mPlaceholder, mThumbnail, mMedia.getContentUri());
|
||||
} else {
|
||||
SelectionSpec.getInstance().imageEngine.loadThumbnail(getContext(), mPreBindInfo.mResize,
|
||||
mPreBindInfo.mPlaceholder, mThumbnail, mMedia.getContentUri());
|
||||
}
|
||||
}
|
||||
|
||||
private void setVideoDuration() {
|
||||
if (mMedia.isVideo()) {
|
||||
mVideoDuration.setVisibility(VISIBLE);
|
||||
mVideoDuration.setText(DateUtils.formatElapsedTime(mMedia.duration / 1000));
|
||||
} else {
|
||||
mVideoDuration.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnMediaGridClickListener(OnMediaGridClickListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void removeOnMediaGridClickListener() {
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
public interface OnMediaGridClickListener {
|
||||
|
||||
void onThumbnailClicked(ImageView thumbnail, Item item, RecyclerView.ViewHolder holder);
|
||||
|
||||
void onCheckViewClicked(CheckView checkView, Item item, RecyclerView.ViewHolder holder);
|
||||
}
|
||||
|
||||
public static class PreBindInfo {
|
||||
int mResize;
|
||||
Drawable mPlaceholder;
|
||||
boolean mCheckViewCountable;
|
||||
RecyclerView.ViewHolder mViewHolder;
|
||||
|
||||
public PreBindInfo(int resize, Drawable placeholder, boolean checkViewCountable,
|
||||
RecyclerView.ViewHolder viewHolder) {
|
||||
mResize = resize;
|
||||
mPlaceholder = placeholder;
|
||||
mCheckViewCountable = checkViewCountable;
|
||||
mViewHolder = viewHolder;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui.widget;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
public class MediaGridInset extends RecyclerView.ItemDecoration {
|
||||
|
||||
private int mSpanCount;
|
||||
private int mSpacing;
|
||||
private boolean mIncludeEdge;
|
||||
|
||||
public MediaGridInset(int spanCount, int spacing, boolean includeEdge) {
|
||||
this.mSpanCount = spanCount;
|
||||
this.mSpacing = spacing;
|
||||
this.mIncludeEdge = includeEdge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
|
||||
RecyclerView.State state) {
|
||||
int position = parent.getChildAdapterPosition(view); // item position
|
||||
int column = position % mSpanCount; // item column
|
||||
|
||||
if (mIncludeEdge) {
|
||||
// spacing - column * ((1f / spanCount) * spacing)
|
||||
outRect.left = mSpacing - column * mSpacing / mSpanCount;
|
||||
// (column + 1) * ((1f / spanCount) * spacing)
|
||||
outRect.right = (column + 1) * mSpacing / mSpanCount;
|
||||
|
||||
if (position < mSpanCount) { // top edge
|
||||
outRect.top = mSpacing;
|
||||
}
|
||||
outRect.bottom = mSpacing; // item bottom
|
||||
} else {
|
||||
// column * ((1f / spanCount) * spacing)
|
||||
outRect.left = column * mSpacing / mSpanCount;
|
||||
// spacing - (column + 1) * ((1f / spanCount) * spacing)
|
||||
outRect.right = mSpacing - (column + 1) * mSpacing / mSpanCount;
|
||||
if (position >= mSpanCount) {
|
||||
outRect.top = mSpacing; // item top
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import it.sephiroth.android.library.imagezoom.ImageViewTouch;
|
||||
|
||||
public class PreviewViewPager extends ViewPager {
|
||||
|
||||
public PreviewViewPager(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
|
||||
if (v instanceof ImageViewTouch) {
|
||||
return ((ImageViewTouch) v).canScroll(dx) || super.canScroll(v, checkV, dx, x, y);
|
||||
}
|
||||
return super.canScroll(v, checkV, dx, x, y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.RectF;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
public class RoundedRectangleImageView extends AppCompatImageView {
|
||||
|
||||
private float mRadius; // dp
|
||||
private Path mRoundedRectPath;
|
||||
private RectF mRectF;
|
||||
|
||||
public RoundedRectangleImageView(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public RoundedRectangleImageView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public RoundedRectangleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
float density = context.getResources().getDisplayMetrics().density;
|
||||
mRadius = 2.0f * density;
|
||||
mRoundedRectPath = new Path();
|
||||
mRectF = new RectF();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
mRectF.set(0.0f, 0.0f, getMeasuredWidth(), getMeasuredHeight());
|
||||
mRoundedRectPath.addRoundRect(mRectF, mRadius, mRadius, Path.Direction.CW);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
canvas.clipPath(mRoundedRectPath);
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.ui.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
public class SquareFrameLayout extends FrameLayout {
|
||||
|
||||
public SquareFrameLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SquareFrameLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.utils;
|
||||
|
||||
import android.media.ExifInterface;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Bug fixture for ExifInterface constructor.
|
||||
*/
|
||||
final class ExifInterfaceCompat {
|
||||
private static final String TAG = ExifInterfaceCompat.class.getSimpleName();
|
||||
private static final int EXIF_DEGREE_FALLBACK_VALUE = -1;
|
||||
|
||||
/**
|
||||
* Do not instantiate this class.
|
||||
*/
|
||||
private ExifInterfaceCompat() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new instance of {@link ExifInterface}.
|
||||
* Original constructor won't check filename value, so if null value has been passed,
|
||||
* the process will be killed because of SIGSEGV.
|
||||
* Google Play crash report system cannot perceive this crash, so this method will throw
|
||||
* {@link NullPointerException} when the filename is null.
|
||||
*
|
||||
* @param filename a JPEG filename.
|
||||
* @return {@link ExifInterface} instance.
|
||||
* @throws IOException something wrong with I/O.
|
||||
*/
|
||||
public static ExifInterface newInstance(String filename) throws IOException {
|
||||
if (filename == null) throw new NullPointerException("filename should not be null");
|
||||
return new ExifInterface(filename);
|
||||
}
|
||||
|
||||
private static Date getExifDateTime(String filepath) {
|
||||
ExifInterface exif;
|
||||
try {
|
||||
// ExifInterface does not check whether file path is null or not,
|
||||
// so passing null file path argument to its constructor causing SIGSEGV.
|
||||
// We should avoid such a situation by checking file path string.
|
||||
exif = newInstance(filepath);
|
||||
} catch (IOException ex) {
|
||||
Log.e(TAG, "cannot read exif", ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
String date = exif.getAttribute(ExifInterface.TAG_DATETIME);
|
||||
if (TextUtils.isEmpty(date)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
|
||||
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
return formatter.parse(date);
|
||||
} catch (ParseException e) {
|
||||
Log.d(TAG, "failed to parse date taken", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read exif info and get datetime value of the photo.
|
||||
*
|
||||
* @param filepath to get datetime
|
||||
* @return when a photo taken.
|
||||
*/
|
||||
public static long getExifDateTimeInMillis(String filepath) {
|
||||
Date datetime = getExifDateTime(filepath);
|
||||
if (datetime == null) {
|
||||
return -1;
|
||||
}
|
||||
return datetime.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read exif info and get orientation value of the photo.
|
||||
*
|
||||
* @param filepath to get exif.
|
||||
* @return exif orientation value
|
||||
*/
|
||||
public static int getExifOrientation(String filepath) {
|
||||
ExifInterface exif;
|
||||
try {
|
||||
// ExifInterface does not check whether file path is null or not,
|
||||
// so passing null file path argument to its constructor causing SIGSEGV.
|
||||
// We should avoid such a situation by checking file path string.
|
||||
exif = newInstance(filepath);
|
||||
} catch (IOException ex) {
|
||||
Log.e(TAG, "cannot read exif", ex);
|
||||
return EXIF_DEGREE_FALLBACK_VALUE;
|
||||
}
|
||||
|
||||
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, EXIF_DEGREE_FALLBACK_VALUE);
|
||||
if (orientation == EXIF_DEGREE_FALLBACK_VALUE) {
|
||||
return 0;
|
||||
}
|
||||
// We only recognize a subset of orientation tag values.
|
||||
switch (orientation) {
|
||||
case ExifInterface.ORIENTATION_ROTATE_90:
|
||||
return 90;
|
||||
case ExifInterface.ORIENTATION_ROTATE_180:
|
||||
return 180;
|
||||
case ExifInterface.ORIENTATION_ROTATE_270:
|
||||
return 270;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.core.os.EnvironmentCompat;
|
||||
|
||||
import com.zhihu.matisse.internal.entity.CaptureStrategy;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class MediaStoreCompat {
|
||||
|
||||
private final WeakReference<Activity> mContext;
|
||||
private final WeakReference<Fragment> mFragment;
|
||||
private CaptureStrategy mCaptureStrategy;
|
||||
private Uri mCurrentPhotoUri;
|
||||
private String mCurrentPhotoPath;
|
||||
|
||||
public MediaStoreCompat(Activity activity) {
|
||||
mContext = new WeakReference<>(activity);
|
||||
mFragment = null;
|
||||
}
|
||||
|
||||
public MediaStoreCompat(Activity activity, Fragment fragment) {
|
||||
mContext = new WeakReference<>(activity);
|
||||
mFragment = new WeakReference<>(fragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the device has a camera feature or not.
|
||||
*
|
||||
* @param context a context to check for camera feature.
|
||||
* @return true if the device has a camera feature. false otherwise.
|
||||
*/
|
||||
public static boolean hasCameraFeature(Context context) {
|
||||
PackageManager pm = context.getApplicationContext().getPackageManager();
|
||||
return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
|
||||
}
|
||||
|
||||
public void setCaptureStrategy(CaptureStrategy strategy) {
|
||||
mCaptureStrategy = strategy;
|
||||
}
|
||||
|
||||
public void dispatchCaptureIntent(Context context, int requestCode) {
|
||||
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
if (captureIntent.resolveActivity(context.getPackageManager()) != null) {
|
||||
File photoFile = null;
|
||||
try {
|
||||
photoFile = createImageFile();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (photoFile != null) {
|
||||
mCurrentPhotoPath = photoFile.getAbsolutePath();
|
||||
mCurrentPhotoUri = FileProvider.getUriForFile(mContext.get(),
|
||||
mCaptureStrategy.authority, photoFile);
|
||||
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCurrentPhotoUri);
|
||||
captureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
List<ResolveInfo> resInfoList = context.getPackageManager()
|
||||
.queryIntentActivities(captureIntent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
for (ResolveInfo resolveInfo : resInfoList) {
|
||||
String packageName = resolveInfo.activityInfo.packageName;
|
||||
context.grantUriPermission(packageName, mCurrentPhotoUri,
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
}
|
||||
if (mFragment != null) {
|
||||
mFragment.get().startActivityForResult(captureIntent, requestCode);
|
||||
} else {
|
||||
mContext.get().startActivityForResult(captureIntent, requestCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
private File createImageFile() throws IOException {
|
||||
// Create an image file name
|
||||
String timeStamp =
|
||||
new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
|
||||
String imageFileName = String.format("JPEG_%s.jpg", timeStamp);
|
||||
File storageDir;
|
||||
if (mCaptureStrategy.isPublic) {
|
||||
storageDir = Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_PICTURES);
|
||||
if (!storageDir.exists()) storageDir.mkdirs();
|
||||
} else {
|
||||
storageDir = mContext.get().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
|
||||
}
|
||||
if (mCaptureStrategy.directory != null) {
|
||||
storageDir = new File(storageDir, mCaptureStrategy.directory);
|
||||
if (!storageDir.exists()) storageDir.mkdirs();
|
||||
}
|
||||
|
||||
// Avoid joining path components manually
|
||||
File tempFile = new File(storageDir, imageFileName);
|
||||
|
||||
// Handle the situation that user's external storage is not ready
|
||||
if (!Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(tempFile))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
public Uri getCurrentPhotoUri() {
|
||||
return mCurrentPhotoUri;
|
||||
}
|
||||
|
||||
public String getCurrentPhotoPath() {
|
||||
return mCurrentPhotoPath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.zhihu.matisse.internal.utils;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
/**
|
||||
* http://stackoverflow.com/a/27271131/4739220
|
||||
*/
|
||||
|
||||
public class PathUtils {
|
||||
/**
|
||||
* Get a file path from a Uri. This will get the the path for Storage Access
|
||||
* Framework Documents, as well as the _data field for the MediaStore and
|
||||
* other file-based ContentProviders.
|
||||
*
|
||||
* @param context The context.
|
||||
* @param uri The Uri to query.
|
||||
* @author paulburke
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
public static String getPath(final Context context, final Uri uri) {
|
||||
// DocumentProvider
|
||||
if (Platform.hasKitKat() && DocumentsContract.isDocumentUri(context, uri)) {
|
||||
// ExternalStorageProvider
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
if ("primary".equalsIgnoreCase(type)) {
|
||||
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||
}
|
||||
|
||||
// TODO handle non-primary volumes
|
||||
} else if (isDownloadsDocument(uri)) { // DownloadsProvider
|
||||
|
||||
final String id = DocumentsContract.getDocumentId(uri);
|
||||
final Uri contentUri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||
|
||||
return getDataColumn(context, contentUri, null, null);
|
||||
} else if (isMediaDocument(uri)) { // MediaProvider
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
Uri contentUri = null;
|
||||
if ("image".equals(type)) {
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("video".equals(type)) {
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("audio".equals(type)) {
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
|
||||
final String selection = "_id=?";
|
||||
final String[] selectionArgs = new String[]{
|
||||
split[1]
|
||||
};
|
||||
|
||||
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||
}
|
||||
} else if ("content".equalsIgnoreCase(uri.getScheme())) { // MediaStore (and general)
|
||||
return getDataColumn(context, uri, null, null);
|
||||
} else if ("file".equalsIgnoreCase(uri.getScheme())) { // File
|
||||
return uri.getPath();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the data column for this Uri. This is useful for
|
||||
* MediaStore Uris, and other file-based ContentProviders.
|
||||
*
|
||||
* @param context The context.
|
||||
* @param uri The Uri to query.
|
||||
* @param selection (Optional) Filter used in the query.
|
||||
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||
* @return The value of the _data column, which is typically a file path.
|
||||
*/
|
||||
public static String getDataColumn(Context context, Uri uri, String selection,
|
||||
String[] selectionArgs) {
|
||||
|
||||
Cursor cursor = null;
|
||||
final String column = "_data";
|
||||
final String[] projection = {
|
||||
column
|
||||
};
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int columnIndex = cursor.getColumnIndexOrThrow(column);
|
||||
return cursor.getString(columnIndex);
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is ExternalStorageProvider.
|
||||
*/
|
||||
public static boolean isExternalStorageDocument(Uri uri) {
|
||||
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is DownloadsProvider.
|
||||
*/
|
||||
public static boolean isDownloadsDocument(Uri uri) {
|
||||
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is MediaProvider.
|
||||
*/
|
||||
public static boolean isMediaDocument(Uri uri) {
|
||||
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (C) 2014 nohana, Inc.
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Point;
|
||||
import android.media.ExifInterface;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
|
||||
import com.zhihu.matisse.MimeType;
|
||||
import com.zhihu.matisse.R;
|
||||
import com.zhihu.matisse.filter.Filter;
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
import com.zhihu.matisse.internal.entity.IncapableCause;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class PhotoMetadataUtils {
|
||||
private static final String TAG = PhotoMetadataUtils.class.getSimpleName();
|
||||
private static final int MAX_WIDTH = 1600;
|
||||
private static final String SCHEME_CONTENT = "content";
|
||||
|
||||
private PhotoMetadataUtils() {
|
||||
throw new AssertionError("oops! the utility class is about to be instantiated...");
|
||||
}
|
||||
|
||||
public static int getPixelsCount(ContentResolver resolver, Uri uri) {
|
||||
Point size = getBitmapBound(resolver, uri);
|
||||
return size.x * size.y;
|
||||
}
|
||||
|
||||
public static Point getBitmapSize(Uri uri, Activity activity) {
|
||||
ContentResolver resolver = activity.getContentResolver();
|
||||
Point imageSize = getBitmapBound(resolver, uri);
|
||||
int w = imageSize.x;
|
||||
int h = imageSize.y;
|
||||
if (PhotoMetadataUtils.shouldRotate(resolver, uri)) {
|
||||
w = imageSize.y;
|
||||
h = imageSize.x;
|
||||
}
|
||||
if (h == 0) return new Point(MAX_WIDTH, MAX_WIDTH);
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||
float screenWidth = (float) metrics.widthPixels;
|
||||
float screenHeight = (float) metrics.heightPixels;
|
||||
float widthScale = screenWidth / w;
|
||||
float heightScale = screenHeight / h;
|
||||
if (widthScale > heightScale) {
|
||||
return new Point((int) (w * widthScale), (int) (h * heightScale));
|
||||
}
|
||||
return new Point((int) (w * widthScale), (int) (h * heightScale));
|
||||
}
|
||||
|
||||
public static Point getBitmapBound(ContentResolver resolver, Uri uri) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
is = resolver.openInputStream(uri);
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
int width = options.outWidth;
|
||||
int height = options.outHeight;
|
||||
return new Point(width, height);
|
||||
} catch (FileNotFoundException e) {
|
||||
return new Point(0, 0);
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String getPath(ContentResolver resolver, Uri uri) {
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (SCHEME_CONTENT.equals(uri.getScheme())) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = resolver.query(uri, new String[]{MediaStore.Images.ImageColumns.DATA},
|
||||
null, null, null);
|
||||
if (cursor == null || !cursor.moveToFirst()) {
|
||||
return null;
|
||||
}
|
||||
return cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA));
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
return uri.getPath();
|
||||
}
|
||||
|
||||
public static IncapableCause isAcceptable(Context context, Item item) {
|
||||
if (!isSelectableType(context, item)) {
|
||||
return new IncapableCause(context.getString(R.string.error_file_type));
|
||||
}
|
||||
|
||||
if (SelectionSpec.getInstance().filters != null) {
|
||||
for (Filter filter : SelectionSpec.getInstance().filters) {
|
||||
IncapableCause incapableCause = filter.filter(context, item);
|
||||
if (incapableCause != null) {
|
||||
return incapableCause;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isSelectableType(Context context, Item item) {
|
||||
if (context == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
for (MimeType type : SelectionSpec.getInstance().mimeTypeSet) {
|
||||
if (type.checkType(resolver, item.getContentUri())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean shouldRotate(ContentResolver resolver, Uri uri) {
|
||||
ExifInterface exif;
|
||||
try {
|
||||
exif = ExifInterfaceCompat.newInstance(getPath(resolver, uri));
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "could not read exif info of the image: " + uri);
|
||||
return false;
|
||||
}
|
||||
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
|
||||
return orientation == ExifInterface.ORIENTATION_ROTATE_90
|
||||
|| orientation == ExifInterface.ORIENTATION_ROTATE_270;
|
||||
}
|
||||
|
||||
public static float getSizeInMB(long sizeInBytes) {
|
||||
DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
|
||||
df.applyPattern("0.0");
|
||||
String result = df.format((float) sizeInBytes / 1024 / 1024);
|
||||
Log.e(TAG, "getSizeInMB: " + result);
|
||||
result = result.replaceAll(",", "."); // in some case , 0.0 will be 0,0
|
||||
return Float.valueOf(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.zhihu.matisse.internal.utils;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
/**
|
||||
* @author JoongWon Baik
|
||||
*/
|
||||
public class Platform {
|
||||
public static boolean hasICS() {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
}
|
||||
|
||||
public static boolean hasKitKat() {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.zhihu.matisse.internal.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.net.Uri;
|
||||
|
||||
/**
|
||||
* @author 工藤
|
||||
* @email gougou@16fan.com
|
||||
* create at 2018年10月23日12:17:59
|
||||
* description:媒体扫描
|
||||
*/
|
||||
public class SingleMediaScanner implements MediaScannerConnection.MediaScannerConnectionClient {
|
||||
|
||||
private MediaScannerConnection mMsc;
|
||||
private String mPath;
|
||||
private ScanListener mListener;
|
||||
|
||||
public interface ScanListener {
|
||||
|
||||
/**
|
||||
* scan finish
|
||||
*/
|
||||
void onScanFinish();
|
||||
}
|
||||
|
||||
public SingleMediaScanner(Context context, String mPath, ScanListener mListener) {
|
||||
this.mPath = mPath;
|
||||
this.mListener = mListener;
|
||||
this.mMsc = new MediaScannerConnection(context, this);
|
||||
this.mMsc.connect();
|
||||
}
|
||||
|
||||
@Override public void onMediaScannerConnected() {
|
||||
mMsc.scanFile(mPath, null);
|
||||
}
|
||||
|
||||
@Override public void onScanCompleted(String mPath, Uri mUri) {
|
||||
mMsc.disconnect();
|
||||
if (mListener != null) {
|
||||
mListener.onScanFinish();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.internal.utils;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class UIUtils {
|
||||
|
||||
public static int spanCount(Context context, int gridExpectedSize) {
|
||||
int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
|
||||
float expected = (float) screenWidth / (float) gridExpectedSize;
|
||||
int spanCount = Math.round(expected);
|
||||
if (spanCount == 0) {
|
||||
spanCount = 1;
|
||||
}
|
||||
return spanCount;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.zhihu.matisse.listener;
|
||||
|
||||
|
||||
/**
|
||||
* when original is enabled , callback immediately when user check or uncheck original.
|
||||
*/
|
||||
public interface OnCheckedListener {
|
||||
void onCheck(boolean isChecked);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.zhihu.matisse.listener;
|
||||
|
||||
/**
|
||||
* PreViewItemFragment 和 BasePreViewActivity 通信的接口 ,为了方便拿到 ImageViewTouch 的点击事件
|
||||
*/
|
||||
public interface OnFragmentInteractionListener {
|
||||
/**
|
||||
* ImageViewTouch 被点击了
|
||||
*/
|
||||
void onClick();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.listener;
|
||||
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface OnSelectedListener {
|
||||
/**
|
||||
* @param uriList the selected item {@link Uri} list.
|
||||
* @param pathList the selected item file path list.
|
||||
*/
|
||||
void onSelected(@NonNull List<Uri> uriList, @NonNull List<String> pathList);
|
||||
}
|
||||
456
matisse/src/main/java/com/zhihu/matisse/ui/MatisseActivity.java
Normal file
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
* Copyright 2017 Zhihu Inc.
|
||||
*
|
||||
* 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.zhihu.matisse.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.zhihu.matisse.R;
|
||||
import com.zhihu.matisse.internal.entity.Album;
|
||||
import com.zhihu.matisse.internal.entity.Item;
|
||||
import com.zhihu.matisse.internal.entity.SelectionSpec;
|
||||
import com.zhihu.matisse.internal.model.AlbumCollection;
|
||||
import com.zhihu.matisse.internal.model.SelectedItemCollection;
|
||||
import com.zhihu.matisse.internal.ui.AlbumPreviewActivity;
|
||||
import com.zhihu.matisse.internal.ui.BasePreviewActivity;
|
||||
import com.zhihu.matisse.internal.ui.MediaSelectionFragment;
|
||||
import com.zhihu.matisse.internal.ui.SelectedPreviewActivity;
|
||||
import com.zhihu.matisse.internal.ui.adapter.AlbumMediaAdapter;
|
||||
import com.zhihu.matisse.internal.ui.adapter.AlbumsAdapter;
|
||||
import com.zhihu.matisse.internal.ui.widget.AlbumsSpinner;
|
||||
import com.zhihu.matisse.internal.ui.widget.CheckRadioView;
|
||||
import com.zhihu.matisse.internal.ui.widget.IncapableDialog;
|
||||
import com.zhihu.matisse.internal.utils.MediaStoreCompat;
|
||||
import com.zhihu.matisse.internal.utils.PathUtils;
|
||||
import com.zhihu.matisse.internal.utils.PhotoMetadataUtils;
|
||||
import com.zhihu.matisse.internal.utils.SingleMediaScanner;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Main Activity to display albums and media content (images/videos) in each album
|
||||
* and also support media selecting operations.
|
||||
*/
|
||||
public class MatisseActivity extends AppCompatActivity implements
|
||||
AlbumCollection.AlbumCallbacks, AdapterView.OnItemSelectedListener,
|
||||
MediaSelectionFragment.SelectionProvider, View.OnClickListener,
|
||||
AlbumMediaAdapter.CheckStateListener, AlbumMediaAdapter.OnMediaClickListener,
|
||||
AlbumMediaAdapter.OnPhotoCapture {
|
||||
|
||||
public static final String EXTRA_RESULT_SELECTION = "extra_result_selection";
|
||||
public static final String EXTRA_RESULT_SELECTION_PATH = "extra_result_selection_path";
|
||||
public static final String EXTRA_RESULT_SELECTION_TYPE = "extra_result_selection_type";
|
||||
|
||||
public static final String EXTRA_RESULT_ORIGINAL_ENABLE = "extra_result_original_enable";
|
||||
private static final int REQUEST_CODE_PREVIEW = 23;
|
||||
private static final int REQUEST_CODE_CAPTURE = 24;
|
||||
public static final String CHECK_STATE = "checkState";
|
||||
private final AlbumCollection mAlbumCollection = new AlbumCollection();
|
||||
private MediaStoreCompat mMediaStoreCompat;
|
||||
private SelectedItemCollection mSelectedCollection = new SelectedItemCollection(this);
|
||||
private SelectionSpec mSpec;
|
||||
|
||||
private AlbumsSpinner mAlbumsSpinner;
|
||||
private AlbumsAdapter mAlbumsAdapter;
|
||||
private TextView mButtonPreview;
|
||||
private TextView mButtonApply;
|
||||
private View mContainer;
|
||||
private View mEmptyView;
|
||||
|
||||
private LinearLayout mOriginalLayout;
|
||||
private CheckRadioView mOriginal;
|
||||
private boolean mOriginalEnable;
|
||||
|
||||
private WeakReference<Fragment> fragmentRef;
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
// programmatically set theme before super.onCreate()
|
||||
// getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);//实现状态栏文字颜色为暗色
|
||||
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||
getWindow().setStatusBarColor(getResources().getColor(R.color.dracula_capture));
|
||||
mSpec = SelectionSpec.getInstance();
|
||||
setTheme(mSpec.themeId);
|
||||
super.onCreate(savedInstanceState);
|
||||
if (!mSpec.hasInited) {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
setContentView(R.layout.activity_matisse);
|
||||
|
||||
if (mSpec.needOrientationRestriction()) {
|
||||
setRequestedOrientation(mSpec.orientation);
|
||||
}
|
||||
|
||||
if (mSpec.capture) {
|
||||
mMediaStoreCompat = new MediaStoreCompat(this);
|
||||
if (mSpec.captureStrategy == null)
|
||||
throw new RuntimeException("Don't forget to set CaptureStrategy.");
|
||||
mMediaStoreCompat.setCaptureStrategy(mSpec.captureStrategy);
|
||||
}
|
||||
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayShowTitleEnabled(false);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
Drawable navigationIcon = toolbar.getNavigationIcon();
|
||||
TypedArray ta = getTheme().obtainStyledAttributes(new int[]{R.attr.album_element_color});
|
||||
int color = ta.getColor(0, 0);
|
||||
ta.recycle();
|
||||
navigationIcon.setColorFilter(color, PorterDuff.Mode.SRC_IN);
|
||||
|
||||
mButtonPreview = (TextView) findViewById(R.id.button_preview);
|
||||
mButtonApply = (TextView) findViewById(R.id.button_apply);
|
||||
mButtonPreview.setOnClickListener(this);
|
||||
mButtonApply.setOnClickListener(this);
|
||||
mContainer = findViewById(R.id.container);
|
||||
mEmptyView = findViewById(R.id.empty_view);
|
||||
mOriginalLayout = findViewById(R.id.originalLayout);
|
||||
mOriginal = findViewById(R.id.original);
|
||||
mOriginalLayout.setOnClickListener(this);
|
||||
|
||||
mSelectedCollection.onCreate(savedInstanceState);
|
||||
if (savedInstanceState != null) {
|
||||
mOriginalEnable = savedInstanceState.getBoolean(CHECK_STATE);
|
||||
}
|
||||
updateBottomToolbar();
|
||||
|
||||
mAlbumsAdapter = new AlbumsAdapter(this, null, false);
|
||||
mAlbumsSpinner = new AlbumsSpinner(this);
|
||||
mAlbumsSpinner.setOnItemSelectedListener(this);
|
||||
mAlbumsSpinner.setSelectedTextView((TextView) findViewById(R.id.selected_album));
|
||||
mAlbumsSpinner.setPopupAnchorView(findViewById(R.id.toolbar));
|
||||
mAlbumsSpinner.setAdapter(mAlbumsAdapter);
|
||||
mAlbumCollection.onCreate(this, this);
|
||||
mAlbumCollection.onRestoreInstanceState(savedInstanceState);
|
||||
mAlbumCollection.loadAlbums();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
mSelectedCollection.onSaveInstanceState(outState);
|
||||
mAlbumCollection.onSaveInstanceState(outState);
|
||||
outState.putBoolean("checkState", mOriginalEnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mAlbumCollection.onDestroy();
|
||||
mSpec.onCheckedListener = null;
|
||||
mSpec.onSelectedListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (resultCode != RESULT_OK)
|
||||
return;
|
||||
|
||||
if (requestCode == REQUEST_CODE_PREVIEW) {
|
||||
Log.i("尴尬","尴尬1:");
|
||||
Bundle resultBundle = data.getBundleExtra(BasePreviewActivity.EXTRA_RESULT_BUNDLE);
|
||||
ArrayList<Item> selected = resultBundle.getParcelableArrayList(SelectedItemCollection.STATE_SELECTION);
|
||||
mOriginalEnable = data.getBooleanExtra(BasePreviewActivity.EXTRA_RESULT_ORIGINAL_ENABLE, false);
|
||||
int collectionType = resultBundle.getInt(SelectedItemCollection.STATE_COLLECTION_TYPE,
|
||||
SelectedItemCollection.COLLECTION_UNDEFINED);
|
||||
if (data.getBooleanExtra(BasePreviewActivity.EXTRA_RESULT_APPLY, false)) {
|
||||
Intent result = new Intent();
|
||||
ArrayList<Uri> selectedUris = new ArrayList<>();
|
||||
ArrayList<String> selectedPaths = new ArrayList<>();
|
||||
boolean isVideos = false;
|
||||
|
||||
if (selected != null) {
|
||||
for (Item item : selected) {
|
||||
selectedUris.add(item.getContentUri());
|
||||
selectedPaths.add(PathUtils.getPath(this, item.getContentUri()));
|
||||
isVideos = item.isVideo();
|
||||
}
|
||||
}
|
||||
result.putParcelableArrayListExtra(EXTRA_RESULT_SELECTION, selectedUris);
|
||||
result.putStringArrayListExtra(EXTRA_RESULT_SELECTION_PATH, selectedPaths);
|
||||
result.putExtra(EXTRA_RESULT_SELECTION_TYPE, isVideos);
|
||||
result.putExtra(EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);
|
||||
setResult(RESULT_OK, result);
|
||||
finish();
|
||||
} else {
|
||||
mSelectedCollection.overwrite(selected, collectionType);
|
||||
Fragment mediaSelectionFragment = getSupportFragmentManager().findFragmentByTag(
|
||||
MediaSelectionFragment.class.getSimpleName());
|
||||
if (mediaSelectionFragment instanceof MediaSelectionFragment) {
|
||||
((MediaSelectionFragment) mediaSelectionFragment).refreshMediaGrid();
|
||||
}
|
||||
updateBottomToolbar();
|
||||
}
|
||||
} else if (requestCode == REQUEST_CODE_CAPTURE) {
|
||||
Log.i("尴尬","尴尬2:");
|
||||
|
||||
// Just pass the data back to previous calling Activity.
|
||||
Uri contentUri = mMediaStoreCompat.getCurrentPhotoUri();
|
||||
String path = mMediaStoreCompat.getCurrentPhotoPath();
|
||||
ArrayList<Uri> selected = new ArrayList<>();
|
||||
selected.add(contentUri);
|
||||
ArrayList<String> selectedPath = new ArrayList<>();
|
||||
selectedPath.add(path);
|
||||
Intent result = new Intent();
|
||||
result.putParcelableArrayListExtra(EXTRA_RESULT_SELECTION, selected);
|
||||
result.putStringArrayListExtra(EXTRA_RESULT_SELECTION_PATH, selectedPath);
|
||||
setResult(RESULT_OK, result);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
MatisseActivity.this.revokeUriPermission(contentUri,
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
new SingleMediaScanner(this.getApplicationContext(), path, () -> Log.i("SingleMediaScanner", "scan finish!"));
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBottomToolbar() {
|
||||
|
||||
int selectedCount = mSelectedCollection.count();
|
||||
if (selectedCount == 0) {
|
||||
mButtonPreview.setEnabled(false);
|
||||
mButtonApply.setEnabled(false);
|
||||
mButtonApply.setText(getString(R.string.button_apply_default));
|
||||
} else if (selectedCount == 1 && mSpec.singleSelectionModeEnabled()) {
|
||||
mButtonPreview.setEnabled(true);
|
||||
mButtonApply.setText(R.string.button_apply_default);
|
||||
mButtonApply.setEnabled(true);
|
||||
} else {
|
||||
mButtonPreview.setEnabled(true);
|
||||
mButtonApply.setEnabled(true);
|
||||
mButtonApply.setText(getString(R.string.button_apply, selectedCount));
|
||||
}
|
||||
|
||||
|
||||
if (mSpec.originalable) {
|
||||
mOriginalLayout.setVisibility(View.VISIBLE);
|
||||
updateOriginalState();
|
||||
} else {
|
||||
mOriginalLayout.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void updateOriginalState() {
|
||||
mOriginal.setChecked(mOriginalEnable);
|
||||
if (countOverMaxSize() > 0) {
|
||||
|
||||
if (mOriginalEnable) {
|
||||
IncapableDialog incapableDialog = IncapableDialog.newInstance("",
|
||||
getString(R.string.error_over_original_size, mSpec.originalMaxSize));
|
||||
incapableDialog.show(getSupportFragmentManager(),
|
||||
IncapableDialog.class.getName());
|
||||
|
||||
mOriginal.setChecked(false);
|
||||
mOriginalEnable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int countOverMaxSize() {
|
||||
int count = 0;
|
||||
int selectedCount = mSelectedCollection.count();
|
||||
for (int i = 0; i < selectedCount; i++) {
|
||||
Item item = mSelectedCollection.asList().get(i);
|
||||
|
||||
if (item.isImage()) {
|
||||
float size = PhotoMetadataUtils.getSizeInMB(item.size);
|
||||
if (size > mSpec.originalMaxSize) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v.getId() == R.id.button_preview) {
|
||||
Intent intent = new Intent(this, SelectedPreviewActivity.class);
|
||||
intent.putExtra(BasePreviewActivity.EXTRA_DEFAULT_BUNDLE, mSelectedCollection.getDataWithBundle());
|
||||
intent.putExtra(BasePreviewActivity.EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);
|
||||
startActivityForResult(intent, REQUEST_CODE_PREVIEW);
|
||||
} else if (v.getId() == R.id.button_apply) {
|
||||
Log.i("尴尬","尴尬3:");
|
||||
Intent result = new Intent();
|
||||
ArrayList<Uri> selectedUris = (ArrayList<Uri>) mSelectedCollection.asListOfUri();
|
||||
result.putParcelableArrayListExtra(EXTRA_RESULT_SELECTION, selectedUris);
|
||||
ArrayList<String> selectedPaths = (ArrayList<String>) mSelectedCollection.asListOfString();
|
||||
result.putStringArrayListExtra(EXTRA_RESULT_SELECTION_PATH, selectedPaths);
|
||||
result.putExtra(EXTRA_RESULT_SELECTION_TYPE,mSelectedCollection.asListOfType());
|
||||
|
||||
result.putExtra(EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);
|
||||
setResult(RESULT_OK, result);
|
||||
finish();
|
||||
} else if (v.getId() == R.id.originalLayout) {
|
||||
int count = countOverMaxSize();
|
||||
if (count > 0) {
|
||||
IncapableDialog incapableDialog = IncapableDialog.newInstance("",
|
||||
getString(R.string.error_over_original_count, count, mSpec.originalMaxSize));
|
||||
incapableDialog.show(getSupportFragmentManager(),
|
||||
IncapableDialog.class.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
mOriginalEnable = !mOriginalEnable;
|
||||
mOriginal.setChecked(mOriginalEnable);
|
||||
|
||||
if (mSpec.onCheckedListener != null) {
|
||||
mSpec.onCheckedListener.onCheck(mOriginalEnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
mAlbumCollection.setStateCurrentSelection(position);
|
||||
mAlbumsAdapter.getCursor().moveToPosition(position);
|
||||
Album album = Album.valueOf(mAlbumsAdapter.getCursor());
|
||||
if (album.isAll() && SelectionSpec.getInstance().capture) {
|
||||
album.addCaptureCount();
|
||||
}
|
||||
onAlbumSelected(album);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAlbumLoad(final Cursor cursor) {
|
||||
mAlbumsAdapter.swapCursor(cursor);
|
||||
// select default album.
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.post(() -> {
|
||||
cursor.moveToPosition(mAlbumCollection.getCurrentSelection());
|
||||
mAlbumsSpinner.setSelection(MatisseActivity.this,
|
||||
mAlbumCollection.getCurrentSelection());
|
||||
Album album = Album.valueOf(cursor);
|
||||
if (album.isAll() && SelectionSpec.getInstance().capture) {
|
||||
album.addCaptureCount();
|
||||
}
|
||||
onAlbumSelected(album);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAlbumReset() {
|
||||
mAlbumsAdapter.swapCursor(null);
|
||||
}
|
||||
|
||||
private Fragment fragment;
|
||||
private void onAlbumSelected(Album album) {
|
||||
if (album.isAll() && album.isEmpty()) {
|
||||
mContainer.setVisibility(View.GONE);
|
||||
mEmptyView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mContainer.setVisibility(View.VISIBLE);
|
||||
mEmptyView.setVisibility(View.GONE);
|
||||
if (fragment != null) {
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.remove(fragment)
|
||||
.commitAllowingStateLoss();
|
||||
}
|
||||
fragment = MediaSelectionFragment.newInstance(album);
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.add(R.id.container, fragment, MediaSelectionFragment.class.getSimpleName())
|
||||
.commitAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
// notify bottom toolbar that check state changed.
|
||||
updateBottomToolbar();
|
||||
|
||||
if (mSpec.onSelectedListener != null) {
|
||||
mSpec.onSelectedListener.onSelected(
|
||||
mSelectedCollection.asListOfUri(), mSelectedCollection.asListOfString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaClick(Album album, Item item, int adapterPosition) {
|
||||
Intent intent = new Intent(this, AlbumPreviewActivity.class);
|
||||
intent.putExtra(AlbumPreviewActivity.EXTRA_ALBUM, album);
|
||||
intent.putExtra(AlbumPreviewActivity.EXTRA_ITEM, item);
|
||||
intent.putExtra(BasePreviewActivity.EXTRA_DEFAULT_BUNDLE, mSelectedCollection.getDataWithBundle());
|
||||
intent.putExtra(BasePreviewActivity.EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);
|
||||
startActivityForResult(intent, REQUEST_CODE_PREVIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectedItemCollection provideSelectedItemCollection() {
|
||||
return mSelectedCollection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void capture() {
|
||||
if (mMediaStoreCompat != null) {
|
||||
mMediaStoreCompat.dispatchCaptureIntent(this, REQUEST_CODE_CAPTURE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
21
matisse/src/main/res/color/dracula_bottom_toolbar_apply.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/dracula_bottom_toolbar_apply_text_disable"
|
||||
android:state_enabled="false"/>
|
||||
<item android:color="@color/dracula_bottom_toolbar_apply_text"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/dracula_bottom_toolbar_preview_text_disable"
|
||||
android:state_enabled="false"/>
|
||||
<item android:color="@color/dracula_bottom_toolbar_preview_text"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/dracula_preview_bottom_toolbar_apply_text_disable"
|
||||
android:state_enabled="false"/>
|
||||
<item android:color="@color/dracula_preview_bottom_toolbar_apply_text"/>
|
||||
</selector>
|
||||
21
matisse/src/main/res/color/zhihu_bottom_toolbar_apply.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/zhihu_bottom_toolbar_apply_text_disable"
|
||||
android:state_enabled="false"/>
|
||||
<item android:color="@color/zhihu_bottom_toolbar_apply_text"/>
|
||||
</selector>
|
||||
21
matisse/src/main/res/color/zhihu_bottom_toolbar_preview.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/zhihu_bottom_toolbar_preview_text_disable"
|
||||
android:state_enabled="false"/>
|
||||
<item android:color="@color/zhihu_bottom_toolbar_preview_text"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/zhihu_preview_bottom_toolbar_apply_text_disable"
|
||||
android:state_enabled="false"/>
|
||||
<item android:color="@color/zhihu_preview_bottom_toolbar_apply_text"/>
|
||||
</selector>
|
||||
|
After Width: | Height: | Size: 123 B |
BIN
matisse/src/main/res/drawable-hdpi/ic_check_white_18dp.png
Normal file
|
After Width: | Height: | Size: 152 B |
BIN
matisse/src/main/res/drawable-hdpi/ic_empty_dracula.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
matisse/src/main/res/drawable-hdpi/ic_empty_zhihu.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
matisse/src/main/res/drawable-hdpi/ic_gif.png
Normal file
|
After Width: | Height: | Size: 929 B |
|
After Width: | Height: | Size: 1023 B |
BIN
matisse/src/main/res/drawable-hdpi/ic_preview_radio_off.webp
Normal file
|
After Width: | Height: | Size: 290 B |
BIN
matisse/src/main/res/drawable-hdpi/ic_preview_radio_on.webp
Normal file
|
After Width: | Height: | Size: 338 B |
|
After Width: | Height: | Size: 95 B |
BIN
matisse/src/main/res/drawable-mdpi/ic_check_white_18dp.png
Normal file
|
After Width: | Height: | Size: 146 B |
BIN
matisse/src/main/res/drawable-mdpi/ic_empty_dracula.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
matisse/src/main/res/drawable-mdpi/ic_empty_zhihu.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
matisse/src/main/res/drawable-mdpi/ic_gif.png
Normal file
|
After Width: | Height: | Size: 638 B |
|
After Width: | Height: | Size: 699 B |
BIN
matisse/src/main/res/drawable-mdpi/ic_preview_radio_off.webp
Normal file
|
After Width: | Height: | Size: 410 B |
BIN
matisse/src/main/res/drawable-mdpi/ic_preview_radio_on.webp
Normal file
|
After Width: | Height: | Size: 486 B |
|
After Width: | Height: | Size: 120 B |
BIN
matisse/src/main/res/drawable-xhdpi/ic_check_white_18dp.png
Normal file
|
After Width: | Height: | Size: 181 B |
BIN
matisse/src/main/res/drawable-xhdpi/ic_empty_dracula.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
matisse/src/main/res/drawable-xhdpi/ic_empty_zhihu.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
matisse/src/main/res/drawable-xhdpi/ic_gif.png
Normal file
|
After Width: | Height: | Size: 993 B |
|
After Width: | Height: | Size: 1.3 KiB |
BIN
matisse/src/main/res/drawable-xhdpi/ic_preview_radio_off.webp
Normal file
|
After Width: | Height: | Size: 544 B |
BIN
matisse/src/main/res/drawable-xhdpi/ic_preview_radio_on.webp
Normal file
|
After Width: | Height: | Size: 644 B |
|
After Width: | Height: | Size: 152 B |
BIN
matisse/src/main/res/drawable-xxhdpi/ic_black_back.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
matisse/src/main/res/drawable-xxhdpi/ic_check_white_18dp.png
Normal file
|
After Width: | Height: | Size: 236 B |
BIN
matisse/src/main/res/drawable-xxhdpi/ic_empty_dracula.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
matisse/src/main/res/drawable-xxhdpi/ic_empty_zhihu.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
matisse/src/main/res/drawable-xxhdpi/ic_gif.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 185 B |
BIN
matisse/src/main/res/drawable-xxxhdpi/ic_check_white_18dp.png
Normal file
|
After Width: | Height: | Size: 276 B |
BIN
matisse/src/main/res/drawable-xxxhdpi/ic_empty_dracula.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
matisse/src/main/res/drawable-xxxhdpi/ic_empty_zhihu.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
matisse/src/main/res/drawable-xxxhdpi/ic_gif.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 894 B |
|
After Width: | Height: | Size: 2.8 KiB |
150
matisse/src/main/res/layout/activity_matisse.xml
Normal file
@@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<RelativeLayout 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:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?colorPrimary"
|
||||
app:navigationIcon="@drawable/ic_black_back"
|
||||
android:navigationIcon="@drawable/ic_black_back"
|
||||
android:elevation="4dp"
|
||||
android:theme="?toolbar"
|
||||
tools:targetApi="lollipop">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/selected_album"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="?actionBarSize"
|
||||
android:visibility="gone"
|
||||
android:drawableRight="@drawable/ic_arrow_drop_down_white_24dp"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:textColor="?attr/album.element.color"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<!-- <TextView-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="?actionBarSize"-->
|
||||
<!-- android:foreground="?selectableItemBackground"-->
|
||||
<!-- android:gravity="center"-->
|
||||
<!-- android:layout_gravity="center"-->
|
||||
<!-- android:text="更换头像"-->
|
||||
<!-- android:textColor="?attr/album.element.color"-->
|
||||
<!-- android:textSize="16sp" />-->
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/bottom_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="?attr/bottomToolbar.bg"
|
||||
android:elevation="4dp"
|
||||
tools:targetApi="lollipop">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/button_preview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:padding="16dp"
|
||||
android:text="@string/button_preview"
|
||||
android:textColor="?attr/bottomToolbar.preview.textColor"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:padding="16dp"
|
||||
android:id="@+id/originalLayout"
|
||||
android:visibility="visible"
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="wrap_content"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:layout_height="wrap_content"
|
||||
tools:showIn="@layout/activity_matisse">
|
||||
|
||||
|
||||
<com.zhihu.matisse.internal.ui.widget.CheckRadioView
|
||||
android:id="@+id/original"
|
||||
android:src="@drawable/ic_preview_radio_off"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:enabled="true"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingLeft="4dp"
|
||||
android:text="@string/button_original"
|
||||
android:textColor="?attr/bottomToolbar.preview.textColor"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/button_apply"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:padding="16dp"
|
||||
android:textColor="?attr/bottomToolbar.apply.textColor"
|
||||
android:textSize="16sp" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@id/bottom_toolbar"
|
||||
android:layout_below="@id/toolbar"
|
||||
android:visibility="gone" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/empty_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@id/bottom_toolbar"
|
||||
android:layout_below="@id/toolbar"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/empty_view_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:drawablePadding="8dp"
|
||||
android:drawableTop="?attr/album.emptyView"
|
||||
android:gravity="center"
|
||||
android:text="@string/empty_text"
|
||||
android:textColor="?attr/album.emptyView.textColor"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</FrameLayout>
|
||||
</RelativeLayout>
|
||||
130
matisse/src/main/res/layout/activity_media_preview.xml
Normal file
@@ -0,0 +1,130 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.zhihu.matisse.internal.ui.widget.PreviewViewPager
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/bottom_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@color/preview_bottom_toolbar_bg"
|
||||
android:elevation="4dp"
|
||||
tools:targetApi="lollipop">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/button_back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:padding="16dp"
|
||||
android:layout_gravity="start"
|
||||
android:text="@string/button_back"
|
||||
android:textColor="?attr/preview.bottomToolbar.back.textColor"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:padding="16dp"
|
||||
android:id="@+id/originalLayout"
|
||||
android:visibility="gone"
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="wrap_content"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:layout_height="wrap_content"
|
||||
tools:showIn="@layout/activity_matisse">
|
||||
|
||||
|
||||
<com.zhihu.matisse.internal.ui.widget.CheckRadioView
|
||||
android:id="@+id/original"
|
||||
android:src="@drawable/ic_preview_radio_off"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:tint="#ffffff"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:enabled="true"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingLeft="4dp"
|
||||
android:text="@string/button_original"
|
||||
android:textColor="?attr/preview.bottomToolbar.back.textColor"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="@color/preview_bottom_size"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/button_apply"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:padding="16dp"
|
||||
android:textColor="?attr/preview.bottomToolbar.apply.textColor"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/top_toolbar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_margin="8dp"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.zhihu.matisse.internal.ui.widget.CheckView
|
||||
android:id="@+id/check_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"/>
|
||||
</FrameLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
57
matisse/src/main/res/layout/album_list_item.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/album_item_height">
|
||||
|
||||
<com.zhihu.matisse.internal.ui.widget.RoundedRectangleImageView
|
||||
android:id="@+id/album_cover"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginLeft="16dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@id/album_cover"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_toEndOf="@id/album_cover"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?attr/album.dropdown.title.color"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_toRightOf="@id/album_cover" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_media_count"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignStart="@id/album_name"
|
||||
android:layout_below="@id/album_name"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_toEndOf="@id/album_cover"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?album.dropdown.count.color"
|
||||
android:textSize="14sp"
|
||||
android:layout_alignLeft="@id/album_name"
|
||||
android:layout_toRightOf="@id/album_cover" />
|
||||
</RelativeLayout>
|
||||
31
matisse/src/main/res/layout/fragment_media_selection.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/page.bg">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="@dimen/media_grid_spacing"
|
||||
android:paddingTop="@dimen/media_grid_spacing"/>
|
||||
|
||||
</FrameLayout>
|
||||
32
matisse/src/main/res/layout/fragment_preview_item.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<it.sephiroth.android.library.imagezoom.ImageViewTouch
|
||||
android:id="@+id/image_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/video_play_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_play_circle_outline_white_48dp"/>
|
||||
</FrameLayout>
|
||||
50
matisse/src/main/res/layout/media_grid_content.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/media_thumbnail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<com.zhihu.matisse.internal.ui.widget.CheckView
|
||||
android:id="@+id/check_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top|right"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/gif"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right|bottom"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:src="@drawable/ic_gif"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/video_duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right|bottom"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"/>
|
||||
</merge>
|
||||
21
matisse/src/main/res/layout/media_grid_item.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<com.zhihu.matisse.internal.ui.widget.MediaGrid
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?selectableItemBackground"/>
|
||||
36
matisse/src/main/res/layout/photo_capture_item.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<com.zhihu.matisse.internal.ui.widget.SquareFrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?item.placeholder"
|
||||
android:foreground="?selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/hint"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:drawablePadding="8dp"
|
||||
android:drawableTop="@drawable/ic_photo_camera_white_24dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/photo_grid_capture"
|
||||
android:textColor="?capture.textColor"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
</com.zhihu.matisse.internal.ui.widget.SquareFrameLayout>
|
||||
38
matisse/src/main/res/values-zh-rTW/strings.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<string name="album_name_all">全部</string>
|
||||
|
||||
<string name="button_preview">預覽</string>
|
||||
<string name="button_apply_default">使用</string>
|
||||
<string name="button_apply">使用(%1$d)</string>
|
||||
<string name="button_back">返回</string>
|
||||
<string name="photo_grid_capture">拍一張</string>
|
||||
<string name="empty_text">還沒有圖片或影片</string>
|
||||
<string name="button_ok">我知道了</string>
|
||||
|
||||
<string name="error_over_count_default">您已經達到最大選擇數量</string>
|
||||
<string name="error_over_count">最多只能選擇 %1$d 個文件</string>
|
||||
<string name="error_under_quality">圖片質量太低</string>
|
||||
<string name="error_over_quality">圖片質量太高</string>
|
||||
<string name="error_file_type">不支援的文件類型</string>
|
||||
<string name="error_type_conflict">不能同時選擇圖片和影片</string>
|
||||
<string name="error_no_video_activity">沒有支持影片預覽的應用程式</string>
|
||||
<string name="button_original">原圖</string>
|
||||
<string name="button_sure_default">确定</string>
|
||||
<string name="button_sure">确定(%1$d)</string>
|
||||
</resources>
|
||||
40
matisse/src/main/res/values-zh/strings.xml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<string name="album_name_all">全部</string>
|
||||
|
||||
<string name="button_preview">预览</string>
|
||||
<string name="button_apply_default">使用</string>
|
||||
<string name="button_apply">使用(%1$d)</string>
|
||||
<string name="button_back">返回</string>
|
||||
<string name="photo_grid_capture">拍一张</string>
|
||||
<string name="empty_text">还没有图片或视频</string>
|
||||
<string name="button_ok">我知道了</string>
|
||||
|
||||
<string name="error_over_count_default">您已经达到最大选择数量</string>
|
||||
<string name="error_over_count">最多只能选择 %1$d 个文件</string>
|
||||
<string name="error_under_quality">图片质量太低</string>
|
||||
<string name="error_over_quality">图片质量太高</string>
|
||||
<string name="error_file_type">不支持的文件类型</string>
|
||||
<string name="error_type_conflict">不能同时选择图片和视频</string>
|
||||
<string name="error_no_video_activity">没有支持视频预览的应用</string>
|
||||
<string name="error_over_original_size">"该照片大于 %1$d M,无法上传将取消勾选原图"</string>
|
||||
<string name="error_over_original_count">"有 %1$d 张照片大于 %2$d M\n无法上传,将取消勾选原图"</string>
|
||||
<string name="button_original">原图</string>
|
||||
<string name="button_sure_default">确定</string>
|
||||
<string name="button_sure">确定(%1$d)</string>
|
||||
</resources>
|
||||
36
matisse/src/main/res/values/attrs.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<attr name="toolbar" format="reference"/>
|
||||
<attr name="album.dropdown.title.color" format="reference|color"/>
|
||||
<attr name="album.dropdown.count.color" format="reference|color"/>
|
||||
<attr name="album.element.color" format="reference|color"/>
|
||||
<attr name="album.thumbnail.placeholder" format="reference|color"/>
|
||||
<attr name="album.emptyView" format="reference"/>
|
||||
<attr name="album.emptyView.textColor" format="reference|color"/>
|
||||
<attr name="listPopupWindowStyle" format="reference"/>
|
||||
<attr name="page.bg" format="reference|color"/>
|
||||
<attr name="bottomToolbar.bg" format="reference|color"/>
|
||||
<attr name="bottomToolbar.preview.textColor" format="reference|color"/>
|
||||
<attr name="bottomToolbar.apply.textColor" format="reference|color"/>
|
||||
<attr name="preview.bottomToolbar.back.textColor" format="reference|color"/>
|
||||
<attr name="preview.bottomToolbar.apply.textColor" format="reference|color"/>
|
||||
<attr name="item.placeholder" format="reference|color"/>
|
||||
<attr name="item.checkCircle.backgroundColor" format="reference|color"/>
|
||||
<attr name="item.checkCircle.borderColor" format="reference|color"/>
|
||||
<attr name="capture.textColor" format="reference|color"/>
|
||||
</resources>
|
||||
22
matisse/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<color name="preview_bottom_toolbar_bg">#CC000000</color>
|
||||
<color name="preview_bottom_size">#61FFFFFF</color>
|
||||
|
||||
</resources>
|
||||
43
matisse/src/main/res/values/colors_dracula.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<color name="dracula_primary">#000000</color>
|
||||
<color name="dracula_primary_dark">#000000</color>
|
||||
|
||||
<color name="dracula_album_popup_bg">#000000</color>
|
||||
<color name="dracula_album_dropdown_title_text">#FFFFFF</color>
|
||||
<color name="dracula_album_dropdown_count_text">#89FFFFFF</color>
|
||||
<color name="dracula_album_dropdown_thumbnail_placeholder">#000000</color>
|
||||
<color name="dracula_album_empty_view">#4DFFFFFF</color>
|
||||
|
||||
<color name="dracula_item_placeholder">#000000</color>
|
||||
<color name="dracula_item_checkCircle_backgroundColor">#263237</color>
|
||||
<color name="dracula_item_checkCircle_borderColor">#FFFFFF</color>
|
||||
<color name="dracula_capture">#FFFFFF</color>
|
||||
|
||||
<color name="dracula_page_bg">#000000</color>
|
||||
<color name="dracula_bottom_toolbar_bg">#000000</color>
|
||||
|
||||
<color name="dracula_bottom_toolbar_preview_text">#DEFFFFFF</color>
|
||||
<color name="dracula_bottom_toolbar_preview_text_disable">#4DFFFFFF</color>
|
||||
<color name="dracula_bottom_toolbar_apply_text">#03A9F4</color>
|
||||
<color name="dracula_bottom_toolbar_apply_text_disable">#4D03A9F4</color>
|
||||
|
||||
<color name="dracula_preview_bottom_toolbar_back_text">#FFFFFF</color>
|
||||
<color name="dracula_preview_bottom_toolbar_apply_text">#03A9F4</color>
|
||||
<color name="dracula_preview_bottom_toolbar_apply_text_disable">#4D03A9F4</color>
|
||||
</resources>
|
||||
45
matisse/src/main/res/values/colors_zhihu.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<color name="zhihu_primary">#343EEF</color>
|
||||
<color name="zhihu_primary_dark">#343EEF</color>
|
||||
|
||||
<color name="zhihu_album_popup_bg">#FFFFFF</color>
|
||||
<color name="zhihu_album_dropdown_title_text">#DE000000</color>
|
||||
<color name="zhihu_album_dropdown_count_text">#999999</color>
|
||||
<color name="zhihu_album_dropdown_thumbnail_placeholder">#EAEEF4</color>
|
||||
<color name="zhihu_album_empty_view">#4D000000</color>
|
||||
|
||||
<color name="zhihu_item_placeholder">#EAEEF4</color>
|
||||
<color name="zhihu_item_checkCircle_backgroundColor">#1E8AE8</color>
|
||||
<color name="zhihu_item_checkCircle_borderColor">#FFFFFF</color>
|
||||
<color name="zhihu_capture">#424242</color>
|
||||
|
||||
<color name="zhihu_page_bg">#FFFFFF</color>
|
||||
<color name="zhihu_bottom_toolbar_bg">#FFFFFF</color>
|
||||
|
||||
<color name="zhihu_bottom_toolbar_preview_text">#DE000000</color>
|
||||
<color name="zhihu_bottom_toolbar_preview_text_disable">#4D000000</color>
|
||||
<color name="zhihu_bottom_toolbar_apply_text">#0077D9</color>
|
||||
<color name="zhihu_bottom_toolbar_apply_text_disable">#4D0077D9</color>
|
||||
|
||||
<color name="zhihu_preview_bottom_toolbar_back_text">#FFFFFF</color>
|
||||
<color name="zhihu_preview_bottom_toolbar_apply_text">#0077D9</color>
|
||||
<color name="zhihu_preview_bottom_toolbar_apply_text_disable">#4D0077D9</color>
|
||||
|
||||
<color name="zhihu_check_original_radio_disable">#808080</color>
|
||||
</resources>
|
||||
22
matisse/src/main/res/values/dimens.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<dimen name="media_grid_size">48dp</dimen>
|
||||
<dimen name="media_grid_spacing">4dp</dimen>
|
||||
|
||||
<dimen name="album_item_height">72dp</dimen>
|
||||
</resources>
|
||||
43
matisse/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:ignore="MissingTranslation">
|
||||
<string name="album_name_all">All Media</string>
|
||||
|
||||
<string name="button_preview">Preview</string>
|
||||
<string name="button_apply_default">Apply</string>
|
||||
<string name="button_apply">Apply(%1$d)</string>
|
||||
<string name="button_back">Back</string>
|
||||
<string name="photo_grid_capture">Camera</string>
|
||||
<string name="empty_text">No media yet</string>
|
||||
<string name="button_ok">OK</string>
|
||||
|
||||
<string name="error_over_count_default">You have reached max selectable</string>
|
||||
<string name="error_over_count">You can only select up to %1$d media files</string>
|
||||
<string name="error_under_quality">Under quality</string>
|
||||
<string name="error_over_quality">Over quality</string>
|
||||
<string name="error_file_type">Unsupported file type</string>
|
||||
<string name="error_type_conflict">Can\'t select images and videos at the same time</string>
|
||||
<string name="error_no_video_activity">No App found supporting video preview</string>
|
||||
<string name="error_over_original_size">Can\'t select the images larger than %1$d MB</string>
|
||||
<string name="error_over_original_count">%1$d images over %2$d MB. Original will be unchecked</string>
|
||||
<string name="button_original">Original</string>
|
||||
<string name="button_sure_default">Sure</string>
|
||||
<string name="button_sure">Sure(%1$d)</string>
|
||||
|
||||
</resources>
|
||||
125
matisse/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,125 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Zhihu Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<style name="AppThemeLoginM" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<item name="android:windowTranslucentStatus" tools:ignore="NewApi">false</item>
|
||||
<item name="android:statusBarColor" tools:ignore="NewApi">@android:color/transparent</item>
|
||||
<item name="colorPrimary">@color/zhihu_album_popup_bg</item>
|
||||
<item name="colorPrimaryDark">@color/zhihu_album_popup_bg</item>
|
||||
<item name="colorAccent">@color/zhihu_album_popup_bg</item>
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="android:windowBackground">@color/zhihu_album_popup_bg</item>
|
||||
<item name="android:navigationBarColor">@color/zhihu_album_popup_bg</item>
|
||||
<!-- 隐藏Activity窗口的Title标题栏 -->
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
//====================================== Theme Zhihu ===========================================
|
||||
|
||||
<style name="Matisse.Zhihu" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="colorPrimary">@color/zhihu_page_bg</item>
|
||||
<item name="colorPrimaryDark">@color/zhihu_page_bg</item>
|
||||
<item name="toolbar">@style/Toolbar.Zhihu</item>
|
||||
<item name="album.dropdown.title.color">@color/zhihu_album_dropdown_title_text</item>
|
||||
<item name="album.dropdown.count.color">@color/zhihu_album_dropdown_count_text</item>
|
||||
<item name="album.element.color">@android:color/black</item>
|
||||
<item name="album.thumbnail.placeholder">@color/zhihu_album_dropdown_thumbnail_placeholder</item>
|
||||
<item name="album.emptyView">@drawable/ic_empty_zhihu</item>
|
||||
<item name="album.emptyView.textColor">@color/zhihu_album_empty_view</item>
|
||||
<item name="item.placeholder">@color/zhihu_item_placeholder</item>
|
||||
<item name="item.checkCircle.backgroundColor">@color/zhihu_item_checkCircle_backgroundColor</item>
|
||||
<item name="item.checkCircle.borderColor">@color/zhihu_item_checkCircle_borderColor</item>
|
||||
<item name="page.bg">@color/zhihu_page_bg</item>
|
||||
<item name="bottomToolbar.bg">@color/zhihu_bottom_toolbar_bg</item>
|
||||
<item name="bottomToolbar.preview.textColor">@color/zhihu_bottom_toolbar_preview</item>
|
||||
<item name="bottomToolbar.apply.textColor">@color/zhihu_bottom_toolbar_apply</item>
|
||||
<item name="preview.bottomToolbar.back.textColor">@color/zhihu_preview_bottom_toolbar_back_text</item>
|
||||
<item name="preview.bottomToolbar.apply.textColor">@color/zhihu_preview_bottom_toolbar_apply</item>
|
||||
<item name="listPopupWindowStyle">@style/Popup.Zhihu</item>
|
||||
<item name="capture.textColor">@color/zhihu_capture</item>
|
||||
</style>
|
||||
|
||||
<style name="Toolbar.Zhihu" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
|
||||
|
||||
</style>
|
||||
|
||||
<style name="Popup.Zhihu" parent="Widget.AppCompat.Light.ListPopupWindow">
|
||||
<item name="android:popupBackground">@color/zhihu_album_popup_bg</item>
|
||||
</style>
|
||||
|
||||
//===================================== Theme Dracula ==========================================
|
||||
|
||||
<!-- <style name="Matisse.Dracula" parent="Theme.AppCompat.NoActionBar">-->
|
||||
<!-- <item name="colorPrimary">@color/dracula_primary_dark</item>-->
|
||||
<!-- <item name="colorPrimaryDark">@color/dracula_primary_dark</item>-->
|
||||
<!-- <item name="toolbar">@style/Toolbar.Dracula</item>-->
|
||||
<!-- <item name="album.dropdown.title.color">@color/dracula_primary_dark</item>-->
|
||||
<!-- <item name="album.dropdown.count.color">@color/dracula_primary_dark</item>-->
|
||||
<!-- <item name="album.element.color">@android:color/black</item>-->
|
||||
<!-- <item name="album.thumbnail.placeholder">@color/dracula_primary_dark</item>-->
|
||||
<!-- <item name="album.emptyView">@drawable/ic_empty_dracula</item>-->
|
||||
<!-- <item name="album.emptyView.textColor">@color/dracula_album_empty_view</item>-->
|
||||
<!-- <item name="item.placeholder">@color/dracula_primary_dark</item>-->
|
||||
<!-- <item name="item.checkCircle.backgroundColor">@color/dracula_item_checkCircle_backgroundColor</item>-->
|
||||
<!-- <item name="item.checkCircle.borderColor">@color/dracula_item_checkCircle_borderColor</item>-->
|
||||
<!-- <item name="page.bg">@color/dracula_primary_dark</item>-->
|
||||
<!-- <item name="bottomToolbar.bg">@color/dracula_primary_dark</item>-->
|
||||
<!-- <item name="bottomToolbar.preview.textColor">@color/dracula_bottom_toolbar_preview</item>-->
|
||||
<!-- <item name="bottomToolbar.apply.textColor">@color/dracula_bottom_toolbar_apply</item>-->
|
||||
<!-- <item name="preview.bottomToolbar.back.textColor">@color/dracula_preview_bottom_toolbar_back_text</item>-->
|
||||
<!-- <item name="preview.bottomToolbar.apply.textColor">@color/dracula_preview_bottom_toolbar_apply</item>-->
|
||||
<!-- <item name="android:listPopupWindowStyle">@style/Popup.Dracula</item>-->
|
||||
<!-- <item name="listPopupWindowStyle">@style/Popup.Dracula</item>-->
|
||||
<!-- <item name="capture.textColor">@color/dracula_capture</item>-->
|
||||
<!-- </style>-->
|
||||
|
||||
|
||||
<style name="Matisse.Dracula" parent="Theme.AppCompat.NoActionBar">
|
||||
<item name="colorPrimary">@color/dracula_primary</item>
|
||||
<item name="colorPrimaryDark">@color/dracula_primary_dark</item>
|
||||
<item name="toolbar">@style/Toolbar.Dracula</item>
|
||||
<item name="album.dropdown.title.color">@color/dracula_capture</item>
|
||||
<item name="album.dropdown.count.color">@color/dracula_capture</item>
|
||||
<item name="album.element.color">@color/dracula_capture</item>
|
||||
<item name="album.thumbnail.placeholder">@color/dracula_album_dropdown_thumbnail_placeholder</item>
|
||||
<item name="album.emptyView">@drawable/ic_empty_dracula</item>
|
||||
<item name="album.emptyView.textColor">@color/dracula_album_empty_view</item>
|
||||
<item name="item.placeholder">@color/dracula_item_placeholder</item>
|
||||
<item name="item.checkCircle.backgroundColor">@color/dracula_item_checkCircle_backgroundColor</item>
|
||||
<item name="item.checkCircle.borderColor">@color/dracula_item_checkCircle_borderColor</item>
|
||||
<item name="page.bg">@color/dracula_page_bg</item>
|
||||
<item name="bottomToolbar.bg">@color/dracula_bottom_toolbar_bg</item>
|
||||
<item name="bottomToolbar.preview.textColor">@color/dracula_bottom_toolbar_preview</item>
|
||||
<item name="bottomToolbar.apply.textColor">@color/dracula_bottom_toolbar_apply</item>
|
||||
<item name="preview.bottomToolbar.back.textColor">@color/dracula_preview_bottom_toolbar_back_text</item>
|
||||
<item name="preview.bottomToolbar.apply.textColor">@color/dracula_preview_bottom_toolbar_apply</item>
|
||||
<item name="android:listPopupWindowStyle">@style/Popup.Dracula</item>
|
||||
<item name="listPopupWindowStyle">@style/Popup.Dracula</item>
|
||||
<item name="capture.textColor">@color/dracula_capture</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="Toolbar.Dracula" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
|
||||
|
||||
</style>
|
||||
|
||||
<style name="Popup.Dracula" parent="Widget.AppCompat.ListPopupWindow">
|
||||
<item name="android:popupBackground">@color/dracula_primary</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
5
settings.gradle
Normal file
@@ -0,0 +1,5 @@
|
||||
include ':basicLib'
|
||||
include ':app'
|
||||
include ':cili'
|
||||
include ':youyou'
|
||||
include ':matisse'
|
||||