Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Sign in / Register
Toggle navigation
P
PhoneManager
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
Yang
PhoneManager
Commits
17db8452
Commit
17db8452
authored
Apr 16, 2025
by
CZ1004
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
优化
parent
96dd0303
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
424 additions
and
228 deletions
+424
-228
AppDelegate.swift
PhoneManager/AppDelegate.swift
+45
-102
ChargeViewController.swift
...lass/Session/Charge/Controller/ChargeViewController.swift
+2
-2
CompressCompletedViewController.swift
...Compress/Controller/CompressCompletedViewController.swift
+1
-1
CompressNavView.swift
...Manager/Class/Session/Compress/View/CompressNavView.swift
+7
-2
HomeViewController.swift
...er/Class/Session/Home/Controller/HomeViewController.swift
+4
-0
HomeNavView.swift
PhoneManager/Class/Session/Home/View/HomeNavView.swift
+6
-2
HomePhotosModel .swift
...ager/Class/Session/Home/View/Model/HomePhotosModel .swift
+35
-103
PayCompletedViewController.swift
...ssion/Pay/ViewController/PayCompletedViewController.swift
+102
-0
SettingViewHeaderCell.swift
...r/Class/Session/Settings/View/SettingViewHeaderCell.swift
+24
-5
SettingViewController.swift
...ssion/Settings/ViewController/SettingViewController.swift
+5
-1
PhotoAndVideoMananger.swift
...l/Class/PhotoAndVideoMananger/PhotoAndVideoMananger.swift
+193
-10
No files found.
PhoneManager/AppDelegate.swift
View file @
17db8452
...
@@ -67,16 +67,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
...
@@ -67,16 +67,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func
getHomeCacheData
(){
// 首页有7个数据源
PhotoAndVideoMananger
.
mananger
.
setAssets
()
}
var
dataArrayDu
:
[[
AssetModel
]]
=
[]
var
dataArrayDu
:
[[
AssetModel
]]
=
[]
var
dataArraySi
:
[[
AssetModel
]]
=
[]
var
dataArraySi
:
[[
AssetModel
]]
=
[]
...
@@ -175,6 +165,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
...
@@ -175,6 +165,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
Print
(
"更新截图数据结束"
)
Print
(
"更新截图数据结束"
)
})
})
}
}
Print
(
"更新相似截图数据"
)
PhotoAndVideoMananger
.
mananger
.
groupSimilarImages
(
assets
:
PhotoAndVideoMananger
.
mananger
.
screenShotAssets
)
{
similarGroups
in
model
.
otherModelArray
[
1
]
.
assets
=
similarGroups
model
.
otherModelArray
[
1
]
.
allFileSize
=
0
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
model
)
let
dataUpdated
=
Notification
.
Name
(
"HomeSimilarScreenshotResourceUpdate"
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
}
completion
:
{
similarGroups
in
Print
(
"更新相似截图数据结束"
)
}
}
}
...
@@ -200,106 +203,46 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
...
@@ -200,106 +203,46 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
}
}
}
// 更新重复图片和截图
// 查看更新到了哪一个时间点了【这里以时间为基准】
var
allImageUpdateLocation
=
UserDefaults
.
standard
.
object
(
forKey
:
"allImageUpdateLocationDate"
)
if
allImageUpdateLocation
==
nil
{
allImageUpdateLocation
=
[]
}
// 找到位置
if
screeshotCount
!=
PhotoAndVideoMananger
.
mananger
.
screenShotAssets
.
count
||
imageCount
!=
PhotoAndVideoMananger
.
mananger
.
otherAssets
.
count
{
var
newArray
:
[
PHAsset
]
=
[]
Print
(
"更新重复图片数据"
)
for
item
in
PhotoAndVideoMananger
.
mananger
.
allAssets
{
PhotoAndVideoMananger
.
mananger
.
groupDuplicateImages
(
assets
:
PhotoAndVideoMananger
.
mananger
.
allAssets
)
{
similarGroups
in
if
!
(
allImageUpdateLocation
as!
[
String
])
.
contains
(
item
.
localIdentifier
)
{
newArray
.
append
(
item
)
}
}
if
newArray
.
count
>
0
{
// 继续比较
PhotoAndVideoMananger
.
mananger
.
dealSimilarPhotos
(
assets
:
newArray
,
type
:
0
,
threshold
:
0.99
)
{
data1
,
data2
in
var
dataArray1
:
[
AssetModel
]
=
[]
var
dataArray
:
[
AssetModel
]
=
[]
for
item
in
data1
{
for
item
in
similarGroups
{
dataArray1
=
dataArray1
+
item
dataArray
=
dataArray
+
item
}
var
dataArray2
:
[
AssetModel
]
=
[]
for
item
in
data2
{
dataArray2
=
dataArray2
+
item
}
}
PhotoAndVideoMananger
.
mananger
.
calculateTotalSize
(
of
:
PhotoAndVideoMananger
.
mananger
.
getPHAsssetwithIDs
(
ids
:
dataArray
)
!
,
progress
:
{
fileSiez
,
index
in
PhotoAndVideoMananger
.
mananger
.
calculateTotalSize
(
of
:
PhotoAndVideoMananger
.
mananger
.
getPHAsssetwithIDs
(
ids
:
dataArray1
)
!
,
progress
:
{
fileSiez
,
index
in
// 不做处理
// 不做处理
},
completion
:
{[
dataArray1
=
dataArray1
]
fileSize
,
index
in
},
completion
:
{
fileSize
,
index
in
if
dataArray1
.
count
>
0
{
model
.
titleModelArray
[
0
]
.
allFileSize
=
Double
(
fileSize
)
self
.
dataArrayDu
.
append
(
dataArray1
)
model
.
titleModelArray
[
0
]
.
assets
=
similarGroups
model
.
titleModelArray
[
0
]
.
assets
=
self
.
dataArrayDu
self
.
dataArrayDu
.
remove
(
at
:
self
.
dataArrayDu
.
count
-
1
)
model
.
titleModelArray
[
0
]
.
allFileSize
=
self
.
dataDuFileSize
+
Double
(
fileSize
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
model
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
model
)
let
dataUpdated
=
Notification
.
Name
(
"HomeDupImageResourceUpdate"
)
let
dataUpdated
=
Notification
.
Name
(
"HomeDupImageResourceUpdate"
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
}
})
})
}
completion
:
{
similarGroups
in
PhotoAndVideoMananger
.
mananger
.
calculateTotalSize
(
of
:
PhotoAndVideoMananger
.
mananger
.
getPHAsssetwithIDs
(
ids
:
dataArray2
)
!
,
progress
:
{
fileSize
,
index
in
Print
(
"更新重复图片数据结束"
)
// 不做处理
},
completion
:
{[
dataArray2
=
dataArray2
]
fileSize
,
index
in
if
dataArray2
.
count
>
0
{
self
.
dataArraySi
.
append
(
dataArray2
)
model
.
titleModelArray
[
1
]
.
assets
=
self
.
dataArraySi
self
.
dataArraySi
.
remove
(
at
:
self
.
dataArraySi
.
count
-
1
)
model
.
titleModelArray
[
1
]
.
allFileSize
=
self
.
dataSiFileSize
+
Double
(
fileSize
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
model
)
let
dataUpdated
=
Notification
.
Name
(
"HomeSimilarImageResourceUpdate"
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
}
}
})
}
completionHandler
:
{
data1
,
data2
in
}
Print
(
"更新相似图片数据"
)
}
PhotoAndVideoMananger
.
mananger
.
groupSimilarImages
(
assets
:
PhotoAndVideoMananger
.
mananger
.
allAssets
)
{
similarGroups
in
// 处理相似截图
var
dataArray
:
[
AssetModel
]
=
[]
var
screenshotUpdateLocation
=
UserDefaults
.
standard
.
object
(
forKey
:
"screenshotUpdateLocationDate"
)
for
item
in
similarGroups
{
if
screenshotUpdateLocation
==
nil
{
dataArray
=
dataArray
+
item
screenshotUpdateLocation
=
[]
}
// 找到位置
var
screenshotNewArray
:
[
PHAsset
]
=
[]
for
item
in
PhotoAndVideoMananger
.
mananger
.
screenShotAssets
{
if
!
(
screenshotUpdateLocation
as!
[
String
])
.
contains
(
item
.
localIdentifier
)
{
screenshotNewArray
.
append
(
item
)
}
}
if
screenshotNewArray
.
count
>
0
{
PhotoAndVideoMananger
.
mananger
.
dealSimilarPhotos
(
assets
:
screenshotNewArray
,
type
:
1
,
threshold
:
0.99
)
{
data1
,
data2
in
var
dataArray2
:
[
AssetModel
]
=
[]
for
item
in
data2
{
dataArray2
=
dataArray2
+
item
}
}
PhotoAndVideoMananger
.
mananger
.
calculateTotalSize
(
of
:
PhotoAndVideoMananger
.
mananger
.
getPHAsssetwithIDs
(
ids
:
dataArray
)
!
,
progress
:
{
fileSiez
,
index
in
PhotoAndVideoMananger
.
mananger
.
calculateTotalSize
(
of
:
PhotoAndVideoMananger
.
mananger
.
getPHAsssetwithIDs
(
ids
:
dataArray2
)
!
,
progress
:
{
fileSiez
,
index
in
// 不做处理
// 不做处理
},
completion
:
{[
dataArray2
=
dataArray2
]
fileSize
,
index
in
},
completion
:
{
fileSize
,
index
in
if
dataArray2
.
count
>
0
{
model
.
titleModelArray
[
1
]
.
assets
=
similarGroups
self
.
dataArraySc
.
append
(
dataArray2
)
model
.
titleModelArray
[
1
]
.
allFileSize
=
Double
(
fileSize
)
model
.
titleModelArray
[
0
]
.
assets
=
self
.
dataArraySc
self
.
dataArraySc
.
remove
(
at
:
self
.
dataArraySc
.
count
-
1
)
model
.
otherModelArray
[
1
]
.
allFileSize
=
self
.
dataScFileSize
+
Double
(
fileSize
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
model
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
model
)
let
dataUpdated
=
Notification
.
Name
(
"HomeSimilarScreenshotResourceUpdate"
)
let
dataUpdated
=
Notification
.
Name
(
"HomeSimilarScreenshotResourceUpdate"
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
}
})
})
}
completion
Handler
:
{
data1
,
data2
in
}
completion
:
{
similarGroups
in
Print
(
"更新相似图片数据结束"
)
}
}
}
}
...
...
PhoneManager/Class/Session/Charge/Controller/ChargeViewController.swift
View file @
17db8452
...
@@ -14,8 +14,8 @@ class ChargeViewController:BaseViewController {
...
@@ -14,8 +14,8 @@ class ChargeViewController:BaseViewController {
let
sview
:
UIButton
=
UIButton
()
let
sview
:
UIButton
=
UIButton
()
sview
.
setImage
(
UIImage
(
named
:
"ic_details_charging"
),
for
:
.
normal
)
sview
.
setImage
(
UIImage
(
named
:
"ic_details_charging"
),
for
:
.
normal
)
sview
.
width
=
2
0
sview
.
width
=
2
8
sview
.
height
=
2
0
sview
.
height
=
2
8
sview
.
x
=
view
.
width
-
sview
.
width
-
15
sview
.
x
=
view
.
width
-
sview
.
width
-
15
sview
.
centerY
=
navCenterY
sview
.
centerY
=
navCenterY
sview
.
addTarget
(
self
,
action
:
#selector(
guideClick
)
,
for
:
.
touchUpInside
)
sview
.
addTarget
(
self
,
action
:
#selector(
guideClick
)
,
for
:
.
touchUpInside
)
...
...
PhoneManager/Class/Session/Compress/Controller/CompressCompletedViewController.swift
View file @
17db8452
...
@@ -305,7 +305,7 @@ class CompressCompletedViewController : BaseViewController{
...
@@ -305,7 +305,7 @@ class CompressCompletedViewController : BaseViewController{
}){
success
,
error
in
}){
success
,
error
in
if
(
success
){
if
(
success
){
self
.
updateCompressData
(
flag
:
data
.
ident
)
self
.
updateCompressData
(
flag
:
data
.
ident
)
var
deleteModel
=
AssetModel
(
localIdentifier
:
data
.
ident
,
assetSize
:
data
.
orgSize
,
createDate
:
data
.
createDate
)
let
deleteModel
=
AssetModel
(
localIdentifier
:
data
.
ident
,
assetSize
:
data
.
orgSize
,
createDate
:
data
.
createDate
)
PhotoDataManager
.
manager
.
removeDataWhenDeleteInPage
(
data
:
[
deleteModel
])
PhotoDataManager
.
manager
.
removeDataWhenDeleteInPage
(
data
:
[
deleteModel
])
print
(
"删除文件成功"
)
print
(
"删除文件成功"
)
}
else
{
}
else
{
...
...
PhoneManager/Class/Session/Compress/View/CompressNavView.swift
View file @
17db8452
...
@@ -58,9 +58,14 @@ class CompressNavView : UIView {
...
@@ -58,9 +58,14 @@ class CompressNavView : UIView {
}
}
@objc
private
func
proBtnClick
()
{
@objc
private
func
proBtnClick
()
{
HomePayViewController
.
show
{
if
HomePayModel
.
share
.
isNoAd
==
false
{
HomePayViewController
.
show
{}
}
else
{
let
vc
:
PayCompletedViewController
=
PayCompletedViewController
()
vc
.
modalPresentationStyle
=
.
fullScreen
self
.
responderViewController
()?
.
present
(
vc
,
animated
:
true
)
}
}
}
}
...
...
PhoneManager/Class/Session/Home/Controller/HomeViewController.swift
View file @
17db8452
...
@@ -225,6 +225,10 @@ class HomeViewController:BaseViewController {
...
@@ -225,6 +225,10 @@ class HomeViewController:BaseViewController {
}
else
{
}
else
{
if
HomePayModel
.
share
.
isNoAd
==
false
{
if
HomePayModel
.
share
.
isNoAd
==
false
{
HomePayViewController
.
show
{}
HomePayViewController
.
show
{}
}
else
{
let
vc
:
PayCompletedViewController
=
PayCompletedViewController
()
vc
.
modalPresentationStyle
=
.
fullScreen
self
.
present
(
vc
,
animated
:
true
)
}
}
}
}
}
}
...
...
PhoneManager/Class/Session/Home/View/HomeNavView.swift
View file @
17db8452
...
@@ -88,8 +88,12 @@ class HomeNavView:UIView {
...
@@ -88,8 +88,12 @@ class HomeNavView:UIView {
}
}
@objc
private
func
proBtnClick
()
{
@objc
private
func
proBtnClick
()
{
HomePayViewController
.
show
{
if
HomePayModel
.
share
.
isNoAd
==
false
{
HomePayViewController
.
show
{}
}
else
{
let
vc
:
PayCompletedViewController
=
PayCompletedViewController
()
vc
.
modalPresentationStyle
=
.
fullScreen
self
.
responderViewController
()?
.
present
(
vc
,
animated
:
true
)
}
}
}
}
...
...
PhoneManager/Class/Session/Home/View/Model/HomePhotosModel .swift
View file @
17db8452
...
@@ -111,26 +111,6 @@ class PhotoDataManager {
...
@@ -111,26 +111,6 @@ class PhotoDataManager {
self
.
saveToFileSystem
(
model
:
model
)
self
.
saveToFileSystem
(
model
:
model
)
}
}
})
})
// 将磁盘中存的内容也删除
let
allImageUpdateLocation
:
[
String
]
=
UserDefaults
.
standard
.
object
(
forKey
:
"allImageUpdateLocationDate"
)
as!
[
String
]
var
tempArray
=
allImageUpdateLocation
for
item
in
data
{
tempArray
.
removeAll
{
$0
==
item
.
localIdentifier
}
}
UserDefaults
.
standard
.
set
(
tempArray
,
forKey
:
"allImageUpdateLocationDate"
)
var
screenshotUpdateLocation
:
[
String
]
=
UserDefaults
.
standard
.
object
(
forKey
:
"screenshotUpdateLocationDate"
)
as!
[
String
]
var
tempArraySC
=
screenshotUpdateLocation
for
item
in
data
{
tempArraySC
.
removeAll
{
$0
==
item
.
localIdentifier
}
}
UserDefaults
.
standard
.
set
(
tempArraySC
,
forKey
:
"screenshotUpdateLocationDate"
)
// 删除更新中的数据
for
item
in
data
{
screenshotUpdateLocation
.
removeAll
{
$0
==
item
.
localIdentifier
}
}
// 将单利中的数据也删除
// 将单利中的数据也删除
for
item
in
data
{
for
item
in
data
{
Singleton
.
shared
.
resourceModel
.
removeAll
{
$0
.
ident
==
item
.
localIdentifier
}
Singleton
.
shared
.
resourceModel
.
removeAll
{
$0
.
ident
==
item
.
localIdentifier
}
...
@@ -176,71 +156,48 @@ class PhotoDataManager {
...
@@ -176,71 +156,48 @@ class PhotoDataManager {
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
allModel
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
allModel
)
Print
(
"正在处理重复图片"
)
PhotoAndVideoMananger
.
mananger
.
groupDuplicateImages
(
assets
:
PhotoAndVideoMananger
.
mananger
.
allAssets
)
{
similarGroups
in
var
dataArray
:
[
AssetModel
]
=
[]
let
group
=
DispatchGroup
()
for
item
in
similarGroups
{
dataArray
=
dataArray
+
item
Print
(
"正在处理重复和相似图片"
)
group
.
enter
()
PhotoAndVideoMananger
.
mananger
.
dealSimilarPhotos
(
assets
:
PhotoAndVideoMananger
.
mananger
.
allAssets
,
type
:
0
,
threshold
:
0.99
)
{
data1
,
data2
in
var
dataArray1
:
[
AssetModel
]
=
[]
var
dataArray2
:
[
AssetModel
]
=
[]
for
item
in
data1
{
dataArray1
=
dataArray1
+
item
}
for
item
in
data2
{
dataArray2
=
dataArray2
+
item
}
PhotoAndVideoMananger
.
mananger
.
calculateTotalSize
(
of
:
PhotoAndVideoMananger
.
mananger
.
getPHAsssetwithIDs
(
ids
:
dataArray1
)
!
,
progress
:
{
fileSiez
,
index
in
// 不做处理
},
completion
:
{
fileSize
,
index
in
// model1.assets = data1
// model1.allFileSize = Double(fileSize)
// PhotoDataManager.manager.saveToFileSystem(model: allModel)
let
dataUpdated
=
Notification
.
Name
(
"HomeDupImageResourceUpdate"
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
})
PhotoAndVideoMananger
.
mananger
.
calculateTotalSize
(
of
:
PhotoAndVideoMananger
.
mananger
.
getPHAsssetwithIDs
(
ids
:
dataArray2
)
!
,
progress
:
{
fileSize
,
index
in
// 不做处理
},
completion
:
{
fileSize
,
index
in
model2
.
assets
=
data2
model2
.
allFileSize
=
Double
(
fileSize
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
allModel
)
let
dataUpdated
=
Notification
.
Name
(
"HomeSimilarImageResourceUpdate"
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
})
}
completionHandler
:
{
data1
,
data2
in
var
dataArray1
:
[
AssetModel
]
=
[]
var
dataArray2
:
[
AssetModel
]
=
[]
for
item
in
data1
{
dataArray1
=
dataArray1
+
item
}
for
item
in
data2
{
dataArray2
=
dataArray2
+
item
}
}
PhotoAndVideoMananger
.
mananger
.
calculateTotalSize
(
of
:
PhotoAndVideoMananger
.
mananger
.
getPHAsssetwithIDs
(
ids
:
dataArray
1
)
!
,
progress
:
{
fileSiez
,
index
in
PhotoAndVideoMananger
.
mananger
.
calculateTotalSize
(
of
:
PhotoAndVideoMananger
.
mananger
.
getPHAsssetwithIDs
(
ids
:
dataArray
)
!
,
progress
:
{
fileSiez
,
index
in
// 不做处理
// 不做处理
},
completion
:
{
fileSize
,
index
in
},
completion
:
{
fileSize
,
index
in
model1
.
assets
=
data1
model1
.
assets
=
similarGroups
model1
.
allFileSize
=
Double
(
fileSize
)
model1
.
allFileSize
=
Double
(
fileSize
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
allModel
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
allModel
)
let
dataUpdated
=
Notification
.
Name
(
"HomeDupImageResourceUpdate"
)
let
dataUpdated
=
Notification
.
Name
(
"HomeDupImageResourceUpdate"
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
})
})
PhotoAndVideoMananger
.
mananger
.
calculateTotalSize
(
of
:
PhotoAndVideoMananger
.
mananger
.
getPHAsssetwithIDs
(
ids
:
dataArray1
)
!
,
progress
:
{
fileSize
,
index
in
}
completion
:
{
model
in
Print
(
"处理重复图片结束"
)
}
Print
(
"正在处理相似图片"
)
PhotoAndVideoMananger
.
mananger
.
groupSimilarImages
(
assets
:
PhotoAndVideoMananger
.
mananger
.
allAssets
)
{
(
similarGroups
)
in
var
dataArray
:
[
AssetModel
]
=
[]
for
item
in
similarGroups
{
dataArray
=
dataArray
+
item
}
PhotoAndVideoMananger
.
mananger
.
calculateTotalSize
(
of
:
PhotoAndVideoMananger
.
mananger
.
getPHAsssetwithIDs
(
ids
:
dataArray
)
!
,
progress
:
{
fileSiez
,
index
in
// 不做处理
// 不做处理
},
completion
:
{
fileSize
,
index
in
},
completion
:
{
fileSize
,
index
in
model2
.
assets
=
data2
model2
.
assets
=
similarGroups
model2
.
allFileSize
=
Double
(
fileSize
)
model2
.
allFileSize
=
Double
(
fileSize
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
allModel
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
allModel
)
let
dataUpdated
=
Notification
.
Name
(
"HomeSimilarImageResourceUpdate"
)
let
dataUpdated
=
Notification
.
Name
(
"HomeSimilarImageResourceUpdate"
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
})
})
group
.
leave
()
Print
(
"处理重复和相似图片结束"
)
}
completion
:
{
similarGroups
in
Print
(
"处理相似图片结束"
)
}
}
// 视频数据
// 视频数据
...
@@ -262,43 +219,19 @@ class PhotoDataManager {
...
@@ -262,43 +219,19 @@ class PhotoDataManager {
})
})
}
}
// 相似截图
Print
(
"正在处理相似截图"
)
Print
(
"正在处理相似截图"
)
PhotoAndVideoMananger
.
mananger
.
dealSimilarPhotos
(
assets
:
PhotoAndVideoMananger
.
mananger
.
screenShotAssets
,
type
:
1
,
threshold
:
0.99
)
{
data1
,
data2
in
PhotoAndVideoMananger
.
mananger
.
groupSimilarImages
(
assets
:
PhotoAndVideoMananger
.
mananger
.
screenShotAssets
)
{
(
similarGroups
)
in
var
dataArray1
:
[
AssetModel
]
=
[]
model4
.
assets
=
similarGroups
for
item
in
data1
{
model4
.
allFileSize
=
0
dataArray1
=
dataArray1
+
item
}
PhotoAndVideoMananger
.
mananger
.
calculateTotalSize
(
of
:
PhotoAndVideoMananger
.
mananger
.
getPHAsssetwithIDs
(
ids
:
dataArray1
)
!
,
progress
:
{
fileSiez
,
index
in
// 不做处理
},
completion
:
{
fileSize
,
index
in
model4
.
assets
=
data2
model4
.
allFileSize
=
Double
(
fileSize
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
allModel
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
allModel
)
let
dataUpdated
=
Notification
.
Name
(
"HomeSimilarScreenshotResourceUpdate"
)
let
dataUpdated
=
Notification
.
Name
(
"HomeSimilarScreenshotResourceUpdate"
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
}
completion
:
{
similarGroups
in
})
Print
(
"处理相似截图结束"
)
}
completionHandler
:
{
data1
,
data2
in
var
dataArray1
:
[
AssetModel
]
=
[]
for
item
in
data1
{
dataArray1
=
dataArray1
+
item
}
PhotoAndVideoMananger
.
mananger
.
calculateTotalSize
(
of
:
PhotoAndVideoMananger
.
mananger
.
getPHAsssetwithIDs
(
ids
:
dataArray1
)
!
,
progress
:
{
fileSiez
,
index
in
// 不做处理
},
completion
:
{
fileSize
,
index
in
model4
.
assets
=
data2
model4
.
allFileSize
=
Double
(
fileSize
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
allModel
)
let
dataUpdated
=
Notification
.
Name
(
"HomeSimilarScreenshotResourceUpdate"
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
})
}
}
// 截图数据
// 截图数据
Print
(
"正在处理截图"
)
Print
(
"正在处理截图"
)
group
.
enter
()
PhotoAndVideoMananger
.
mananger
.
dealScreenShotData
{
data
in
PhotoAndVideoMananger
.
mananger
.
dealScreenShotData
{
data
in
var
dataArray
:
[
AssetModel
]
=
[]
var
dataArray
:
[
AssetModel
]
=
[]
for
item
in
data
{
for
item
in
data
{
...
@@ -312,7 +245,6 @@ class PhotoDataManager {
...
@@ -312,7 +245,6 @@ class PhotoDataManager {
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
allModel
)
PhotoDataManager
.
manager
.
saveToFileSystem
(
model
:
allModel
)
let
dataUpdated
=
Notification
.
Name
(
"HomeScreenShotResourceUpdate"
)
let
dataUpdated
=
Notification
.
Name
(
"HomeScreenShotResourceUpdate"
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
NotificationCenter
.
default
.
post
(
name
:
dataUpdated
,
object
:
nil
)
group
.
leave
()
Print
(
"处理截图结束"
)
Print
(
"处理截图结束"
)
})
})
}
}
...
...
PhoneManager/Class/Session/Pay/ViewController/PayCompletedViewController.swift
0 → 100644
View file @
17db8452
//
// PayCompletedViewController.swift
// PhoneManager
//
// Created by edy on 2025/4/16.
//
import
Foundation
class
PayCompletedViewController
:
BaseViewController
{
lazy
var
logoImageView
:
UIImageView
=
{
let
view
=
UIImageView
()
view
.
layer
.
cornerRadius
=
22.5
view
.
clipsToBounds
=
true
view
.
image
=
UIImage
(
named
:
"logo_phone_manager"
)
return
view
}()
lazy
var
titleLabel
:
UILabel
=
{
let
view
=
UILabel
()
view
.
text
=
"Phone Manager Pro Edition"
view
.
textColor
=
UIColor
(
red
:
0.2
,
green
:
0.2
,
blue
:
0.2
,
alpha
:
1
)
view
.
font
=
UIFont
.
systemFont
(
ofSize
:
18
,
weight
:
.
bold
)
view
.
textAlignment
=
.
center
return
view
}()
lazy
var
detailTitleLabel
:
UILabel
=
{
let
view
=
UILabel
()
view
.
text
=
"You can now access all Phone Manager features without restrictions"
view
.
textColor
=
UIColor
(
red
:
0.4
,
green
:
0.4
,
blue
:
0.4
,
alpha
:
1
)
view
.
font
=
UIFont
.
systemFont
(
ofSize
:
14
,
weight
:
.
regular
)
view
.
textAlignment
=
.
center
view
.
numberOfLines
=
0
return
view
}()
lazy
var
closeButton
:
UIButton
=
{
let
view
=
UIButton
()
view
.
backgroundColor
=
.
clear
view
.
setImage
(
UIImage
(
named
:
"ic_close_charging"
),
for
:
.
normal
)
view
.
addTarget
(
self
,
action
:
#selector(
closeCurrentPage
)
,
for
:
.
touchUpInside
)
return
view
}()
override
func
viewDidLoad
()
{
super
.
viewDidLoad
()
setUI
()
}
func
setUI
(){
self
.
view
.
addSubview
(
self
.
closeButton
)
self
.
view
.
addSubview
(
self
.
logoImageView
)
self
.
view
.
addSubview
(
self
.
titleLabel
)
self
.
view
.
addSubview
(
self
.
detailTitleLabel
)
self
.
closeButton
.
snp
.
makeConstraints
{
make
in
make
.
right
.
equalToSuperview
()
.
offset
(
-
15
)
make
.
top
.
equalToSuperview
()
.
offset
(
statusBarHeight
+
15
)
make
.
width
.
height
.
equalTo
(
28
)
}
self
.
logoImageView
.
snp
.
makeConstraints
{
make
in
make
.
width
.
height
.
equalTo
(
150
)
make
.
top
.
equalToSuperview
()
.
offset
(
210
)
make
.
centerX
.
equalToSuperview
()
}
self
.
titleLabel
.
snp
.
makeConstraints
{
make
in
make
.
top
.
equalTo
(
self
.
logoImageView
.
snp
.
bottom
)
.
offset
(
40
)
make
.
left
.
equalToSuperview
()
.
offset
(
30
)
make
.
right
.
equalToSuperview
()
.
offset
(
-
30
)
make
.
height
.
equalTo
(
25
)
}
self
.
detailTitleLabel
.
snp
.
makeConstraints
{
make
in
make
.
top
.
equalTo
(
self
.
titleLabel
.
snp
.
bottom
)
.
offset
(
4
)
make
.
left
.
equalToSuperview
()
.
offset
(
30
)
make
.
right
.
equalToSuperview
()
.
offset
(
-
30
)
make
.
height
.
equalTo
(
40
)
}
}
}
extension
PayCompletedViewController
{
@objc
func
closeCurrentPage
(){
self
.
dismiss
(
animated
:
true
)
}
}
PhoneManager/Class/Session/Settings/View/SettingViewHeaderCell.swift
View file @
17db8452
...
@@ -21,7 +21,12 @@ class SettingViewHeaderCell : UITableViewCell {
...
@@ -21,7 +21,12 @@ class SettingViewHeaderCell : UITableViewCell {
lazy
var
fLabel
:
UILabel
=
{
lazy
var
fLabel
:
UILabel
=
{
let
label
=
UILabel
();
let
label
=
UILabel
();
if
HomePayModel
.
share
.
isNoAd
==
false
{
label
.
text
=
"Unlock Unlimited Access"
label
.
text
=
"Unlock Unlimited Access"
}
else
{
label
.
text
=
"Phone Manager Pro Edition"
}
label
.
font
=
UIFont
(
name
:
"PingFang SC-Bold"
,
size
:
18
)
label
.
font
=
UIFont
(
name
:
"PingFang SC-Bold"
,
size
:
18
)
label
.
textColor
=
UIColor
(
red
:
0.2
,
green
:
0.2
,
blue
:
0.2
,
alpha
:
1
)
label
.
textColor
=
UIColor
(
red
:
0.2
,
green
:
0.2
,
blue
:
0.2
,
alpha
:
1
)
label
.
textAlignment
=
.
center
label
.
textAlignment
=
.
center
...
@@ -29,11 +34,17 @@ class SettingViewHeaderCell : UITableViewCell {
...
@@ -29,11 +34,17 @@ class SettingViewHeaderCell : UITableViewCell {
}()
}()
lazy
var
sLabel
:
UILabel
=
{
lazy
var
sLabel
:
UILabel
=
{
let
label
=
UILabel
();
let
label
=
UILabel
()
label
.
numberOfLines
=
0
if
HomePayModel
.
share
.
isNoAd
==
false
{
label
.
text
=
"Enjoy unlimited access with all Cleanup features"
label
.
text
=
"Enjoy unlimited access with all Cleanup features"
}
else
{
label
.
text
=
"You can now access all Phone Manager features without restrictions"
}
label
.
font
=
UIFont
(
name
:
"PingFang SC-Regular"
,
size
:
14
)
label
.
font
=
UIFont
(
name
:
"PingFang SC-Regular"
,
size
:
14
)
label
.
textColor
=
UIColor
(
red
:
0.4
,
green
:
0.4
,
blue
:
0.4
,
alpha
:
1
)
label
.
textColor
=
UIColor
(
red
:
0.4
,
green
:
0.4
,
blue
:
0.4
,
alpha
:
1
)
label
.
textAlignment
=
.
center
label
.
textAlignment
=
.
center
return
label
return
label
}()
}()
...
@@ -90,7 +101,11 @@ class SettingViewHeaderCell : UITableViewCell {
...
@@ -90,7 +101,11 @@ class SettingViewHeaderCell : UITableViewCell {
make
.
left
.
equalToSuperview
()
.
offset
(
15
)
make
.
left
.
equalToSuperview
()
.
offset
(
15
)
make
.
right
.
equalToSuperview
()
.
offset
(
-
15
)
make
.
right
.
equalToSuperview
()
.
offset
(
-
15
)
make
.
top
.
equalTo
(
fLabel
.
snp
.
bottom
)
.
offset
(
4
)
make
.
top
.
equalTo
(
fLabel
.
snp
.
bottom
)
.
offset
(
4
)
if
HomePayModel
.
share
.
isNoAd
==
false
{
make
.
height
.
equalTo
(
20
)
make
.
height
.
equalTo
(
20
)
}
else
{
make
.
height
.
equalTo
(
42
)
}
}
}
self
.
preButtonImageView
.
snp
.
makeConstraints
{
make
in
self
.
preButtonImageView
.
snp
.
makeConstraints
{
make
in
...
@@ -101,7 +116,11 @@ class SettingViewHeaderCell : UITableViewCell {
...
@@ -101,7 +116,11 @@ class SettingViewHeaderCell : UITableViewCell {
self
.
moreButton
.
snp
.
makeConstraints
{
make
in
self
.
moreButton
.
snp
.
makeConstraints
{
make
in
make
.
width
.
equalTo
(
245
)
make
.
width
.
equalTo
(
245
)
if
HomePayModel
.
share
.
isNoAd
==
false
{
make
.
height
.
equalTo
(
46
)
make
.
height
.
equalTo
(
46
)
}
else
{
make
.
height
.
equalTo
(
0
)
}
make
.
centerX
.
equalToSuperview
()
make
.
centerX
.
equalToSuperview
()
make
.
top
.
equalTo
(
self
.
sLabel
.
snp
.
bottom
)
.
offset
(
20
)
make
.
top
.
equalTo
(
self
.
sLabel
.
snp
.
bottom
)
.
offset
(
20
)
}
}
...
...
PhoneManager/Class/Session/Settings/ViewController/SettingViewController.swift
View file @
17db8452
...
@@ -138,7 +138,11 @@ class SettingViewController : BaseViewController , UITableViewDelegate, UITableV
...
@@ -138,7 +138,11 @@ class SettingViewController : BaseViewController , UITableViewDelegate, UITableV
func
tableView
(
_
tableView
:
UITableView
,
heightForRowAt
indexPath
:
IndexPath
)
->
CGFloat
{
func
tableView
(
_
tableView
:
UITableView
,
heightForRowAt
indexPath
:
IndexPath
)
->
CGFloat
{
if
indexPath
.
section
==
0
{
if
indexPath
.
section
==
0
{
if
HomePayModel
.
share
.
isNoAd
==
false
{
return
385
return
385
}
else
{
return
339
}
}
}
return
60
return
60
}
}
...
...
PhoneManager/Class/Tool/Class/PhotoAndVideoMananger/PhotoAndVideoMananger.swift
View file @
17db8452
...
@@ -12,6 +12,8 @@ import Vision
...
@@ -12,6 +12,8 @@ import Vision
import
CoreML
import
CoreML
import
HXPhotoPicker
import
HXPhotoPicker
import
CryptoKit
import
CryptoKit
import
Photos
import
CoreImage
enum
SoureceType
{
enum
SoureceType
{
case
Photo
case
Photo
...
@@ -431,7 +433,7 @@ class PhotoAndVideoMananger {
...
@@ -431,7 +433,7 @@ class PhotoAndVideoMananger {
// 1. 判断创建时间(误差 < 1s)
// 1. 判断创建时间(误差 < 1s)
let
timeC
=
abs
((
asset1
.
creationDate
?
.
timeIntervalSince1970
??
0
)
-
(
asset2
.
creationDate
?
.
timeIntervalSince1970
??
0
))
let
timeC
=
abs
((
asset1
.
creationDate
?
.
timeIntervalSince1970
??
0
)
-
(
asset2
.
creationDate
?
.
timeIntervalSince1970
??
0
))
//
//
if
timeC
>
1000
{
if
timeC
>
1000
{
return
false
return
false
...
@@ -450,7 +452,7 @@ class PhotoAndVideoMananger {
...
@@ -450,7 +452,7 @@ class PhotoAndVideoMananger {
return
false
return
false
}
}
// // 4. 判断文件大小
// // 4. 判断文件大小
let
resource1
=
PHAssetResource
.
assetResources
(
for
:
asset1
)
.
first
let
resource1
=
PHAssetResource
.
assetResources
(
for
:
asset1
)
.
first
let
resource2
=
PHAssetResource
.
assetResources
(
for
:
asset2
)
.
first
let
resource2
=
PHAssetResource
.
assetResources
(
for
:
asset2
)
.
first
...
@@ -896,6 +898,179 @@ class PhotoAndVideoMananger {
...
@@ -896,6 +898,179 @@ class PhotoAndVideoMananger {
}
}
}
}
// 计算图片的哈希值
func
calculateHash
(
for
asset
:
PHAsset
)
->
String
?
{
let
manager
=
PHImageManager
.
default
()
let
options
=
PHImageRequestOptions
()
options
.
isSynchronous
=
false
// 异步请求
options
.
deliveryMode
=
.
fastFormat
// 快速格式
let
semaphore
=
DispatchSemaphore
(
value
:
0
)
var
hashString
:
String
?
manager
.
requestImageData
(
for
:
asset
,
options
:
options
)
{
(
imageData
,
_
,
_
,
_
)
in
defer
{
semaphore
.
signal
()
}
if
let
data
=
imageData
{
let
hash
=
Insecure
.
MD5
.
hash
(
data
:
data
)
hashString
=
hash
.
compactMap
{
String
(
format
:
"%02x"
,
$0
)
}
.
joined
()
}
}
semaphore
.
wait
()
return
hashString
}
// 分组重复图片
func
groupDuplicateImages
(
assets
:
[
PHAsset
],
progressCompletion
:
@escaping
([[
AssetModel
]])
->
Void
,
completion
:
@escaping
([[
AssetModel
]])
->
Void
)
{
DispatchQueue
.
global
()
.
async
{
var
hashToAssets
:
[
String
:
[
PHAsset
]]
=
[:]
var
allDuplicateGroups
:
[[
AssetModel
]]
=
[]
let
chunkSize
=
10
// 每次处理 10 张图片
func
isGroupDuplicate
(
_
newGroup
:
[
AssetModel
])
->
Bool
{
for
existingGroup
in
allDuplicateGroups
{
if
Set
(
newGroup
.
map
{
$0
.
localIdentifier
})
==
Set
(
existingGroup
.
map
{
$0
.
localIdentifier
})
{
return
true
}
}
return
false
}
for
chunk
in
assets
.
chunked
(
into
:
chunkSize
)
{
for
asset
in
chunk
{
if
let
hash
=
self
.
calculateHash
(
for
:
asset
)
{
hashToAssets
[
hash
,
default
:
[]]
.
append
(
asset
)
if
hashToAssets
[
hash
]
!.
count
>
1
&&
hashToAssets
[
hash
]
!.
count
==
2
{
var
currentGroup
:
[
AssetModel
]
=
[]
for
asset
in
hashToAssets
[
hash
]
!
{
currentGroup
.
append
(
AssetModel
(
localIdentifier
:
asset
.
localIdentifier
,
assetSize
:
self
.
findAssetSize
(
asset
:
asset
),
createDate
:
asset
.
creationDate
!
))
}
if
!
isGroupDuplicate
(
currentGroup
)
{
allDuplicateGroups
.
append
(
currentGroup
)
}
// 持续返回已处理好的分组数据
progressCompletion
(
allDuplicateGroups
)
}
}
}
}
// 过滤出重复的分组
let
duplicateGroups
=
hashToAssets
.
filter
{
$0
.
value
.
count
>
1
}
.
map
{
$0
.
value
}
for
(
_
,
group
)
in
duplicateGroups
.
enumerated
()
{
var
currentGroup
:
[
AssetModel
]
=
[]
for
asset
in
group
{
currentGroup
.
append
(
AssetModel
(
localIdentifier
:
asset
.
localIdentifier
,
assetSize
:
self
.
findAssetSize
(
asset
:
asset
),
createDate
:
asset
.
creationDate
!
))
}
if
!
isGroupDuplicate
(
currentGroup
)
{
allDuplicateGroups
.
append
(
currentGroup
)
}
// 持续返回已处理好的分组数据
progressCompletion
(
allDuplicateGroups
)
}
// 全部找完之后返回所有重复组
completion
(
allDuplicateGroups
)
}
}
// 计算图片的 PHash 值
func
pHash
(
for
image
:
UIImage
)
->
String
{
let
size
=
CGSize
(
width
:
32
,
height
:
32
)
UIGraphicsBeginImageContextWithOptions
(
size
,
false
,
1.0
)
image
.
draw
(
in
:
CGRect
(
origin
:
.
zero
,
size
:
size
))
let
resizedImage
=
UIGraphicsGetImageFromCurrentImageContext
()
UIGraphicsEndImageContext
()
guard
let
ciImage
=
CIImage
(
image
:
resizedImage
!
)
else
{
return
""
}
let
context
=
CIContext
()
let
filter
=
CIFilter
(
name
:
"CILanczosScaleTransform"
)
filter
?
.
setValue
(
ciImage
,
forKey
:
kCIInputImageKey
)
filter
?
.
setValue
(
32
/
ciImage
.
extent
.
width
,
forKey
:
kCIInputScaleKey
)
filter
?
.
setValue
(
1.0
,
forKey
:
kCIInputAspectRatioKey
)
guard
let
outputImage
=
filter
?
.
outputImage
else
{
return
""
}
let
cgImage
=
context
.
createCGImage
(
outputImage
,
from
:
outputImage
.
extent
)
!
let
data
=
cgImage
.
dataProvider
!.
data
!
if
let
pointer
=
CFDataGetBytePtr
(
data
)
{
var
hash
=
""
for
i
in
0
..<
8
{
hash
+=
String
(
format
:
"%02x"
,
pointer
[
i
])
}
return
hash
}
return
""
}
// 计算两个哈希值的汉明距离
func
hammingDistance
(
_
hash1
:
String
,
_
hash2
:
String
)
->
Int
{
var
distance
=
0
for
(
char1
,
char2
)
in
zip
(
hash1
,
hash2
)
{
let
int1
=
Int
(
String
(
char1
),
radix
:
16
)
!
let
int2
=
Int
(
String
(
char2
),
radix
:
16
)
!
let
xor
=
int1
^
int2
distance
+=
String
(
xor
,
radix
:
2
)
.
filter
{
$0
==
"1"
}
.
count
}
return
distance
}
func
groupSimilarImages
(
assets
:
[
PHAsset
],
progressCompletion
:
@escaping
([[
AssetModel
]])
->
Void
,
completion
:
@escaping
([[
AssetModel
]])
->
Void
)
{
DispatchQueue
.
global
()
.
async
{
var
assetModels
:
[
AssetModel
]
=
[]
var
hashes
:
[
String
:
AssetModel
]
=
[:]
var
groupedImages
:
[[
AssetModel
]]
=
[]
for
asset
in
assets
{
_
=
asset
.
pixelWidth
*
asset
.
pixelHeight
let
createDate
=
asset
.
creationDate
??
Date
()
let
model
=
AssetModel
(
localIdentifier
:
asset
.
localIdentifier
,
assetSize
:
self
.
findAssetSize
(
asset
:
asset
),
createDate
:
createDate
)
assetModels
.
append
(
model
)
let
manager
=
PHImageManager
.
default
()
manager
.
requestImage
(
for
:
asset
,
targetSize
:
CGSize
(
width
:
32
,
height
:
32
),
contentMode
:
.
aspectFit
,
options
:
nil
)
{
(
image
,
_
)
in
if
let
image
=
image
{
let
hash
=
self
.
pHash
(
for
:
image
)
if
hash
!=
""
{
hashes
[
hash
]
=
model
}
}
}
}
var
usedHashes
:
Set
<
String
>
=
[]
for
(
hash1
,
model1
)
in
hashes
{
if
usedHashes
.
contains
(
hash1
)
{
continue
}
var
similarModels
:
[
AssetModel
]
=
[
model1
]
usedHashes
.
insert
(
hash1
)
for
(
hash2
,
model2
)
in
hashes
{
if
usedHashes
.
contains
(
hash2
)
{
continue
}
let
distance
=
self
.
hammingDistance
(
hash1
,
hash2
)
if
distance
<
4
{
// 汉明距离小于 5 认为相似
similarModels
.
append
(
model2
)
usedHashes
.
insert
(
hash2
)
}
}
if
similarModels
.
count
>=
2
{
groupedImages
.
append
(
similarModels
)
// 每次找到新的相似组,通过 progressCompletion 回调返回当前已处理好的分组数据
DispatchQueue
.
main
.
async
{
progressCompletion
(
groupedImages
)
}
}
}
DispatchQueue
.
main
.
async
{
completion
(
groupedImages
)
}
}
}
}
}
...
@@ -965,3 +1140,11 @@ class SecretPhotoManager: NSObject, PhotoPickerControllerDelegate , CameraContro
...
@@ -965,3 +1140,11 @@ class SecretPhotoManager: NSObject, PhotoPickerControllerDelegate , CameraContro
}
}
extension
Array
{
func
chunked
(
into
size
:
Int
)
->
[[
Element
]]
{
return
stride
(
from
:
0
,
to
:
count
,
by
:
size
)
.
map
{
Array
(
self
[
$0
..<
Swift
.
min
(
$0
+
size
,
count
)])
}
}
}
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