Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Sign in / Register
Toggle navigation
H
HFile Manager Master
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wanglei
HFile Manager Master
Commits
66562843
Commit
66562843
authored
Mar 12, 2024
by
wanglei
Browse files
Options
Browse Files
Download
Plain Diff
大文件获取功能
parents
37094dd6
d8cf82d1
Hide whitespace changes
Inline
Side-by-side
Showing
30 changed files
with
1534 additions
and
53 deletions
+1534
-53
build.gradle.kts
app/build.gradle.kts
+2
-0
AndroidManifest.xml
app/src/main/AndroidManifest.xml
+13
-0
ManagerFragment.kt
.../main/java/com/zxhy/hfilemanagermaster/ManagerFragment.kt
+20
-18
OverviewActivity.kt
...main/java/com/zxhy/hfilemanagermaster/OverviewActivity.kt
+8
-0
MediaDataC.kt
.../main/java/com/zxhy/hfilemanagermaster/data/MediaDataC.kt
+16
-0
FilesFragment.kt
...n/java/com/zxhy/hfilemanagermaster/files/FilesFragment.kt
+74
-0
ImageAdapter.kt
...in/java/com/zxhy/hfilemanagermaster/files/ImageAdapter.kt
+51
-0
GlideAppModule.kt
.../java/com/zxhy/hfilemanagermaster/glide/GlideAppModule.kt
+12
-0
GlideEx.kt
...rc/main/java/com/zxhy/hfilemanagermaster/glide/GlideEx.kt
+19
-0
FileUriEx.kt
.../main/java/com/zxhy/hfilemanagermaster/knife/FileUriEx.kt
+137
-0
RecentMediaStore.kt
...ava/com/zxhy/hfilemanagermaster/knife/RecentMediaStore.kt
+146
-0
RecentPicture.kt
...n/java/com/zxhy/hfilemanagermaster/knife/RecentPicture.kt
+39
-0
LargeFileActivity.kt
...om/zxhy/hfilemanagermaster/largefile/LargeFileActivity.kt
+26
-1
LargeFileFragment.kt
...om/zxhy/hfilemanagermaster/largefile/LargeFileFragment.kt
+1
-10
MediaSelectAdapter.kt
...m/zxhy/hfilemanagermaster/largefile/MediaSelectAdapter.kt
+111
-0
ManagerFragment.kt
...va/com/zxhy/hfilemanagermaster/manager/ManagerFragment.kt
+37
-12
Dialogs.kt
...in/java/com/zxhy/hfilemanagermaster/permission/Dialogs.kt
+434
-0
IntentLauncher.kt
.../com/zxhy/hfilemanagermaster/permission/IntentLauncher.kt
+31
-0
MediaPermission.kt
...com/zxhy/hfilemanagermaster/permission/MediaPermission.kt
+89
-0
PermissionCheck.kt
...com/zxhy/hfilemanagermaster/permission/PermissionCheck.kt
+30
-0
PermissionLauncher.kt
.../zxhy/hfilemanagermaster/permission/PermissionLauncher.kt
+39
-0
IndicatorView.kt
.../java/com/zxhy/hfilemanagermaster/widget/IndicatorView.kt
+62
-5
bg_circle_selector.xml
app/src/main/res/drawable/bg_circle_selector.xml
+12
-0
bg_media_selector.xml
app/src/main/res/drawable/bg_media_selector.xml
+5
-0
activity_large_file.xml
app/src/main/res/layout/activity_large_file.xml
+7
-4
fragment_files.xml
app/src/main/res/layout/fragment_files.xml
+13
-2
item_image.xml
app/src/main/res/layout/item_image.xml
+17
-0
item_media_select.xml
app/src/main/res/layout/item_media_select.xml
+78
-0
nav.xml
app/src/main/res/navigation/nav.xml
+1
-1
libs.versions.toml
gradle/libs.versions.toml
+4
-0
No files found.
app/build.gradle.kts
View file @
66562843
...
@@ -50,4 +50,6 @@ dependencies {
...
@@ -50,4 +50,6 @@ dependencies {
testImplementation
(
libs
.
junit
)
testImplementation
(
libs
.
junit
)
androidTestImplementation
(
libs
.
androidx
.
junit
)
androidTestImplementation
(
libs
.
androidx
.
junit
)
androidTestImplementation
(
libs
.
androidx
.
espresso
.
core
)
androidTestImplementation
(
libs
.
androidx
.
espresso
.
core
)
implementation
(
libs
.
glide
)
}
}
\ No newline at end of file
app/src/main/AndroidManifest.xml
View file @
66562843
...
@@ -2,6 +2,19 @@
...
@@ -2,6 +2,19 @@
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
>
xmlns:tools=
"http://schemas.android.com/tools"
>
<uses-permission
android:name=
"android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion=
"32"
/>
<!-- https://developer.android.com/about/versions/14/changes/partial-photo-video-access?hl=zh-cn -->
<uses-permission
android:name=
"android.permission.READ_MEDIA_VISUAL_USER_SELECTED"
/>
<uses-permission
android:name=
"android.permission.READ_MEDIA_IMAGES"
tools:ignore=
"SelectedPhotoAccess"
/>
<uses-permission
android:name=
"android.permission.READ_MEDIA_VIDEO"
tools:ignore=
"SelectedPhotoAccess"
/>
<application
<application
android:allowBackup=
"true"
android:allowBackup=
"true"
android:dataExtractionRules=
"@xml/data_extraction_rules"
android:dataExtractionRules=
"@xml/data_extraction_rules"
...
...
app/src/main/java/com/zxhy/hfilemanagermaster/
Files
Fragment.kt
→
app/src/main/java/com/zxhy/hfilemanagermaster/
Manager
Fragment.kt
View file @
66562843
...
@@ -5,9 +5,8 @@ import androidx.fragment.app.Fragment
...
@@ -5,9 +5,8 @@ import androidx.fragment.app.Fragment
import
android.view.LayoutInflater
import
android.view.LayoutInflater
import
android.view.View
import
android.view.View
import
android.view.ViewGroup
import
android.view.ViewGroup
import
androidx.navigation.fragment.findNavController
import
com.example.hfilemanagermaster.R
import
com.example.hfilemanagermaster.R
import
com.example.hfilemanagermaster.databinding.Fragment
Files
Binding
import
com.example.hfilemanagermaster.databinding.Fragment
Manager
Binding
// TODO: Rename parameter arguments, choose names that match
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
...
@@ -16,14 +15,13 @@ private const val ARG_PARAM2 = "param2"
...
@@ -16,14 +15,13 @@ private const val ARG_PARAM2 = "param2"
/**
/**
* A simple [Fragment] subclass.
* A simple [Fragment] subclass.
* Use the [
Files
Fragment.newInstance] factory method to
* Use the [
Manager
Fragment.newInstance] factory method to
* create an instance of this fragment.
* create an instance of this fragment.
*/
*/
class
Files
Fragment
:
Fragment
()
{
class
Manager
Fragment
:
Fragment
()
{
// TODO: Rename and change types of parameters
private
lateinit
var
binding
:
FragmentManagerBinding
private
var
param1
:
String
?
=
null
private
var
param1
:
String
?
=
null
private
var
param2
:
String
?
=
null
private
var
param2
:
String
?
=
null
private
lateinit
var
binding
:
FragmentFilesBinding
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
...
@@ -31,27 +29,31 @@ class FilesFragment : Fragment() {
...
@@ -31,27 +29,31 @@ class FilesFragment : Fragment() {
param1
=
it
.
getString
(
ARG_PARAM1
)
param1
=
it
.
getString
(
ARG_PARAM1
)
param2
=
it
.
getString
(
ARG_PARAM2
)
param2
=
it
.
getString
(
ARG_PARAM2
)
}
}
(
activity
as
OverviewActivity
).
setStatusBarColor
(
R
.
color
.
white
)
}
}
override
fun
onCreateView
(
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?
savedInstanceState
:
Bundle
?
):
View
?
{
):
View
?
{
// Inflate the layout for this fragment
val
view
=
inflater
.
inflate
(
R
.
layout
.
fragment_manager
,
container
,
false
)
val
view
=
inflater
.
inflate
(
R
.
layout
.
fragment_files
,
container
,
false
)
binding
=
FragmentManagerBinding
.
bind
(
view
.
rootView
)
binding
=
FragmentFilesBinding
.
bind
(
view
)
return
view
return
view
}
}
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
super
.
onViewCreated
(
view
,
savedInstanceState
)
binding
.
ivImage
.
setOnClickListener
{
findNavController
().
navigate
(
R
.
id
.
imageActivity
)
binding
.
tvUse
.
text
=
"25%"
}
binding
.
tvFree
.
text
=
"75GB"
binding
.
ivVideo
.
setOnClickListener
{
binding
.
cardView1
.
setOnClickListener
{
}
findNavController
().
navigate
(
R
.
id
.
videoActivity
)
binding
.
cardView2
.
setOnClickListener
{
}
}
binding
.
cardView3
.
setOnClickListener
{
}
binding
.
ivFile
.
setOnClickListener
{
}
binding
.
ivWord
.
setOnClickListener
{
}
binding
.
ivExcel
.
setOnClickListener
{
}
binding
.
ivPdf
.
setOnClickListener
{
}
binding
.
ivPpt
.
setOnClickListener
{
}
}
}
companion
object
{
companion
object
{
...
@@ -61,12 +63,12 @@ class FilesFragment : Fragment() {
...
@@ -61,12 +63,12 @@ class FilesFragment : Fragment() {
*
*
* @param param1 Parameter 1.
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @param param2 Parameter 2.
* @return A new instance of fragment
Files
Fragment.
* @return A new instance of fragment
Manage
Fragment.
*/
*/
// TODO: Rename and change types and number of parameters
// TODO: Rename and change types and number of parameters
@JvmStatic
@JvmStatic
fun
newInstance
(
param1
:
String
,
param2
:
String
)
=
fun
newInstance
(
param1
:
String
,
param2
:
String
)
=
Files
Fragment
().
apply
{
Manager
Fragment
().
apply
{
arguments
=
Bundle
().
apply
{
arguments
=
Bundle
().
apply
{
putString
(
ARG_PARAM1
,
param1
)
putString
(
ARG_PARAM1
,
param1
)
putString
(
ARG_PARAM2
,
param2
)
putString
(
ARG_PARAM2
,
param2
)
...
...
app/src/main/java/com/zxhy/hfilemanagermaster/OverviewActivity.kt
View file @
66562843
...
@@ -13,14 +13,22 @@ import androidx.core.view.WindowInsetsCompat
...
@@ -13,14 +13,22 @@ import androidx.core.view.WindowInsetsCompat
import
androidx.navigation.findNavController
import
androidx.navigation.findNavController
import
com.example.hfilemanagermaster.R
import
com.example.hfilemanagermaster.R
import
com.example.hfilemanagermaster.databinding.ActivityOverviewBinding
import
com.example.hfilemanagermaster.databinding.ActivityOverviewBinding
import
com.zxhy.hfilemanagermaster.permission.IntentLauncher
import
com.zxhy.hfilemanagermaster.permission.PermissionLauncher
class
OverviewActivity
:
AppCompatActivity
()
{
class
OverviewActivity
:
AppCompatActivity
()
{
private
lateinit
var
binding
:
ActivityOverviewBinding
private
lateinit
var
binding
:
ActivityOverviewBinding
lateinit
var
permissionLauncher
:
PermissionLauncher
lateinit
var
intentLauncher
:
IntentLauncher
@SuppressLint
(
"SetTextI18n"
)
@SuppressLint
(
"SetTextI18n"
)
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
enableEdgeToEdge
()
enableEdgeToEdge
()
permissionLauncher
=
PermissionLauncher
(
this
)
intentLauncher
=
IntentLauncher
(
this
)
binding
=
ActivityOverviewBinding
.
inflate
(
layoutInflater
)
binding
=
ActivityOverviewBinding
.
inflate
(
layoutInflater
)
setContentView
(
binding
.
root
)
setContentView
(
binding
.
root
)
ViewCompat
.
setOnApplyWindowInsetsListener
(
findViewById
(
R
.
id
.
main
))
{
v
,
insets
->
ViewCompat
.
setOnApplyWindowInsetsListener
(
findViewById
(
R
.
id
.
main
))
{
v
,
insets
->
...
...
app/src/main/java/com/zxhy/hfilemanagermaster/data/MediaDataC.kt
0 → 100644
View file @
66562843
package
com.zxhy.hfilemanagermaster.data
import
android.net.Uri
const
val
MediaDataC_TYPE_IMAGE
=
101
const
val
MediaDataC_TYPE_VIDEO
=
102
data class
MediaDataC
(
val
name
:
String
=
"File name"
,
val
size
:
String
=
"20MB"
,
val
time
:
String
=
"January 5, 2024"
,
val
type
:
Int
=
MediaDataC_TYPE_IMAGE
,
val
uri
:
Uri
=
Uri
.
EMPTY
,
val
path
:
String
=
""
,
var
select
:
Boolean
=
false
,
)
app/src/main/java/com/zxhy/hfilemanagermaster/files/FilesFragment.kt
0 → 100644
View file @
66562843
package
com.zxhy.hfilemanagermaster.files
import
android.Manifest
import
android.os.Bundle
import
android.view.Display
import
android.view.LayoutInflater
import
android.view.View
import
android.view.ViewGroup
import
androidx.fragment.app.Fragment
import
androidx.lifecycle.lifecycleScope
import
androidx.navigation.fragment.findNavController
import
com.example.hfilemanagermaster.R
import
com.example.hfilemanagermaster.databinding.FragmentFilesBinding
import
com.zxhy.hfilemanagermaster.OverviewActivity
import
com.zxhy.hfilemanagermaster.knife.getRecentPhoto
import
com.zxhy.hfilemanagermaster.knife.testPhoto
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.launch
import
java.security.Permission
import
java.security.Permissions
/**
* A simple [Fragment] subclass.
* Use the [FilesFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class
FilesFragment
:
Fragment
()
{
private
lateinit
var
binding
:
FragmentFilesBinding
private
lateinit
var
imageAdapter
:
ImageAdapter
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
arguments
?.
let
{
}
(
activity
as
OverviewActivity
).
setStatusBarColor
(
R
.
color
.
white
)
}
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?
):
View
?
{
val
view
=
inflater
.
inflate
(
R
.
layout
.
fragment_files
,
container
,
false
)
binding
=
FragmentFilesBinding
.
bind
(
view
)
return
view
}
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
imageAdapter
=
ImageAdapter
()
binding
.
ivImage
.
setOnClickListener
{
findNavController
().
navigate
(
R
.
id
.
imageActivity
)
}
binding
.
ivVideo
.
setOnClickListener
{
findNavController
().
navigate
(
R
.
id
.
videoActivity
)
}
lifecycleScope
.
launch
(
Dispatchers
.
IO
)
{
val
list
=
requireContext
().
testPhoto
()
launch
(
Dispatchers
.
Main
)
{
binding
.
rv
.
adapter
=
imageAdapter
imageAdapter
.
setData
(
list
)
}
}
}
companion
object
{
@JvmStatic
fun
newInstance
(
param1
:
String
,
param2
:
String
)
=
FilesFragment
().
apply
{
arguments
=
Bundle
().
apply
{
}
}
}
}
\ No newline at end of file
app/src/main/java/com/zxhy/hfilemanagermaster/files/ImageAdapter.kt
0 → 100644
View file @
66562843
package
com.zxhy.hfilemanagermaster.files
import
android.annotation.SuppressLint
import
android.net.Uri
import
android.view.LayoutInflater
import
android.view.View
import
android.view.ViewGroup
import
androidx.recyclerview.widget.RecyclerView
import
com.example.hfilemanagermaster.R
import
com.example.hfilemanagermaster.databinding.ItemImageBinding
class
ImageAdapter
:
RecyclerView
.
Adapter
<
ImageAdapter
.
ImageViewHolder
>()
{
private
val
imageList
=
arrayListOf
<
Uri
>()
class
ImageViewHolder
(
view
:
View
)
:
RecyclerView
.
ViewHolder
(
view
)
{
val
binding
=
ItemImageBinding
.
bind
(
view
)
}
override
fun
onCreateViewHolder
(
parent
:
ViewGroup
,
viewType
:
Int
):
ImageViewHolder
{
val
root
=
R
.
layout
.
item_image
.
inflate
(
parent
)
return
ImageViewHolder
(
root
)
}
override
fun
getItemCount
():
Int
{
return
imageList
.
size
}
override
fun
onBindViewHolder
(
holder
:
ImageViewHolder
,
position
:
Int
)
{
// val context = holder.binding.root.context
val
data
=
imageList
[
position
]
holder
.
binding
.
iv
.
setImageURI
(
data
)
}
/**
* 解析xml布局
*
* @param parent 父布局
* @param attachToRoot 是否依附到父布局
*/
fun
Int
.
inflate
(
parent
:
ViewGroup
,
attachToRoot
:
Boolean
=
false
):
View
{
return
LayoutInflater
.
from
(
parent
.
context
).
inflate
(
this
,
parent
,
attachToRoot
)
}
@SuppressLint
(
"NotifyDataSetChanged"
)
fun
setData
(
data
:
List
<
Uri
>)
{
imageList
.
clear
()
imageList
.
addAll
(
data
)
notifyDataSetChanged
()
}
}
app/src/main/java/com/zxhy/hfilemanagermaster/glide/GlideAppModule.kt
0 → 100644
View file @
66562843
package
com.zxhy.hfilemanagermaster.glide
import
com.bumptech.glide.annotation.GlideModule
import
com.bumptech.glide.module.AppGlideModule
/**
* 说明:使用Glide的注解,用于生产出GlideApp,该文件需要处于包的根目录
* Created by code_nil on 2017/12/29.
*/
@GlideModule
class
GlideAppModule
:
AppGlideModule
()
{}
\ No newline at end of file
app/src/main/java/com/zxhy/hfilemanagermaster/glide/GlideEx.kt
0 → 100644
View file @
66562843
package
com.zxhy.hfilemanagermaster.glide
import
android.content.Context
import
android.net.Uri
import
android.widget.ImageView
import
com.bumptech.glide.Glide
import
java.net.URI
fun
loadIntoImageView
(
context
:
Context
,
uri
:
String
,
imageView
:
ImageView
)
{
Glide
.
with
(
context
)
.
load
(
uri
)
.
into
(
imageView
)
}
fun
loadIntoImageView
(
context
:
Context
,
uri
:
Uri
,
imageView
:
ImageView
)
{
Glide
.
with
(
context
)
.
load
(
uri
)
.
into
(
imageView
)
}
\ No newline at end of file
app/src/main/java/com/zxhy/hfilemanagermaster/knife/FileUriEx.kt
0 → 100644
View file @
66562843
package
com.zxhy.hfilemanagermaster.knife
import
android.content.ContentResolver
import
android.content.ContentUris
import
android.content.Context
import
android.database.Cursor
import
android.net.Uri
import
android.os.Environment
import
android.provider.DocumentsContract
import
android.provider.MediaStore
/**
* Uri获取File
*/
/**
* 根据Uri获取File路径
*/
fun
getFilePathByUri
(
context
:
Context
,
uri
:
Uri
):
String
?
{
var
path
:
String
?
=
null
// 以 file:// 开头的
if
(
ContentResolver
.
SCHEME_FILE
==
uri
.
scheme
)
{
path
=
uri
.
path
return
path
}
// 以 content:// 开头的,比如 content://media/extenral/images/media/17766
if
(
ContentResolver
.
SCHEME_CONTENT
==
uri
.
scheme
)
{
val
cursor
=
context
.
contentResolver
.
query
(
uri
,
arrayOf
(
MediaStore
.
Images
.
Media
.
DATA
),
null
,
null
,
null
)
if
(
cursor
!=
null
)
{
if
(
cursor
.
moveToFirst
())
{
val
columnIndex
=
cursor
.
getColumnIndexOrThrow
(
MediaStore
.
Images
.
Media
.
DATA
)
if
(
columnIndex
>
-
1
)
{
path
=
cursor
.
getString
(
columnIndex
)
}
}
cursor
.
close
()
}
return
path
}
// 4.4及之后的 是以 content:// 开头的,比如 content://com.android.providers.media.documents/document/image%3A235700
if
(
ContentResolver
.
SCHEME_CONTENT
==
uri
.
scheme
)
{
if
(
DocumentsContract
.
isDocumentUri
(
context
,
uri
))
{
if
(
isExternalStorageDocument
(
uri
))
{
// ExternalStorageProvider
val
docId
=
DocumentsContract
.
getDocumentId
(
uri
)
val
split
=
docId
.
split
(
":"
.
toRegex
()).
dropLastWhile
{
it
.
isEmpty
()
}.
toTypedArray
()
val
type
=
split
[
0
]
if
(
"primary"
.
equals
(
type
,
ignoreCase
=
true
))
{
path
=
Environment
.
getExternalStorageDirectory
().
toString
()
+
"/"
+
split
[
1
]
return
path
}
}
else
if
(
isDownloadsDocument
(
uri
))
{
// DownloadsProvider
val
id
=
DocumentsContract
.
getDocumentId
(
uri
)
val
contentUri
=
ContentUris
.
withAppendedId
(
Uri
.
parse
(
"content://downloads/public_downloads"
),
java
.
lang
.
Long
.
valueOf
(
id
)
)
path
=
getDataColumn
(
context
,
contentUri
,
null
,
null
)
return
path
}
else
if
(
isMediaDocument
(
uri
))
{
// MediaProvider
val
docId
=
DocumentsContract
.
getDocumentId
(
uri
)
val
split
=
docId
.
split
(
":"
.
toRegex
()).
dropLastWhile
{
it
.
isEmpty
()
}.
toTypedArray
()
val
type
=
split
[
0
]
var
contentUri
:
Uri
?
=
null
when
(
type
)
{
"image"
->
{
contentUri
=
MediaStore
.
Images
.
Media
.
EXTERNAL_CONTENT_URI
}
"video"
->
{
contentUri
=
MediaStore
.
Video
.
Media
.
EXTERNAL_CONTENT_URI
}
"audio"
->
{
contentUri
=
MediaStore
.
Audio
.
Media
.
EXTERNAL_CONTENT_URI
}
}
val
selection
=
"_id=?"
val
selectionArgs
=
arrayOf
(
split
[
1
])
path
=
getDataColumn
(
context
,
contentUri
,
selection
,
selectionArgs
)
return
path
}
}
}
return
null
}
/**
* 通过 MediaStore Uri获取值,或者其他基于 ContentProviders 的 Uri
*/
fun
getDataColumn
(
context
:
Context
,
uri
:
Uri
?,
selection
:
String
?,
selectionArgs
:
Array
<
String
>?
):
String
?
{
var
cursor
:
Cursor
?
=
null
val
column
=
"_data"
val
projection
=
arrayOf
(
column
)
try
{
cursor
=
context
.
contentResolver
.
query
(
uri
!!
,
projection
,
selection
,
selectionArgs
,
null
)
if
(
cursor
!=
null
&&
cursor
.
moveToFirst
())
{
val
columnIndex
=
cursor
.
getColumnIndexOrThrow
(
column
)
return
cursor
.
getString
(
columnIndex
)
}
}
catch
(
e
:
Exception
)
{
e
.
printStackTrace
()
}
finally
{
cursor
?.
close
()
}
return
null
}
fun
isExternalStorageDocument
(
uri
:
Uri
):
Boolean
{
return
"com.android.externalstorage.documents"
==
uri
.
authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
fun
isMediaDocument
(
uri
:
Uri
):
Boolean
{
return
"com.android.providers.media.documents"
==
uri
.
authority
}
// Whether the Uri authority is DownloadsProvider.
fun
isDownloadsDocument
(
uri
:
Uri
):
Boolean
{
return
"com.android.providers.downloads.documents"
==
uri
.
authority
}
app/src/main/java/com/zxhy/hfilemanagermaster/knife/RecentMediaStore.kt
0 → 100644
View file @
66562843
package
com.zxhy.hfilemanagermaster.knife
import
android.annotation.SuppressLint
import
android.content.ContentResolver
import
android.content.ContentUris
import
android.content.Context
import
android.database.Cursor
import
android.media.MediaMetadataRetriever
import
android.net.Uri
import
android.provider.MediaStore
import
android.text.format.Formatter
import
com.zxhy.hfilemanagermaster.data.MediaDataC
import
com.zxhy.hfilemanagermaster.data.MediaDataC_TYPE_VIDEO
import
java.io.File
import
java.text.SimpleDateFormat
import
java.util.Date
import
java.util.Locale
/**
* MediaStore访问最新媒体
*/
fun
Context
.
testPhoto
():
ArrayList
<
Uri
>
{
val
list
=
arrayListOf
<
Uri
>()
var
cursor
:
Cursor
?
=
null
// 查询照片的Uri和字段
val
uri
=
MediaStore
.
Images
.
Media
.
EXTERNAL_CONTENT_URI
val
projection
=
arrayOf
(
MediaStore
.
Images
.
Media
.
_ID
,
MediaStore
.
Images
.
Media
.
DISPLAY_NAME
)
try
{
// 执行查询
val
contentResolver
:
ContentResolver
=
contentResolver
cursor
=
contentResolver
.
query
(
uri
,
projection
,
null
,
null
,
null
)
// 遍历结果
if
(
cursor
!=
null
&&
cursor
.
moveToFirst
())
{
do
{
// 获取照片的ID和名称
val
id
=
cursor
.
getLong
(
cursor
.
getColumnIndexOrThrow
(
MediaStore
.
Images
.
Media
.
_ID
))
val
name
=
cursor
.
getString
(
cursor
.
getColumnIndexOrThrow
(
MediaStore
.
Images
.
Media
.
DISPLAY_NAME
))
// 根据ID构建照片的Uri
val
photoUri
=
ContentUris
.
withAppendedId
(
uri
,
id
)
// println(photoUri)
list
.
add
(
photoUri
)
// 在此处进行照片的操作,例如显示、复制、删除等
// ...
if
(
list
.
size
==
10
)
{
break
}
}
while
(
cursor
.
moveToNext
())
}
else
{
println
(
"无数据"
)
}
}
catch
(
e
:
Exception
)
{
e
.
printStackTrace
()
}
finally
{
cursor
?.
close
()
}
return
list
}
@SuppressLint
(
"SimpleDateFormat"
)
fun
Context
.
largeVideo
():
ArrayList
<
MediaDataC
>
{
val
list
=
arrayListOf
<
MediaDataC
>()
var
cursor
:
Cursor
?
=
null
// 查询视频的Uri和字段
val
uri
=
MediaStore
.
Video
.
Media
.
EXTERNAL_CONTENT_URI
val
projection
=
arrayOf
(
MediaStore
.
Video
.
Media
.
_ID
,
MediaStore
.
Video
.
Media
.
DISPLAY_NAME
,
MediaStore
.
Video
.
Media
.
SIZE
,
)
try
{
// 执行查询
val
contentResolver
:
ContentResolver
=
contentResolver
cursor
=
contentResolver
.
query
(
uri
,
projection
,
null
,
null
,
null
)
// 遍历结果
if
(
cursor
!=
null
&&
cursor
.
moveToFirst
())
{
do
{
// 获取视频的ID
val
id
=
cursor
.
getLong
(
cursor
.
getColumnIndexOrThrow
(
MediaStore
.
Video
.
Media
.
_ID
))
//名称
val
name
=
cursor
.
getString
(
cursor
.
getColumnIndexOrThrow
(
MediaStore
.
Video
.
Media
.
DISPLAY_NAME
))
//大小
val
size
=
cursor
.
getLong
(
cursor
.
getColumnIndexOrThrow
(
MediaStore
.
Video
.
Media
.
SIZE
))
val
sizeS
=
Formatter
.
formatFileSize
(
this
,
size
)
// 根据ID构建视频的Uri
val
videoUri
=
ContentUris
.
withAppendedId
(
uri
,
id
)
//时间
val
filePath
=
getFilePathByUri
(
this
,
videoUri
)
?:
""
val
time
=
File
(
filePath
).
lastModified
()
// val timeS =
// SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time)
val
timeE
=
SimpleDateFormat
(
"MMM dd, yyyy"
,
Locale
.
ENGLISH
).
format
(
time
)
list
.
add
(
MediaDataC
(
name
=
name
,
size
=
sizeS
,
time
=
timeE
,
MediaDataC_TYPE_VIDEO
,
uri
=
videoUri
,
path
=
filePath
)
)
// 在此处进行视频的操作,例如显示、复制、删除等
// ...
if
(
list
.
size
==
10
)
{
break
}
}
while
(
cursor
.
moveToNext
())
}
else
{
println
(
"无数据"
)
}
}
catch
(
e
:
Exception
)
{
e
.
printStackTrace
()
}
finally
{
cursor
?.
close
()
}
return
list
}
//无效
fun
getMediaCreateTime
(
videoPath
:
String
)
{
val
retriever
=
MediaMetadataRetriever
()
try
{
retriever
.
setDataSource
(
videoPath
)
var
creationTime
=
retriever
.
extractMetadata
(
MediaMetadataRetriever
.
METADATA_KEY_DATE
)
?:
"0"
// 如果需要将UNIX时间戳转换为可读的日期格式
val
date
=
Date
(
creationTime
.
toLong
()
*
1000
)
val
sdf
=
SimpleDateFormat
(
"yyyy-MM-dd HH:mm:ss"
,
Locale
.
ENGLISH
)
creationTime
=
sdf
.
format
(
date
)
// 输出或使用创建时间
println
(
creationTime
)
}
catch
(
e
:
Exception
)
{
e
.
printStackTrace
()
}
finally
{
retriever
.
release
()
}
}
\ No newline at end of file
app/src/main/java/com/zxhy/hfilemanagermaster/knife/RecentPicture.kt
0 → 100644
View file @
66562843
package
com.zxhy.hfilemanagermaster.knife
import
android.annotation.SuppressLint
import
android.app.Application
import
android.database.Cursor
import
android.provider.MediaStore
import
java.lang.Exception
/**
* 最近图片
* 无效
*/
@SuppressLint
(
"Recycle"
)
fun
Application
.
getRecentPhoto
()
{
val
list
=
arrayListOf
<
String
>()
var
cursor
:
Cursor
?
=
null
val
currentTime
=
System
.
currentTimeMillis
()
/
1000
-
60
try
{
cursor
=
this
.
applicationContext
.
contentResolver
.
query
(
MediaStore
.
Images
.
Media
.
EXTERNAL_CONTENT_URI
,
arrayOf
(
MediaStore
.
Images
.
Media
.
_ID
,
MediaStore
.
Images
.
Media
.
DATA
),
MediaStore
.
Images
.
Media
.
DATE_ADDED
+
" >= ?"
,
arrayOf
(
"$currentTime"
),
MediaStore
.
Images
.
Media
.
DATE_ADDED
+
" DESC"
)
if
((
cursor
?.
count
?:
0
)
<
1
)
{
cursor
?.
close
()
}
if
(
cursor
?.
moveToFirst
()
==
true
)
{
val
path
=
cursor
.
getString
(
cursor
.
getColumnIndexOrThrow
(
MediaStore
.
Images
.
Media
.
DATA
))
list
.
add
(
path
)
}
}
catch
(
e
:
Exception
)
{
e
.
printStackTrace
()
}
finally
{
cursor
?.
close
()
}
}
app/src/main/java/com/zxhy/hfilemanagermaster/largefile/LargeFileActivity.kt
View file @
66562843
...
@@ -6,12 +6,17 @@ import androidx.appcompat.app.AppCompatActivity
...
@@ -6,12 +6,17 @@ import androidx.appcompat.app.AppCompatActivity
import
androidx.core.content.ContextCompat
import
androidx.core.content.ContextCompat
import
androidx.core.view.ViewCompat
import
androidx.core.view.ViewCompat
import
androidx.core.view.WindowInsetsCompat
import
androidx.core.view.WindowInsetsCompat
import
androidx.
navigation.findNavController
import
androidx.
lifecycle.lifecycleScope
import
com.example.hfilemanagermaster.R
import
com.example.hfilemanagermaster.R
import
com.example.hfilemanagermaster.databinding.ActivityLargeFileBinding
import
com.example.hfilemanagermaster.databinding.ActivityLargeFileBinding
import
com.zxhy.hfilemanagermaster.knife.largeVideo
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.launch
class
LargeFileActivity
:
AppCompatActivity
()
{
class
LargeFileActivity
:
AppCompatActivity
()
{
private
lateinit
var
binding
:
ActivityLargeFileBinding
private
lateinit
var
binding
:
ActivityLargeFileBinding
private
lateinit
var
mediaSelectAdapter
:
MediaSelectAdapter
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
enableEdgeToEdge
()
enableEdgeToEdge
()
...
@@ -26,5 +31,25 @@ class LargeFileActivity : AppCompatActivity() {
...
@@ -26,5 +31,25 @@ class LargeFileActivity : AppCompatActivity() {
binding
.
ivArrow
.
setOnClickListener
{
binding
.
ivArrow
.
setOnClickListener
{
finish
()
finish
()
}
}
mediaSelectAdapter
=
MediaSelectAdapter
{
binding
.
ivAll
.
isSelected
=
it
}
binding
.
rv
.
adapter
=
mediaSelectAdapter
binding
.
tvDelete
.
setOnClickListener
{
}
binding
.
ivAll
.
setOnClickListener
{
view
->
view
.
isSelected
=
mediaSelectAdapter
.
setToggleSelect
()
}
loadData
()
}
private
fun
loadData
()
{
lifecycleScope
.
launch
(
Dispatchers
.
IO
)
{
val
list
=
this
@LargeFileActivity
.
largeVideo
()
lifecycleScope
.
launch
(
Dispatchers
.
Main
)
{
mediaSelectAdapter
.
setData
(
list
)
}
}
}
}
}
}
\ No newline at end of file
app/src/main/java/com/zxhy/hfilemanagermaster/largefile/LargeFileFragment.kt
View file @
66562843
...
@@ -27,22 +27,13 @@ class LargeFileFragment : Fragment() {
...
@@ -27,22 +27,13 @@ class LargeFileFragment : Fragment() {
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?
savedInstanceState
:
Bundle
?
):
View
?
{
):
View
?
{
// Inflate the layout for this fragment
val
view
=
inflater
.
inflate
(
R
.
layout
.
fragment_large_file
,
container
,
false
)
val
view
=
inflater
.
inflate
(
R
.
layout
.
fragment_large_file
,
container
,
false
)
binding
=
FragmentLargeFileBinding
.
bind
(
view
)
binding
=
FragmentLargeFileBinding
.
bind
(
view
)
return
view
return
view
}
}
companion
object
{
companion
object
{
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment LargeFileFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
@JvmStatic
fun
newInstance
(
param1
:
String
,
param2
:
String
)
=
fun
newInstance
(
param1
:
String
,
param2
:
String
)
=
LargeFileFragment
().
apply
{
LargeFileFragment
().
apply
{
...
...
app/src/main/java/com/zxhy/hfilemanagermaster/largefile/MediaSelectAdapter.kt
0 → 100644
View file @
66562843
package
com.zxhy.hfilemanagermaster.largefile
import
android.annotation.SuppressLint
import
android.view.LayoutInflater
import
android.view.View
import
android.view.ViewGroup
import
androidx.recyclerview.widget.RecyclerView
import
com.example.hfilemanagermaster.R
import
com.example.hfilemanagermaster.databinding.ItemMediaSelectBinding
import
com.zxhy.hfilemanagermaster.data.MediaDataC
import
com.zxhy.hfilemanagermaster.glide.loadIntoImageView
class
MediaSelectAdapter
(
val
selectAction
:
((
flag
:
Boolean
)
->
Unit
)?
=
null
)
:
RecyclerView
.
Adapter
<
MediaSelectAdapter
.
MediaSelectViewHolder
>()
{
private
val
mediaList
=
arrayListOf
<
MediaDataC
>()
class
MediaSelectViewHolder
(
view
:
View
)
:
RecyclerView
.
ViewHolder
(
view
)
{
val
binding
=
ItemMediaSelectBinding
.
bind
(
view
)
}
override
fun
onCreateViewHolder
(
parent
:
ViewGroup
,
viewType
:
Int
):
MediaSelectViewHolder
{
val
root
=
R
.
layout
.
item_media_select
.
inflate
(
parent
)
return
MediaSelectViewHolder
(
root
)
}
override
fun
getItemCount
():
Int
{
return
mediaList
.
size
}
override
fun
onBindViewHolder
(
holder
:
MediaSelectViewHolder
,
position
:
Int
)
{
val
context
=
holder
.
binding
.
root
.
context
val
data
=
mediaList
[
position
]
holder
.
binding
.
apply
{
loadIntoImageView
(
context
,
data
.
uri
,
iv
)
tvName
.
text
=
data
.
name
tvSize
.
text
=
data
.
size
ivSelector
.
isSelected
=
data
.
select
flSelector
.
setOnClickListener
{
data
.
select
=
!
data
.
select
notifyItemChanged
(
position
,
"单条刷新"
)
}
}
if
(
data
==
mediaList
.
last
())
{
selectAction
?.
invoke
(
mediaList
.
all
{
it
.
select
})
}
}
override
fun
onBindViewHolder
(
holder
:
MediaSelectViewHolder
,
position
:
Int
,
payloads
:
MutableList
<
Any
>
)
{
val
context
=
holder
.
binding
.
root
.
context
val
data
=
mediaList
[
position
]
//判断是做局部刷新还是单条刷新
if
(
payloads
.
isEmpty
())
{
holder
.
binding
.
apply
{
loadIntoImageView
(
context
,
data
.
uri
,
iv
)
tvName
.
text
=
data
.
name
tvSize
.
text
=
data
.
size
ivSelector
.
isSelected
=
data
.
select
flSelector
.
setOnClickListener
{
data
.
select
=
!
data
.
select
notifyItemChanged
(
position
,
"单条刷新"
)
}
if
(
data
==
mediaList
.
last
())
{
selectAction
?.
invoke
(
mediaList
.
all
{
it
.
select
})
}
}
}
else
{
// 单条刷新
holder
.
binding
.
apply
{
ivSelector
.
isSelected
=
data
.
select
}
super
.
onBindViewHolder
(
holder
,
position
,
payloads
)
selectAction
?.
invoke
(
mediaList
.
all
{
it
.
select
})
}
}
@SuppressLint
(
"NotifyDataSetChanged"
)
fun
setData
(
data
:
List
<
MediaDataC
>)
{
mediaList
.
clear
()
mediaList
.
addAll
(
data
)
notifyDataSetChanged
()
}
/**
* 解析xml布局
*
* @param parent 父布局
* @param attachToRoot 是否依附到父布局
*/
fun
Int
.
inflate
(
parent
:
ViewGroup
,
attachToRoot
:
Boolean
=
false
):
View
{
return
LayoutInflater
.
from
(
parent
.
context
).
inflate
(
this
,
parent
,
attachToRoot
)
}
@SuppressLint
(
"NotifyDataSetChanged"
)
fun
setToggleSelect
():
Boolean
{
val
flag
=
mediaList
.
all
{
it
.
select
}
mediaList
.
forEach
{
it
.
select
=
!
flag
}
notifyDataSetChanged
()
return
!
flag
}
}
\ No newline at end of file
app/src/main/java/com/zxhy/hfilemanagermaster/manager/ManagerFragment.kt
View file @
66562843
package
com.zxhy.hfilemanagermaster.manager
package
com.zxhy.hfilemanagermaster.manager
import
android.Manifest
import
android.annotation.SuppressLint
import
android.annotation.SuppressLint
import
android.os.Build
import
android.os.Bundle
import
android.os.Bundle
import
android.view.LayoutInflater
import
android.view.LayoutInflater
import
android.view.View
import
android.view.View
...
@@ -11,6 +13,9 @@ import com.example.hfilemanagermaster.R
...
@@ -11,6 +13,9 @@ import com.example.hfilemanagermaster.R
import
com.example.hfilemanagermaster.databinding.FragmentManagerBinding
import
com.example.hfilemanagermaster.databinding.FragmentManagerBinding
import
com.zxhy.hfilemanagermaster.OverviewActivity
import
com.zxhy.hfilemanagermaster.OverviewActivity
import
com.zxhy.hfilemanagermaster.knife.getMountInfoList
import
com.zxhy.hfilemanagermaster.knife.getMountInfoList
import
com.zxhy.hfilemanagermaster.permission.IntentLauncher
import
com.zxhy.hfilemanagermaster.permission.PermissionLauncher
import
com.zxhy.hfilemanagermaster.permission.requestPermission
import
java.math.BigDecimal
import
java.math.BigDecimal
/**
/**
...
@@ -23,8 +28,7 @@ class ManagerFragment : Fragment() {
...
@@ -23,8 +28,7 @@ class ManagerFragment : Fragment() {
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
arguments
?.
let
{
arguments
?.
let
{}
}
}
}
override
fun
onCreateView
(
override
fun
onCreateView
(
...
@@ -45,32 +49,52 @@ class ManagerFragment : Fragment() {
...
@@ -45,32 +49,52 @@ class ManagerFragment : Fragment() {
val
percentF
=
BigDecimal
(
disk
.
used
)
val
percentF
=
BigDecimal
(
disk
.
used
)
.
divide
(
BigDecimal
(
disk
.
total
),
2
,
BigDecimal
.
ROUND_HALF_UP
)
.
divide
(
BigDecimal
(
disk
.
total
),
2
,
BigDecimal
.
ROUND_HALF_UP
)
// binding.indicatorView.setPercent(percentF.toFloat())
binding
.
indicatorView
.
setPercent
(
percentF
.
toFloat
())
binding
.
indicatorView
.
setPercent
(
0.3f
)
val
percent
=
percentF
.
multiply
(
BigDecimal
(
100
)).
toInt
()
val
percent
=
percentF
.
multiply
(
BigDecimal
(
100
)).
toInt
()
binding
.
tvUse
.
text
=
"$percent%"
binding
.
tvUse
.
text
=
"$percent%"
(
requireActivity
()
as
OverviewActivity
).
showManager
()
(
requireActivity
()
as
OverviewActivity
).
showManager
()
binding
.
cardView1
.
setOnClickListener
{
}
binding
.
cardView2
.
setOnClickListener
{
}
binding
.
cardView3
.
setOnClickListener
{
}
binding
.
ivFile
.
setOnClickListener
{
binding
.
ivFile
.
setOnClickListener
{
findNavController
().
navigate
(
R
.
id
.
filesFragment
)
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
TIRAMISU
)
{
val
flag
=
requestPermission
(
requireContext
(),
(
requireActivity
()
as
OverviewActivity
).
permissionLauncher
,
(
requireActivity
()
as
OverviewActivity
).
intentLauncher
,
arrayOf
(
Manifest
.
permission
.
READ_MEDIA_IMAGES
),
"设置允许访问媒体图片权限"
)
{
findNavController
().
navigate
(
R
.
id
.
filesFragment
)
}
if
(
flag
)
findNavController
().
navigate
(
R
.
id
.
filesFragment
)
}
}
}
binding
.
ivWord
.
setOnClickListener
{
}
binding
.
ivWord
.
setOnClickListener
{
}
binding
.
ivExcel
.
setOnClickListener
{
}
binding
.
ivExcel
.
setOnClickListener
{
}
binding
.
ivPdf
.
setOnClickListener
{
}
binding
.
ivPdf
.
setOnClickListener
{
}
binding
.
ivPpt
.
setOnClickListener
{
}
binding
.
ivPpt
.
setOnClickListener
{
}
binding
.
cardView1
.
setOnClickListener
{
binding
.
cardView1
.
setOnClickListener
{
findNavController
().
navigate
(
R
.
id
.
dupPictureActivity
)
//
findNavController().navigate(R.id.dupPictureActivity)
}
}
binding
.
cardView2
.
setOnClickListener
{
binding
.
cardView2
.
setOnClickListener
{
findNavController
().
navigate
(
R
.
id
.
largeFileActivity
)
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
TIRAMISU
)
{
val
flag
=
requestPermission
(
requireContext
(),
(
requireActivity
()
as
OverviewActivity
).
permissionLauncher
,
(
requireActivity
()
as
OverviewActivity
).
intentLauncher
,
arrayOf
(
Manifest
.
permission
.
READ_MEDIA_IMAGES
,
Manifest
.
permission
.
READ_MEDIA_VIDEO
),
"设置允许访问媒体图片视频权限"
)
{
findNavController
().
navigate
(
R
.
id
.
largeFileActivity
)
}
if
(
flag
)
findNavController
().
navigate
(
R
.
id
.
largeFileActivity
)
}
}
}
binding
.
cardView3
.
setOnClickListener
{
binding
.
cardView3
.
setOnClickListener
{
findNavController
().
navigate
(
R
.
id
.
emptyFileActivity
)
//
findNavController().navigate(R.id.emptyFileActivity)
}
}
}
}
...
@@ -83,4 +107,5 @@ class ManagerFragment : Fragment() {
...
@@ -83,4 +107,5 @@ class ManagerFragment : Fragment() {
}
}
}
}
}
}
}
}
\ No newline at end of file
app/src/main/java/com/zxhy/hfilemanagermaster/permission/Dialogs.kt
0 → 100644
View file @
66562843
@file
:
Suppress
(
"unused"
)
package
com.zxhy.hfilemanagermaster.permission
import
android.app.Dialog
import
android.content.Context
import
android.content.DialogInterface
import
android.view.KeyEvent
import
androidx.annotation.StringRes
import
androidx.appcompat.app.AlertDialog
import
androidx.fragment.app.Fragment
import
com.google.android.material.dialog.MaterialAlertDialogBuilder
//typealias用于给类取别名 将AlertBuilderFactory<D>重新定义为一个匿名函数,返回它的父类
typealias
AlertBuilderFactory
<
D
>
=
(
Context
)
->
AlertBuilder
<
D
>
val
Material
:
AlertBuilderFactory
<
DialogInterface
>
=
{
context
->
object
:
AlertDialogBuilder
()
{
override
val
builder
:
AlertDialog
.
Builder
=
MaterialAlertDialogBuilder
(
context
)
}
}
//默认的构造工厂
private
var
defaultAlertBuilderFactory
:
AlertBuilderFactory
<*>
=
Material
//region 简易的消息弹窗
//基于上下文扩展的 简易的消息弹窗
fun
Context
.
alert
(
message
:
CharSequence
,
title
:
CharSequence
?
=
null
,
block
:
(
AlertBuilder
<*>.()
->
Unit
)?
=
null
)
{
alert
(
defaultAlertBuilderFactory
,
message
,
title
,
block
)
}
//基于Fragment扩展的建议弹窗
fun
Fragment
.
alert
(
message
:
CharSequence
,
title
:
CharSequence
?
=
null
,
block
:
(
AlertBuilder
<*>.()
->
Unit
)?
=
null
)
{
requireContext
().
alert
(
message
,
title
,
block
)
}
private
inline
fun
<
D
:
DialogInterface
>
Context
.
alert
(
factory
:
AlertBuilderFactory
<
D
>,
message
:
CharSequence
,
title
:
CharSequence
?,
noinline
block
:
(
AlertBuilder
<
D
>.()
->
Unit
)?
=
null
)
{
alertDialog
(
factory
)
{
title
?.
let
{
this
.
title
=
it
}
this
.
message
=
message
block
?.
invoke
(
this
)
}.
show
()
}
//endregion
//region 单项选择弹窗
fun
Context
.
selector
(
items
:
List
<
CharSequence
>,
title
:
CharSequence
?,
onItemSelected
:
(
dialog
:
DialogInterface
,
index
:
Int
)
->
Unit
)
{
alertDialog
(
defaultAlertBuilderFactory
)
{
title
?.
let
{
this
.
title
=
it
}
items
(
items
,
onItemSelected
)
}.
show
()
}
fun
Fragment
.
selector
(
items
:
List
<
CharSequence
>,
title
:
CharSequence
?,
onItemSelected
:
(
dialog
:
DialogInterface
,
index
:
Int
)
->
Unit
)
{
requireContext
().
selector
(
items
,
title
,
onItemSelected
)
}
private
inline
fun
<
D
:
DialogInterface
>
Context
.
selector
(
factory
:
AlertBuilderFactory
<
D
>,
items
:
List
<
CharSequence
>,
title
:
CharSequence
?,
noinline
onItemSelected
:
(
dialog
:
DialogInterface
,
index
:
Int
)
->
Unit
)
{
alertDialog
(
factory
)
{
title
?.
let
{
this
.
title
=
it
}
items
(
items
,
onItemSelected
)
}.
show
()
}
//endregion
//region 单项选择
fun
Context
.
singleChoice
(
items
:
List
<
CharSequence
>,
title
:
CharSequence
?
=
null
,
defaultItem
:
Int
=
-
1
,
//默认没有选中项目
onItemSelected
:
(
DialogInterface
,
Int
)
->
Unit
)
{
singleChoice
(
defaultAlertBuilderFactory
,
items
,
title
,
defaultItem
,
onItemSelected
)
}
fun
Fragment
.
singleChoice
(
items
:
List
<
CharSequence
>,
title
:
CharSequence
?
=
null
,
defaultItem
:
Int
=
-
1
,
//默认没有选中项目
onItemSelected
:
(
DialogInterface
,
Int
)
->
Unit
)
{
requireContext
().
singleChoice
(
items
,
title
,
defaultItem
,
onItemSelected
)
}
private
fun
Context
.
singleChoice
(
factory
:
AlertBuilderFactory
<
DialogInterface
>,
items
:
List
<
CharSequence
>,
title
:
CharSequence
?,
defaultItem
:
Int
=
-
1
,
//默认没有选中项目
onItemSelected
:
(
DialogInterface
,
Int
)
->
Unit
)
{
alertDialog
(
factory
)
{
title
?.
let
{
this
.
title
=
title
}
choiceItems
(
items
,
defaultItem
,
onItemSelected
)
}.
show
()
}
//endregion
//region 多项选择
fun
Context
.
multiChoice
(
items
:
List
<
CharSequence
>,
defaultItems
:
BooleanArray
,
//必须设备每一个项目的选中情况
title
:
CharSequence
?
=
null
,
block
:
(
AlertBuilder
<
DialogInterface
>.()
->
Unit
)?
=
null
,
//用于添加按钮
onItemSelected
:
(
dialog
:
DialogInterface
,
index
:
Int
,
isChecked
:
Boolean
)
->
Unit
)
{
multiChoice
(
defaultAlertBuilderFactory
,
items
,
defaultItems
,
title
,
block
,
onItemSelected
)
}
fun
Fragment
.
multiChoice
(
items
:
List
<
CharSequence
>,
defaultItems
:
BooleanArray
,
//必须设备每一个项目的选中情况
title
:
CharSequence
?
=
null
,
block
:
(
AlertBuilder
<
DialogInterface
>.()
->
Unit
)?
=
null
,
//用于添加按钮
onItemSelected
:
(
dialog
:
DialogInterface
,
index
:
Int
,
isChecked
:
Boolean
)
->
Unit
)
{
requireContext
().
multiChoice
(
items
,
defaultItems
,
title
,
block
,
onItemSelected
)
}
private
inline
fun
<
D
:
DialogInterface
>
Context
.
multiChoice
(
factory
:
AlertBuilderFactory
<
D
>,
items
:
List
<
CharSequence
>,
//必须设备每一个项目的选中情况
defaultItems
:
BooleanArray
,
title
:
CharSequence
?,
noinline
block
:
(
AlertBuilder
<
D
>.()
->
Unit
)?
=
null
,
//用于添加按钮
noinline
onItemSelected
:
(
dialog
:
DialogInterface
,
index
:
Int
,
isChecked
:
Boolean
)
->
Unit
)
{
alertDialog
(
factory
)
{
title
?.
let
{
this
.
title
=
it
}
multiChoiceItems
(
items
,
defaultItems
)
{
dialog
,
index
,
isChecked
->
onItemSelected
(
dialog
,
index
,
isChecked
)
}
//提供外部处理
block
?.
invoke
(
this
)
}.
show
()
}
//endregion
//region 事件响应
//监听取消
fun
DialogInterface
.
doOnCancel
(
block
:
(
DialogInterface
)
->
Unit
)
{
check
(
this
is
Dialog
)
setOnCancelListener
(
block
)
}
fun
Dialog
.
doOnCancel
(
block
:
(
DialogInterface
)
->
Unit
)
{
setOnCancelListener
(
block
)
}
//监听展示
fun
DialogInterface
.
doOnShow
(
block
:
(
DialogInterface
)
->
Unit
)
{
check
(
this
is
Dialog
)
setOnShowListener
(
block
)
}
fun
Dialog
.
doOnShow
(
block
:
(
DialogInterface
)
->
Unit
)
{
setOnShowListener
(
block
)
}
//监听驳回dismiss
fun
DialogInterface
.
doOnDismiss
(
block
:
(
DialogInterface
)
->
Unit
)
{
check
(
this
is
Dialog
)
setOnDismissListener
(
block
)
}
fun
Dialog
.
doOnDismiss
(
block
:
(
DialogInterface
)
->
Unit
)
{
setOnDismissListener
(
block
)
}
//监听返回按钮 一般用于返回键的拦截KeyEvent.KEYCODE_BACK
fun
DialogInterface
.
doOnKeyPressed
(
handler
:
(
DialogInterface
,
keyCode
:
Int
,
e
:
KeyEvent
)
->
Boolean
)
{
check
(
this
is
Dialog
)
setOnKeyListener
(
handler
)
}
fun
Dialog
.
keyListener
(
handler
:
(
DialogInterface
,
keyCode
:
Int
,
e
:
KeyEvent
)
->
Boolean
)
{
setOnKeyListener
(
handler
)
}
//endregion
//region 添加按钮
//确认按钮
fun
AlertBuilder
<*>.
okButton
(
onClicked
:
(
dialog
:
DialogInterface
)
->
Unit
)
=
positiveButton
(
android
.
R
.
string
.
ok
,
onClicked
)
//取消按钮
fun
AlertBuilder
<*>.
cancelButton
(
onClicked
:
(
dialog
:
DialogInterface
)
->
Unit
=
{
it
.
dismiss
()
})
=
negativeButton
(
android
.
R
.
string
.
cancel
,
onClicked
)
//中性按钮
fun
AlertBuilder
<*>.
neutralButton
(
buttonText
:
String
,
onClicked
:
(
dialog
:
DialogInterface
)
->
Unit
=
{
it
.
dismiss
()
}
)
=
neutralPressed
(
buttonText
,
onClicked
)
//endregion
//统一的弹窗处理
inline
fun
<
D
:
DialogInterface
>
Context
.
alertDialog
(
factory
:
AlertBuilderFactory
<
D
>,
block
:
AlertBuilder
<
D
>.()
->
Unit
)
=
factory
(
this
).
apply
(
block
)
//统一的接口,用于管理属性
interface
AlertBuilder
<
out
D
:
DialogInterface
>
{
//上下文
val
context
:
Context
//标题
var
title
:
CharSequence
//标题
var
message
:
CharSequence
//确认按钮
fun
positiveButton
(
buttonText
:
String
,
onClicked
:
(
dialog
:
DialogInterface
)
->
Unit
)
fun
positiveButton
(
@StringRes
buttonTextResource
:
Int
,
onClicked
:
(
dialog
:
DialogInterface
)
->
Unit
)
//取消按钮
fun
negativeButton
(
buttonText
:
String
,
onClicked
:
(
dialog
:
DialogInterface
)
->
Unit
)
fun
negativeButton
(
@StringRes
buttonTextResource
:
Int
,
onClicked
:
(
dialog
:
DialogInterface
)
->
Unit
)
//中性按压事件
fun
neutralPressed
(
buttonText
:
String
,
onClicked
:
(
dialog
:
DialogInterface
)
->
Unit
)
fun
neutralPressed
(
@StringRes
buttonTextResource
:
Int
,
onClicked
:
(
dialog
:
DialogInterface
)
->
Unit
)
//选择列表
fun
items
(
items
:
List
<
CharSequence
>,
onItemSelected
:
(
dialog
:
DialogInterface
,
index
:
Int
)
->
Unit
)
//单项确认
fun
choiceItems
(
items
:
List
<
CharSequence
>,
checkedIndex
:
Int
,
onItemSelected
:
(
DialogInterface
,
Int
)
->
Unit
)
//多项确认
fun
multiChoiceItems
(
items
:
List
<
CharSequence
>,
checkedItems
:
BooleanArray
,
onItemSelected
:
(
DialogInterface
,
Int
,
Boolean
)
->
Unit
)
//驳回
fun
onDismissed
(
handler
:
(
DialogInterface
)
->
Unit
)
//取消
fun
onCancelled
(
handler
:
(
DialogInterface
)
->
Unit
)
//按键处理 常用于返回键的拦截
fun
onKeyPressed
(
handler
:
(
DialogInterface
,
keyCode
:
Int
,
e
:
KeyEvent
)
->
Boolean
)
//是否可以取消
fun
isCancel
(
cancel
:
Boolean
)
//创建
fun
build
():
D
//展示
fun
show
():
D
}
//Alert的构造器
abstract
class
AlertDialogBuilder
:
AlertBuilder
<
AlertDialog
>
{
//提供用于创建实例
abstract
val
builder
:
AlertDialog
.
Builder
//上下文
override
val
context
:
Context
get
()
=
builder
.
context
//标题
override
var
title
:
CharSequence
get
()
=
throw
NotImplementedError
(
"Property does not have a getter"
)
set
(
value
)
{
builder
.
setTitle
(
value
)
}
//消息
override
var
message
:
CharSequence
get
()
=
throw
NotImplementedError
(
"Property does not have a getter"
)
set
(
value
)
{
builder
.
setMessage
(
value
)
}
//消极的按钮
override
fun
negativeButton
(
buttonText
:
String
,
onClicked
:
(
DialogInterface
)
->
Unit
)
{
builder
.
setNegativeButton
(
buttonText
)
{
dialog
,
_
->
onClicked
(
dialog
)
}
}
//消极按钮
override
fun
negativeButton
(
buttonTextResource
:
Int
,
onClicked
:
(
DialogInterface
)
->
Unit
)
{
builder
.
setNegativeButton
(
buttonTextResource
)
{
dialog
,
_
->
onClicked
(
dialog
)
}
}
//中性按钮
override
fun
neutralPressed
(
buttonText
:
String
,
onClicked
:
(
DialogInterface
)
->
Unit
)
{
builder
.
setNeutralButton
(
buttonText
)
{
dialog
,
_
->
onClicked
(
dialog
)
}
}
//中性按钮
override
fun
neutralPressed
(
buttonTextResource
:
Int
,
onClicked
:
(
DialogInterface
)
->
Unit
)
{
builder
.
setNeutralButton
(
buttonTextResource
)
{
dialog
,
_
->
onClicked
(
dialog
)
}
}
//积极按钮
override
fun
positiveButton
(
buttonText
:
String
,
onClicked
:
(
DialogInterface
)
->
Unit
)
{
builder
.
setPositiveButton
(
buttonText
)
{
dialog
,
_
->
onClicked
(
dialog
)
}
}
override
fun
positiveButton
(
buttonTextResource
:
Int
,
onClicked
:
(
DialogInterface
)
->
Unit
)
{
builder
.
setPositiveButton
(
buttonTextResource
)
{
dialog
,
_
->
onClicked
(
dialog
)
}
}
override
fun
items
(
items
:
List
<
CharSequence
>,
onItemSelected
:
(
dialog
:
DialogInterface
,
index
:
Int
)
->
Unit
)
{
builder
.
setItems
(
items
.
toTypedArray
(),
onItemSelected
)
}
override
fun
choiceItems
(
items
:
List
<
CharSequence
>,
checkedIndex
:
Int
,
onItemSelected
:
(
DialogInterface
,
Int
)
->
Unit
)
{
builder
.
setSingleChoiceItems
(
items
.
toTypedArray
(),
checkedIndex
,
onItemSelected
)
}
override
fun
multiChoiceItems
(
items
:
List
<
CharSequence
>,
checkedItems
:
BooleanArray
,
onItemSelected
:
(
DialogInterface
,
Int
,
Boolean
)
->
Unit
)
{
builder
.
setMultiChoiceItems
(
items
.
toTypedArray
(),
checkedItems
,
onItemSelected
)
}
override
fun
onDismissed
(
handler
:
(
DialogInterface
)
->
Unit
)
{
builder
.
setOnDismissListener
(
handler
)
}
override
fun
onCancelled
(
handler
:
(
DialogInterface
)
->
Unit
)
{
builder
.
setOnCancelListener
(
handler
)
}
override
fun
onKeyPressed
(
handler
:
(
DialogInterface
,
keyCode
:
Int
,
e
:
KeyEvent
)
->
Boolean
)
{
builder
.
setOnKeyListener
(
handler
)
}
override
fun
isCancel
(
cancel
:
Boolean
)
{
builder
.
setCancelable
(
false
)
}
//构建
override
fun
build
():
AlertDialog
{
return
builder
.
create
()
}
//展示
override
fun
show
():
AlertDialog
{
return
builder
.
show
()
}
}
\ No newline at end of file
app/src/main/java/com/zxhy/hfilemanagermaster/permission/IntentLauncher.kt
0 → 100644
View file @
66562843
package
com.zxhy.hfilemanagermaster.permission
import
android.content.Intent
import
androidx.activity.result.ActivityResult
import
androidx.activity.result.ActivityResultCallback
import
androidx.activity.result.ActivityResultCaller
import
androidx.activity.result.contract.ActivityResultContracts
/**
* 对应Intent的处理
*/
class
IntentLauncher
(
activityResultCaller
:
ActivityResultCaller
)
{
private
var
activityResultCallback
:
ActivityResultCallback
<
ActivityResult
>?
=
null
private
val
intentLauncher
=
activityResultCaller
.
registerForActivityResult
(
ActivityResultContracts
.
StartActivityForResult
())
{
activityResult
:
ActivityResult
->
activityResultCallback
?.
onActivityResult
(
activityResult
)
}
/**
* it.resultCode == Activity.RESULT_OK
*/
fun
launch
(
intent
:
Intent
,
activityResultCallback
:
ActivityResultCallback
<
ActivityResult
>?
=
null
)
{
this
.
activityResultCallback
=
activityResultCallback
intentLauncher
.
launch
(
intent
)
}
}
\ No newline at end of file
app/src/main/java/com/zxhy/hfilemanagermaster/permission/MediaPermission.kt
0 → 100644
View file @
66562843
package
com.zxhy.hfilemanagermaster.permission
import
android.Manifest
import
android.content.Context
import
android.content.Intent
import
android.content.pm.PackageManager
import
android.net.Uri
import
android.os.Build
import
android.os.Environment
import
android.provider.Settings
import
androidx.core.content.ContextCompat
/**
* 获取媒体图片
*/
fun
requestPermission
(
context
:
Context
,
permissionLauncher
:
PermissionLauncher
,
intentLauncher
:
IntentLauncher
,
permission
:
Array
<
String
>,
tip
:
String
,
agreeAction
:
(()
->
Unit
)?
=
null
):
Boolean
{
var
flag
=
false
val
request
:
()
->
Unit
=
{
val
permissionFlag
=
permission
.
all
{
ContextCompat
.
checkSelfPermission
(
context
,
it
)
==
PackageManager
.
PERMISSION_GRANTED
}
if
(
permissionFlag
)
{
flag
=
true
}
else
{
permissionLauncher
.
launch
(
permission
)
{
callback
->
val
isGrantAll
=
callback
.
values
.
all
{
it
}
if
(!
isGrantAll
)
{
context
.
alert
(
tip
)
{
positiveButton
(
"跳转"
)
{
dialog
->
goToPermissionSettings
(
context
,
intentLauncher
)
dialog
.
dismiss
()
}
negativeButton
(
"取消"
)
{
dialog
->
dialog
.
dismiss
()
}
//禁止取消
isCancel
(
false
)
}
}
else
{
agreeAction
?.
invoke
()
}
}
}
// Toast.makeText(context, "不会等待回调", Toast.LENGTH_SHORT).show()
}
if
(
permission
.
contains
(
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
)
&&
permission
.
contains
(
Manifest
.
permission
.
READ_EXTERNAL_STORAGE
)
)
{
val
managerFlag
=
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
R
)
{
Environment
.
isExternalStorageManager
()
}
else
{
false
}
if
(
managerFlag
)
{
return
true
}
else
{
request
()
}
}
else
{
request
()
}
return
flag
}
//权限设置界面
private
fun
goToPermissionSettings
(
context
:
Context
,
intentLauncher
:
IntentLauncher
)
{
val
intent
=
appSettingsIntent
(
context
.
packageName
)
intentLauncher
.
launch
(
intent
)
}
/**
* 应用设置页面意图
*/
fun
appSettingsIntent
(
packageName
:
String
):
Intent
{
return
Intent
(
Settings
.
ACTION_APPLICATION_DETAILS_SETTINGS
)
.
setData
(
Uri
.
fromParts
(
"package"
,
packageName
,
null
))
}
app/src/main/java/com/zxhy/hfilemanagermaster/permission/PermissionCheck.kt
0 → 100644
View file @
66562843
package
com.zxhy.hfilemanagermaster.permission
import
android.app.ActivityManager
import
android.content.Context
import
android.content.pm.PackageManager
import
androidx.core.content.ContextCompat
//检查是否有所有权限
fun
havePermission
(
context
:
Context
,
permissions
:
Array
<
String
>):
Boolean
{
var
havePermission
=
true
permissions
.
forEach
{
if
(
ContextCompat
.
checkSelfPermission
(
context
,
it
)
!=
PackageManager
.
PERMISSION_GRANTED
)
{
havePermission
=
false
}
}
return
havePermission
}
//检查拒绝的权限
fun
denyPermission
(
context
:
Context
,
permissions
:
Array
<
String
>):
ArrayList
<
String
>
{
val
denys
=
arrayListOf
<
String
>()
permissions
.
forEach
{
if
(
ContextCompat
.
checkSelfPermission
(
context
,
it
)
==
PackageManager
.
PERMISSION_DENIED
)
{
denys
.
add
(
it
)
}
}
return
denys
}
app/src/main/java/com/zxhy/hfilemanagermaster/permission/PermissionLauncher.kt
0 → 100644
View file @
66562843
package
com.zxhy.hfilemanagermaster.permission
import
androidx.activity.ComponentActivity
import
androidx.activity.result.ActivityResultCallback
import
androidx.activity.result.ActivityResultCaller
import
androidx.activity.result.contract.ActivityResultContracts
import
androidx.fragment.app.FragmentActivity
/**
* @param activityResultCaller [ComponentActivity] 实现该接口
*
*/
class
PermissionLauncher
(
activityResultCaller
:
ActivityResultCaller
)
{
/**
* [ActivityResultContracts.RequestMultiplePermissions] 权限协议回调
* ActivityResultContract<String[], java.util.Map<String, Boolean>>
* String[] 表示输入权限
* Map<String, Boolean> 表示权限回调的结果
* 可以参考范型实现 [ResultLauncher]
*/
private
var
permissionCallback
:
ActivityResultCallback
<
Map
<
String
,
Boolean
>>?
=
null
//必须在Activity或者Fragment显示之前注册
//说实话有点傻逼,进过测试,可以直接在Activity的onCreate中初始化
private
val
permissionLauncher
=
activityResultCaller
.
registerForActivityResult
(
ActivityResultContracts
.
RequestMultiplePermissions
())
{
result
:
Map
<
String
,
Boolean
>
->
permissionCallback
?.
onActivityResult
(
result
)
}
fun
launch
(
permissionArray
:
Array
<
String
>,
permissionCallback
:
ActivityResultCallback
<
Map
<
String
,
Boolean
>>?
)
{
this
.
permissionCallback
=
permissionCallback
permissionLauncher
.
launch
(
permissionArray
)
}
}
app/src/main/java/com/zxhy/hfilemanagermaster/widget/IndicatorView.kt
View file @
66562843
...
@@ -30,7 +30,9 @@ class IndicatorView : View {
...
@@ -30,7 +30,9 @@ class IndicatorView : View {
private
var
scale
=
1f
//指针图像缩放比例
private
var
scale
=
1f
//指针图像缩放比例
private
var
rotationDegrees
=
0f
// 旋转的角度
private
var
rotationDegrees
=
0f
// 旋转的角度
private
var
indicatorY
=
0.95f
//绘制指针画布Y移动的距离
private
var
indicatorX
=
0f
//绘制指针画布X移动的修补
private
var
rotationDegreesOffset
=
0f
//指针旋转误差修补
// 画原始刻度的画笔
// 画原始刻度的画笔
private
var
arcPaint
:
Paint
=
Paint
()
private
var
arcPaint
:
Paint
=
Paint
()
...
@@ -91,29 +93,84 @@ class IndicatorView : View {
...
@@ -91,29 +93,84 @@ class IndicatorView : View {
super
.
onDraw
(
canvas
)
super
.
onDraw
(
canvas
)
Log
.
e
(
"IndicatorView"
,
"radius=$radius"
)
//
Log.e("IndicatorView", "radius=$radius")
val
left
=
hMargin
+
strokeWidth
/
2
val
left
=
hMargin
+
strokeWidth
/
2
val
top
=
topMargin
+
strokeWidth
/
2
val
top
=
topMargin
+
strokeWidth
/
2
val
right
=
radius
*
2
+
left
val
right
=
radius
*
2
+
left
val
bottom
=
radius
*
2
+
top
val
bottom
=
radius
*
2
+
top
Log
.
e
(
"IndicatorView"
,
"left=$left top=$top right=$right bottom=$bottom"
)
//
Log.e("IndicatorView", "left=$left top=$top right=$right bottom=$bottom")
//绘制半圆
//绘制半圆
val
arcRect
=
RectF
(
left
,
top
,
right
,
bottom
)
val
arcRect
=
RectF
(
left
,
top
,
right
,
bottom
)
canvas
.
drawArc
(
arcRect
,
-
180f
,
sweepAngle
,
false
,
arcPaint
)
canvas
.
drawArc
(
arcRect
,
-
180f
,
sweepAngle
,
false
,
arcPaint
)
canvas
.
translate
((
measuredWidth
/
2
).
toFloat
()
+
50
,
measuredHeight
-
250f
)
canvas
.
translate
(
(
measuredWidth
/
2
).
toFloat
()
-
(
bgBitmap
.
width
/
2
)
*
indicatorX
,
measuredHeight
.
toFloat
()
*
indicatorY
)
// 绘制旋转后的图片
// 绘制旋转后的图片
// val srcRect = Rect(0, 0, 100, 100)
// val srcRect = Rect(0, 0, 100, 100)
// val dstRect = Rect(0, 0, 100, 100)
// val dstRect = Rect(0, 0, 100, 100)
//绘制指针
//绘制指针
canvas
.
rotate
(
rotationDegrees
)
canvas
.
save
()
canvas
.
drawBitmap
(
bgBitmap
,
0f
,
-
bgBitmap
.
height
.
toFloat
(),
Paint
())
}
}
//绘制百分比
//绘制百分比
fun
setPercent
(
percent
:
Float
)
{
fun
setPercent
(
percent
:
Float
)
{
sweepAngle
=
180f
*
percent
sweepAngle
=
180f
*
percent
rotationDegrees
=
180f
*
percent
-
80f
rotationDegrees
=
180f
*
percent
-
90f
// sweepAngle = 180f
// rotationDegrees = 180f - 90f
//针对指针角度进行误差调整
when
(
sweepAngle
)
{
in
0f
..
29f
->
{
indicatorY
=
0.95f
indicatorX
=
0f
rotationDegreesOffset
=
-
8f
}
in
30f
..
59f
->
{
indicatorY
=
0.98f
indicatorX
=
0f
rotationDegreesOffset
=
-
5f
}
in
60f
..
89f
->
{
indicatorY
=
0.95f
indicatorX
=
1f
rotationDegreesOffset
=
0f
rotationDegrees
+=
rotationDegreesOffset
}
in
90f
..
99f
->
{
indicatorY
=
0.95f
indicatorX
=
1f
rotationDegreesOffset
=
0f
}
in
100f
..
119f
->{
indicatorY
=
0.95f
indicatorX
=
1f
rotationDegreesOffset
=
2f
}
in
120f
..
149f
->
{
indicatorY
=
0.85f
indicatorX
=
1.05f
rotationDegreesOffset
=
8f
}
in
150f
..
180f
->
{
indicatorY
=
0.85f
indicatorX
=
1.08f
rotationDegreesOffset
=
8f
}
}
rotationDegrees
+=
rotationDegreesOffset
// Log.e("IndicatorView", "rotationDegrees=$rotationDegrees")
invalidate
()
invalidate
()
}
}
}
}
\ No newline at end of file
app/src/main/res/drawable/bg_circle_selector.xml
0 → 100644
View file @
66562843
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:innerRadius=
"7dp"
android:shape=
"ring"
android:thickness=
"1dp"
android:useLevel=
"false"
>
<solid
android:color=
"#999999"
/>
<size
android:width=
"18dp"
android:height=
"18dp"
/>
</shape>
\ No newline at end of file
app/src/main/res/drawable/bg_media_selector.xml
0 → 100644
View file @
66562843
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<item
android:drawable=
"@mipmap/dd_7865211111"
android:state_selected=
"true"
/>
<item
android:drawable=
"@mipmap/dd_78652"
android:state_selected=
"false"
/>
</selector>
\ No newline at end of file
app/src/main/res/layout/activity_large_file.xml
View file @
66562843
...
@@ -51,16 +51,17 @@
...
@@ -51,16 +51,17 @@
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:ignore=
"HardcodedText,RtlHardcoded"
/>
tools:ignore=
"HardcodedText,RtlHardcoded"
/>
<ImageView
<ImageView
android:id=
"@+id/iv_all"
android:id=
"@+id/iv_all"
android:layout_width=
"24dp"
android:layout_width=
"24dp"
android:layout_height=
"24dp"
android:layout_height=
"24dp"
android:layout_marginRight=
"5dp"
android:layout_marginRight=
"5dp"
android:src=
"@
mipmap/dd_78652
"
android:src=
"@
drawable/bg_media_selector
"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintRight_toLeftOf=
"@id/tv_all"
app:layout_constraintRight_toLeftOf=
"@id/tv_all"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:ignore=
"RtlHardcoded"
/>
tools:ignore=
"
ContentDescription,
RtlHardcoded"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
...
@@ -111,12 +112,14 @@
...
@@ -111,12 +112,14 @@
android:id=
"@+id/rv"
android:id=
"@+id/rv"
android:layout_width=
"0dp"
android:layout_width=
"0dp"
android:layout_height=
"0dp"
android:layout_height=
"0dp"
android:layout_marginHorizontal=
"
20
dp"
android:layout_marginHorizontal=
"
12
dp"
android:layout_marginVertical=
"16dp"
android:layout_marginVertical=
"16dp"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf=
"@id/tv_delete"
app:layout_constraintBottom_toTopOf=
"@id/tv_delete"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@id/tv_file"
/>
app:layout_constraintTop_toBottomOf=
"@id/tv_file"
tools:listitem=
"@layout/item_media_select"
/>
<TextView
<TextView
...
...
app/src/main/res/layout/fragment_files.xml
View file @
66562843
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
xmlns:tools=
"http://schemas.android.com/tools"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_height=
"match_parent"
tools:context=
"com.zxhy.hfilemanagermaster.FilesFragment"
>
tools:context=
"com.zxhy.hfilemanagermaster.
files.
FilesFragment"
>
<TextView
<TextView
...
@@ -190,6 +190,17 @@
...
@@ -190,6 +190,17 @@
app:cardCornerRadius=
"8dp"
app:cardCornerRadius=
"8dp"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@id/tv_recent"
/>
app:layout_constraintTop_toBottomOf=
"@id/tv_recent"
>
<androidx.recyclerview.widget.RecyclerView
android:id=
"@+id/rv"
android:layout_marginHorizontal=
"6dp"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_gravity=
"center_vertical"
android:orientation=
"horizontal"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem=
"@layout/item_image"
/>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
app/src/main/res/layout/item_image.xml
0 → 100644
View file @
66562843
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginHorizontal=
"6dp"
android:layout_marginVertical=
"12dp"
app:cardCornerRadius=
"8dp"
>
<ImageView
android:id=
"@+id/iv"
android:layout_width=
"80dp"
android:layout_height=
"80dp"
tools:ignore=
"ContentDescription"
/>
</androidx.cardview.widget.CardView>
\ No newline at end of file
app/src/main/res/layout/item_media_select.xml
0 → 100644
View file @
66562843
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginHorizontal=
"8dp"
android:layout_marginVertical=
"8dp"
>
<ImageView
android:id=
"@+id/iv"
android:layout_width=
"40dp"
android:layout_height=
"40dp"
android:layout_marginLeft=
"8dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:ignore=
"ContentDescription,RtlHardcoded"
/>
<TextView
android:id=
"@+id/tv_name"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginLeft=
"12dp"
android:paddingEnd=
"5dp"
android:text=
"File name"
android:textColor=
"#333333"
android:textSize=
"14sp"
app:layout_constraintBottom_toTopOf=
"@id/tv_size"
app:layout_constraintLeft_toRightOf=
"@id/iv"
app:layout_constraintRight_toLeftOf=
"@id/fl_selector"
app:layout_constraintTop_toTopOf=
"parent"
tools:ignore=
"HardcodedText,MissingConstraints,RtlHardcoded,RtlSymmetry"
/>
<TextView
android:id=
"@+id/tv_size"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:text=
"20MB"
android:textColor=
"#999999"
android:textSize=
"12sp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintLeft_toLeftOf=
"@id/tv_name"
app:layout_constraintTop_toBottomOf=
"@id/tv_name"
tools:ignore=
"HardcodedText"
/>
<TextView
android:id=
"@+id/tv_time"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginLeft=
"12dp"
android:text=
"January 5, 2024"
android:textColor=
"#999999"
android:textSize=
"12sp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintLeft_toRightOf=
"@id/tv_size"
app:layout_constraintTop_toBottomOf=
"@id/tv_name"
tools:ignore=
"HardcodedText,RtlHardcoded"
/>
<FrameLayout
android:id=
"@+id/fl_selector"
android:layout_width=
"36dp"
android:layout_height=
"36dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
>
<ImageView
android:id=
"@+id/iv_selector"
android:layout_width=
"18dp"
android:layout_height=
"18dp"
android:layout_gravity=
"center"
android:src=
"@drawable/bg_media_selector"
tools:ignore=
"ContentDescription,RtlHardcoded"
/>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
app/src/main/res/navigation/nav.xml
View file @
66562843
...
@@ -37,7 +37,7 @@
...
@@ -37,7 +37,7 @@
</fragment>
</fragment>
<fragment
<fragment
android:id=
"@+id/filesFragment"
android:id=
"@+id/filesFragment"
android:name=
"com.zxhy.hfilemanagermaster.FilesFragment"
android:name=
"com.zxhy.hfilemanagermaster.
files.
FilesFragment"
android:label=
"fragment_files"
android:label=
"fragment_files"
tools:layout=
"@layout/fragment_files"
>
tools:layout=
"@layout/fragment_files"
>
<action
<action
...
...
gradle/libs.versions.toml
View file @
66562843
...
@@ -11,6 +11,7 @@ activity = "1.8.0"
...
@@ -11,6 +11,7 @@ activity = "1.8.0"
constraintlayout
=
"2.1.4"
constraintlayout
=
"2.1.4"
navigationFragmentKtx
=
"2.7.7"
navigationFragmentKtx
=
"2.7.7"
navigationUiKtx
=
"2.7.7"
navigationUiKtx
=
"2.7.7"
glide
=
"4.16.0"
[libraries]
[libraries]
androidx-core-ktx
=
{
group
=
"androidx.core"
,
name
=
"core-ktx"
,
version.ref
=
"coreKtx"
}
androidx-core-ktx
=
{
group
=
"androidx.core"
,
name
=
"core-ktx"
,
version.ref
=
"coreKtx"
}
...
@@ -24,6 +25,9 @@ androidx-constraintlayout = { group = "androidx.constraintlayout", name = "const
...
@@ -24,6 +25,9 @@ androidx-constraintlayout = { group = "androidx.constraintlayout", name = "const
androidx-navigation-fragment-ktx
=
{
group
=
"androidx.navigation"
,
name
=
"navigation-fragment-ktx"
,
version.ref
=
"navigationFragmentKtx"
}
androidx-navigation-fragment-ktx
=
{
group
=
"androidx.navigation"
,
name
=
"navigation-fragment-ktx"
,
version.ref
=
"navigationFragmentKtx"
}
androidx-navigation-ui-ktx
=
{
group
=
"androidx.navigation"
,
name
=
"navigation-ui-ktx"
,
version.ref
=
"navigationUiKtx"
}
androidx-navigation-ui-ktx
=
{
group
=
"androidx.navigation"
,
name
=
"navigation-ui-ktx"
,
version.ref
=
"navigationUiKtx"
}
#第三方
glide
=
{
group
=
"com.github.bumptech.glide"
,
name
=
"glide"
,
version.ref
=
"glide"
}
[plugins]
[plugins]
androidApplication
=
{
id
=
"com.android.application"
,
version.ref
=
"agp"
}
androidApplication
=
{
id
=
"com.android.application"
,
version.ref
=
"agp"
}
jetbrainsKotlinAndroid
=
{
id
=
"org.jetbrains.kotlin.android"
,
version.ref
=
"kotlin"
}
jetbrainsKotlinAndroid
=
{
id
=
"org.jetbrains.kotlin.android"
,
version.ref
=
"kotlin"
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment