Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Sign in / Register
Toggle navigation
F
File Recovery RecycleBin
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
File Recovery RecycleBin
Commits
7d6614a1
Commit
7d6614a1
authored
Jul 22, 2024
by
wanglei
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of gitlab.huolea.com:koko/file-recovery-recyclebin
parents
7b02ca2e
71854bf7
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
224 additions
and
38 deletions
+224
-38
MeidaContentObserver.kt
...eryrecyclebin/activity/recyclebin/MeidaContentObserver.kt
+5
-5
RecycleBinBean.kt
...va/com/base/filerecoveryrecyclebin/bean/RecycleBinBean.kt
+0
-1
RecycleBinFragment.kt
...ase/filerecoveryrecyclebin/fragment/RecycleBinFragment.kt
+8
-14
BackUpUtils.kt
...va/com/base/filerecoveryrecyclebin/service/BackUpUtils.kt
+89
-0
FileObserverExample.java
...e/filerecoveryrecyclebin/service/FileObserverExample.java
+108
-0
StayNotificationService.kt
...filerecoveryrecyclebin/service/StayNotificationService.kt
+14
-18
No files found.
app/src/main/java/com/base/filerecoveryrecyclebin/activity/recyclebin/MeidaContentObserver.kt
View file @
7d6614a1
...
...
@@ -88,11 +88,11 @@ class MediaContentObserver(val context: Context) : ContentObserver(null) {
if
(
src
.
length
()
>
0L
)
{
val
recycleBinFile
=
File
(
getRecycleBinDir
(),
".$name"
)
src
.
copyTo
(
recycleBinFile
,
true
)
val
binBean
=
RecycleBinBean
(
src
.
path
,
uri
,
deleteTime
=
0
,
size
=
src
.
length
(),
mimeType
=
mimeType
)
val
binFile
=
File
(
recycleBinDir
,
".$name.bin"
)
binFile
.
createNewFile
()
binFile
.
writeText
(
Gson
().
toJson
(
binBean
))
LogEx
.
logDebug
(
TAG
,
"RecycleBin backup"
)
//
val binBean = RecycleBinBean(src.path, uri, deleteTime = 0, size = src.length(), mimeType = mimeType)
//
val binFile = File(recycleBinDir, ".$name.bin")
//
binFile.createNewFile()
//
binFile.writeText(Gson().toJson(binBean))
//
LogEx.logDebug(TAG, "RecycleBin backup")
}
}
}
app/src/main/java/com/base/filerecoveryrecyclebin/bean/RecycleBinBean.kt
View file @
7d6614a1
...
...
@@ -4,7 +4,6 @@ import android.net.Uri
data class
RecycleBinBean
(
val
path
:
String
=
""
,
val
uri
:
Uri
=
Uri
.
EMPTY
,
var
deleteTime
:
Long
=
0
,
val
size
:
Long
=
0
,
val
mimeType
:
String
=
""
,
...
...
app/src/main/java/com/base/filerecoveryrecyclebin/fragment/RecycleBinFragment.kt
View file @
7d6614a1
...
...
@@ -16,6 +16,7 @@ import com.base.filerecoveryrecyclebin.databinding.FragmentRecycleBinBinding
import
com.base.filerecoveryrecyclebin.help.BaseFragment
import
com.base.filerecoveryrecyclebin.help.PermissionHelp.checkStorePermission
import
com.base.filerecoveryrecyclebin.help.PermissionHelp.requestStorePermission
import
com.base.filerecoveryrecyclebin.service.BackUpUtils
import
com.base.filerecoveryrecyclebin.utils.LogEx
import
com.base.filerecoveryrecyclebin.view.DialogViews.showGerPermission
import
com.google.gson.Gson
...
...
@@ -78,22 +79,15 @@ class RecycleBinFragment : BaseFragment<FragmentRecycleBinBinding>() {
private
fun
initData
()
{
lifecycleScope
.
launch
(
Dispatchers
.
IO
)
{
val
binList
=
getRecycleBinDir
().
listFiles
()
?.
filter
{
it
.
name
.
contains
(
".bin"
)
}
val
beanList
=
arrayListOf
<
RecycleBinBean
>()
binList
?.
forEach
{
LogEx
.
logDebug
(
TAG
,
it
.
absolutePath
)
val
bean
=
Gson
().
fromJson
(
it
.
readText
(),
RecycleBinBean
::
class
.
java
)
beanList
.
add
(
bean
)
}
val
deleteFile
=
beanList
.
filter
{
it
.
deleted
}
// val deleteFile = beanList
val
beanList
=
BackUpUtils
.
getBackUpListObj
()
val
deleteFile
=
beanList
?.
filter
{
it
.
deleted
}
launch
(
Dispatchers
.
Main
)
{
binding
.
rv
.
isVisible
=
deleteFile
.
isNotEmpty
()
binding
.
llEmpty
.
isVisible
=
deleteFile
.
isEmpty
()
recycleBinAdapter
.
setData
(
deleteFile
)
binding
.
rv
.
isVisible
=
deleteFile
?.
isNotEmpty
()
==
true
binding
.
llEmpty
.
isVisible
=
deleteFile
?.
isEmpty
()
==
true
if
(
deleteFile
!=
null
)
{
recycleBinAdapter
.
setData
(
deleteFile
)
}
}
}
}
...
...
app/src/main/java/com/base/filerecoveryrecyclebin/service/BackUpUtils.kt
0 → 100644
View file @
7d6614a1
package
com.base.filerecoveryrecyclebin.service
import
android.text.TextUtils
import
com.base.filerecoveryrecyclebin.activity.recyclebin.RecycleBinFileEx.getRecycleBinDir
import
com.base.filerecoveryrecyclebin.bean.RecycleBinBean
import
com.base.filerecoveryrecyclebin.utils.AppPreferences
import
com.google.gson.Gson
import
com.google.gson.reflect.TypeToken
import
com.ironsource.da
import
java.io.File
import
java.util.stream.Collectors
object
BackUpUtils
{
private
const
val
KEY
=
"backup_key"
fun
backupFile
(
src
:
File
,
path
:
String
)
{
val
recycleBinFile
=
File
(
getRecycleBinDir
(),
".\$name"
)
src
.
copyTo
(
recycleBinFile
,
true
)
val
binBean
=
RecycleBinBean
(
src
.
path
,
0
,
src
.
length
(),
getFileExtension
(
src
),
false
)
saveData
(
binBean
,
path
)
}
private
fun
getFileExtension
(
file
:
File
):
String
{
val
fileName
=
file
.
name
return
if
(
fileName
.
lastIndexOf
(
"."
)
!=
-
1
&&
fileName
.
lastIndexOf
(
"."
)
!=
0
)
{
fileName
.
substring
(
fileName
.
lastIndexOf
(
"."
)
+
1
)
}
else
{
""
}
}
fun
saveData
(
binBean
:
RecycleBinBean
,
path
:
String
)
{
val
hashMap
=
queryBackUpSp
()
hashMap
.
put
(
path
,
binBean
)
saveBackUpSp
(
hashMap
)
}
fun
queryBackUpSp
():
HashMap
<
String
,
RecycleBinBean
>
{
val
str
=
AppPreferences
.
getInstance
().
getString
(
KEY
,
""
)
val
hastMap
:
HashMap
<
String
,
RecycleBinBean
>
if
(
TextUtils
.
isEmpty
(
str
))
{
hastMap
=
HashMap
()
}
else
{
val
gson
=
Gson
()
// 使用TypeToken来指定HashMap的类型
hastMap
=
gson
.
fromJson
(
str
,
object
:
TypeToken
<
HashMap
<
String
,
RecycleBinBean
>>()
{}.
type
)
}
return
hastMap
}
fun
saveBackUpSp
(
hash
:
HashMap
<
String
,
RecycleBinBean
>)
{
AppPreferences
.
getInstance
().
put
(
KEY
,
Gson
().
toJson
(
hash
))
}
fun
getBackUpListObj
():
List
<
RecycleBinBean
>?
{
val
hashMap
=
queryBackUpSp
()
if
(
hashMap
.
size
>
0
)
{
return
null
}
else
{
val
values
:
MutableList
<
RecycleBinBean
>?
=
hashMap
.
values
.
stream
().
collect
(
Collectors
.
toList
())
return
values
}
}
fun
queryFileExists
(
path
:
String
):
Boolean
{
val
hashMap
=
queryBackUpSp
()
return
hashMap
.
contains
(
path
)
}
fun
deleteFile
(
path
:
String
)
{
val
isExists
=
queryFileExists
(
path
)
if
(
isExists
)
{
val
hashMap
=
queryBackUpSp
()
val
data
=
hashMap
[
path
]
if
(
data
!=
null
)
{
data
?.
deleted
=
true
data
?.
deleteTime
=
System
.
currentTimeMillis
()
hashMap
[
path
]
=
data
}
}
}
}
\ No newline at end of file
app/src/main/java/com/base/filerecoveryrecyclebin/service/FileObserverExample.java
0 → 100644
View file @
7d6614a1
package
com
.
base
.
filerecoveryrecyclebin
.
service
;
import
android.os.Environment
;
import
android.os.FileObserver
;
import
android.os.Handler
;
import
android.os.HandlerThread
;
import
android.text.TextUtils
;
import
android.util.Log
;
import
com.base.filerecoveryrecyclebin.MyApplication
;
import
com.base.filerecoveryrecyclebin.activity.recyclebin.RecycleBinFileEx
;
import
com.base.filerecoveryrecyclebin.help.FileHelp
;
import
java.io.File
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Set
;
public
class
FileObserverExample
{
private
static
final
String
TAG
=
"FileObserverExample"
;
private
HandlerThread
fileHandlerThread
;
private
Handler
fileHandler
;
public
static
final
Set
<
String
>
FILE_EXTENSIONS
=
new
HashSet
<>(
Arrays
.
asList
(
".jpg"
,
".jpeg"
,
".png"
,
".gif"
,
".bmp"
,
".mp4"
,
".avi"
,
".mov"
,
".mkv"
,
".mp3"
,
".wav"
,
".aac"
,
".pdf"
,
".doc"
,
".docx"
,
".xls"
,
".xlsx"
,
".txt"
));
private
FileObserver
fileObserver
;
List
<
File
>
watchList
;
public
void
startObserving
()
{
// 获取外部存储目录
List
<
File
>
dcimList
=
FileHelp
.
INSTANCE
.
getFileFolder
(
Environment
.
getExternalStoragePublicDirectory
(
Environment
.
DIRECTORY_DCIM
));
List
<
File
>
pictureList
=
FileHelp
.
INSTANCE
.
getFileFolder
(
Environment
.
getExternalStoragePublicDirectory
(
Environment
.
DIRECTORY_PICTURES
));
List
<
File
>
downloadList
=
FileHelp
.
INSTANCE
.
getFileFolder
(
Environment
.
getExternalStoragePublicDirectory
(
Environment
.
DIRECTORY_DOWNLOADS
));
List
<
File
>
videoList
=
FileHelp
.
INSTANCE
.
getFileFolder
(
Environment
.
getExternalStoragePublicDirectory
(
Environment
.
DIRECTORY_MOVIES
));
watchList
=
new
ArrayList
();
watchList
.
addAll
(
dcimList
);
watchList
.
addAll
(
pictureList
);
watchList
.
addAll
(
downloadList
);
watchList
.
addAll
(
videoList
);
fileHandlerThread
=
new
HandlerThread
(
"FileObserverThread"
);
fileHandlerThread
.
start
();
fileHandler
=
new
Handler
(
fileHandlerThread
.
getLooper
());
fileObserver
=
new
FileObserver
(
watchList
,
FileObserver
.
CREATE
|
FileObserver
.
DELETE
)
{
@Override
public
void
onEvent
(
int
event
,
String
path
)
{
Log
.
d
(
TAG
,
"onEvent "
+
event
+
" "
+
path
);
if
(!
TextUtils
.
isEmpty
(
path
)){
Log
.
d
(
TAG
,
"path: "
+
path
);
handleFileEvent
(
path
,
event
);
}
}
};
fileObserver
.
startWatching
();
// 启动观察
}
public
void
stopObserving
()
{
if
(
fileObserver
!=
null
)
{
// 根据 API 版本选择不同的停止方法
fileObserver
.
stopWatching
();
}
}
// 处理文件事件
private
void
handleFileEvent
(
String
path
,
int
event
)
{
Log
.
d
(
TAG
,
path
);
// 检查文件扩展名是否在监听范围内
if
(
FILE_EXTENSIONS
.
contains
(
getFileExtension
(
path
)))
{
if
(
event
==
FileObserver
.
CREATE
)
{
Log
.
d
(
TAG
,
"CREATE"
);
fileHandler
.
post
(()
->
handleFileCreation
(
path
));
}
else
if
(
event
==
FileObserver
.
DELETE
)
{
BackUpUtils
.
INSTANCE
.
deleteFile
(
path
);
}
}
}
// 获取文件扩展名
private
String
getFileExtension
(
String
fileName
)
{
if
(
fileName
.
lastIndexOf
(
"."
)
!=
-
1
&&
fileName
.
lastIndexOf
(
"."
)
!=
0
)
{
return
"."
+
fileName
.
substring
(
fileName
.
lastIndexOf
(
"."
)
+
1
);
}
else
{
return
""
;
}
}
private
void
handleFileCreation
(
String
path
)
{
// 确保线程安全,这里我们只是打印出来,实际使用中可能需要同步机制
for
(
File
dir
:
watchList
)
{
File
src
=
new
File
(
dir
,
path
);
if
(
src
.
exists
())
{
Log
.
d
(
TAG
,
"新创建的文件 "
+
path
+
" 属于目录: "
+
dir
.
getAbsolutePath
());
BackUpUtils
.
INSTANCE
.
backupFile
(
src
,
path
);
break
;
}
}
}
}
\ No newline at end of file
app/src/main/java/com/base/filerecoveryrecyclebin/service/StayNotificationService.kt
View file @
7d6614a1
...
...
@@ -39,6 +39,7 @@ import kotlin.random.Random
class
StayNotificationService
:
Service
()
{
private
val
TAG
=
"StayNotificationService"
private
var
fileObserver
:
FileObserverExample
?
=
null
companion
object
{
const
val
MEDIA_OBSERVER_ACTION
=
"media_observer_action"
...
...
@@ -62,17 +63,10 @@ class StayNotificationService : Service() {
@SuppressLint
(
"ForegroundServiceType"
)
override
fun
onStartCommand
(
intent
:
Intent
?,
flags
:
Int
,
startId
:
Int
):
Int
{
val
action
=
intent
?.
action
if
(
action
==
MEDIA_OBSERVER_ACTION
)
{
observerMediaContentObserver
()
}
if
(
action
==
FILE_OBSERVER_ACTION
)
{
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
Q
)
{
startMyFileObserver
()
}
if
(
fileObserver
==
null
){
fileObserver
=
FileObserverExample
()
}
fileObserver
?.
startObserving
()
if
(!
isRunning
)
{
val
notification
=
createPermanentNotification
(
applicationContext
)
startForeground
(
1
,
notification
)
...
...
@@ -107,10 +101,14 @@ class StayNotificationService : Service() {
fun
startMyFileObserver
()
{
if
(
myFileObserver
==
null
)
{
LogEx
.
logDebug
(
TAG
,
"startMyFileObserver"
)
val
dcimList
=
FileHelp
.
getFileFolder
(
Environment
.
getExternalStoragePublicDirectory
(
Environment
.
DIRECTORY_DCIM
))
val
pictureList
=
FileHelp
.
getFileFolder
(
Environment
.
getExternalStoragePublicDirectory
(
Environment
.
DIRECTORY_PICTURES
))
val
downloadList
=
FileHelp
.
getFileFolder
(
Environment
.
getExternalStoragePublicDirectory
(
Environment
.
DIRECTORY_DOWNLOADS
))
val
videoList
=
FileHelp
.
getFileFolder
(
Environment
.
getExternalStoragePublicDirectory
(
Environment
.
DIRECTORY_MOVIES
))
val
dcimList
=
FileHelp
.
getFileFolder
(
Environment
.
getExternalStoragePublicDirectory
(
Environment
.
DIRECTORY_DCIM
))
val
pictureList
=
FileHelp
.
getFileFolder
(
Environment
.
getExternalStoragePublicDirectory
(
Environment
.
DIRECTORY_PICTURES
))
val
downloadList
=
FileHelp
.
getFileFolder
(
Environment
.
getExternalStoragePublicDirectory
(
Environment
.
DIRECTORY_DOWNLOADS
))
val
videoList
=
FileHelp
.
getFileFolder
(
Environment
.
getExternalStoragePublicDirectory
(
Environment
.
DIRECTORY_MOVIES
))
val
watchList
=
arrayListOf
<
File
>()
watchList
.
addAll
(
dcimList
)
...
...
@@ -135,10 +133,8 @@ class StayNotificationService : Service() {
isRunning
=
false
super
.
onDestroy
()
// 取消监听
mediaContentObserver
?.
let
{
this
.
contentResolver
.
unregisterContentObserver
(
it
)
}
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
Q
)
{
myFileObserver
?.
stopWatching
()
}
fileObserver
?.
stopObserving
()
}
private
fun
createPermanentNotification
(
context
:
Context
):
Notification
{
...
...
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