Compare commits
5
Commits
8839320484
...
dev_lvmeng
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5bc6d2f2c5 | ||
|
|
3dea103e27 | ||
|
|
5a4f136dba | ||
|
|
4973236469 | ||
|
|
c02a2ff08c |
@@ -78,7 +78,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
|
||||||
api rootProject.ext.androidxLibs
|
api rootProject.ext.androidxLibs
|
||||||
// ViewModel and LiveData
|
// ViewModel and LiveData
|
||||||
api rootProject.ext.jetpackLibs
|
api rootProject.ext.jetpackLibs
|
||||||
@@ -112,4 +112,7 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.6.0'
|
implementation 'androidx.fragment:fragment-ktx:1.6.0'
|
||||||
|
|
||||||
|
// implementation 'com.zhihu.android:matisse:0.6.0'
|
||||||
|
implementation project(':matisse')
|
||||||
|
implementation 'com.github.hackware1993:MagicIndicator:1.7.0'
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
package="com.zmkg.coaloperation">
|
package="com.zmkg.coaloperation">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.AppTheme"
|
android:theme="@style/Theme.AppTheme"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
tools:replace="android:allowBackup">
|
tools:replace="android:allowBackup">
|
||||||
|
|
||||||
@@ -69,6 +71,7 @@
|
|||||||
<activity android:name=".ui.wateryield.WaterYieldObserveRecordActivity" />
|
<activity android:name=".ui.wateryield.WaterYieldObserveRecordActivity" />
|
||||||
<activity android:name=".ui.geologging.GeologicalPointActivity" />
|
<activity android:name=".ui.geologging.GeologicalPointActivity" />
|
||||||
<activity android:name=".ui.geologging.AddGeologicalPointActivity" />
|
<activity android:name=".ui.geologging.AddGeologicalPointActivity" />
|
||||||
|
<activity android:name=".ui.geologging.AddGeoLoggingFaceActivity" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.zmkg.coaloperation
|
package com.zmkg.coaloperation
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
@@ -10,7 +11,7 @@ import com.zmkg.coaloperation.bottomtab.HomeBottomTabLayout
|
|||||||
import com.zmkg.coaloperation.databinding.ActivityMainBinding
|
import com.zmkg.coaloperation.databinding.ActivityMainBinding
|
||||||
import com.zmkg.coaloperation.ui.home.fragment.HomeFragment
|
import com.zmkg.coaloperation.ui.home.fragment.HomeFragment
|
||||||
import com.zmkg.coaloperation.ui.record.fragment.RecordFragment
|
import com.zmkg.coaloperation.ui.record.fragment.RecordFragment
|
||||||
import com.zmkg.coaloperation.ui.report.fragment.ReportFragment
|
import com.zmkg.coaloperation.ui.report.fragment.Report2Fragment
|
||||||
import com.zmkg.coaloperation.ui.user.fragment.UserFragment
|
import com.zmkg.coaloperation.ui.user.fragment.UserFragment
|
||||||
|
|
||||||
class MainActivity : BaseVMBActivity<TestViewModel, ActivityMainBinding>(R.layout.activity_main),
|
class MainActivity : BaseVMBActivity<TestViewModel, ActivityMainBinding>(R.layout.activity_main),
|
||||||
@@ -56,7 +57,7 @@ class MainActivity : BaseVMBActivity<TestViewModel, ActivityMainBinding>(R.layou
|
|||||||
}
|
}
|
||||||
|
|
||||||
TAG_REPORT -> {
|
TAG_REPORT -> {
|
||||||
return ReportFragment()
|
return Report2Fragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TAG_MESSAGE -> {
|
// TAG_MESSAGE -> {
|
||||||
@@ -77,4 +78,5 @@ class MainActivity : BaseVMBActivity<TestViewModel, ActivityMainBinding>(R.layou
|
|||||||
override fun transparentStatusBar(): Boolean {
|
override fun transparentStatusBar(): Boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -73,6 +73,8 @@ class MyApplication : Application(), ViewModelStoreOwner {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun initLoginStatusListener() {
|
fun initLoginStatusListener() {
|
||||||
// TUILogin.addLoginListener(loginStatusListener)
|
// TUILogin.addLoginListener(loginStatusListener)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.zmkg.coaloperation.adapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.updateLayoutParams
|
||||||
|
import com.chad.library.adapter.base.BaseQuickAdapter
|
||||||
|
import com.chad.library.adapter.base.viewholder.BaseViewHolder
|
||||||
|
import com.zmkg.coaloperation.R
|
||||||
|
import com.zmkg.coaloperation.bean.CommonImageBean
|
||||||
|
import com.zmkg.coaloperation.databinding.ListItemCommonImageBinding
|
||||||
|
import com.zmkg.coaloperation.superfuntion.loadRoundedImage
|
||||||
|
import com.zmkg.coaloperation.utils.dp
|
||||||
|
import com.zmkg.coaloperation.utils.load
|
||||||
|
|
||||||
|
class CommonImageAdapter(var list: MutableList<CommonImageBean>, var imageSize: Int = 70.dp):
|
||||||
|
BaseQuickAdapter<CommonImageBean, CommonImageAdapter.VH>(
|
||||||
|
R.layout.list_item_common_image, list
|
||||||
|
){
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
|
||||||
|
val binding = ListItemCommonImageBinding.inflate(
|
||||||
|
LayoutInflater.from(context), parent, false
|
||||||
|
)
|
||||||
|
return VH(binding).also {
|
||||||
|
bindViewClickListener(it, viewType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convert(
|
||||||
|
holder: VH,
|
||||||
|
item: CommonImageBean
|
||||||
|
) {
|
||||||
|
holder.binding.ivImage.let {
|
||||||
|
it.updateLayoutParams {
|
||||||
|
width = imageSize
|
||||||
|
height = imageSize
|
||||||
|
}
|
||||||
|
if (item.isAddFlag) {
|
||||||
|
it.load(R.drawable.ic_add_image)
|
||||||
|
} else {
|
||||||
|
it.loadRoundedImage(item.imagePath, 3F)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class VH(var binding: ListItemCommonImageBinding): BaseViewHolder(binding.root)
|
||||||
|
|
||||||
|
}
|
||||||
+9
-11
@@ -5,30 +5,28 @@ import android.view.ViewGroup
|
|||||||
import com.chad.library.adapter.base.BaseQuickAdapter
|
import com.chad.library.adapter.base.BaseQuickAdapter
|
||||||
import com.chad.library.adapter.base.viewholder.BaseViewHolder
|
import com.chad.library.adapter.base.viewholder.BaseViewHolder
|
||||||
import com.zmkg.coaloperation.R
|
import com.zmkg.coaloperation.R
|
||||||
import com.zmkg.coaloperation.bean.GeoPointImage
|
import com.zmkg.coaloperation.databinding.ItemGeologicalListBinding
|
||||||
import com.zmkg.coaloperation.databinding.ListItemPointImageBinding
|
import com.zmkg.coaloperation.db.entity.geologging.GeoLoggingFace
|
||||||
|
|
||||||
class GeoPointImageAdapter(list: MutableList<GeoPointImage>):
|
class GeoLoggingFaceAdapter(list: MutableList<GeoLoggingFace>):
|
||||||
BaseQuickAdapter<GeoPointImage, GeoPointImageAdapter.VH>(
|
BaseQuickAdapter<GeoLoggingFace, GeoLoggingFaceAdapter.VH>(
|
||||||
R.layout.list_item_point_image, list
|
R.layout.item_geological_list, list
|
||||||
){
|
){
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
|
||||||
val binding = ListItemPointImageBinding.inflate(
|
val binding = ItemGeologicalListBinding.inflate(
|
||||||
LayoutInflater.from(context), parent, false
|
LayoutInflater.from(context), parent, false
|
||||||
)
|
)
|
||||||
return VH(binding).also {
|
return VH(binding).also {
|
||||||
bindViewClickListener(it, viewType)
|
bindViewClickListener(it, viewType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun convert(
|
override fun convert(
|
||||||
holder: VH,
|
holder: VH,
|
||||||
item: GeoPointImage
|
item: GeoLoggingFace
|
||||||
) {
|
) {
|
||||||
holder.binding.ivPointImage.setImageResource(R.drawable.ic_add_image)
|
holder.binding.tvFaceName.text = item.surfaceName
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class VH(var binding: ListItemPointImageBinding): BaseViewHolder(binding.root)
|
inner class VH(var binding: ItemGeologicalListBinding): BaseViewHolder(binding.root)
|
||||||
|
|
||||||
}
|
}
|
||||||
+69
-49
@@ -1,114 +1,134 @@
|
|||||||
package com.zmkg.coaloperation.adapter
|
package com.zmkg.coaloperation.adapter
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.chad.library.adapter.base.BaseMultiItemQuickAdapter
|
import com.chad.library.adapter.base.BaseMultiItemQuickAdapter
|
||||||
import com.chad.library.adapter.base.viewholder.BaseViewHolder
|
import com.chad.library.adapter.base.viewholder.BaseViewHolder
|
||||||
|
import com.zhihu.matisse.Matisse
|
||||||
import com.zmkg.coaloperation.R
|
import com.zmkg.coaloperation.R
|
||||||
import com.zmkg.coaloperation.bean.BaseItem
|
import com.zmkg.coaloperation.base.BaseActivity
|
||||||
import com.zmkg.coaloperation.bean.GeoPointImage
|
import com.zmkg.coaloperation.bean.CommonImageBean
|
||||||
import com.zmkg.coaloperation.bean.GeoPointMultiItem
|
import com.zmkg.coaloperation.bean.GeoPointMultiItem
|
||||||
import com.zmkg.coaloperation.databinding.ListItemPointBaseHorizontalBinding
|
import com.zmkg.coaloperation.databinding.ListItemPointBaseHorizontalBinding
|
||||||
import com.zmkg.coaloperation.databinding.ListItemPointBaseVerticalBinding
|
import com.zmkg.coaloperation.databinding.ListItemPointBaseVerticalBinding
|
||||||
|
import com.zmkg.coaloperation.utils.GalleryUtils
|
||||||
import com.zmkg.coaloperation.utils.gone
|
import com.zmkg.coaloperation.utils.gone
|
||||||
|
import com.zmkg.coaloperation.utils.permission.StorageUtils
|
||||||
import com.zmkg.coaloperation.utils.visible
|
import com.zmkg.coaloperation.utils.visible
|
||||||
|
|
||||||
class GeoPointMultiAdapter(list: MutableList<GeoPointMultiItem>):
|
class GeoPointMultiAdapter(list: MutableList<GeoPointMultiItem>) :
|
||||||
BaseMultiItemQuickAdapter<GeoPointMultiItem, BaseViewHolder>(list){
|
BaseMultiItemQuickAdapter<GeoPointMultiItem, BaseViewHolder>(list) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
addItemType(GeoPointMultiItem.TYPE_BASE_INFO, R.layout.list_item_point_base_info)
|
addItemType(GeoPointMultiItem.TYPE_BASE_INFO, R.layout.list_item_point_base_info)
|
||||||
addItemType(GeoPointMultiItem.TYPE_LOGGING_INFO, R.layout.list_item_point_logging_info)
|
addItemType(GeoPointMultiItem.TYPE_LOGGING_INFO, R.layout.list_item_point_logging_info)
|
||||||
addItemType(GeoPointMultiItem.TYPE_CALCULATE_INFO, R.layout.list_item_point_calculate_info)
|
addItemType(GeoPointMultiItem.TYPE_CALCULATE_INFO, R.layout.list_item_point_calculate_info)
|
||||||
addItemType(GeoPointMultiItem.TYPE_UPLOAD_IMAGE, R.layout.list_item_point_upload_image)
|
addItemType(GeoPointMultiItem.TYPE_UPLOAD_IMAGE, R.layout.list_item_point_upload_image)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun convert(
|
override fun convert(
|
||||||
holder: BaseViewHolder,
|
holder: BaseViewHolder,
|
||||||
item: GeoPointMultiItem
|
item: GeoPointMultiItem
|
||||||
) {
|
) {
|
||||||
when (holder.itemViewType) {
|
when (holder.itemViewType) {
|
||||||
GeoPointMultiItem.TYPE_BASE_INFO->{
|
GeoPointMultiItem.TYPE_BASE_INFO -> {
|
||||||
loadBaseInfo(holder, item)
|
loadBaseInfo(holder, item)
|
||||||
}
|
}
|
||||||
GeoPointMultiItem.TYPE_LOGGING_INFO->{
|
|
||||||
|
GeoPointMultiItem.TYPE_LOGGING_INFO -> {
|
||||||
loadLoggingInfo(holder, item)
|
loadLoggingInfo(holder, item)
|
||||||
}
|
}
|
||||||
GeoPointMultiItem.TYPE_CALCULATE_INFO->{
|
|
||||||
|
GeoPointMultiItem.TYPE_CALCULATE_INFO -> {
|
||||||
loadCalculateInfo(holder, item)
|
loadCalculateInfo(holder, item)
|
||||||
}
|
}
|
||||||
GeoPointMultiItem.TYPE_UPLOAD_IMAGE->{
|
|
||||||
|
GeoPointMultiItem.TYPE_UPLOAD_IMAGE -> {
|
||||||
loadUploadImage(holder, item)
|
loadUploadImage(holder, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadUploadImage(holder: BaseViewHolder,item: GeoPointMultiItem) {
|
private fun loadUploadImage(holder: BaseViewHolder, item: GeoPointMultiItem) {
|
||||||
val rvImageList = holder.itemView.findViewById<RecyclerView>(R.id.rvImageList)
|
val rvImageList = holder.itemView.findViewById<RecyclerView>(R.id.rvImageList)
|
||||||
val images = mutableListOf(
|
if (item.images == null) {
|
||||||
GeoPointImage(),
|
item.images = mutableListOf()
|
||||||
GeoPointImage(),
|
}
|
||||||
GeoPointImage(),
|
val images = item.images!!
|
||||||
GeoPointImage()
|
rvImageList.let { rv ->
|
||||||
)
|
|
||||||
rvImageList.let {rv->
|
|
||||||
rv.layoutManager = GridLayoutManager(rv.context, 4, GridLayoutManager.VERTICAL, false)
|
rv.layoutManager = GridLayoutManager(rv.context, 4, GridLayoutManager.VERTICAL, false)
|
||||||
rv.adapter = GeoPointImageAdapter(images).also {
|
rv.adapter = CommonImageAdapter(images).also {
|
||||||
it.setOnItemClickListener { adapter, view, position ->
|
it.setOnItemClickListener { adapter, view, position ->
|
||||||
|
if (it.data[position].isAddFlag.not()) {
|
||||||
|
return@setOnItemClickListener
|
||||||
|
}
|
||||||
|
openGallery(context as BaseActivity, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadCalculateInfo(holder: BaseViewHolder,item: GeoPointMultiItem) {}
|
private fun loadCalculateInfo(holder: BaseViewHolder, item: GeoPointMultiItem) {}
|
||||||
|
|
||||||
private fun loadLoggingInfo(holder: BaseViewHolder,item: GeoPointMultiItem) {
|
private fun loadLoggingInfo(holder: BaseViewHolder, item: GeoPointMultiItem) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadBaseInfo(holder: BaseViewHolder,item: GeoPointMultiItem) {
|
private fun loadBaseInfo(holder: BaseViewHolder, item: GeoPointMultiItem) {
|
||||||
val llBaseTop = holder.itemView.findViewById<LinearLayout>(R.id.llBaseTop)
|
val llBaseTop = holder.itemView.findViewById<LinearLayout>(R.id.llBaseTop)
|
||||||
val topList = mutableListOf(
|
llBaseTop.removeAllViews()
|
||||||
BaseItem(name = "点号", value = "1#"),
|
item.baseTopList?.forEach {
|
||||||
BaseItem(name = "点距", value = "1#"),
|
val itemBinding = ListItemPointBaseHorizontalBinding.inflate(
|
||||||
BaseItem(name = "累距", value = "78.874"),
|
LayoutInflater.from(context),
|
||||||
)
|
llBaseTop,
|
||||||
val bottomList = mutableListOf(
|
false
|
||||||
BaseItem(name = "巷高", value = "2.855"),
|
)
|
||||||
BaseItem(name = "顶板", value = "1024.491"),
|
|
||||||
BaseItem(name = "底板", value = "1018.491"),
|
|
||||||
BaseItem(name = "巷顶煤厚", value = "0.15"),
|
|
||||||
)
|
|
||||||
topList.forEach {
|
|
||||||
val itemBinding = ListItemPointBaseHorizontalBinding.inflate(LayoutInflater.from(context), llBaseTop, false)
|
|
||||||
itemBinding.run {
|
itemBinding.run {
|
||||||
tvItemName.text = it.name
|
tvItemName.text = it.name
|
||||||
tvItemValue.text = it.value
|
etInputValue.setText(it.value)
|
||||||
}
|
}
|
||||||
llBaseTop.addView(itemBinding.root)
|
llBaseTop.addView(itemBinding.root)
|
||||||
}
|
}
|
||||||
val llBaseBottom = holder.itemView.findViewById<LinearLayout>(R.id.llBaseBottom)
|
|
||||||
|
|
||||||
bottomList.forEachIndexed {index, it ->
|
val llBaseBottom = holder.itemView.findViewById<LinearLayout>(R.id.llBaseBottom)
|
||||||
val itemBinding = ListItemPointBaseVerticalBinding.inflate(LayoutInflater.from(context),llBaseBottom, false)
|
llBaseBottom.removeAllViews()
|
||||||
|
item.baseBottomList?.forEachIndexed { index, it ->
|
||||||
|
val itemBinding = ListItemPointBaseVerticalBinding.inflate(
|
||||||
|
LayoutInflater.from(context),
|
||||||
|
llBaseBottom,
|
||||||
|
false
|
||||||
|
)
|
||||||
itemBinding.run {
|
itemBinding.run {
|
||||||
tvItemName.text = it.name
|
tvItemName.text = it.name
|
||||||
tvItemValue.text = it.value
|
etInputValue.setText(it.value)
|
||||||
dividerVertical.run {
|
dividerVertical.run {
|
||||||
if (index == bottomList.size - 1) gone() else visible()
|
if (index == item.baseBottomList!!.size - 1) gone() else visible()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
itemBinding.root.updateLayoutParams<LinearLayout.LayoutParams> {
|
itemBinding.root.updateLayoutParams<LinearLayout.LayoutParams> {
|
||||||
width = 0
|
width = 0
|
||||||
height = LinearLayout.LayoutParams.MATCH_PARENT
|
height = LinearLayout.LayoutParams.MATCH_PARENT
|
||||||
weight = if (index == bottomList.size - 1) 1.24F else 1.0F
|
weight = if (index == item.baseBottomList!!.size - 1) 1.24F else 1.0F
|
||||||
}
|
}
|
||||||
llBaseBottom.addView(itemBinding.root)
|
llBaseBottom.addView(itemBinding.root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
private fun openGallery(activity: BaseActivity, adapter: CommonImageAdapter) {
|
||||||
|
StorageUtils.requestPermission(activity) {
|
||||||
|
GalleryUtils.openGallery(activity) { data ->
|
||||||
|
val paths = Matisse.obtainPathResult(data) // 获取文件路径列表
|
||||||
|
paths?.forEach { path ->
|
||||||
|
val size = adapter.list.size
|
||||||
|
adapter.list.add(size - 1, CommonImageBean(imagePath = path))
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
package com.zmkg.coaloperation.adapter
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.core.view.updateLayoutParams
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import com.chad.library.adapter.base.BaseQuickAdapter
|
||||||
|
import com.chad.library.adapter.base.viewholder.BaseViewHolder
|
||||||
|
import com.zhihu.matisse.Matisse
|
||||||
|
import com.zmkg.coaloperation.R
|
||||||
|
import com.zmkg.coaloperation.base.BaseActivity
|
||||||
|
import com.zmkg.coaloperation.bean.CommonImageBean
|
||||||
|
import com.zmkg.coaloperation.bean.ReportItem
|
||||||
|
import com.zmkg.coaloperation.databinding.ListItemReportBinding
|
||||||
|
import com.zmkg.coaloperation.utils.GalleryUtils
|
||||||
|
import com.zmkg.coaloperation.utils.dp
|
||||||
|
import com.zmkg.coaloperation.utils.gone
|
||||||
|
import com.zmkg.coaloperation.utils.permission.StorageUtils
|
||||||
|
import com.zmkg.coaloperation.utils.visible
|
||||||
|
|
||||||
|
class ReportAdapter(list: MutableList<ReportItem>) :
|
||||||
|
BaseQuickAdapter<ReportItem, ReportAdapter.VH>(R.layout.list_item_report, list) {
|
||||||
|
|
||||||
|
inner class VH(var binding: ListItemReportBinding) : BaseViewHolder(binding.root)
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
|
||||||
|
val binding = ListItemReportBinding.inflate(
|
||||||
|
LayoutInflater.from(context), parent, false
|
||||||
|
)
|
||||||
|
return VH(binding).also {
|
||||||
|
bindViewClickListener(it, viewType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convert(holder: VH, item: ReportItem) {
|
||||||
|
val binding = holder.binding
|
||||||
|
val position = holder.layoutPosition
|
||||||
|
binding.tvItemName.run {
|
||||||
|
text = item.name
|
||||||
|
gravity = Gravity.CENTER_VERTICAL
|
||||||
|
updateLayoutParams<LinearLayout.LayoutParams> {
|
||||||
|
gravity = if (item.viewType == 4) Gravity.CENTER_VERTICAL else Gravity.TOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.divider.run {
|
||||||
|
if (position == data.size - 1) gone() else visible()
|
||||||
|
}
|
||||||
|
when (item.viewType) {
|
||||||
|
1 -> {
|
||||||
|
binding.tvItemValue.gone()
|
||||||
|
binding.rvReportImage.gone()
|
||||||
|
binding.etInputValue.let { et ->
|
||||||
|
et.visible()
|
||||||
|
et.setText(item.value)
|
||||||
|
et.hint = item.inputHint
|
||||||
|
et.isSingleLine = item.isSingleLine
|
||||||
|
if (item.isSingleLine) {
|
||||||
|
et.gravity = Gravity.CENTER_VERTICAL
|
||||||
|
et.setLines(1)
|
||||||
|
et.updateLayoutParams<LinearLayout.LayoutParams> { height = 50.dp }
|
||||||
|
} else {
|
||||||
|
et.gravity = Gravity.TOP
|
||||||
|
et.setLines(item.inputShowLines)
|
||||||
|
et.updateLayoutParams<LinearLayout.LayoutParams> {
|
||||||
|
height = LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
topMargin = 15.dp
|
||||||
|
bottomMargin = 15.dp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
2 -> {
|
||||||
|
binding.rvReportImage.gone()
|
||||||
|
binding.etInputValue.gone()
|
||||||
|
binding.tvItemValue.let { tv ->
|
||||||
|
tv.visible()
|
||||||
|
tv.text = item.value
|
||||||
|
tv.setCompoundDrawablesWithIntrinsicBounds(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
R.drawable.ic_arrow_down_gray,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
tv.updateLayoutParams<LinearLayout.LayoutParams> {
|
||||||
|
width = LinearLayout.LayoutParams.MATCH_PARENT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
3 -> {
|
||||||
|
binding.rvReportImage.gone()
|
||||||
|
binding.etInputValue.gone()
|
||||||
|
binding.tvItemValue.let { tv ->
|
||||||
|
tv.visible()
|
||||||
|
tv.text = item.value
|
||||||
|
tv.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_select_time, 0)
|
||||||
|
tv.updateLayoutParams<LinearLayout.LayoutParams> {
|
||||||
|
width = LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
4 -> {
|
||||||
|
binding.tvItemValue.gone()
|
||||||
|
binding.etInputValue.gone()
|
||||||
|
if (item.images == null) {
|
||||||
|
item.images = mutableListOf()
|
||||||
|
}
|
||||||
|
binding.rvReportImage.let { rv ->
|
||||||
|
rv.visible()
|
||||||
|
rv.layoutManager = GridLayoutManager(rv.context, 2)
|
||||||
|
rv.adapter = CommonImageAdapter(item.images!!, 80.dp).also {
|
||||||
|
it.setOnItemClickListener { adapter, view, position ->
|
||||||
|
if (it.data[position].isAddFlag.not()) {
|
||||||
|
return@setOnItemClickListener
|
||||||
|
}
|
||||||
|
openGallery(context as BaseActivity, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
private fun openGallery(activity: BaseActivity, adapter: CommonImageAdapter) {
|
||||||
|
StorageUtils.requestPermission(activity) {
|
||||||
|
GalleryUtils.openGallery(activity) { data ->
|
||||||
|
val paths = Matisse.obtainPathResult(data) // 获取文件路径列表
|
||||||
|
paths?.forEach { path ->
|
||||||
|
val size = adapter.list.size
|
||||||
|
adapter.list.add(size - 1, CommonImageBean(imagePath = path))
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package com.zmkg.coaloperation.base;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import com.zmkg.coaloperation.bean.IActivityResult;
|
||||||
|
|
||||||
|
public class BaseActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
// public static final String COLOR_WHITE = "#FFFFFF";
|
||||||
|
//
|
||||||
|
// public void whiteStatusBar(boolean isFits) {
|
||||||
|
// whiteStatusBar(isFits, false);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void whiteStatusBar(boolean isFits, boolean keyboardEnable) {
|
||||||
|
// hasColorStatusBar(COLOR_WHITE, isFits, true, keyboardEnable);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void transparentStatusBar(boolean isFits, boolean isDarkFont) {
|
||||||
|
// ImmersionBar.with(this)
|
||||||
|
// .fitsSystemWindows(isFits)
|
||||||
|
// .statusBarDarkFont(isDarkFont)
|
||||||
|
// .transparentStatusBar()
|
||||||
|
// .navigationBarColor(R.color.white)
|
||||||
|
// .navigationBarDarkIcon(true)
|
||||||
|
// .init();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void hasColorStatusBar(String statusBarColor, boolean isFits, boolean isDarkFont) {
|
||||||
|
// hasColorStatusBar(statusBarColor, isFits, isDarkFont, false);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void hasColorStatusBar(String statusBarColor, boolean isFits, boolean isDarkFont, boolean keyboardEnable) {
|
||||||
|
// ImmersionBar.with(this)
|
||||||
|
// .fitsSystemWindows(isFits)
|
||||||
|
// .statusBarColor(statusBarColor)
|
||||||
|
// .statusBarDarkFont(isDarkFont)
|
||||||
|
// .keyboardEnable(keyboardEnable)
|
||||||
|
// .navigationBarColor(R.color.white)
|
||||||
|
// .navigationBarDarkIcon(true)
|
||||||
|
// .init();
|
||||||
|
// }
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<Intent> activityLauncher =
|
||||||
|
registerForActivityResult(
|
||||||
|
new ActivityResultContracts.StartActivityForResult(),
|
||||||
|
result -> this.launchActivityCallback.callback(result)
|
||||||
|
);
|
||||||
|
private IActivityResult.LaunchActivityCallback launchActivityCallback;
|
||||||
|
|
||||||
|
public void launchActivityResult(Intent intent, IActivityResult.LaunchActivityCallback activityCallback) {
|
||||||
|
this.launchActivityCallback = activityCallback;
|
||||||
|
activityLauncher.launch(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IActivityResult.LaunchTackPictureCallback launchTackPictureCallback;
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<Uri> tackPictureLauncher =
|
||||||
|
registerForActivityResult(
|
||||||
|
new ActivityResultContracts.TakePicture(),
|
||||||
|
result -> this.launchTackPictureCallback.callback(result)
|
||||||
|
);
|
||||||
|
|
||||||
|
public void launchTackPicture(Uri uri, IActivityResult.LaunchTackPictureCallback callback) {
|
||||||
|
this.launchTackPictureCallback = callback;
|
||||||
|
tackPictureLauncher.launch(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IActivityResult.RequestPermissionCallback launchPermissionCallback;
|
||||||
|
|
||||||
|
public void requestSinglePermissionResult(String permission, IActivityResult.RequestPermissionCallback permissionCallback) {
|
||||||
|
this.launchPermissionCallback = permissionCallback;
|
||||||
|
permissionLauncher.launch(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<String> permissionLauncher =
|
||||||
|
registerForActivityResult(
|
||||||
|
new ActivityResultContracts.RequestPermission(),
|
||||||
|
result -> this.launchPermissionCallback.callback(result)
|
||||||
|
);
|
||||||
|
|
||||||
|
private IActivityResult.RequestMultiplePermissionsCallback launchMultiplePermissionsCallback;
|
||||||
|
|
||||||
|
public void requestMultiplePermissionsResult(String[] permissions, IActivityResult.RequestMultiplePermissionsCallback permissionCallback) {
|
||||||
|
this.launchMultiplePermissionsCallback = permissionCallback;
|
||||||
|
multiplePermissionLauncher.launch(permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<String[]> multiplePermissionLauncher =
|
||||||
|
registerForActivityResult(
|
||||||
|
new ActivityResultContracts.RequestMultiplePermissions(),
|
||||||
|
result -> this.launchMultiplePermissionsCallback.callback(result)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -55,7 +55,7 @@ import java.sql.SQLException
|
|||||||
* 封装了ViewModel和DataBinding的Activity基类
|
* 封装了ViewModel和DataBinding的Activity基类
|
||||||
*/
|
*/
|
||||||
abstract class BaseVMBActivity<VM : BaseViewModel, B : ViewDataBinding>(private val contentViewResId: Int) :
|
abstract class BaseVMBActivity<VM : BaseViewModel, B : ViewDataBinding>(private val contentViewResId: Int) :
|
||||||
AppCompatActivity(), View.OnClickListener {
|
BaseActivity(), View.OnClickListener {
|
||||||
var mContext: Context? = null
|
var mContext: Context? = null
|
||||||
var mActivity: Activity? = null
|
var mActivity: Activity? = null
|
||||||
lateinit var mViewModel: VM
|
lateinit var mViewModel: VM
|
||||||
@@ -447,4 +447,5 @@ abstract class BaseVMBActivity<VM : BaseViewModel, B : ViewDataBinding>(private
|
|||||||
return "0"
|
return "0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.zmkg.coaloperation.bean;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResult;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface IActivityResult {
|
||||||
|
|
||||||
|
interface LaunchActivityCallback {
|
||||||
|
void callback(ActivityResult result);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LaunchTackPictureCallback {
|
||||||
|
void callback(boolean result);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RequestPermissionCallback {
|
||||||
|
void callback(boolean result);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RequestMultiplePermissionsCallback {
|
||||||
|
void callback(Map<String,Boolean> result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,7 +63,10 @@ data class GeoPointMultiItem(
|
|||||||
var pointName: String = "",
|
var pointName: String = "",
|
||||||
var pointDistance: String = "",
|
var pointDistance: String = "",
|
||||||
var totalDistance: String = "",
|
var totalDistance: String = "",
|
||||||
): MultiItemEntity {
|
var baseTopList: MutableList<BaseItem>? = null,
|
||||||
|
var baseBottomList: MutableList<BaseItem>? = null,
|
||||||
|
var images: MutableList<CommonImageBean>? = null
|
||||||
|
) : MultiItemEntity {
|
||||||
companion object {
|
companion object {
|
||||||
const val TYPE_BASE_INFO = 1
|
const val TYPE_BASE_INFO = 1
|
||||||
const val TYPE_LOGGING_INFO = 2
|
const val TYPE_LOGGING_INFO = 2
|
||||||
@@ -72,6 +75,19 @@ data class GeoPointMultiItem(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class GeoPointImage(
|
data class CommonImageBean(
|
||||||
var imagePath: String = ""
|
var imagePath: String = "",
|
||||||
|
var isAddFlag: Boolean = false
|
||||||
|
)
|
||||||
|
|
||||||
|
data class ReportItem(
|
||||||
|
var name: String = "",
|
||||||
|
var value: String = "",
|
||||||
|
var id: String = "",
|
||||||
|
//1-输入框,2-文本选择弹窗,3-时间选择弹窗,4-上传图片
|
||||||
|
var viewType: Int = 0,
|
||||||
|
var inputHint: String = "",
|
||||||
|
var inputShowLines: Int = 0,
|
||||||
|
var isSingleLine: Boolean = true,
|
||||||
|
var images: MutableList<CommonImageBean>? = null
|
||||||
)
|
)
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package com.zmkg.coaloperation.db.entity.geologging
|
||||||
|
|
||||||
|
data class GeoLoggingFace(
|
||||||
|
var surfaceId: Long = 0,
|
||||||
|
|
||||||
|
var userId: String = "",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作面名称
|
||||||
|
*/
|
||||||
|
var surfaceName: String? = "",
|
||||||
|
)
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.zmkg.coaloperation.dialog
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.annotation.StyleRes
|
||||||
|
import com.zmkg.coaloperation.R
|
||||||
|
|
||||||
|
open class BaseDialog(context: Context, @StyleRes dialogTheme: Int) :
|
||||||
|
Dialog(context, dialogTheme) {
|
||||||
|
constructor(context: Context) : this(context, R.style.DialogTheme)
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
super.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package com.zmkg.coaloperation.dialog
|
||||||
|
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.SpannableStringBuilder
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import com.allen.library.shape.ShapeButton
|
||||||
|
import com.zmkg.coaloperation.databinding.DialogCommonBinding
|
||||||
|
import com.zmkg.coaloperation.utils.dp
|
||||||
|
|
||||||
|
class CustomCenterDialog(
|
||||||
|
var activity: FragmentActivity,
|
||||||
|
var defaultWidth: Int = 300.dp
|
||||||
|
) : BaseDialog(activity) {
|
||||||
|
|
||||||
|
private lateinit var binding: DialogCommonBinding
|
||||||
|
|
||||||
|
var dialogCancelable = false
|
||||||
|
var dialogCanceledOnTouchOutside = false
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
binding = DialogCommonBinding.inflate(LayoutInflater.from(activity))
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
setCancelable(dialogCancelable)
|
||||||
|
setCanceledOnTouchOutside(dialogCanceledOnTouchOutside)
|
||||||
|
window?.apply {
|
||||||
|
setLayout(defaultWidth, WindowManager.LayoutParams.WRAP_CONTENT)
|
||||||
|
setBackgroundDrawable(ColorDrawable())
|
||||||
|
setGravity(Gravity.CENTER)
|
||||||
|
}
|
||||||
|
binding.btnCancel.setOnClickListener { onLeftClick?.invoke(this) }
|
||||||
|
binding.btnConfirm.setOnClickListener { onRightClick?.invoke(this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private var dialogTitle: String = ""
|
||||||
|
fun setDialogTitle(title: String) {
|
||||||
|
this.dialogTitle = title
|
||||||
|
}
|
||||||
|
|
||||||
|
private var dialogContent: String = ""
|
||||||
|
fun setDialogContent(content: String) {
|
||||||
|
this.dialogContent = content
|
||||||
|
}
|
||||||
|
|
||||||
|
private var spannableBuilder: SpannableStringBuilder? = null
|
||||||
|
fun setDialogContent(spannableBuilder: SpannableStringBuilder) {
|
||||||
|
this.spannableBuilder = spannableBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
private var dialogLeftBtnText = ""
|
||||||
|
private var onLeftClick: ((CustomCenterDialog) -> Unit)? = null
|
||||||
|
fun setCancelButton(
|
||||||
|
text: String,
|
||||||
|
onClick: (CustomCenterDialog) -> Unit
|
||||||
|
) {
|
||||||
|
this.dialogLeftBtnText = text
|
||||||
|
this.onLeftClick = onClick
|
||||||
|
}
|
||||||
|
|
||||||
|
private var dialogRightBtnText = ""
|
||||||
|
private var onRightClick: ((CustomCenterDialog) -> Unit)? = null
|
||||||
|
fun setConfirmButton(
|
||||||
|
text: String,
|
||||||
|
onClick: (CustomCenterDialog) -> Unit
|
||||||
|
) {
|
||||||
|
this.dialogRightBtnText = text
|
||||||
|
this.onRightClick = onClick
|
||||||
|
}
|
||||||
|
|
||||||
|
private var showSingleButton = false
|
||||||
|
fun setShowSingleButton(show: Boolean) {
|
||||||
|
this.showSingleButton = show
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var bgLayoutSetting: ((View) -> Unit)? = null
|
||||||
|
var titleSetting: ((TextView) -> Unit)? = null
|
||||||
|
var contentSetting: ((TextView) -> Unit)? = null
|
||||||
|
var leftBtnSetting: ((ShapeButton) -> Unit)? = null
|
||||||
|
var rightBtnSetting: ((ShapeButton) -> Unit)? = null
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
super.show()
|
||||||
|
|
||||||
|
binding.tvDialogTitle.apply {
|
||||||
|
text = dialogTitle
|
||||||
|
}
|
||||||
|
binding.tvDialogContent.apply {
|
||||||
|
text = dialogContent
|
||||||
|
}
|
||||||
|
|
||||||
|
bgLayoutSetting?.invoke(binding.root)
|
||||||
|
titleSetting?.invoke(binding.tvDialogTitle)
|
||||||
|
contentSetting?.invoke(binding.tvDialogContent)
|
||||||
|
leftBtnSetting?.invoke(binding.btnCancel)
|
||||||
|
rightBtnSetting?.invoke(binding.btnConfirm)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -207,7 +207,7 @@ fun ImageView.loadCircle(@DrawableRes resourceId: Int) {
|
|||||||
* @param imageView View
|
* @param imageView View
|
||||||
* @param placeholder 占位图
|
* @param placeholder 占位图
|
||||||
*/
|
*/
|
||||||
fun ImageView.loadRoundedImage(
|
fun ImageView.loadRoundedImage2(
|
||||||
url: String?,
|
url: String?,
|
||||||
roundingRadius: Float,
|
roundingRadius: Float,
|
||||||
@DrawableRes defaultResId: Int = 0,
|
@DrawableRes defaultResId: Int = 0,
|
||||||
@@ -239,6 +239,32 @@ fun ImageView.loadRoundedImage(
|
|||||||
.into(this)
|
.into(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ImageView.loadRoundedImage(
|
||||||
|
any: Any,
|
||||||
|
roundingRadius: Float,
|
||||||
|
@DrawableRes defaultResId: Int = 0,
|
||||||
|
isCenterCrop:Boolean = true
|
||||||
|
) {
|
||||||
|
//设置图片圆角角度
|
||||||
|
val roundedCorners = RoundedCorners(dp2px(roundingRadius))
|
||||||
|
val multiTransformation: MultiTransformation<Bitmap> = MultiTransformation(
|
||||||
|
if (isCenterCrop) CenterCrop() else FitCenter(), roundedCorners
|
||||||
|
)
|
||||||
|
val options = RequestOptions
|
||||||
|
.bitmapTransform(multiTransformation)
|
||||||
|
if (defaultResId == 0) {
|
||||||
|
options.placeholder(R.drawable.ic_default)
|
||||||
|
.error(R.drawable.ic_default)
|
||||||
|
} else {
|
||||||
|
options.placeholder(defaultResId)
|
||||||
|
.error(defaultResId)
|
||||||
|
}
|
||||||
|
Glide.with(context)
|
||||||
|
.load(any)
|
||||||
|
.apply(options)
|
||||||
|
.into(this)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载图片到ImageView
|
* 加载图片到ImageView
|
||||||
* @param imageUrl 图片地址
|
* @param imageUrl 图片地址
|
||||||
|
|||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
package com.zmkg.coaloperation.ui.geologging
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import com.zmkg.coaloperation.R
|
||||||
|
import com.zmkg.coaloperation.base.BaseVMBActivity
|
||||||
|
import com.zmkg.coaloperation.base.viewmodel.TestViewModel
|
||||||
|
import com.zmkg.coaloperation.databinding.ActivityAddGeoLoggingFaceBinding
|
||||||
|
|
||||||
|
class AddGeoLoggingFaceActivity :
|
||||||
|
BaseVMBActivity<TestViewModel, ActivityAddGeoLoggingFaceBinding>(
|
||||||
|
R.layout.activity_add_geo_logging_face
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun initView(savedInstanceState: Bundle?) {
|
||||||
|
mBinding.toolbarLay.title = "新增工作面"
|
||||||
|
mBinding.btnAddFace.setOnClickListener {
|
||||||
|
val faceName = mBinding.etWorkfaceName.text.toString().trim()
|
||||||
|
if (faceName.isBlank()) {
|
||||||
|
showToast("请输入工作面名称")
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initData() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindEvent() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processClick(v: View?) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+89
-20
@@ -1,19 +1,40 @@
|
|||||||
package com.zmkg.coaloperation.ui.geologging
|
package com.zmkg.coaloperation.ui.geologging
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.GradientDrawable
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.core.graphics.toColorInt
|
||||||
|
import androidx.core.view.marginLeft
|
||||||
|
import androidx.core.view.marginStart
|
||||||
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.commit
|
import androidx.fragment.app.commit
|
||||||
import com.zmkg.coaloperation.R
|
import com.zmkg.coaloperation.R
|
||||||
import com.zmkg.coaloperation.base.BaseVMBActivity
|
import com.zmkg.coaloperation.base.BaseVMBActivity
|
||||||
import com.zmkg.coaloperation.base.viewmodel.TestViewModel
|
import com.zmkg.coaloperation.base.viewmodel.TestViewModel
|
||||||
import com.zmkg.coaloperation.databinding.ActivityAddGeologicalPointBinding
|
import com.zmkg.coaloperation.databinding.ActivityAddGeologicalPointBinding
|
||||||
|
import com.zmkg.coaloperation.utils.dp
|
||||||
|
import com.zmkg.coaloperation.utils.useShapeSetBackground
|
||||||
|
import net.lucode.hackware.magicindicator.FragmentContainerHelper
|
||||||
|
import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator
|
||||||
|
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter
|
||||||
|
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView
|
||||||
|
import net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators.LinePagerIndicator
|
||||||
|
import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.ColorTransitionPagerTitleView
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 地质编录点新增
|
* 地质编录点新增
|
||||||
*/
|
*/
|
||||||
class AddGeologicalPointActivity :
|
class AddGeologicalPointActivity :
|
||||||
BaseVMBActivity<TestViewModel, ActivityAddGeologicalPointBinding>(R.layout.activity_add_geological_point) {
|
BaseVMBActivity<TestViewModel, ActivityAddGeologicalPointBinding>(
|
||||||
|
R.layout.activity_add_geological_point
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
override fun initView(savedInstanceState: Bundle?) {
|
override fun initView(savedInstanceState: Bundle?) {
|
||||||
@@ -21,13 +42,8 @@ class AddGeologicalPointActivity :
|
|||||||
// mBinding.toolbarLay.rightText = "新增工作面"
|
// mBinding.toolbarLay.rightText = "新增工作面"
|
||||||
|
|
||||||
initFragments()
|
initFragments()
|
||||||
//switchFragment(0)
|
initMagicIndicator()
|
||||||
|
showFragment(0)
|
||||||
supportFragmentManager.commit {
|
|
||||||
setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
|
|
||||||
show(fragments[0])
|
|
||||||
setReorderingAllowed(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initData() {
|
override fun initData() {
|
||||||
@@ -40,22 +56,75 @@ class AddGeologicalPointActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private val fragments = mutableListOf<Fragment>()
|
private val fragments = mutableListOf<AddGeologicalPointFragment>()
|
||||||
private fun initFragments() {
|
private fun initFragments() {
|
||||||
fragments.apply {
|
titles.forEachIndexed { index, string ->
|
||||||
add(AddGeologicalPointFragment())
|
val fragment = AddGeologicalPointFragment()
|
||||||
add(AddGeologicalPointFragment())
|
fragments.add(fragment)
|
||||||
add(AddGeologicalPointFragment())
|
supportFragmentManager.commit {
|
||||||
add(AddGeologicalPointFragment())
|
add(R.id.frgContainerView, fragment, "frg_${index}").hide(fragment)
|
||||||
add(AddGeologicalPointFragment())
|
// replace(R.id.frgContainerView, fragments[0], "point01")
|
||||||
}
|
}
|
||||||
supportFragmentManager.commit {
|
|
||||||
// add(R.id.fragment_container, fragments[0], "volume").hide(fragments[0])
|
|
||||||
replace(R.id.frgContainerView, fragments[0], "point01")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun switchFragment(position: Int) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
fragments[showFrgIndex].onActivityResult(requestCode, resultCode, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val titles = listOf("1#", "2#", "3#", "4#", "5#", "6#")
|
||||||
|
private fun initMagicIndicator() {
|
||||||
|
val commonNavigator = CommonNavigator(this).apply {
|
||||||
|
adapter = object : CommonNavigatorAdapter() {
|
||||||
|
override fun getCount() = titles.size
|
||||||
|
override fun getTitleView(context: Context?, index: Int): IPagerTitleView {
|
||||||
|
return ColorTransitionPagerTitleView(context).apply {
|
||||||
|
// setPadding(10.dp, 5.dp,10.dp, 5.dp)
|
||||||
|
width = 65.dp
|
||||||
|
text = titles[index]
|
||||||
|
textSize = 20f
|
||||||
|
normalColor = Color.WHITE
|
||||||
|
selectedColor = "#30E0A1".toColorInt()
|
||||||
|
useShapeSetBackground(
|
||||||
|
cornerRadius = 12.dp.toFloat(),
|
||||||
|
solidColor = "#1430E0A1".toColorInt(),
|
||||||
|
strokeColor = "#0C0D0E".toColorInt(),
|
||||||
|
strokeWidth = 8.dp
|
||||||
|
)
|
||||||
|
setOnClickListener { showFragment(index) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getIndicator(context: Context?) = LinePagerIndicator(context).apply {
|
||||||
|
// mode = LinePagerIndicator.MODE_WRAP_CONTENT
|
||||||
|
// setColors("#30E0A1".toColorInt())
|
||||||
|
// lineHeight = 1.dp.toFloat()
|
||||||
|
//lineWidth = 10.dp.toFloat()
|
||||||
|
// background = GradientDrawable().also {
|
||||||
|
// it.shape = GradientDrawable.RECTANGLE
|
||||||
|
// it.cornerRadius = 5.dp.toFloat()
|
||||||
|
// it.setColor("#30E0A1".toColorInt())
|
||||||
|
//// it.setStroke(strokeWidth, strokeColor)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mBinding.magicIndicator.let {
|
||||||
|
fragmentContainerHelper = FragmentContainerHelper(it)
|
||||||
|
it.navigator = commonNavigator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var fragmentContainerHelper:FragmentContainerHelper
|
||||||
|
|
||||||
|
private var showFrgIndex = 0
|
||||||
|
private fun showFragment(position: Int) {
|
||||||
|
showFrgIndex = position
|
||||||
|
// supportFragmentManager.beginTransaction()
|
||||||
|
// .replace(R.id.frgContainerView, fragments[position])
|
||||||
|
// .commit()
|
||||||
|
fragmentContainerHelper.handlePageSelected(position)
|
||||||
supportFragmentManager.commit {
|
supportFragmentManager.commit {
|
||||||
setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
|
setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||||
fragments.forEachIndexed { index, fragment ->
|
fragments.forEachIndexed { index, fragment ->
|
||||||
|
|||||||
+33
-2
@@ -7,23 +7,54 @@ import com.zmkg.coaloperation.R
|
|||||||
import com.zmkg.coaloperation.adapter.GeoPointMultiAdapter
|
import com.zmkg.coaloperation.adapter.GeoPointMultiAdapter
|
||||||
import com.zmkg.coaloperation.base.BaseVMBFragment
|
import com.zmkg.coaloperation.base.BaseVMBFragment
|
||||||
import com.zmkg.coaloperation.base.viewmodel.TestViewModel
|
import com.zmkg.coaloperation.base.viewmodel.TestViewModel
|
||||||
|
import com.zmkg.coaloperation.bean.BaseItem
|
||||||
|
import com.zmkg.coaloperation.bean.CommonImageBean
|
||||||
import com.zmkg.coaloperation.bean.GeoPointMultiItem
|
import com.zmkg.coaloperation.bean.GeoPointMultiItem
|
||||||
import com.zmkg.coaloperation.databinding.FragmentAddGeologicalPointBinding
|
import com.zmkg.coaloperation.databinding.FragmentAddGeologicalPointBinding
|
||||||
|
|
||||||
class AddGeologicalPointFragment :
|
class AddGeologicalPointFragment :
|
||||||
BaseVMBFragment<TestViewModel, FragmentAddGeologicalPointBinding>(R.layout.fragment_add_geological_point) {
|
BaseVMBFragment<TestViewModel, FragmentAddGeologicalPointBinding>(R.layout.fragment_add_geological_point) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val IMAGE_REQUEST_CODE = 1000
|
||||||
|
}
|
||||||
|
|
||||||
private val geoPointList: MutableList<GeoPointMultiItem> = mutableListOf()
|
private val geoPointList: MutableList<GeoPointMultiItem> = mutableListOf()
|
||||||
private val geoPointAdapter by lazy {
|
private val geoPointAdapter by lazy {
|
||||||
GeoPointMultiAdapter(geoPointList)
|
GeoPointMultiAdapter(geoPointList)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initView(root: View?, savedInstanceState: Bundle?) {
|
override fun initView(root: View?, savedInstanceState: Bundle?) {
|
||||||
|
geoPointList.clear()
|
||||||
|
val topList = mutableListOf(
|
||||||
|
BaseItem(name = "点号", value = ""),
|
||||||
|
BaseItem(name = "点距", value = ""),
|
||||||
|
BaseItem(name = "累距", value = ""),
|
||||||
|
)
|
||||||
|
val bottomList = mutableListOf(
|
||||||
|
BaseItem(name = "巷高", value = ""),
|
||||||
|
BaseItem(name = "顶板", value = ""),
|
||||||
|
BaseItem(name = "底板", value = ""),
|
||||||
|
BaseItem(name = "巷顶煤厚", value = ""),
|
||||||
|
)
|
||||||
|
val imageList = mutableListOf<CommonImageBean>()
|
||||||
|
imageList.add(CommonImageBean(isAddFlag = true))
|
||||||
geoPointList.run {
|
geoPointList.run {
|
||||||
add(GeoPointMultiItem(itemType = GeoPointMultiItem.TYPE_BASE_INFO))
|
add(
|
||||||
|
GeoPointMultiItem(
|
||||||
|
itemType = GeoPointMultiItem.TYPE_BASE_INFO,
|
||||||
|
baseTopList = topList,
|
||||||
|
baseBottomList = bottomList
|
||||||
|
)
|
||||||
|
)
|
||||||
add(GeoPointMultiItem(itemType = GeoPointMultiItem.TYPE_LOGGING_INFO))
|
add(GeoPointMultiItem(itemType = GeoPointMultiItem.TYPE_LOGGING_INFO))
|
||||||
add(GeoPointMultiItem(itemType = GeoPointMultiItem.TYPE_CALCULATE_INFO))
|
add(GeoPointMultiItem(itemType = GeoPointMultiItem.TYPE_CALCULATE_INFO))
|
||||||
add(GeoPointMultiItem(itemType = GeoPointMultiItem.TYPE_UPLOAD_IMAGE))
|
add(
|
||||||
|
GeoPointMultiItem(
|
||||||
|
itemType = GeoPointMultiItem.TYPE_UPLOAD_IMAGE,
|
||||||
|
images = imageList
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
mBinding.rvPointList.let {
|
mBinding.rvPointList.let {
|
||||||
it.layoutManager = LinearLayoutManager(it.context)
|
it.layoutManager = LinearLayoutManager(it.context)
|
||||||
|
|||||||
+29
-1
@@ -2,10 +2,13 @@ package com.zmkg.coaloperation.ui.geologging
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.zmkg.coaloperation.R
|
import com.zmkg.coaloperation.R
|
||||||
|
import com.zmkg.coaloperation.adapter.GeoLoggingFaceAdapter
|
||||||
import com.zmkg.coaloperation.base.BaseVMBActivity
|
import com.zmkg.coaloperation.base.BaseVMBActivity
|
||||||
import com.zmkg.coaloperation.base.viewmodel.TestViewModel
|
import com.zmkg.coaloperation.base.viewmodel.TestViewModel
|
||||||
import com.zmkg.coaloperation.databinding.ActivityGeologicalBinding
|
import com.zmkg.coaloperation.databinding.ActivityGeologicalBinding
|
||||||
|
import com.zmkg.coaloperation.db.entity.geologging.GeoLoggingFace
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 地质编录
|
* 地质编录
|
||||||
@@ -13,11 +16,36 @@ import com.zmkg.coaloperation.databinding.ActivityGeologicalBinding
|
|||||||
class GeologicalActivity :
|
class GeologicalActivity :
|
||||||
BaseVMBActivity<TestViewModel, ActivityGeologicalBinding>(R.layout.activity_geological) {
|
BaseVMBActivity<TestViewModel, ActivityGeologicalBinding>(R.layout.activity_geological) {
|
||||||
|
|
||||||
|
val faceList: MutableList<GeoLoggingFace> = mutableListOf()
|
||||||
|
val faceAdapter by lazy {
|
||||||
|
GeoLoggingFaceAdapter(faceList).apply {
|
||||||
|
setOnItemClickListener { adapter, view, position ->
|
||||||
|
toActivity(GeologicalPointActivity::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun initView(savedInstanceState: Bundle?) {
|
override fun initView(savedInstanceState: Bundle?) {
|
||||||
mBinding.toolbarLay.title = "编录位置选择"
|
mBinding.toolbarLay.title = "编录位置选择"
|
||||||
mBinding.toolbarLay.rightText = "新增工作面"
|
mBinding.toolbarLay.rightText = "新增工作面"
|
||||||
mBinding.toolbarLay.titleTvRight.setOnClickListener {
|
mBinding.toolbarLay.titleTvRight.setOnClickListener {
|
||||||
toActivity(GeologicalPointActivity::class.java)
|
toActivity(AddGeoLoggingFaceActivity::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
faceList.run {
|
||||||
|
add(GeoLoggingFace(surfaceName = "5-20304回风工作面"))
|
||||||
|
add(GeoLoggingFace(surfaceName = "5-20305回风工作面"))
|
||||||
|
add(GeoLoggingFace(surfaceName = "5-20306回风工作面"))
|
||||||
|
add(GeoLoggingFace(surfaceName = "5-20307回风工作面"))
|
||||||
|
add(GeoLoggingFace(surfaceName = "5-20308回风工作面"))
|
||||||
|
add(GeoLoggingFace(surfaceName = "5-20309回风工作面"))
|
||||||
|
add(GeoLoggingFace(surfaceName = "5-20310回风工作面"))
|
||||||
|
add(GeoLoggingFace(surfaceName = "5-20311回风工作面"))
|
||||||
|
}
|
||||||
|
|
||||||
|
mBinding.rvGeoLogging.let {
|
||||||
|
it.layoutManager = LinearLayoutManager(this)
|
||||||
|
it.adapter = faceAdapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+123
-123
@@ -1,123 +1,123 @@
|
|||||||
package com.zmkg.coaloperation.ui.report.adapter
|
//package com.zmkg.coaloperation.ui.report.adapter
|
||||||
|
//
|
||||||
import android.view.View
|
//import android.view.View
|
||||||
import com.chad.library.adapter.base.module.LoadMoreModule
|
//import com.chad.library.adapter.base.module.LoadMoreModule
|
||||||
import com.chad.library.adapter.base.viewholder.BaseDataBindingHolder
|
//import com.chad.library.adapter.base.viewholder.BaseDataBindingHolder
|
||||||
import com.zmkg.coaloperation.R
|
//import com.zmkg.coaloperation.R
|
||||||
import com.zmkg.coaloperation.adapter.common.BaseDataBindingAdapter
|
//import com.zmkg.coaloperation.adapter.common.BaseDataBindingAdapter
|
||||||
import com.zmkg.coaloperation.bean.ImageBean
|
//import com.zmkg.coaloperation.bean.ImageBean
|
||||||
import com.zmkg.coaloperation.databinding.ItemRecycleImageBinding
|
//import com.zmkg.coaloperation.databinding.ItemRecycleImageBinding
|
||||||
import com.zmkg.coaloperation.superfuntion.loadRoundedImage
|
//import com.zmkg.coaloperation.superfuntion.loadRoundedImage
|
||||||
import java.io.File
|
//import java.io.File
|
||||||
|
//
|
||||||
class ImageAdapter(var maxNum: Int): BaseDataBindingAdapter<ImageBean, ItemRecycleImageBinding>(
|
//class ImageAdapter(var maxNum: Int): BaseDataBindingAdapter<ImageBean, ItemRecycleImageBinding>(
|
||||||
R.layout.item_recycle_image
|
// R.layout.item_recycle_image
|
||||||
), LoadMoreModule {
|
//), LoadMoreModule {
|
||||||
private var isEditModel = false
|
// private var isEditModel = false
|
||||||
private var editImageShow = false
|
// private var editImageShow = false
|
||||||
override fun bindViewClickListener(
|
// override fun bindViewClickListener(
|
||||||
viewHolder: BaseDataBindingHolder<ItemRecycleImageBinding>,
|
// viewHolder: BaseDataBindingHolder<ItemRecycleImageBinding>,
|
||||||
viewType: Int
|
// viewType: Int
|
||||||
) {
|
// ) {
|
||||||
addChildClickViewIds(R.id.btn_delete)
|
// addChildClickViewIds(R.id.btn_delete)
|
||||||
super.bindViewClickListener(viewHolder, viewType)
|
// super.bindViewClickListener(viewHolder, viewType)
|
||||||
}
|
// }
|
||||||
override fun convert(
|
// override fun convert(
|
||||||
holder: BaseDataBindingHolder<ItemRecycleImageBinding>,
|
// holder: BaseDataBindingHolder<ItemRecycleImageBinding>,
|
||||||
item: ImageBean
|
// item: ImageBean
|
||||||
) {
|
// ) {
|
||||||
val mBinding = holder.dataBinding
|
// val mBinding = holder.dataBinding
|
||||||
mBinding?.let {
|
// mBinding?.let {
|
||||||
if(item.isAddButton){
|
// if(item.isAddButton){
|
||||||
mBinding.ivImage.setImageResource(R.mipmap.upload)
|
// mBinding.ivImage.setImageResource(R.mipmap.upload)
|
||||||
mBinding.btnDelete.visibility = View.GONE
|
// mBinding.btnDelete.visibility = View.GONE
|
||||||
}else{
|
// }else{
|
||||||
if(item.isFilePath){
|
// if(item.isFilePath){
|
||||||
mBinding.ivImage.loadRoundedImage(File(item.imageUrl), 5f)
|
// mBinding.ivImage.loadRoundedImage(File(item.imageUrl), 5f)
|
||||||
}else{
|
// }else{
|
||||||
mBinding.ivImage.loadRoundedImage(item.imageUrl, 5f, R.drawable.ic_default)
|
// mBinding.ivImage.loadRoundedImage(item.imageUrl, 5f, R.drawable.ic_default)
|
||||||
}
|
// }
|
||||||
if(isEditModel){
|
// if(isEditModel){
|
||||||
mBinding.btnDelete.visibility = View.VISIBLE
|
// mBinding.btnDelete.visibility = View.VISIBLE
|
||||||
}else{
|
// }else{
|
||||||
mBinding.btnDelete.visibility = View.GONE
|
// mBinding.btnDelete.visibility = View.GONE
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
fun isEditModel(isEdit: Boolean){
|
// fun isEditModel(isEdit: Boolean){
|
||||||
isEditModel = isEdit
|
// isEditModel = isEdit
|
||||||
notifyDataSetChanged()
|
// notifyDataSetChanged()
|
||||||
if(isEditModel && !editImageShow){
|
// if(isEditModel && !editImageShow){
|
||||||
showAddButton(true)
|
// showAddButton(true)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
override fun setList(list: Collection<ImageBean>?) {
|
// override fun setList(list: Collection<ImageBean>?) {
|
||||||
super.setList(list)
|
// super.setList(list)
|
||||||
if(isEditModel){
|
// if(isEditModel){
|
||||||
showAddButton(true)
|
// showAddButton(true)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
override fun setNewInstance(list: MutableList<ImageBean>?) {
|
// override fun setNewInstance(list: MutableList<ImageBean>?) {
|
||||||
super.setNewInstance(list)
|
// super.setNewInstance(list)
|
||||||
if(isEditModel){
|
// if(isEditModel){
|
||||||
showAddButton(true)
|
// showAddButton(true)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
override fun addData(newData: Collection<ImageBean>) {
|
// override fun addData(newData: Collection<ImageBean>) {
|
||||||
if(isEditModel){
|
// if(isEditModel){
|
||||||
if(editImageShow){
|
// if(editImageShow){
|
||||||
var lastPosition = data.size - 1
|
// var lastPosition = data.size - 1
|
||||||
if(lastPosition < 0){
|
// if(lastPosition < 0){
|
||||||
lastPosition = 0
|
// lastPosition = 0
|
||||||
}
|
// }
|
||||||
addData(lastPosition, newData)
|
// addData(lastPosition, newData)
|
||||||
}else{
|
// }else{
|
||||||
showAddButton(true)
|
// showAddButton(true)
|
||||||
}
|
// }
|
||||||
if(data.size > maxNum){
|
// if(data.size > maxNum){
|
||||||
showAddButton(false)
|
// showAddButton(false)
|
||||||
}
|
// }
|
||||||
}else{
|
// }else{
|
||||||
super.addData(newData)
|
// super.addData(newData)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
override fun removeAt(position: Int) {
|
// override fun removeAt(position: Int) {
|
||||||
super.removeAt(position)
|
// super.removeAt(position)
|
||||||
if(isEditModel && !editImageShow){
|
// if(isEditModel && !editImageShow){
|
||||||
showAddButton(true)
|
// showAddButton(true)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
fun getImageList(): MutableList<ImageBean>{
|
// fun getImageList(): MutableList<ImageBean>{
|
||||||
var list = mutableListOf<ImageBean>()
|
// var list = mutableListOf<ImageBean>()
|
||||||
data.forEach { imageBean ->
|
// data.forEach { imageBean ->
|
||||||
if(!imageBean.isAddButton){
|
// if(!imageBean.isAddButton){
|
||||||
list.add(imageBean)
|
// list.add(imageBean)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return list
|
// return list
|
||||||
}
|
// }
|
||||||
fun getImageSize(): Int {
|
// fun getImageSize(): Int {
|
||||||
if(isEditModel && editImageShow){
|
// if(isEditModel && editImageShow){
|
||||||
return getDefItemCount() - 1
|
// return getDefItemCount() - 1
|
||||||
}
|
// }
|
||||||
return getDefItemCount()
|
// return getDefItemCount()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 显示添加图片按钮
|
// * 显示添加图片按钮
|
||||||
* @param isShow true为显示 false为隐藏
|
// * @param isShow true为显示 false为隐藏
|
||||||
*/
|
// */
|
||||||
private fun showAddButton(isShow: Boolean){
|
// private fun showAddButton(isShow: Boolean){
|
||||||
editImageShow = if(isShow){
|
// editImageShow = if(isShow){
|
||||||
addData(ImageBean(isAddButton = true))
|
// addData(ImageBean(isAddButton = true))
|
||||||
true
|
// true
|
||||||
}else{
|
// }else{
|
||||||
removeAt(data.size-1)
|
// removeAt(data.size-1)
|
||||||
false
|
// false
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
+79
@@ -0,0 +1,79 @@
|
|||||||
|
package com.zmkg.coaloperation.ui.report.fragment
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import androidx.core.view.updateLayoutParams
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.zmkg.coaloperation.R
|
||||||
|
import com.zmkg.coaloperation.adapter.ReportAdapter
|
||||||
|
import com.zmkg.coaloperation.base.BaseVMBFragment
|
||||||
|
import com.zmkg.coaloperation.bean.CommonImageBean
|
||||||
|
import com.zmkg.coaloperation.bean.ReportItem
|
||||||
|
import com.zmkg.coaloperation.databinding.FragmentReport2Binding
|
||||||
|
import com.zmkg.coaloperation.ui.report.viewmodel.ReportViewModel
|
||||||
|
import com.zmkg.coaloperation.utils.dp
|
||||||
|
import com.zmkg.coaloperation.utils.gone
|
||||||
|
|
||||||
|
class Report2Fragment :
|
||||||
|
BaseVMBFragment<ReportViewModel, FragmentReport2Binding>(R.layout.fragment_report2) {
|
||||||
|
|
||||||
|
lateinit var pageStatus: PageStatus
|
||||||
|
|
||||||
|
enum class PageStatus(val status: Int) {
|
||||||
|
ADD(1),
|
||||||
|
EDIT(2),
|
||||||
|
SEE(3),
|
||||||
|
NO_EDIT(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val reportItemList = mutableListOf(
|
||||||
|
ReportItem(viewType = 2, name = "上报类型"),
|
||||||
|
ReportItem(viewType = 2, name = "上报区域"),
|
||||||
|
ReportItem(viewType = 1, name = "上报地点", isSingleLine = true, inputHint = "请输入"),
|
||||||
|
ReportItem(viewType = 1, name = "上报描述", isSingleLine = true, inputHint = "请输入"),
|
||||||
|
ReportItem(viewType = 3, name = "上报时间"),
|
||||||
|
ReportItem(viewType = 2, name = "处理状态"),
|
||||||
|
ReportItem(viewType = 2, name = "上报人"),
|
||||||
|
ReportItem(viewType = 2, name = "处理人"),
|
||||||
|
ReportItem(viewType = 1, name = "处理策略", isSingleLine = false, inputShowLines = 4, inputHint = "请输入"),
|
||||||
|
ReportItem(viewType = 1, name = "处理结果", isSingleLine = false, inputShowLines = 4, inputHint = "请输入"),
|
||||||
|
ReportItem(viewType = 3, name = "处理时间"),
|
||||||
|
ReportItem(viewType = 4, name = "上传图片", images = mutableListOf(
|
||||||
|
CommonImageBean(isAddFlag = true)
|
||||||
|
)),
|
||||||
|
ReportItem(viewType = 1, name = "备注", isSingleLine = false, inputShowLines = 6 ,inputHint = "请输入备注信息,120字以内"),
|
||||||
|
)
|
||||||
|
private val reportAdapter by lazy {
|
||||||
|
ReportAdapter(reportItemList)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initView(root: View?, savedInstanceState: Bundle?) {
|
||||||
|
mBinding.toolbarLay.title = "新增临时上报"
|
||||||
|
mBinding.toolbarLay.rightText = "查看记录"
|
||||||
|
mBinding.toolbarLay.titleIvBack.gone()
|
||||||
|
mBinding.toolbarLay.titleTvName.updateLayoutParams<RelativeLayout.LayoutParams> {
|
||||||
|
leftMargin = 20.dp
|
||||||
|
}
|
||||||
|
mBinding.rvReport.let {
|
||||||
|
it.layoutManager = LinearLayoutManager(it.context)
|
||||||
|
it.adapter = reportAdapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun bindEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun transparentStatusBar(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createObserve() {
|
||||||
|
super.createObserve()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+204
-200
@@ -1,200 +1,204 @@
|
|||||||
package com.zmkg.coaloperation.ui.report.fragment
|
//package com.zmkg.coaloperation.ui.report.fragment
|
||||||
|
//
|
||||||
import android.os.Bundle
|
//import android.os.Bundle
|
||||||
import android.view.View
|
//import android.view.View
|
||||||
import androidx.core.view.ViewCompat
|
//import androidx.core.view.ViewCompat
|
||||||
import androidx.lifecycle.Lifecycle
|
//import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
//import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
//import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
//import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.chad.library.adapter.base.BaseQuickAdapter
|
//import com.chad.library.adapter.base.BaseQuickAdapter
|
||||||
import com.chad.library.adapter.base.listener.OnItemChildClickListener
|
//import com.chad.library.adapter.base.listener.OnItemChildClickListener
|
||||||
import com.chad.library.adapter.base.listener.OnItemClickListener
|
//import com.chad.library.adapter.base.listener.OnItemClickListener
|
||||||
import com.luck.picture.lib.basic.PictureSelectionModel
|
//import com.luck.picture.lib.basic.PictureSelectionModel
|
||||||
import com.luck.picture.lib.basic.PictureSelector
|
//import com.luck.picture.lib.basic.PictureSelector
|
||||||
import com.luck.picture.lib.config.SelectMimeType
|
//import com.luck.picture.lib.config.SelectMimeType
|
||||||
import com.luck.picture.lib.config.SelectModeConfig
|
//import com.luck.picture.lib.config.SelectModeConfig
|
||||||
import com.luck.picture.lib.entity.LocalMedia
|
//import com.luck.picture.lib.entity.LocalMedia
|
||||||
import com.luck.picture.lib.interfaces.OnResultCallbackListener
|
//import com.luck.picture.lib.interfaces.OnResultCallbackListener
|
||||||
import com.luck.picture.lib.utils.SandboxTransformUtils
|
//import com.luck.picture.lib.utils.SandboxTransformUtils
|
||||||
import com.zmkg.coaloperation.utils.pictureSelector.ImageFileCompressEngine
|
//import com.zmkg.coaloperation.utils.pictureSelector.ImageFileCompressEngine
|
||||||
import com.zmkg.coaloperation.R
|
//import com.zmkg.coaloperation.R
|
||||||
import com.zmkg.coaloperation.base.BaseVMBFragment
|
//import com.zmkg.coaloperation.base.BaseActivity
|
||||||
import com.zmkg.coaloperation.bean.ImageBean
|
//import com.zmkg.coaloperation.base.BaseVMBFragment
|
||||||
import com.zmkg.coaloperation.databinding.FragmentReportBinding
|
//import com.zmkg.coaloperation.bean.ImageBean
|
||||||
import com.zmkg.coaloperation.ui.report.adapter.ImageAdapter
|
//import com.zmkg.coaloperation.databinding.FragmentReportBinding
|
||||||
import com.zmkg.coaloperation.ui.report.viewmodel.ReportViewModel
|
//import com.zmkg.coaloperation.ui.report.adapter.ImageAdapter
|
||||||
import com.zmkg.coaloperation.utils.pictureSelector.GlideEngine
|
//import com.zmkg.coaloperation.ui.report.viewmodel.ReportViewModel
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
//import com.zmkg.coaloperation.utils.permission.StorageUtils
|
||||||
import kotlinx.coroutines.launch
|
//import com.zmkg.coaloperation.utils.pictureSelector.GlideEngine
|
||||||
|
//import kotlinx.coroutines.flow.collectLatest
|
||||||
class ReportFragment :
|
//import kotlinx.coroutines.launch
|
||||||
BaseVMBFragment<ReportViewModel, FragmentReportBinding>(R.layout.fragment_report),
|
//
|
||||||
OnItemClickListener, OnItemChildClickListener {
|
//class ReportFragment :
|
||||||
|
// BaseVMBFragment<ReportViewModel, FragmentReportBinding>(R.layout.fragment_report),
|
||||||
private val imageAdapter by lazy { ImageAdapter(maxImageNum) }
|
// OnItemClickListener, OnItemChildClickListener {
|
||||||
lateinit var pageStatus: PageStatus
|
//
|
||||||
|
// private val imageAdapter by lazy { ImageAdapter(maxImageNum) }
|
||||||
companion object {
|
// lateinit var pageStatus: PageStatus
|
||||||
const val maxImageNum = 5
|
//
|
||||||
}
|
// companion object {
|
||||||
|
// const val maxImageNum = 5
|
||||||
override fun initView(root: View?, savedInstanceState: Bundle?) {
|
// }
|
||||||
|
//
|
||||||
mBinding.apply {
|
// override fun initView(root: View?, savedInstanceState: Bundle?) {
|
||||||
val gridLayoutManager = GridLayoutManager(context, 3)
|
//
|
||||||
layInspectionReport.rvList.layoutManager = gridLayoutManager
|
// mBinding.apply {
|
||||||
imageAdapter.setOnItemClickListener(this@ReportFragment)
|
// val gridLayoutManager = GridLayoutManager(context, 3)
|
||||||
imageAdapter.setOnItemChildClickListener(this@ReportFragment)
|
// layInspectionReport.rvList.layoutManager = gridLayoutManager
|
||||||
mBinding.layInspectionReport.rvList.adapter = imageAdapter
|
// imageAdapter.setOnItemClickListener(this@ReportFragment)
|
||||||
ViewCompat.setNestedScrollingEnabled(layInspectionReport.rvList, false)
|
// imageAdapter.setOnItemChildClickListener(this@ReportFragment)
|
||||||
}
|
// mBinding.layInspectionReport.rvList.adapter = imageAdapter
|
||||||
|
// ViewCompat.setNestedScrollingEnabled(layInspectionReport.rvList, false)
|
||||||
setPageModel(PageStatus.ADD)
|
// }
|
||||||
|
//
|
||||||
}
|
// setPageModel(PageStatus.ADD)
|
||||||
|
//
|
||||||
enum class PageStatus(val status: Int) {
|
// }
|
||||||
ADD(1),
|
//
|
||||||
EDIT(2),
|
// enum class PageStatus(val status: Int) {
|
||||||
SEE(3),
|
// ADD(1),
|
||||||
NO_EDIT(4)
|
// EDIT(2),
|
||||||
}
|
// SEE(3),
|
||||||
|
// NO_EDIT(4)
|
||||||
private fun setPageModel(status: PageStatus) {
|
// }
|
||||||
this.pageStatus = status
|
//
|
||||||
mBinding?.apply {
|
// private fun setPageModel(status: PageStatus) {
|
||||||
when (status) {
|
// this.pageStatus = status
|
||||||
PageStatus.ADD -> {
|
// mBinding?.apply {
|
||||||
lifecycleScope.launch {
|
// when (status) {
|
||||||
mViewModel.noEdit.emit(false)
|
// PageStatus.ADD -> {
|
||||||
}
|
// lifecycleScope.launch {
|
||||||
}
|
// mViewModel.noEdit.emit(false)
|
||||||
|
// }
|
||||||
PageStatus.EDIT -> {
|
// }
|
||||||
lifecycleScope.launch {
|
//
|
||||||
mViewModel.noEdit.emit(false)
|
// PageStatus.EDIT -> {
|
||||||
}
|
// lifecycleScope.launch {
|
||||||
|
// mViewModel.noEdit.emit(false)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
PageStatus.SEE -> {
|
// }
|
||||||
lifecycleScope.launch {
|
//
|
||||||
mViewModel.noEdit.emit(true)
|
// PageStatus.SEE -> {
|
||||||
}
|
// lifecycleScope.launch {
|
||||||
}
|
// mViewModel.noEdit.emit(true)
|
||||||
|
// }
|
||||||
PageStatus.NO_EDIT -> {
|
// }
|
||||||
lifecycleScope.launch {
|
//
|
||||||
mViewModel.noEdit.emit(true)
|
// PageStatus.NO_EDIT -> {
|
||||||
}
|
// lifecycleScope.launch {
|
||||||
}
|
// mViewModel.noEdit.emit(true)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
override fun bindEvent() {
|
//
|
||||||
}
|
//
|
||||||
|
// override fun bindEvent() {
|
||||||
override fun onClick(v: View?) {
|
// }
|
||||||
}
|
//
|
||||||
|
// override fun onClick(v: View?) {
|
||||||
override fun transparentStatusBar(): Boolean {
|
// }
|
||||||
return true
|
//
|
||||||
}
|
// override fun transparentStatusBar(): Boolean {
|
||||||
|
// return true
|
||||||
override fun createObserve() {
|
// }
|
||||||
super.createObserve()
|
//
|
||||||
lifecycleScope.launch {
|
// override fun createObserve() {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
// super.createObserve()
|
||||||
mViewModel.noEdit.collectLatest { noEdit ->
|
// lifecycleScope.launch {
|
||||||
mBinding.apply {
|
// repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
imageAdapter.isEditModel(!noEdit)
|
// mViewModel.noEdit.collectLatest { noEdit ->
|
||||||
}
|
// mBinding.apply {
|
||||||
}
|
// imageAdapter.isEditModel(!noEdit)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
override fun onItemClick(adapter: BaseQuickAdapter<*, *>, view: View, position: Int) {
|
// }
|
||||||
val imageBean: ImageBean = adapter.getItem(position) as ImageBean
|
//
|
||||||
imageBean?.let { it ->
|
// override fun onItemClick(adapter: BaseQuickAdapter<*, *>, view: View, position: Int) {
|
||||||
if (it.isAddButton) {
|
// val imageBean: ImageBean = adapter.getItem(position) as ImageBean
|
||||||
initPictureSelectorModel()
|
// imageBean?.let { it ->
|
||||||
.forResult(object : OnResultCallbackListener<LocalMedia?> {
|
// if (it.isAddButton) {
|
||||||
override fun onResult(result: ArrayList<LocalMedia?>?) {
|
// StorageUtils.requestPermission(context as BaseActivity) {
|
||||||
result?.let { list ->
|
// initPictureSelectorModel()
|
||||||
var resultList = localMediaToImageList(list)
|
// .forResult(object : OnResultCallbackListener<LocalMedia?> {
|
||||||
imageAdapter.addData(resultList)
|
// override fun onResult(result: ArrayList<LocalMedia?>?) {
|
||||||
setImageNum(imageAdapter.getImageSize())
|
// result?.let { list ->
|
||||||
}
|
// var resultList = localMediaToImageList(list)
|
||||||
}
|
// imageAdapter.addData(resultList)
|
||||||
|
// setImageNum(imageAdapter.getImageSize())
|
||||||
override fun onCancel() {}
|
// }
|
||||||
})
|
// }
|
||||||
} else {
|
//
|
||||||
// startFullScreenImageActivity(
|
// override fun onCancel() {}
|
||||||
// it.isFilePath,
|
// })
|
||||||
// it.imageUrl
|
// }
|
||||||
// )
|
// } else {
|
||||||
}
|
//// startFullScreenImageActivity(
|
||||||
}
|
//// it.isFilePath,
|
||||||
}
|
//// it.imageUrl
|
||||||
|
//// )
|
||||||
fun setImageNum(size: Int) {
|
// }
|
||||||
mBinding.layInspectionReport.tvImageNum.text = getString(
|
// }
|
||||||
R.string.image_list_num,
|
// }
|
||||||
size,
|
//
|
||||||
maxImageNum
|
// fun setImageNum(size: Int) {
|
||||||
)
|
// mBinding.layInspectionReport.tvImageNum.text = getString(
|
||||||
}
|
// R.string.image_list_num,
|
||||||
|
// size,
|
||||||
private fun initPictureSelectorModel(): PictureSelectionModel {
|
// maxImageNum
|
||||||
return PictureSelector.create(this)
|
// )
|
||||||
.openGallery(SelectMimeType.TYPE_IMAGE)
|
// }
|
||||||
.setSelectionMode(SelectModeConfig.MULTIPLE)
|
//
|
||||||
.setMaxSelectNum(maxImageNum - imageAdapter.getImageSize())
|
// private fun initPictureSelectorModel(): PictureSelectionModel {
|
||||||
.setCompressEngine(ImageFileCompressEngine())
|
// return PictureSelector.create(this)
|
||||||
.setImageEngine(GlideEngine.createGlideEngine())
|
// .openGallery(SelectMimeType.TYPE_IMAGE)
|
||||||
.setSandboxFileEngine { context, srcPath, mineType, call ->
|
// .setSelectionMode(SelectModeConfig.MULTIPLE)
|
||||||
if (call != null) {
|
// .setMaxSelectNum(maxImageNum - imageAdapter.getImageSize())
|
||||||
var sandboxPath =
|
// .setCompressEngine(ImageFileCompressEngine())
|
||||||
SandboxTransformUtils.copyPathToSandbox(context, srcPath, mineType)
|
// .setImageEngine(GlideEngine.createGlideEngine())
|
||||||
call.onCallback(srcPath, sandboxPath)
|
// .setSandboxFileEngine { context, srcPath, mineType, call ->
|
||||||
}
|
// if (call != null) {
|
||||||
}
|
// var sandboxPath =
|
||||||
}
|
// SandboxTransformUtils.copyPathToSandbox(context, srcPath, mineType)
|
||||||
|
// call.onCallback(srcPath, sandboxPath)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
override fun onItemChildClick(adapter: BaseQuickAdapter<*, *>, view: View, position: Int) {
|
//
|
||||||
adapter.removeAt(position)
|
//
|
||||||
}
|
//
|
||||||
|
//
|
||||||
fun localMediaToImageList(result: ArrayList<LocalMedia?>): MutableList<ImageBean> {
|
// override fun onItemChildClick(adapter: BaseQuickAdapter<*, *>, view: View, position: Int) {
|
||||||
var imageList = mutableListOf<ImageBean>()
|
// adapter.removeAt(position)
|
||||||
var imageBean: ImageBean
|
// }
|
||||||
result.forEach {
|
//
|
||||||
it?.let {
|
// fun localMediaToImageList(result: ArrayList<LocalMedia?>): MutableList<ImageBean> {
|
||||||
imageBean = ImageBean(getImagePath(it), true)
|
// var imageList = mutableListOf<ImageBean>()
|
||||||
imageList.add(imageBean)
|
// var imageBean: ImageBean
|
||||||
}
|
// result.forEach {
|
||||||
}
|
// it?.let {
|
||||||
return imageList
|
// imageBean = ImageBean(getImagePath(it), true)
|
||||||
}
|
// imageList.add(imageBean)
|
||||||
|
// }
|
||||||
private fun getImagePath(localMedia: LocalMedia): String {
|
// }
|
||||||
if (!localMedia.cutPath.isNullOrEmpty()) {
|
// return imageList
|
||||||
return localMedia.cutPath
|
// }
|
||||||
}
|
//
|
||||||
if (!localMedia.compressPath.isNullOrEmpty()) {
|
// private fun getImagePath(localMedia: LocalMedia): String {
|
||||||
return localMedia.compressPath
|
// if (!localMedia.cutPath.isNullOrEmpty()) {
|
||||||
}
|
// return localMedia.cutPath
|
||||||
if (!localMedia.sandboxPath.isNullOrEmpty()) {
|
// }
|
||||||
return localMedia.sandboxPath
|
// if (!localMedia.compressPath.isNullOrEmpty()) {
|
||||||
}
|
// return localMedia.compressPath
|
||||||
return localMedia.path
|
// }
|
||||||
}
|
// if (!localMedia.sandboxPath.isNullOrEmpty()) {
|
||||||
}
|
// return localMedia.sandboxPath
|
||||||
|
// }
|
||||||
|
// return localMedia.path
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import android.graphics.Color
|
|||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -126,4 +128,8 @@ fun View.useShapeSetBackground(
|
|||||||
this.background = shape
|
this.background = shape
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ImageView.load(any:Any) {
|
||||||
|
Glide.with(this.context).load(any).into(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.zmkg.coaloperation.utils
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.ActivityInfo
|
||||||
|
import com.zhihu.matisse.Matisse
|
||||||
|
import com.zhihu.matisse.MimeType
|
||||||
|
import com.zhihu.matisse.R
|
||||||
|
import com.zhihu.matisse.engine.impl.GlideEngine
|
||||||
|
import com.zhihu.matisse.internal.entity.CaptureStrategy
|
||||||
|
import com.zmkg.coaloperation.base.BaseActivity
|
||||||
|
|
||||||
|
object GalleryUtils {
|
||||||
|
|
||||||
|
fun openGallery(activity: BaseActivity, action:(Intent)-> Unit) {
|
||||||
|
Matisse.from(activity)
|
||||||
|
.choose(MimeType.ofImage(), false) // 只选图片
|
||||||
|
.capture(true)
|
||||||
|
.captureStrategy(
|
||||||
|
CaptureStrategy(
|
||||||
|
true,
|
||||||
|
"${activity.packageName}.fileprovider",
|
||||||
|
"coalImage"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.countable(true) // 显示选中数量
|
||||||
|
.maxSelectable(9) // 最多选9张
|
||||||
|
.gridExpectedSize(120.dp)
|
||||||
|
.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
|
||||||
|
.thumbnailScale(0.8f)
|
||||||
|
.theme(R.style.Matisse_Dracula)
|
||||||
|
.imageEngine(GlideEngine()) // 必须设置引擎
|
||||||
|
.forResultLaunch { intent ->
|
||||||
|
activity.launchActivityResult(intent) { result ->
|
||||||
|
result.data?.let {
|
||||||
|
action(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+80
@@ -0,0 +1,80 @@
|
|||||||
|
package com.zmkg.coaloperation.utils.permission//package com.dheaven.mscapp.SIGFFCNFN.permission
|
||||||
|
//
|
||||||
|
//import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
//import androidx.fragment.app.Fragment
|
||||||
|
//
|
||||||
|
//class InvisibleFragment : Fragment() {
|
||||||
|
//
|
||||||
|
// private var singlePermissionAction: ((Boolean) -> Unit)? = null
|
||||||
|
// private var multiplePermissionAction: ((MutableMap<String,Boolean>) -> Unit)? = null
|
||||||
|
//
|
||||||
|
// private val singlePermissionLauncher = registerForActivityResult(
|
||||||
|
// ActivityResultContracts.RequestPermission()
|
||||||
|
// ) {
|
||||||
|
// singlePermissionAction?.invoke(it)
|
||||||
|
// }
|
||||||
|
// private val multiplePermissionLauncher = registerForActivityResult(
|
||||||
|
// ActivityResultContracts.RequestMultiplePermissions()
|
||||||
|
// ) {
|
||||||
|
// multiplePermissionAction?.invoke(it)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private fun requestSinglePermission(permission: String, action: (Boolean) -> Unit) {
|
||||||
|
// singlePermissionAction = action
|
||||||
|
// singlePermissionLauncher.launch(permission)
|
||||||
|
// }
|
||||||
|
// private fun requestMultiplePermission(permission: Array<String>, action: (MutableMap<String,Boolean>) -> Unit) {
|
||||||
|
// multiplePermissionAction = action
|
||||||
|
// multiplePermissionLauncher.launch(permission)
|
||||||
|
// }
|
||||||
|
// fun requestNow(
|
||||||
|
// callback: (granted: Boolean, deniedList: List<String>) -> Unit,
|
||||||
|
// args: Array<String>
|
||||||
|
// ) {
|
||||||
|
// permissionCallback = callback
|
||||||
|
//// requestPermissions(args, 1)
|
||||||
|
//
|
||||||
|
// if (args.size == 1) {
|
||||||
|
// requestSinglePermission(args[0]) {
|
||||||
|
// val deniedList = mutableListOf<String>()
|
||||||
|
// if (it.not()) {
|
||||||
|
// deniedList.add(args[0])
|
||||||
|
// }
|
||||||
|
// callback(it, deniedList)
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// requestMultiplePermission(args){
|
||||||
|
// val deniedList = mutableListOf<String>()
|
||||||
|
// it.entries.forEach { entry ->
|
||||||
|
// entry.apply {
|
||||||
|
// if (value.not()) {
|
||||||
|
// deniedList.add(key)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// val granted = deniedList.isEmpty()
|
||||||
|
// callback(granted, deniedList)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//// override fun onRequestPermissionsResult(
|
||||||
|
//// requestCode: Int,
|
||||||
|
//// permissions: Array<String>,
|
||||||
|
//// grantResults: IntArray
|
||||||
|
//// ) {
|
||||||
|
//// super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
//// if (requestCode == 1) {
|
||||||
|
//// val deniedList: MutableList<String> = ArrayList()
|
||||||
|
//// for (i in grantResults.indices) {
|
||||||
|
//// if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
//// deniedList.add(permissions[i])
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//// val allGranted = deniedList.isEmpty()
|
||||||
|
//// permissionCallback?.invoke(allGranted, deniedList)
|
||||||
|
//// }
|
||||||
|
//// }
|
||||||
|
//
|
||||||
|
// private var permissionCallback: ((granted: Boolean, deniedList: List<String>) -> Unit)? = null
|
||||||
|
//}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
package com.dheaven.mscapp.SIGFFCNFN.permission
|
||||||
|
|
||||||
|
import com.zmkg.coaloperation.base.BaseActivity
|
||||||
|
|
||||||
|
object PermissionUtils {
|
||||||
|
// private const val TAG = "InvisibleFragment"
|
||||||
|
fun request(
|
||||||
|
activity: BaseActivity,
|
||||||
|
permissions: Array<String>,
|
||||||
|
callback: (granted: Boolean, deniedList: List<String>) -> Unit
|
||||||
|
) {
|
||||||
|
if (permissions.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// val fragmentManager = activity.supportFragmentManager
|
||||||
|
// val fragment = fragmentManager.findFragmentByTag(TAG)
|
||||||
|
// var invisibleFragment: InvisibleFragment? = null
|
||||||
|
// if (invisibleFragment == null) {
|
||||||
|
// invisibleFragment = InvisibleFragment()
|
||||||
|
// fragmentManager.beginTransaction().add(invisibleFragment, TAG).commitNow()
|
||||||
|
// } else {
|
||||||
|
// invisibleFragment = fragment as InvisibleFragment
|
||||||
|
// }
|
||||||
|
// invisibleFragment.requestNow(callback, permissions)
|
||||||
|
//单权限请求
|
||||||
|
if (permissions.size == 1) {
|
||||||
|
val permission = permissions[0]
|
||||||
|
activity.requestSinglePermissionResult(permission) {
|
||||||
|
val deniedList = mutableListOf<String>()
|
||||||
|
if (it.not()) {
|
||||||
|
deniedList.add(permission)
|
||||||
|
}
|
||||||
|
callback(it, deniedList)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//多权限请求
|
||||||
|
activity.requestMultiplePermissionsResult(permissions) {
|
||||||
|
val deniedList = mutableListOf<String>()
|
||||||
|
it.entries.forEach { entry ->
|
||||||
|
entry.apply {
|
||||||
|
if (value.not()) {
|
||||||
|
deniedList.add(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val granted = deniedList.isEmpty()
|
||||||
|
callback(granted, deniedList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
package com.zmkg.coaloperation.utils.permission
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
|
import android.provider.Settings
|
||||||
|
import com.zmkg.coaloperation.base.BaseActivity
|
||||||
|
|
||||||
|
object StorageUtils {
|
||||||
|
|
||||||
|
fun requestPermission(activity: BaseActivity, callback: () -> Unit) {
|
||||||
|
activity.requestSinglePermissionResult(Manifest.permission.CAMERA) {
|
||||||
|
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
// if (Environment.isExternalStorageManager()) {
|
||||||
|
// callback()
|
||||||
|
// } else {
|
||||||
|
// val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
|
||||||
|
//// activity.startActivityForResult(intent, MANAGE_REQUEST_CODE)
|
||||||
|
// activity.launchActivityResult(intent) {
|
||||||
|
// callback()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
activity.requestSinglePermissionResult(Manifest.permission.WRITE_EXTERNAL_STORAGE) {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 699 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,73 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black_bg"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/toolbar_lay"
|
||||||
|
layout="@layout/lay_title_right_bar" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:layout_marginHorizontal="20dp"
|
||||||
|
android:background="@drawable/bg_stroke_gray"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="工作面名称"
|
||||||
|
android:textColor="#6A6F87"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etWorkfaceName"
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_marginEnd="15dp"
|
||||||
|
android:background="@drawable/rectangle_stroke_grey_corner8"
|
||||||
|
android:hint="请输入"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textColorHint="#757B8C"
|
||||||
|
android:textCursorDrawable="@drawable/cursor_white"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<com.allen.library.shape.ShapeButton
|
||||||
|
android:id="@+id/btnAddFace"
|
||||||
|
style="?android:attr/buttonBarButtonStyle"
|
||||||
|
android:layout_width="250dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginBottom="35dp"
|
||||||
|
android:text="确认新增"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:shapeCornersRadius="36dp"
|
||||||
|
app:shapeSolidColor="#21A69A" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
||||||
@@ -37,77 +37,9 @@
|
|||||||
app:shapeCornersRadius="36dp"
|
app:shapeCornersRadius="36dp"
|
||||||
app:shapeSolidColor="#21A69A" />
|
app:shapeSolidColor="#21A69A" />
|
||||||
|
|
||||||
<LinearLayout
|
<net.lucode.hackware.magicindicator.MagicIndicator
|
||||||
|
android:id="@+id/magicIndicator"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="56dp"
|
android:layout_height="56dp"/>
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.allen.library.shape.ShapeTextView
|
|
||||||
android:layout_width="35dp"
|
|
||||||
android:layout_height="35dp"
|
|
||||||
android:layout_marginStart="5dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="1#"
|
|
||||||
android:textColor="#30E0A1"
|
|
||||||
android:textSize="20sp"
|
|
||||||
app:shapeCornersRadius="8dp"
|
|
||||||
app:shapeSolidColor="#1430E0A1" />
|
|
||||||
|
|
||||||
<com.allen.library.shape.ShapeTextView
|
|
||||||
android:layout_width="35dp"
|
|
||||||
android:layout_height="35dp"
|
|
||||||
android:layout_marginStart="5dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="1#"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="20sp"
|
|
||||||
app:shapeCornersRadius="8dp"
|
|
||||||
app:shapeSolidColor="#1430E0A1" />
|
|
||||||
|
|
||||||
<com.allen.library.shape.ShapeTextView
|
|
||||||
android:layout_width="35dp"
|
|
||||||
android:layout_height="35dp"
|
|
||||||
android:layout_marginStart="5dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="1#"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="20sp"
|
|
||||||
app:shapeCornersRadius="8dp"
|
|
||||||
app:shapeSolidColor="#1430E0A1" />
|
|
||||||
|
|
||||||
<com.allen.library.shape.ShapeTextView
|
|
||||||
android:layout_width="35dp"
|
|
||||||
android:layout_height="35dp"
|
|
||||||
android:layout_marginStart="5dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="1#"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="20sp"
|
|
||||||
app:shapeCornersRadius="8dp"
|
|
||||||
app:shapeSolidColor="#1430E0A1" />
|
|
||||||
|
|
||||||
<com.allen.library.shape.ShapeTextView
|
|
||||||
android:layout_width="35dp"
|
|
||||||
android:layout_height="35dp"
|
|
||||||
android:layout_marginStart="5dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="1#"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="20sp"
|
|
||||||
app:shapeCornersRadius="8dp"
|
|
||||||
app:shapeSolidColor="#1430E0A1" />
|
|
||||||
|
|
||||||
<com.allen.library.shape.ShapeTextView
|
|
||||||
android:layout_width="35dp"
|
|
||||||
android:layout_height="35dp"
|
|
||||||
android:layout_marginStart="5dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="1#"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="20sp"
|
|
||||||
app:shapeCornersRadius="8dp"
|
|
||||||
app:shapeSolidColor="#1430E0A1" />
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</layout>
|
</layout>
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
android:id="@+id/rvGeoLogging"
|
android:id="@+id/rvGeoLogging"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:overScrollMode="never"
|
||||||
tools:listitem="@layout/item_geological_list" />
|
tools:listitem="@layout/item_geological_list" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -38,25 +38,25 @@
|
|||||||
android:paddingBottom="20dp">
|
android:paddingBottom="20dp">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnRecord"
|
android:id="@+id/btnStartLogging"
|
||||||
style="?android:attr/buttonBarButtonStyle"
|
style="?android:attr/buttonBarButtonStyle"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="44dp"
|
android:layout_height="44dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:background="@drawable/bg_btn_save"
|
android:background="@drawable/bg_btn_save"
|
||||||
android:text="查看记录"
|
android:text="开始编录"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnSubmit"
|
android:id="@+id/btnCompleteLogging"
|
||||||
style="?android:attr/buttonBarButtonStyle"
|
style="?android:attr/buttonBarButtonStyle"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="44dp"
|
android:layout_height="44dp"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:background="@drawable/bg_btn_export"
|
android:background="@drawable/bg_btn_export"
|
||||||
android:text="确认提交"
|
android:text="编录完成"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<com.allen.library.shape.ShapeLinearLayout
|
||||||
|
android:layout_width="300dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:shapeCornersRadius="30dp"
|
||||||
|
app:shapeSolidColor="#17181C"
|
||||||
|
app:shapeStrokeColor="#282C38"
|
||||||
|
app:shapeStrokeWidth="1dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDialogTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="50dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
tools:text="保存" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDialogContent"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:textColor="#6A6F87"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="是否确认保存输入的消息" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginBottom="50dp"
|
||||||
|
android:layout_marginTop="30dp">
|
||||||
|
|
||||||
|
<com.allen.library.shape.ShapeButton
|
||||||
|
android:id="@+id/btnCancel"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:shapeCornersRadius="22dp"
|
||||||
|
tools:text="取消"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:shapeSolidColor="#353846" />
|
||||||
|
|
||||||
|
<com.allen.library.shape.ShapeButton
|
||||||
|
android:id="@+id/btnConfirm"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="确定"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:shapeCornersRadius="22dp"
|
||||||
|
app:shapeSolidColor="#21A69A" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.allen.library.shape.ShapeLinearLayout>
|
||||||
|
</layout>
|
||||||
@@ -28,14 +28,13 @@
|
|||||||
android:layout_marginBottom="70dp">
|
android:layout_marginBottom="70dp">
|
||||||
|
|
||||||
|
|
||||||
<com.allen.library.shape.ShapeLinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="10dp"
|
android:layout_marginHorizontal="10dp"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
app:shapeCornersRadius="6dp"
|
android:background="@drawable/bg_stroke_gray">
|
||||||
app:shapeSolidColor="@color/theme_color">
|
|
||||||
|
|
||||||
<com.zmkg.coaloperation.view.CustomTextSelectView
|
<com.zmkg.coaloperation.view.CustomTextSelectView
|
||||||
android:id="@+id/type"
|
android:id="@+id/type"
|
||||||
@@ -153,7 +152,7 @@
|
|||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
|
||||||
</com.allen.library.shape.ShapeLinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black_bg"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/toolbar_lay"
|
||||||
|
layout="@layout/lay_title_right_bar"
|
||||||
|
app:goneBack="@{true}"
|
||||||
|
app:title="@{@string/title_report}"
|
||||||
|
app:transparentBg="@{true}" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvReport"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:overScrollMode="never"
|
||||||
|
tools:listitem="@layout/list_item_report"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:itemCount="10"
|
||||||
|
android:background="@drawable/bg_stroke_gray"
|
||||||
|
android:layout_marginHorizontal="20dp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<com.allen.library.shape.ShapeButton
|
||||||
|
android:id="@+id/btnConfirm"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginHorizontal="45dp"
|
||||||
|
android:layout_marginVertical="20dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="确认"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:shapeCornersRadius="20dp"
|
||||||
|
app:shapeSolidColor="@color/text_green" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
||||||
@@ -7,83 +7,35 @@
|
|||||||
|
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<LinearLayout
|
<com.allen.library.shape.ShapeLinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="74dp"
|
||||||
android:orientation="vertical">
|
android:layout_marginHorizontal="20dp"
|
||||||
|
app:shapeCornersRadius="6dp"
|
||||||
|
android:layout_marginVertical="5dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
app:shapeSolidColor="#1521A69A"
|
||||||
|
app:shapeStrokeColor="#282C38"
|
||||||
|
app:shapeStrokeWidth="1dp">
|
||||||
|
|
||||||
<com.allen.library.shape.ShapeRelativeLayout
|
<ImageView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="30dp"
|
||||||
android:layout_height="74dp"
|
android:layout_height="38dp"
|
||||||
android:layout_marginHorizontal="20dp"
|
android:layout_marginStart="20dp"
|
||||||
android:layout_marginTop="10dp"
|
android:background="@drawable/ic_geological" />
|
||||||
app:shapeCornersRadius="6dp"
|
|
||||||
app:shapeSolidColor="#1521A69A"
|
|
||||||
app:shapeStrokeColor="#10514C"
|
|
||||||
app:shapeStrokeWidth="1dp">
|
|
||||||
|
|
||||||
<ImageView
|
<TextView
|
||||||
android:layout_width="30dp"
|
android:id="@+id/tvFaceName"
|
||||||
android:layout_height="38dp"
|
android:layout_width="0dp"
|
||||||
android:layout_centerVertical="true"
|
android:layout_weight="1"
|
||||||
android:layout_marginLeft="20dp"
|
android:maxLines="1"
|
||||||
android:layout_marginRight="13dp"
|
android:ellipsize="end"
|
||||||
android:background="@drawable/ic_geological" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_marginLeft="60dp"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="14sp"
|
|
||||||
tools:text="5-20304回风工作面" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/expand"
|
|
||||||
android:layout_width="36dp"
|
|
||||||
android:layout_height="36dp"
|
|
||||||
android:padding="6dp"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_marginRight="13dp"
|
|
||||||
android:src="@drawable/ic_arrow_right_gray"
|
|
||||||
android:tag="0" />
|
|
||||||
|
|
||||||
|
|
||||||
</com.allen.library.shape.ShapeRelativeLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/record_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginStart="10dp"
|
||||||
android:orientation="vertical"
|
android:textColor="@color/white"
|
||||||
android:visibility="gone">
|
android:textSize="14sp"
|
||||||
|
tools:text="5-20304回风工作面" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
</com.allen.library.shape.ShapeLinearLayout>
|
||||||
android:id="@+id/rv_content"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="200dp"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:itemCount="3"
|
|
||||||
tools:listitem="@layout/item_geological_list_content" />
|
|
||||||
|
|
||||||
<com.allen.library.shape.ShapeTextView
|
|
||||||
android:id="@+id/start_record"
|
|
||||||
android:layout_width="154dp"
|
|
||||||
android:layout_height="44dp"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="开始记录"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
app:shapeCornersRadius="30dp"
|
|
||||||
app:shapeSolidColor="#21A69A" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</layout>
|
</layout>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivImage"
|
||||||
|
android:layout_width="70dp"
|
||||||
|
android:layout_height="70dp"
|
||||||
|
android:layout_marginVertical="5dp"
|
||||||
|
android:layout_marginHorizontal="5dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/ic_add_image"/>
|
||||||
|
</FrameLayout>
|
||||||
|
</layout>
|
||||||
@@ -25,18 +25,22 @@
|
|||||||
android:layout_marginStart="20dp"
|
android:layout_marginStart="20dp"
|
||||||
tools:text="瓦斯含量" />
|
tools:text="瓦斯含量" />
|
||||||
|
|
||||||
<TextView
|
<EditText
|
||||||
android:id="@+id/tvItemValue"
|
android:id="@+id/etInputValue"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
android:layout_marginEnd="15dp"
|
android:layout_marginEnd="15dp"
|
||||||
|
android:minWidth="80dp"
|
||||||
android:gravity="end|center_vertical"
|
android:gravity="end|center_vertical"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp"
|
android:hint="请输入"
|
||||||
tools:text="15m/s" />
|
android:background="@color/transparent"
|
||||||
|
android:textCursorDrawable="@drawable/cursor_white"
|
||||||
|
android:textColorHint="#757B8C"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
<com.google.android.material.divider.MaterialDivider
|
<com.google.android.material.divider.MaterialDivider
|
||||||
android:id="@+id/divider"
|
android:id="@+id/divider"
|
||||||
|
|||||||
@@ -38,16 +38,19 @@
|
|||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
app:dividerColor="#20222B" />
|
app:dividerColor="#20222B" />
|
||||||
|
|
||||||
<TextView
|
<EditText
|
||||||
android:id="@+id/tvItemValue"
|
android:id="@+id/etInputValue"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:textCursorDrawable="@drawable/cursor_white"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="14sp"
|
android:hint="请输入"
|
||||||
tools:text="15m/s" />
|
android:textColorHint="#757B8C"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -62,13 +62,14 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
|
android:paddingStart="20dp"
|
||||||
|
android:paddingEnd="15dp"
|
||||||
android:background="@drawable/bottom_border"
|
android:background="@drawable/bottom_border"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:layout_marginStart="20dp"
|
|
||||||
android:layout_weight="1">
|
android:layout_weight="1">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -93,14 +94,17 @@
|
|||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
tools:text="1024.341" />
|
tools:text="1024.341" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.divider.MaterialDivider
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:layout_marginHorizontal="8dp"
|
||||||
|
app:dividerColor="#20222B" />
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:layout_marginStart="15dp"
|
|
||||||
android:layout_marginEnd="15dp"
|
|
||||||
android:layout_weight="1">
|
android:layout_weight="1">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -133,8 +137,8 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:layout_marginStart="20dp"
|
android:paddingStart="20dp"
|
||||||
android:layout_marginEnd="15dp"
|
android:paddingEnd="15dp"
|
||||||
android:background="@drawable/bottom_border"
|
android:background="@drawable/bottom_border"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
@@ -168,10 +172,16 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.divider.MaterialDivider
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:layout_marginHorizontal="8dp"
|
||||||
|
app:dividerColor="#20222B" />
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:layout_marginStart="15dp"
|
|
||||||
android:layout_weight="1">
|
android:layout_weight="1">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/ivPointImage"
|
|
||||||
android:layout_width="60dp"
|
|
||||||
android:layout_height="60dp"
|
|
||||||
android:src="@drawable/ic_add_image"/>
|
|
||||||
</FrameLayout>
|
|
||||||
</layout>
|
|
||||||
@@ -24,10 +24,12 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:overScrollMode="never"
|
android:overScrollMode="never"
|
||||||
|
android:background="@drawable/bg_stroke_gray"
|
||||||
tools:itemCount="4"
|
tools:itemCount="4"
|
||||||
app:spanCount="4"
|
app:spanCount="4"
|
||||||
|
android:paddingVertical="5dp"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:listitem="@layout/list_item_point_image"
|
tools:listitem="@layout/list_item_common_image"
|
||||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"/>
|
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:background="@color/black_bg">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvItemName"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="#6A6F87"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="功能名称" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etInputValue"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:hint="请输入"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textColorHint="#757B8C"
|
||||||
|
android:textCursorDrawable="@drawable/cursor_white"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvItemValue"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:hint="请选择"
|
||||||
|
android:drawablePadding="20dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textColorHint="#757B8C"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:drawableEndCompat="@drawable/ic_arrow_down_gray" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvReportImage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
|
app:spanCount="3"
|
||||||
|
tools:itemCount="6"
|
||||||
|
tools:listitem="@layout/list_item_common_image" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.divider.MaterialDivider
|
||||||
|
android:id="@+id/divider"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
app:dividerColor="#282C38" />
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
||||||
@@ -38,5 +38,9 @@
|
|||||||
<!-- 遮罩层 -->
|
<!-- 遮罩层 -->
|
||||||
<item name="android:backgroundDimAmount">0.5</item>
|
<item name="android:backgroundDimAmount">0.5</item>
|
||||||
</style>
|
</style>
|
||||||
|
<!-- <style name="Theme_dialog" parent="@android:style/Theme.Dialog">-->
|
||||||
|
<!-- <item name="android:windowBackground">@android:color/transparent</item>-->
|
||||||
|
<!-- <item name="android:windowNoTitle">true</item>-->
|
||||||
|
<!-- <item name="android:windowIsFloating">true</item>-->
|
||||||
|
<!-- </style>-->
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,4 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<external-path name="external_files" path="."/>
|
<external-path name="external_files" path="."/>
|
||||||
|
<files-path name="my_files" path="." />
|
||||||
|
<cache-path name="my_cache" path="." />
|
||||||
|
<!-- <external-path name="my_external" path="." />-->
|
||||||
|
<external-files-path name="external_files" path="." />
|
||||||
|
<external-cache-path name="external_cache" path="." />
|
||||||
|
<external-path name="external_storage" path="." />
|
||||||
</paths>
|
</paths>
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* 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: 'com.novoda.bintray-release'/
|
||||||
|
//apply plugin: 'checkstyle'
|
||||||
|
|
||||||
|
android {
|
||||||
|
// compileSdkVersion 29
|
||||||
|
// buildToolsVersion '29.0.2'
|
||||||
|
compileSdk rootProject.ext.compileSdkVersion
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
// minSdkVersion 14
|
||||||
|
// targetSdkVersion 29
|
||||||
|
minSdk rootProject.ext.minSdkVersion
|
||||||
|
targetSdk rootProject.ext.targetSdkVersion
|
||||||
|
}
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
|
||||||
|
// jcenter configuration for novoda's bintray-release
|
||||||
|
// $ ./gradlew clean build bintrayUpload -PbintrayUser=BINTRAY_USERNAME -PbintrayKey=BINTRAY_KEY -PdryRun=false
|
||||||
|
//publish {
|
||||||
|
// userOrg = 'zhihu'
|
||||||
|
// groupId = 'com.zhihu.android'
|
||||||
|
// artifactId = 'matisse'
|
||||||
|
// publishVersion = '0.5.3-beta3'
|
||||||
|
// desc = 'A well-designed local image selector for Android'
|
||||||
|
// website = 'https://www.zhihu.com/'
|
||||||
|
//}
|
||||||
|
|
||||||
|
//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'
|
||||||
|
//}
|
||||||
+19
@@ -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.**
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?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"/>
|
||||||
|
<activity android:name="com.zhihu.matisse.internal.ui.AlbumPreviewActivity"/>
|
||||||
|
<activity android:name="com.zhihu.matisse.internal.ui.SelectedPreviewActivity"/>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* 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 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,383 @@
|
|||||||
|
/*
|
||||||
|
* 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 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 java.util.function.Consumer;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forResultLaunch(Consumer<Intent> consumer) {
|
||||||
|
Activity activity = mMatisse.getActivity();
|
||||||
|
if (activity == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Intent intent = new Intent(activity, MatisseActivity.class);
|
||||||
|
if (consumer!=null) {
|
||||||
|
consumer.accept(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+33
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+83
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+121
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+293
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+197
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+114
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+107
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+256
@@ -0,0 +1,256 @@
|
|||||||
|
/*
|
||||||
|
* 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 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.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 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.plurals.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// depends
|
||||||
|
private int currentMaxSelectable() {
|
||||||
|
SelectionSpec spec = SelectionSpec.getInstance();
|
||||||
|
if (spec.maxSelectable > 0) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+97
@@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+358
@@ -0,0 +1,358 @@
|
|||||||
|
/*
|
||||||
|
* 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(new View.OnClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View 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(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+159
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+129
@@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* 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(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View 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(new ImageViewTouch.OnImageViewTouchSingleTapListener() {
|
||||||
|
@Override
|
||||||
|
public void onSingleTapConfirmed() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+300
@@ -0,0 +1,300 @@
|
|||||||
|
/*
|
||||||
|
* 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(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (v.getContext() instanceof OnPhotoCapture) {
|
||||||
|
((OnPhotoCapture) v.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+71
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
+70
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+105
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+129
@@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* 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((int) (216 * density));
|
||||||
|
mListPopupWindow.setHorizontalOffset((int) (16 * density));
|
||||||
|
mListPopupWindow.setVerticalOffset((int) (-48 * density));
|
||||||
|
|
||||||
|
mListPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long 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(new View.OnClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+60
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+229
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* 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, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
}
|
||||||
+161
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+60
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+131
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+145
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
+178
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -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);
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
package com.zhihu.matisse.listener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PreViewItemFragment 和 BasePreViewActivity 通信的接口 ,为了方便拿到 ImageViewTouch 的点击事件
|
||||||
|
*/
|
||||||
|
public interface OnFragmentInteractionListener {
|
||||||
|
/**
|
||||||
|
* ImageViewTouch 被点击了
|
||||||
|
*/
|
||||||
|
void onClick();
|
||||||
|
}
|
||||||
+30
@@ -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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,437 @@
|
|||||||
|
/*
|
||||||
|
* 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 androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
|
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 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.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_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;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
// programmatically set theme before super.onCreate()
|
||||||
|
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) {
|
||||||
|
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<>();
|
||||||
|
if (selected != null) {
|
||||||
|
for (Item item : selected) {
|
||||||
|
selectedUris.add(item.getContentUri());
|
||||||
|
selectedPaths.add(PathUtils.getPath(this, item.getContentUri()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.putParcelableArrayListExtra(EXTRA_RESULT_SELECTION, selectedUris);
|
||||||
|
result.putStringArrayListExtra(EXTRA_RESULT_SELECTION_PATH, selectedPaths);
|
||||||
|
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) {
|
||||||
|
// 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, new SingleMediaScanner.ScanListener() {
|
||||||
|
@Override public void onScanFinish() {
|
||||||
|
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) {
|
||||||
|
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_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(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
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 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);
|
||||||
|
Fragment fragment = MediaSelectionFragment.newInstance(album);
|
||||||
|
getSupportFragmentManager()
|
||||||
|
.beginTransaction()
|
||||||
|
.replace(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user