Commit 0d6a6274 authored by wanglei's avatar wanglei

...

parent 78d5d7bc
...@@ -136,6 +136,11 @@ dependencies { ...@@ -136,6 +136,11 @@ dependencies {
//work //work
implementation("androidx.work:work-runtime-ktx:2.7.1") // 请使用最新版本 implementation("androidx.work:work-runtime-ktx:2.7.1") // 请使用最新版本
implementation("com.github.pokercc:ExpandableRecyclerView:0.9.3")
implementation("com.github.bumptech.glide:glide:4.16.0")
annotationProcessor("com.github.bumptech.glide:glide:4.16.0")
} }
...@@ -19,9 +19,9 @@ ...@@ -19,9 +19,9 @@
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/logo" android:icon="@mipmap/logo"
android:label="@string/app_name" android:label="@string/app_name"
android:largeHeap="true"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/logo" android:roundIcon="@mipmap/logo"
android:largeHeap="true"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.PDFViewerScannerWhite" android:theme="@style/Theme.PDFViewerScannerWhite"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
...@@ -179,6 +179,37 @@ ...@@ -179,6 +179,37 @@
android:screenOrientation="portrait" android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" /> tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".mix.ScreenshotCleanActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".mix.SimilarPhotosActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".mix.WhatsappCleanActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".mix.WhatsappCleanDetailActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".mix.LargeFileCleanActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider" android:authorities="${applicationId}.provider"
......
{"v":"5.6.3","fr":25,"ip":0,"op":36,"w":870,"h":870,"nm":"1-扫描动画","ddd":0,"assets":[{"id":"image_0","w":120,"h":120,"u":"images/","p":"img_0.png","e":0},{"id":"image_1","w":198,"h":198,"u":"images/","p":"img_1.png","e":0},{"id":"image_2","w":678,"h":678,"u":"images/","p":"img_2.png","e":0},{"id":"image_3","w":678,"h":678,"u":"images/","p":"img_3.png","e":0}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[60],"e":[0]},{"t":40}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[141,893,0],"e":[437,429,0],"to":[0,0,0],"ti":[0,0,0]},{"t":35}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":5,"s":[60],"e":[0]},{"t":25}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":5,"s":[892,226,0],"e":[462,460,0],"to":[0,0,0],"ti":[0,0,0]},{"t":20}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":5,"s":[60],"e":[0]},{"t":25}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":5,"s":[-81,761,0],"e":[431,435,0],"to":[0,0,0],"ti":[0,0,0]},{"t":30}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[22,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":10,"s":[60],"e":[0]},{"t":30}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":10,"s":[574,-48,0],"e":[434,408,0],"to":[0,0,0],"ti":[0,0,0]},{"t":30}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":0,"s":[60],"e":[0]},{"t":20}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":0,"s":[-48,-40,0],"e":[444,444,0],"to":[0,0,0],"ti":[0,0,0]},{"t":18}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[36,36],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.2],"y":[0]},"t":16,"s":[60],"e":[0]},{"t":36}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":16,"s":[1018,824,0],"e":[446,462,0],"to":[0,0,0],"ti":[0,0,0]},{"t":31}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[26,26],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":2,"nm":"icon_clean_home","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[60,60,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":2,"nm":"小圆","refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[99,99,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":2,"nm":"转圈园","refId":"image_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0],"e":[360]},{"t":35}],"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[339,339,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":2,"nm":"背景圆","refId":"image_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[339,339,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
{"v":"5.6.3","fr":25,"ip":0,"op":36,"w":870,"h":870,"nm":"1-扫描动画","ddd":0,"assets":[{"id":"image_0","w":120,"h":120,"u":"images/","p":"img_0.png","e":0},{"id":"image_1","w":198,"h":198,"u":"images/","p":"img_1.png","e":0},{"id":"image_2","w":678,"h":678,"u":"images/","p":"img_2.png","e":0},{"id":"image_3","w":678,"h":678,"u":"images/","p":"img_3.png","e":0}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[60],"e":[0]},{"t":40}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[141,893,0],"e":[437,429,0],"to":[0,0,0],"ti":[0,0,0]},{"t":35}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":5,"s":[60],"e":[0]},{"t":25}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":5,"s":[892,226,0],"e":[462,460,0],"to":[0,0,0],"ti":[0,0,0]},{"t":20}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":5,"s":[60],"e":[0]},{"t":25}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":5,"s":[-81,761,0],"e":[431,435,0],"to":[0,0,0],"ti":[0,0,0]},{"t":30}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[22,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":10,"s":[60],"e":[0]},{"t":30}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":10,"s":[574,-48,0],"e":[434,408,0],"to":[0,0,0],"ti":[0,0,0]},{"t":30}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":0,"s":[60],"e":[0]},{"t":20}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":0,"s":[-48,-40,0],"e":[444,444,0],"to":[0,0,0],"ti":[0,0,0]},{"t":18}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[36,36],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.2],"y":[0]},"t":16,"s":[60],"e":[0]},{"t":36}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":16,"s":[1018,824,0],"e":[446,462,0],"to":[0,0,0],"ti":[0,0,0]},{"t":31}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[26,26],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":2,"nm":"icon_clean_home","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[60,60,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":2,"nm":"小圆","refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[99,99,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":2,"nm":"转圈园","refId":"image_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0],"e":[360]},{"t":35}],"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[339,339,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":2,"nm":"背景圆","refId":"image_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[339,339,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
{"v":"5.6.3","fr":25,"ip":0,"op":36,"w":870,"h":870,"nm":"1-扫描动画","ddd":0,"assets":[{"id":"image_0","w":120,"h":120,"u":"images/","p":"img_0.png","e":0},{"id":"image_1","w":198,"h":198,"u":"images/","p":"img_1.png","e":0},{"id":"image_2","w":678,"h":678,"u":"images/","p":"img_2.png","e":0},{"id":"image_3","w":678,"h":678,"u":"images/","p":"img_3.png","e":0}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[60],"e":[0]},{"t":40}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[141,893,0],"e":[437,429,0],"to":[0,0,0],"ti":[0,0,0]},{"t":35}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":5,"s":[60],"e":[0]},{"t":25}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":5,"s":[892,226,0],"e":[462,460,0],"to":[0,0,0],"ti":[0,0,0]},{"t":20}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":5,"s":[60],"e":[0]},{"t":25}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":5,"s":[-81,761,0],"e":[431,435,0],"to":[0,0,0],"ti":[0,0,0]},{"t":30}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[22,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":10,"s":[60],"e":[0]},{"t":30}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":10,"s":[574,-48,0],"e":[434,408,0],"to":[0,0,0],"ti":[0,0,0]},{"t":30}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":0,"s":[60],"e":[0]},{"t":20}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":0,"s":[-48,-40,0],"e":[444,444,0],"to":[0,0,0],"ti":[0,0,0]},{"t":18}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[36,36],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.2],"y":[0]},"t":16,"s":[60],"e":[0]},{"t":36}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":16,"s":[1018,824,0],"e":[446,462,0],"to":[0,0,0],"ti":[0,0,0]},{"t":31}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[26,26],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":2,"nm":"icon_clean_home","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[60,60,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":2,"nm":"小圆","refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[99,99,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":2,"nm":"转圈园","refId":"image_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0],"e":[360]},{"t":35}],"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[339,339,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":2,"nm":"背景圆","refId":"image_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[339,339,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
{"v":"5.6.3","fr":25,"ip":0,"op":36,"w":870,"h":870,"nm":"1-扫描动画","ddd":0,"assets":[{"id":"image_0","w":120,"h":120,"u":"images/","p":"img_0.png","e":0},{"id":"image_1","w":198,"h":198,"u":"images/","p":"img_1.png","e":0},{"id":"image_2","w":678,"h":678,"u":"images/","p":"img_2.png","e":0},{"id":"image_3","w":678,"h":678,"u":"images/","p":"img_3.png","e":0}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[60],"e":[0]},{"t":40}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[141,893,0],"e":[437,429,0],"to":[0,0,0],"ti":[0,0,0]},{"t":35}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":5,"s":[60],"e":[0]},{"t":25}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":5,"s":[892,226,0],"e":[462,460,0],"to":[0,0,0],"ti":[0,0,0]},{"t":20}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":5,"s":[60],"e":[0]},{"t":25}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":5,"s":[-81,761,0],"e":[431,435,0],"to":[0,0,0],"ti":[0,0,0]},{"t":30}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[22,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":10,"s":[60],"e":[0]},{"t":30}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":10,"s":[574,-48,0],"e":[434,408,0],"to":[0,0,0],"ti":[0,0,0]},{"t":30}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":0,"s":[60],"e":[0]},{"t":20}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":0,"s":[-48,-40,0],"e":[444,444,0],"to":[0,0,0],"ti":[0,0,0]},{"t":18}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[36,36],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.2],"y":[0]},"t":16,"s":[60],"e":[0]},{"t":36}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":16,"s":[1018,824,0],"e":[446,462,0],"to":[0,0,0],"ti":[0,0,0]},{"t":31}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[26,26],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":2,"nm":"icon_clean_home","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[60,60,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":2,"nm":"小圆","refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[99,99,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":2,"nm":"转圈园","refId":"image_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0],"e":[360]},{"t":35}],"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[339,339,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":2,"nm":"背景圆","refId":"image_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[339,339,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
...@@ -61,6 +61,7 @@ object ConstObject { ...@@ -61,6 +61,7 @@ object ConstObject {
const val FUNCTION_SCAM_JUNK = "function_scam_junk" const val FUNCTION_SCAM_JUNK = "function_scam_junk"
const val FUNCTION_APP_PROCESS = "function_app_process" const val FUNCTION_APP_PROCESS = "function_app_process"
const val FUNCTION_SCREEN_SHORT = "function_app_screen"
var ifAgreePrivacy = false var ifAgreePrivacy = false
get() { get() {
......
package com.base.pdfviewerscannerwhite.bean
class FunctionUIBean(
val key: String = "",
val icon: Int = 0,
val desc: String = "",
) {
companion object {
const val KEY_PROCESS = "key_process"
const val KEY_FILE_MANAGER = "key_file_manager"
const val KEY_MERGE = "key_merge"
const val KEY_SPLIT = "key_split"
const val KEY_SCAN = "key_scan"
const val KEY_LOCK = "key_lock"
const val KEY_UNLOCK = "key_unlock"
const val KEY_IMAGE = "key_image"
const val KEY_SCREENSHOT = "key_screenshot"
const val KEY_SIMILAR = "key_similar"
const val KEY_WHATSAPP = "key_whatsapp"
const val KEY_LARGE_FILE = "key_large_file"
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.bean
data class SimilarBean(
val url: String,
val hashCode: String,
val avgPixel: Int,
val size: Long,
var isSelect: Boolean,
var items: MutableList<SimilarBean> = mutableListOf()
)
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.content.Context
import com.base.pdfviewerscannerwhite.utils.ActivityLauncher
object ActivityJumpHelps {
private var isShowStoragePermission = false
fun start(
context: Context,
launcher: ActivityLauncher,
nameId: Int
) {
when(nameId) {
// R.string.junk_scan -> requestPermission(context, launcher) { CleanJunkActivity.start(context) }
// R.string.app_process -> AppProcessActivity.start(context)
// R.string.whatsapp_clean -> requestPermission(context, launcher) { WhatsappCleanActivity.start(context) }
// R.string.screenshot_clean -> requestPermission(context, launcher) { ScreenshotCleanActivity.start(context) }
// R.string.similar_photos -> requestPermission(context, launcher) { com.base.pdfviewerscannerwhite.mix.SimilarPhotosActivity.start(context) }
}
}
fun startFunction(
context: Context,
launcher: ActivityLauncher,
notificationId: Int
) {
when (notificationId) {
// ID_CLEAN_JUNK -> requestPermission(context, launcher) { CleanJunkActivity.start(context) }
// ID_WHATSAPP -> requestPermission(context, launcher) { com.base.scanqrclear.luma.WhatsappCleanActivity.start(context) }
// ID_LARGE_FILE_CLEANER -> requestPermission(context, launcher) { com.base.pdfviewerscannerwhite.mix.LargeFileCleanActivity.start(context) }
// ID_PHOTO_COMPRESSION -> requestPermission(context, launcher) { PhotoCompressionActivity.start(context) }
// ID_SCREENSHOT_CLEAN -> requestPermission(context, launcher) { ScreenshotCleanActivity.start(context) }
}
}
fun requestPermission(
context: Context,
launcher: ActivityLauncher,
block: (() -> Unit)? = null
) {
if (PermissionHelps.checkFilesAccessPermission(context)) {
block?.invoke()
} else {
if (isShowStoragePermission) return
isShowStoragePermission = true
DialogHelps.showStoragePermissionDialog(
context,
{
PermissionHelps.requestFilesAccessPermission(context, launcher, result = {
if (it) block?.invoke()
isShowStoragePermission = false
})
},
{
isShowStoragePermission = false
}
)
}
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.app.Activity
import java.util.Stack
object ActivityManagerHelps {
private val activityStack = Stack<Activity>()
/**
* 添加Activity到堆栈
*/
fun add(activity: Activity) {
activityStack.add(activity)
}
/**
* 移除Activity从堆栈
*/
fun remove(activity: Activity) {
activityStack.remove(activity)
}
/**
* 结束指定的Activity
*/
fun finish(activity: Activity?) {
if (activity != null) {
activityStack.remove(activity)
activity.finish()
}
}
/**
* 结束所有Activity
*/
fun finishAll() {
for (activity in activityStack) {
activity?.finish()
}
activityStack.clear()
}
fun finishAll(excludeCls: Class<*>?) {
val iterator = activityStack.iterator()
while (iterator.hasNext()) {
val item = iterator.next()
if (excludeCls != item.javaClass) {
item.finish()
iterator.remove()
}
}
}
/**
* 检查Activity是否存在于堆栈中
*/
fun isActivityInStack(cls: Class<*>?): Boolean {
var isExist = false
if (cls != null) {
for (activity in activityStack) {
if (cls == activity.javaClass) {
isExist = true
break
}
}
}
return isExist
}
val all: List<Activity>
get() = activityStack
val topActivity: Activity?
get() = if (activityStack.isEmpty()) {
null
} else {
activityStack.peek()
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.app.ActivityManager
import android.content.Context
import android.os.Environment
import android.os.StatFs
object AppHelps {
fun getMemTotal(context: Context): Long {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val memoryInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memoryInfo)
return memoryInfo.totalMem
}
fun getMemAvailable(context: Context): Long {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val memoryInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memoryInfo)
return memoryInfo.availMem
}
fun getTotalExternalSize(): Long {
try {
val statFs = StatFs(Environment.getExternalStorageDirectory().path)
val statFs2 = StatFs(Environment.getRootDirectory().path)
val blockSizeLong =
statFs2.blockSizeLong * statFs2.blockCountLong + statFs.blockSizeLong * statFs.blockCountLong
return calculateTotalExternal(blockSizeLong)
} catch (e: Exception) {
return 0
}
}
fun getAvailableExternalSize(): Long {
try {
val statFs = StatFs(Environment.getExternalStorageDirectory().path)
val statFs2 = StatFs(Environment.getRootDirectory().path)
val blackSize =
statFs2.blockSizeLong * statFs2.availableBlocksLong + statFs.blockSizeLong * statFs.availableBlocksLong
return calculateAvailableExternal(blackSize)
} catch (e: Exception) {
return 0
}
}
private fun calculateTotalExternal(size: Long): Long {
var powerOfTwo: Long = 1
while (powerOfTwo < size) {
powerOfTwo *= 2
}
return powerOfTwo
}
private fun calculateAvailableExternal(size: Long): Long {
var value = size
var n = 0
while (true) {
val m: Long = 1000
if (value < m) {
break
}
value /= m
n++
}
for (i in 0 until n) {
value *= 1024
}
return value
}
fun Context.dpToPx(dp: Int): Float {
val density = resources.displayMetrics.density
return dp * density
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.app.Activity
import android.app.Dialog
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.view.animation.LinearInterpolator
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.lifecycleScope
import com.airbnb.lottie.LottieAnimationView
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.ads.AdmobHelper
import com.base.pdfviewerscannerwhite.ads.admob.AdmobInterstitialUtils
import com.base.pdfviewerscannerwhite.helper.EventUtils
import com.base.pdfviewerscannerwhite.mix.DialogHelps.showExitDialog
import com.base.pdfviewerscannerwhite.ui.appprocess.AppProcessActivity
import com.base.pdfviewerscannerwhite.ui.main.MainActivity
import com.base.pdfviewerscannerwhite.utils.ActivityLauncher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.random.Random
open class BaseActivity2 : AppCompatActivity() {
protected lateinit var launcher: ActivityLauncher
protected var loadingDialog: Dialog? = null
protected var isDisableBack = false
private var isShowAdInterstitial = false
private var isPause = false
private var lottieType: LottieType? = null
private var complete: (() -> Unit?)? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
launcher = ActivityLauncher(this)
immersive()
ActivityManagerHelps.add(this)
EventUtils.event("${javaClass.simpleName}_show")
}
override fun onResume() {
super.onResume()
isPause = false
}
override fun onPause() {
super.onPause()
isPause = true
}
override fun onDestroy() {
super.onDestroy()
ActivityManagerHelps.remove(this)
lottieType = null
complete = null
}
protected open fun immersive() {
StatusBarHelps.immersive(this, getColor(R.color.color_8eb0ff))
}
protected fun showAnimationAd(
lottieType: LottieType = LottieType.CLEAN,
isShowAnimation: Boolean = true,
complete: (() -> Unit?)? = null
) {
this.lottieType = lottieType
this.complete = complete
AdmobInterstitialUtils.showInterstitialAd(this, false, false) {
// showAdInterstitial(true) {
val animationView = findViewById<ConstraintLayout>(R.id.animation)
animationView.setOnClickListener { }
if (isShowAnimation) {
StatusBarHelps.immersive(this, getColor(R.color.color_8eb0ff))
showAnimation(animationView, lottieType, complete)
} else {
complete?.invoke()
}
}
}
protected fun showAdInterstitial(isShow: Boolean, complete: (() -> Unit?)? = null) {
if (this.isFinishing || this.isDestroyed || isShowAdInterstitial) return
isShowAdInterstitial = true
if (isShow) {
AdmobHelps.showInterstitialAd(
this,
dismissed = {
clearLoading()
immersive()
complete?.invoke()
},
completed = {
StatusBarHelps.immersive(this, getColor(R.color.black))
loadingDialog?.dismiss()
},
failed = {
clearLoading()
immersive()
complete?.invoke()
}
)
} else {
isShowAdInterstitial = false
complete?.invoke()
}
}
protected fun showAnimation(
view: View,
lottieType: LottieType,
complete: (() -> Unit?)? = null
) {
isDisableBack = true
view.visibility = View.VISIBLE
val progress = view.findViewById<androidx.appcompat.widget.AppCompatTextView>(R.id.tv_progress)
progress.text = "0%"
val lottie = view.findViewById<LottieAnimationView>(R.id.lottie_animation)
lottie.setAnimation(lottieType.data)
lottie.loop(isLoop(lottieType))
lottie.playAnimation()
if (!lottieType.images.isNullOrEmpty())
lottie.imageAssetsFolder = lottieType.images
if (!isLoop(lottieType)) {
lottie.addAnimatorUpdateListener { animation ->
val value = animation.animatedValue as Float * 100
progress.text = "${value.toInt()}%"
if (value.toInt() == 100) {
view.visibility = View.GONE
complete?.invoke()
isDisableBack = false
}
}
return
}
lifecycleScope.launch(Dispatchers.Main) {
val durationTime = Random.Default.nextLong(3000, 3500)
ValueAnimator.ofInt(0, 100).apply {
duration = durationTime
interpolator = LinearInterpolator()
addUpdateListener { animation ->
val value = animation.animatedValue as Int
progress.text = "${value}%"
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
view.visibility = View.GONE
complete?.invoke()
isDisableBack = false
lottie.cancelAnimation()
}
})
}.start()
}
}
private fun isLoop(lottieType: LottieType): Boolean {
return lottieType != LottieType.CLEAN
}
private fun clearLoading() {
loadingDialog?.dismiss()
loadingDialog = null
isShowAdInterstitial = false
}
protected fun cleanFiles(list: List<FileBean>) {
lifecycleScope.launch(Dispatchers.IO) {
val paths = list.filter { it.isSelected }.map { it.path }.toTypedArray()
paths.forEach { FileHelps.deleteFile(it) }
withContext(Dispatchers.Main) {
MediaHelps.updateMedia(applicationContext, paths)
}
}
}
protected fun backPressed() {
onBackPressedDispatcher.onBackPressed()
}
fun handleBackPressed(activity: Activity) {
when (activity) {
// is CleanJunkActivity -> exit(ExitType.CLEAN)
// is SimilarPhotosActivity -> exit(ExitType.SIMILAR_PHOTOS)
is ScreenshotCleanActivity -> exit(ExitType.SCREENSHOT)
// is WhatsappCleanActivity -> exit(ExitType.WHATSAPP)
// is WhatsappCleanDetailActivity -> exit(ExitType.WHATSAPP)
is AppProcessActivity -> exit(ExitType.APP_PROCESS)
else -> finishToMainTop()
}
}
private fun exit(exit: ExitType) {
showExitDialog(this, getString(exit.title), getString(exit.content)) {
backAdJump()
}
}
fun backAdJump() {
if (AdmobHelper.isShowCloseDocumentInter()) {
showAdInterstitial(true) {
finishToMainTop()
}
} else {
finishToMainTop()
}
}
fun finishToMainTop() {
val intent = Intent(this, MainActivity::class.java)
intent.putExtra("where", "finishToMainTop")
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
startActivity(intent)
finish()
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
abstract class BaseAdapter<T, VB : ViewBinding>(
private var items: List<T> = emptyList()
) : RecyclerView.Adapter<BaseViewHolder<VB>>() {
interface OnClickCallback<T> {
fun onClicked(view: View, position: Int, item: T)
}
var callback: OnClickCallback<T>? = null
fun submitList(data: List<T>) {
items = data
notifyDataSetChanged()
}
fun add(item: T) {
items = items.toMutableList().apply { add(item) }
notifyItemInserted(items.size - 1)
}
val list: MutableList<T> get() = items.toMutableList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<VB> {
return BaseViewHolder(getViewBinding(LayoutInflater.from(parent.context), parent))
}
override fun onBindViewHolder(holder: BaseViewHolder<VB>, position: Int) {
bind(holder, position, items[position])
}
override fun getItemCount(): Int = items.size
abstract fun getViewBinding(layoutInflater: LayoutInflater, parent: ViewGroup): VB
abstract fun bind(holder: BaseViewHolder<VB>, position: Int, item: T)
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
class BaseViewHolder<VB : ViewBinding>(val binding: VB) : RecyclerView.ViewHolder(binding.root)
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.media.tv.TvTrackInfo.TYPE_AUDIO
import android.media.tv.TvTrackInfo.TYPE_VIDEO
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.databinding.ItemCleanDetailBinding
import com.base.pdfviewerscannerwhite.mix.AppHelps.dpToPx
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.request.RequestOptions
class CleanDetailAdapter(
var data: List<FileBean>,
private val type: Int = TYPE_VIDEO
) : BaseAdapter<FileBean, ItemCleanDetailBinding>(data) {
constructor(type: Int = TYPE_VIDEO) : this(emptyList(), type)
override fun getViewBinding(
layoutInflater: LayoutInflater,
parent: ViewGroup
): ItemCleanDetailBinding {
return ItemCleanDetailBinding.inflate(layoutInflater, parent, false)
}
override fun bind(
holder: BaseViewHolder<ItemCleanDetailBinding>,
position: Int,
item: FileBean
) {
if (type == TYPE_AUDIO) {
holder.binding.clVideoImage.visibility = View.GONE
holder.binding.clAudio.visibility = View.VISIBLE
holder.itemView.layoutParams =
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
holder.binding.tvAudioName.text = item.name
holder.binding.tvAudioSize.text = Utils.getSize(item.length)
setSelection(holder.binding.ivAudioSelect, item)
holder.binding.viewLine.visibility = if (position == itemCount - 1) View.GONE else View.VISIBLE
holder.itemView.setOnClickListener {
item.isSelected = !item.isSelected
setSelection(holder.binding.ivAudioSelect, item)
callback?.onClicked(it, position, item)
}
} else {
holder.binding.clVideoImage.visibility = View.VISIBLE
holder.binding.clAudio.visibility = View.GONE
holder.binding.ivVideo.visibility = if (type == TYPE_VIDEO) View.VISIBLE else View.GONE
holder.itemView.layoutParams =
ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val width = holder.itemView.context.resources.displayMetrics.widthPixels
val margin = holder.itemView.context.dpToPx(44).toInt()
val imageWidth = (width - margin) / 3
holder.binding.ivImage.layoutParams.width = imageWidth
holder.binding.ivImage.layoutParams.height = imageWidth
val round = holder.itemView.context.dpToPx(8).toInt()
val options = RequestOptions().transform(CenterCrop(), RoundedCorners(round))
Glide.with(holder.itemView.context)
.load(item.path)
.apply(options)
.into(holder.binding.ivImage)
holder.binding.tvSize.text = Utils.getSize(item.length)
holder.binding.ivVideo.visibility = if (type == 0) View.VISIBLE else View.GONE
setSelection(holder.binding.ivSelect, item)
holder.itemView.setOnClickListener {
item.isSelected = !item.isSelected
setSelection(holder.binding.ivSelect, item)
callback?.onClicked(it, position, item)
}
}
}
private fun setSelection(imageView: ImageView, item: FileBean) {
imageView.setImageResource(if (item.isSelected) R.mipmap.ic_check_s else R.mipmap.ic_check_n)
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.animation.ObjectAnimator
import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.animation.LinearInterpolator
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.databinding.DialogDeleteBinding
import com.base.pdfviewerscannerwhite.databinding.DialogDeleteMixBinding
import com.base.pdfviewerscannerwhite.databinding.DialogExitBinding
import com.base.pdfviewerscannerwhite.databinding.DialogStoragePermissionBinding
import com.google.android.material.bottomsheet.BottomSheetDialog
object DialogHelps {
fun showStoragePermissionDialog(context: Context, open: () -> Unit, dismiss: () -> Unit) {
val binding = DialogStoragePermissionBinding.inflate(LayoutInflater.from(context))
val dialog = BottomSheetDialog(context, R.style.BottomSheetDialog).apply {
setContentView(binding.root)
show()
}
dialog.setOnDismissListener { dismiss.invoke() }
binding.tvSet.setOnClickListener {
it.isEnabled = false
open.invoke()
it.postDelayed({ dialog.dismiss() }, 500)
}
}
private fun scoreAnimation(view: View) {
ObjectAnimator.ofFloat(
view,
View.SCALE_X,
0.75f,
1.2f,
1f
).apply {
duration = 500
interpolator = LinearInterpolator()
start()
}
ObjectAnimator.ofFloat(
view,
View.SCALE_Y,
0.75f,
1.2f,
1f
).apply {
duration = 500
interpolator = LinearInterpolator()
start()
}
}
fun showDeleteDialog(context: Context, view: View, confirm: (() -> Unit?)?): DialogDeleteMixBinding {
view.isEnabled = false
val binding = DialogDeleteMixBinding.inflate(LayoutInflater.from(context))
val dialog = Dialog(context, R.style.CustomDialogStyle).apply {
setCancelable(false)
setCanceledOnTouchOutside(false)
setContentView(binding.root)
show()
}
binding.tvCancel.setOnClickListener {
dialog.dismiss()
view.isEnabled = true
}
binding.tvDelete.setOnClickListener {
it.isEnabled = false
dialog.dismiss()
confirm?.invoke()
view.isEnabled = true
}
return binding
}
fun showExitDialog(
context: Activity,
title: String,
content: Any,
confirm: () -> Unit,
) {
val binding = DialogExitBinding.inflate(LayoutInflater.from(context))
val dialog = Dialog(context, R.style.CustomDialogStyle).apply {
setCancelable(false)
setCanceledOnTouchOutside(false)
setContentView(binding.root)
show()
}
binding.tvTitle.text = title
binding.tvContent.text = content.toString()
binding.ivCancel.setOnClickListener {
dialog.dismiss()
}
binding.tvCancel.setOnClickListener {
dialog.dismiss()
}
binding.tvSure.setOnClickListener {
it.isEnabled = false
dialog.dismiss()
confirm.invoke()
}
AdmobHelps.showNativeAd(context, completed = {
binding.adNative.setExitNativeAd(it)
}, maxCompleted = { nativeAdLoader, nativeMaxAd ->
binding.adNative.setExitNativeAd(nativeAdLoader, nativeMaxAd)
})
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import com.base.pdfviewerscannerwhite.R
enum class ExitType(val title: Int, val content: Int) {
APP_PROCESS(R.string.exit_app_process, R.string.exit_app_process_content1),
SCREENSHOT(R.string.exit_screenshot_cleaner, R.string.exit_screenshot_cleaner_content),
SIMILAR_PHOTOS(R.string.exit_similar_photos, R.string.exit_similar_photos_content),
WHATSAPP(R.string.exit_whatsapp_clean, R.string.exit_whatsapp_clean_content),
CLEAN(R.string.exit_clean_junk, R.string.exit_clean_junk_content),
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
data class FileBean(
val name: String,
val path: String = "",
var icon: Int = 0,
var length: Long = 0,
var lastModified: Long = 0,
var mineType: String = "",
var isSelected: Boolean = false,
var isScanning: Boolean = true,
var state: Int = 0,
var type: Int = 0,
var items: List<FileBean> = emptyList()
)
package com.base.pdfviewerscannerwhite.mix
import android.os.Environment
import androidx.lifecycle.LifecycleCoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
object FileHelps {
private const val WHATS_APP_MEDIA_DIR = "Android/media/com.whatsapp/WhatsApp/Media/"
fun findWhatsappFiles(): MutableList<FileBean> {
val rootDir = File(getAppSpecificDirPath(WHATS_APP_MEDIA_DIR))
// val rootDir = File(getAppSpecificDirPath("Pictures/"))
val list = findFilesRecursive(rootDir, arrayOf())
list.sortByDescending { it.lastModified }
return list
}
suspend fun findEmptyFolders(lifecycleScope: LifecycleCoroutineScope): MutableList<FileBean> {
val rootDir = Environment.getExternalStorageDirectory()
val filters = arrayOf("/storage/emulated/0/Android/data", "/storage/emulated/0/Android/obb")
val emptyFolders = mutableListOf<File>()
// 使用协程并发遍历文件夹
withContext(Dispatchers.IO) {
findEmptyFolders(lifecycleScope, rootDir, filters, emptyFolders)
}
return emptyFolders.map { FileBean(it.name ?: "null", it.path, length = 4 * 1024) }.toMutableList()
}
private suspend fun findEmptyFolders(
lifecycleScope: LifecycleCoroutineScope,
folder: File,
filters: Array<String>,
emptyFolders: MutableList<File>
) {
val files = folder.listFiles() ?: return
if (files.isEmpty() && !filters.contains(folder.path)) {
emptyFolders.add(folder)
return
}
files.filter { it.isDirectory }.map { file ->
lifecycleScope.launch(Dispatchers.IO) {
findEmptyFolders(lifecycleScope, file, filters, emptyFolders)
}
}.joinAll() // 等待所有子任务完成
}
// fun findEmptyFolders(): MutableList<FileBean> {
// val externalStorageDir = Environment.getExternalStorageDirectory()
// val filters = arrayOf("/storage/emulated/0/Android/data", "/storage/emulated/0/Android/obb")
// val emptyFolders = getAllEmptyFolders(externalStorageDir, filters)
// return emptyFolders.map { FileBean(it.name, it.path, length = 4 * 1024) }.toMutableList()
// }
fun findTempFiles(): MutableList<FileBean> {
val rootDir = Environment.getExternalStorageDirectory()
return findFilesRecursive(rootDir, arrayOf(".temp"))
}
fun findApkFiles(): MutableList<FileBean> {
val rootDir = Environment.getExternalStorageDirectory()
return findFilesRecursive(rootDir, arrayOf(".apk", ".aab"))
}
fun findLogFiles(): MutableList<FileBean> {
val rootDir = Environment.getExternalStorageDirectory()
return findFilesRecursive(rootDir, arrayOf(".log"))
}
fun deleteFile(filePath: String) {
File(filePath).deleteIfExists()
}
fun File?.deleteIfExists(): Boolean {
if (this?.exists() == true) {
return this.delete()
}
return false
}
private fun findFilesRecursive(dir: File, suffixes: Array<String>): MutableList<FileBean> {
return dir.walk()
.filter { it.isFile && it.endsWith(suffixes) }
.map { FileBean(it.name, it.path, length = it.length(), lastModified = it.lastModified()) }
.toMutableList()
}
private fun File.endsWith(suffixes: Array<String>): Boolean {
return suffixes.isEmpty() || suffixes.any { name.lowercase().endsWith(it.lowercase()) }
}
private fun getAppSpecificDirPath(relativePath: String): String {
return "${Environment.getExternalStorageDirectory()}/$relativePath"
}
private fun getAllEmptyFolders(root: File, filters: Array<String>): List<File> {
return root.walk()
.filter {
it.isDirectory && !filters.contains(it.path) && !it.isHidden && it.list()?.isEmpty() == true
}
.toList()
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.media.ThumbnailUtils
import com.base.pdfviewerscannerwhite.bean.SimilarBean
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import kotlin.math.pow
object ImageHelpers {
private fun loadBitmapFromFile(path: String): Bitmap? {
val bitmap = BitmapFactory.decodeFile(path)
if (bitmap.width <= 0 || bitmap.height <= 0) {
return null
}
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(path, options)
if (options.outHeight == -1 || options.outWidth == -1) {
return null
}
var inSampleSize =
(0.5f + options.outHeight.toFloat() / bitmap.height.toFloat()).coerceAtLeast(0.5f + options.outWidth.toFloat() / bitmap.width.toFloat())
.toInt()
inSampleSize += 1
options.inSampleSize = inSampleSize.coerceAtLeast(1)
options.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, options)
}
suspend fun createImage(path: String): SimilarBean? {
return withContext(Dispatchers.IO) {
try {
val width = 8
val height = 8
val source = loadBitmapFromFile(path)
val thumb = ThumbnailUtils.extractThumbnail(source, width, height)
val pixels = IntArray(width * height)
for (i in 0 until width) {
for (j in 0 until height) {
pixels[i * height + j] = rgbToGray(thumb.getPixel(i, j))
}
}
val avgPixel = average(pixels)
val comps = IntArray(width * height)
for (i in comps.indices) {
if (pixels[i] >= avgPixel) {
comps[i] = 1
} else {
comps[i] = 0
}
}
val hashCode = StringBuffer()
var i = 0
while (i < comps.size) {
val result = comps[i] * 2.0.pow(3.0).toInt() + (comps[i + 1] * 2.0.pow(2.0)
.toInt()) + (comps[i + 2] * 2.0.pow(1.0).toInt()) + comps[i + 3]
hashCode.append(binaryToHex(result))
i += 4
}
recycleBitmap(thumb)
recycleBitmap(source)
return@withContext SimilarBean(
path,
hashCode.toString(),
avgPixel,
File(path).length(),
false
)
} catch (_: Exception) {
return@withContext null
}
}
}
private fun rgbToGray(pixels: Int): Int {
val red = Color.red(pixels)
val green = Color.green(pixels)
val blue = Color.blue(pixels)
return (0.3 * red + 0.59 * green + 0.11 * blue).toInt()
}
private fun average(pixels: IntArray): Int {
return (pixels.sumOf { it }.toFloat() / pixels.size).toInt()
}
private fun recycleBitmap(thumb: Bitmap?) {
if (thumb?.isRecycled == false) {
thumb.recycle()
}
}
fun similarCondition(first: SimilarBean, second: SimilarBean): Boolean {
return hammingDistance(
first.hashCode,
second.hashCode
) <= 6 && (first.avgPixel.toFloat() / second.avgPixel) in 0.8..1.0
}
private fun hammingDistance(sourceHashCode: String, hashCode: String): Int {
var difference = 0
for (i in sourceHashCode.indices) {
if (sourceHashCode[i] != hashCode[i]) {
difference++
}
}
return difference
}
private fun binaryToHex(binary: Int) = when (binary) {
0 -> '0'
1 -> '1'
2 -> '2'
3 -> '3'
4 -> '4'
5 -> '5'
6 -> '6'
7 -> '7'
8 -> '8'
9 -> '9'
10 -> 'a'
11 -> 'b'
12 -> 'c'
13 -> 'd'
14 -> 'e'
15 -> 'f'
else -> ' '
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.databinding.ItemLargeFileCleanBinding
import com.base.pdfviewerscannerwhite.mix.AppHelps.dpToPx
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
class LargeFileCleanAdapter : BaseAdapter<FileBean, ItemLargeFileCleanBinding>() {
override fun getViewBinding(
layoutInflater: LayoutInflater,
parent: ViewGroup
): ItemLargeFileCleanBinding {
return ItemLargeFileCleanBinding.inflate(layoutInflater, parent, false)
}
override fun bind(
holder: BaseViewHolder<ItemLargeFileCleanBinding>,
position: Int,
item: FileBean
) {
holder.binding.ivVideo.isVisible = MineTypeHelps.isVideo(item.mineType)
if (MineTypeHelps.isImage(item.mineType) || MineTypeHelps.isVideo(item.mineType)) {
showImageVideo(holder, item)
} else if (MineTypeHelps.isAudio(item.mineType)) {
holder.binding.ivIcon.setImageResource(R.mipmap.icon1)
} else if (MineTypeHelps.isApk(item.mineType)) {
holder.binding.ivIcon.setImageResource(R.mipmap.icon3)
} else if (MineTypeHelps.isDoc(item.mineType)) {
holder.binding.ivIcon.setImageResource(R.mipmap.icon4)
} else {
holder.binding.ivIcon.setImageResource(R.mipmap.icon2)
}
holder.binding.tvName.text = item.name
holder.binding.tvLength.text = Utils.getSize(item.length)
holder.binding.ivSelect.setImageResource(if (item.isSelected) R.mipmap.icon_yuan_junk_s else R.mipmap.icon_all_photo_off)
holder.binding.viewLine.visibility = if (position == itemCount - 1) View.GONE else View.VISIBLE
holder.itemView.setOnClickListener {
item.isSelected = !item.isSelected
notifyItemChanged(position)
callback?.onClicked(it, position, item)
}
}
private fun showImageVideo(
holder: BaseViewHolder<ItemLargeFileCleanBinding>,
item: FileBean
) {
val round = holder.itemView.context.dpToPx(4).toInt()
val options = RequestOptions().transform(CenterCrop(), RoundedCorners(round))
Glide.with(holder.itemView.context)
.load(item.path)
.apply(options)
.into(holder.binding.ivIcon)
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.base.pdfviewerscannerwhite.databinding.ItemLargeFileCleanTabBinding
import com.base.pdfviewerscannerwhite.R
class LargeFileCleanTabAdapter : BaseAdapter<FileBean, ItemLargeFileCleanTabBinding>() {
var type = 0
override fun getViewBinding(
layoutInflater: LayoutInflater,
parent: ViewGroup
): ItemLargeFileCleanTabBinding {
return ItemLargeFileCleanTabBinding.inflate(layoutInflater, parent, false)
}
override fun bind(
holder: BaseViewHolder<ItemLargeFileCleanTabBinding>,
position: Int,
item: FileBean
) {
holder.binding.tvName.text = item.name
holder.binding.ivSelect.setImageResource(if (item.isSelected) R.mipmap.icon_yuan_junk_s else R.mipmap.icon_all_photo_off)
holder.binding.viewLine.visibility = if (position == itemCount - 1) View.GONE else View.VISIBLE
holder.itemView.setOnClickListener {
list.forEach { it.isSelected = false }
item.isSelected = !item.isSelected
callback?.onClicked(it, position, item)
}
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
enum class LottieType(val data: String, val images: String? = null) {
APP_MANAGER("app_manager/scan/data.json", "app_manager/scan/images"),
APP_PROCESS("app_process/scan/data.json", "app_process/scan/images"),
BATTERY_INFO("battery_info/scan/data.json", "battery_info/scan/images"),
LARGE_FILE("large_files/scan/data.json", "large_files/scan/images"),
PHOTO_COMPRESSION("photo_compression/scan/data.json", "photo_compression/scan/images"),
PHOTO_COMPRESSION_COMPRESS("photo_compression/compress/data.json", "photo_compression/compress/images"),
SCREENSHOT("screenshot/scan/data.json", "screenshot/scan/images"),
SIMILAR_PHOTOS("similar_photos/scan/data.json", "similar_photos/scan/images"),
WHATSAPP("whatsapp/scan/data.json", "whatsapp/scan/images"),
CLEAN("clean.json")
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.annotation.SuppressLint
import android.content.Context
import android.database.Cursor
import android.media.MediaScannerConnection
import android.net.Uri
import android.provider.MediaStore
import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
object MediaHelps {
private val projection = arrayOf(
MediaStore.Files.FileColumns.DISPLAY_NAME,
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.SIZE,
MediaStore.Files.FileColumns.DATE_MODIFIED,
MediaStore.Files.FileColumns.MIME_TYPE
)
private const val EXTERNAL = "external"
fun updateMedia(context:Context, paths: Array<String>) {
MediaScannerConnection.scanFile(context, paths, null, null)
}
fun findFiles(context: Context, size: Long = 0): List<FileBean> {
return queryFilesWithSize(context, MediaStore.Files.getContentUri(EXTERNAL), size)
}
fun findSpecifiedFiles(context: Context): List<FileBean> {
val suffixes = arrayOf("%.log", "%.apk", "%.aab", "%.temp", "%.LOG", "%.APK", "%.AAB", "%.TEMP")
return queryFilesWithSuffixes(context, MediaStore.Files.getContentUri(EXTERNAL), suffixes)
}
fun findScreenshotsFiles(context: Context): Map<String, MutableList<FileBean>> {
val selection = "${MediaStore.Images.Media.RELATIVE_PATH} LIKE ?"
val selectionArgs = arrayOf("%Screenshots%")
val list = query(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, projection[3])
val map = mutableMapOf<String, MutableList<FileBean>>()
val dateFormat = SimpleDateFormat("yyyy/MM", Locale.getDefault())
list.forEach { map.getOrPut(dateFormat.format(it.lastModified)) { mutableListOf() }.add(it) }
return map
}
fun findImageFiles(context: Context, size: Long = 0): List<FileBean> {
return queryFilesWithSize(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, size)
}
fun findVideoFiles(context: Context, size: Long = 0): List<FileBean> {
return queryFilesWithSize(context, MediaStore.Video.Media.EXTERNAL_CONTENT_URI, size)
}
fun findAudioFiles(context: Context, size: Long = 0): List<FileBean> {
return queryFilesWithSize(context, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, size)
}
fun findDocFiles(context: Context, size: Long = 0): List<FileBean> {
val mimeTypes = arrayOf(
"text/plain",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/pdf",
)
return queryFilesWithMimeTypes(context, MediaStore.Files.getContentUri(EXTERNAL), mimeTypes, size)
}
private fun queryFilesWithSuffixes(context: Context, uri: Uri, suffixes: Array<String>): List<FileBean> {
val selection = suffixes.joinToString(separator = " OR ") { "${MediaStore.Files.FileColumns.DISPLAY_NAME} LIKE ?" }
return query(context, uri, projection, selection, suffixes)
}
private fun queryFilesWithSize(context: Context, uri: Uri, size: Long): List<FileBean> {
var selection: String? = null
var selectionArgs: Array<String>? = null
if (size > 0) {
selection = "${MediaStore.Files.FileColumns.SIZE} >= ?"
selectionArgs = arrayOf(size.toString())
}
return query(context, uri, projection, selection, selectionArgs)
}
private fun queryFilesWithMimeTypes(context: Context, uri: Uri, mimeTypes: Array<String>, size: Long): List<FileBean> {
val (selection, selectionArgs) = buildSelectionWithMimeTypes(mimeTypes, size)
return query(context, uri, projection, selection, selectionArgs)
}
private fun buildSelectionWithMimeTypes(mimeTypes: Array<String>, size: Long): Pair<String, Array<String>> {
val mimeTypeConditions = mimeTypes.joinToString(separator = " OR ") { "${MediaStore.Files.FileColumns.MIME_TYPE}=?" }
val sizeCondition = "${MediaStore.Files.FileColumns.SIZE} >=?"
val selection = "($mimeTypeConditions) AND ($sizeCondition)"
val selectionArgs = mimeTypes + size.toString()
return selection to selectionArgs
}
private fun buildSelectionWithMimeTypes(mimeTypes: Array<String>): Pair<String, Array<String>> {
val selection = mimeTypes.joinToString(prefix = "${MediaStore.Files.FileColumns.MIME_TYPE}=?", separator = " OR ") { "'$it'" }
val selectionArgs = mimeTypes
return Pair(selection, selectionArgs)
}
@SuppressLint("Range")
private fun query(
context: Context,
uri: Uri,
projection: Array<String>,
selection: String? = null,
selectionArgs: Array<String>? = null,
sortOrder: String = MediaStore.Files.FileColumns.DATE_MODIFIED
): List<FileBean> {
val list = mutableListOf<FileBean>()
var cursor: Cursor? = null
try {
val contentResolver = context.contentResolver
cursor = contentResolver.query(
uri,
projection,
selection,
selectionArgs,
"${sortOrder} DESC"
)
cursor?.let {
while (it.moveToNext()) {
val filePath = it.getString(it.getColumnIndex(projection[1]))
if (!File(filePath).exists()) continue
val displayName = it.getString(it.getColumnIndex(projection[0]))
val size = it.getLong(it.getColumnIndex(projection[2]))
val dateModified = it.getLong(it.getColumnIndex(projection[3])) * 1000
val mineType = it.getString(it.getColumnIndex(projection[4]))
list.add(FileBean(displayName, filePath, 0, size, dateModified, mineType))
}
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
cursor?.close()
}
return list
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.webkit.MimeTypeMap
object MineTypeHelps {
val TYPE_VIDEO = 0
val TYPE_IMAGE = 1
val TYPE_AUDIO = 2
val mineTypes by lazy {
listOf(
"image/",
"video/",
"audio/",
"text/",
MimeTypeMap.getSingleton().getMimeTypeFromExtension("pdf") ?: "",
MimeTypeMap.getSingleton().getMimeTypeFromExtension("doc") ?: "",
MimeTypeMap.getSingleton().getMimeTypeFromExtension("docx") ?: "",
MimeTypeMap.getSingleton().getMimeTypeFromExtension("xls") ?: "",
MimeTypeMap.getSingleton().getMimeTypeFromExtension("xlsx") ?: "",
MimeTypeMap.getSingleton().getMimeTypeFromExtension("ppt") ?: "",
MimeTypeMap.getSingleton().getMimeTypeFromExtension("apk") ?: "",
)
}
val images = listOf(".jpeg", ".jpg", ".png", ".gif", ".webp", ".ico", ".raw") // 图像
val videos = listOf(".mp4", ".avi", ".mov", ".wmv", ".flv") // 视频
val audios = listOf(".mp3", ".wav", ".m4a", ".ncm") // 音频
fun isImage(mineType: String): Boolean {
return mineType.startsWith(mineTypes[0])
}
fun isVideo(mineType: String): Boolean {
return mineType.startsWith(mineTypes[1])
}
fun isAudio(mineType: String): Boolean {
return mineType.startsWith(mineTypes[2])
}
fun isDoc(mineType: String): Boolean {
return mineType.startsWith(mineTypes[3])
|| mineType.startsWith(mineTypes[4])
|| mineType.startsWith(mineTypes[5])
|| mineType.startsWith(mineTypes[6])
|| mineType.startsWith(mineTypes[7])
|| mineType.startsWith(mineTypes[8])
|| mineType.startsWith(mineTypes[9])
}
fun isApk(mineType: String): Boolean {
return mineType.startsWith(mineTypes[10])
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.LayoutRes
import com.applovin.mediation.MaxAd
import com.applovin.mediation.nativeAds.MaxNativeAdLoader
import com.applovin.mediation.nativeAds.MaxNativeAdView
import com.applovin.mediation.nativeAds.MaxNativeAdViewBinder
import com.base.pdfviewerscannerwhite.R
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.nativead.NativeAdView
class NativeView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
): FrameLayout(context, attrs) {
fun setExitNativeAd(nativeAd: NativeAd) {
setNativeAd(nativeAd, R.layout.layout_ad_native_exit)
}
fun setExitNativeAd(
nativeAdLoader: MaxNativeAdLoader,
nativeAd: MaxAd,
@LayoutRes resource: Int = R.layout.layout_ad_native_exit
) {
setNativeAd(nativeAdLoader, nativeAd, resource)
}
fun setNativeAd(
nativeAdLoader: MaxNativeAdLoader,
nativeAd: MaxAd,
@LayoutRes resource: Int = R.layout.layout_ad_native
) {
val builder = MaxNativeAdViewBinder.Builder(resource)
.setTitleTextViewId(R.id.ad_headline)
.setIconImageViewId(R.id.ad_icon)
.setMediaContentViewGroupId(R.id.ad_media)
.setCallToActionButtonId(R.id.ad_call_to_action)
if (resource != R.layout.layout_ad_native)
builder.setBodyTextViewId(R.id.ad_body)
visibility = VISIBLE
val adView = MaxNativeAdView(builder.build(), context)
nativeAdLoader.render(adView, nativeAd)
removeAllViews()
addView(adView)
}
fun setNativeAd(nativeAd: NativeAd, @LayoutRes resource: Int ?= R.layout.layout_ad_native) {
visibility = VISIBLE
val adView = resource?.let { LayoutInflater.from(context).inflate(it, null) } as NativeAdView
adView.mediaView = adView.findViewById(R.id.ad_media)
adView.headlineView = adView.findViewById(R.id.ad_headline)
adView.callToActionView = adView.findViewById(R.id.ad_call_to_action)
adView.iconView = adView.findViewById(R.id.ad_icon)
(adView.headlineView as TextView?)?.text = nativeAd.headline
adView.mediaView!!.mediaContent = nativeAd.mediaContent
if (nativeAd.callToAction != null) {
(adView.callToActionView as TextView?)?.text = nativeAd.callToAction
}
if (nativeAd.icon != null) {
(adView.iconView as ImageView?)?.setImageDrawable(nativeAd.icon!!.drawable)
}
val body = adView.findViewById<TextView>(R.id.ad_body)
if (body != null) {
adView.bodyView = body
if (nativeAd.body == null) {
adView.bodyView?.visibility = GONE
} else {
adView.bodyView?.visibility = VISIBLE
(adView.bodyView as TextView?)?.text = nativeAd.body
}
}
adView.setNativeAd(nativeAd)
removeAllViews()
addView(adView)
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
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.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import com.base.pdfviewerscannerwhite.utils.ActivityLauncher
object PermissionHelps {
fun requestFilesAccessPermission(
context: Context,
launcher: ActivityLauncher,
result: (Boolean) -> Unit
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
try {
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse("package:${context.packageName}")
launcher.launch(intent) {
result.invoke(checkFilesAccessPermission(context))
}
} catch (_: Exception) {
}
} else {
val array = arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
launcher.launch(array) { permissions ->
result(permissions.values.all { it })
}
}
}
fun requestNotificationPermission(
context: Context,
launcher: ActivityLauncher,
result: (Boolean) -> Unit
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val array = arrayOf(Manifest.permission.POST_NOTIFICATIONS)
launcher.launch(array) { permissions ->
val flag = permissions.values.all { it }
if (!flag) notificationSettings(context, launcher, result)
}
} else {
notificationSettings(context, launcher, result)
}
}
fun notificationSettings(
context: Context,
launcher: ActivityLauncher,
result: (Boolean) -> Unit
) {
val intent = Intent()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
} else {
intent.action = "android.settings.APP_NOTIFICATION_SETTINGS"
intent.putExtra("app_package", context.packageName)
}
launcher.launch(intent) {
result.invoke(checkNotificationPermission(context))
}
}
fun checkNotificationPermission(context: Context): Boolean {
return NotificationManagerCompat.from(context).areNotificationsEnabled()
}
fun checkFilesAccessPermission(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return Environment.isExternalStorageManager()
} else {
val readPermission = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE)
val writePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
return readPermission == PackageManager.PERMISSION_GRANTED && writePermission == PackageManager.PERMISSION_GRANTED
}
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.activity.addCallback
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.ads.admob.AdmobInterstitialUtils
import com.base.pdfviewerscannerwhite.bean.ConstObject
import com.base.pdfviewerscannerwhite.databinding.ActivityScreenshotCleanBinding
import com.base.pdfviewerscannerwhite.ui.result.ResultActivity
import com.base.pdfviewerscannerwhite.utils.LogEx
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import pokercc.android.expandablerecyclerview.ExpandableAdapter
class ScreenshotCleanActivity : BaseActivity2() {
companion object {
fun start(context: Context) {
val intent = Intent(context, ScreenshotCleanActivity::class.java)
context.startActivity(intent)
}
}
private val binding by lazy {
ActivityScreenshotCleanBinding.inflate(layoutInflater)
}
private lateinit var adapter: ScreenshotCleanAdapter
private var total: Long = 0
private var isSelectAll = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initView()
initData()
showAnimationAd(LottieType.SCREENSHOT) {
}
onBackPressedDispatcher.addCallback {
handleBackPressed(this@ScreenshotCleanActivity)
}
}
private fun initView() {
adapter = ScreenshotCleanAdapter()
adapter.selectionData.observe(this) { list ->
isSelectAll = list.stream().allMatch(FileBean::isSelected)
val selectionList = list.filter { it.isSelected }
total = selectionList.sumOf { it.length }
updateView(selectionList.isNotEmpty())
}
binding.expandableFiles.adapter = adapter
val gridLayoutManager = GridLayoutManager(this, 3)
gridLayoutManager.spanSizeLookup = MediaImageSpanLookup(3, adapter)
binding.expandableFiles.layoutManager = gridLayoutManager
binding.flBack.setOnClickListener { backPressed() }
binding.llSelectAll.setOnClickListener {
isSelectAll = !isSelectAll
adapter.child.flatten().forEach { it.isSelected = isSelectAll }
adapter.setData(adapter.group, adapter.child)
total = adapter.child.flatten().filter { it.isSelected }.sumOf { it.length }
updateView(isSelectAll)
}
binding.tvDelete.setOnClickListener {
if (total <= 0) return@setOnClickListener
DialogHelps.showDeleteDialog(this, binding.tvDelete) {
AdmobInterstitialUtils.showInterstitialAd(this, false, false) {
startActivity(Intent(this, ResultActivity::class.java).apply {
putExtra("from", ConstObject.FUNCTION_SCREEN_SHORT)
putExtra("clean_size", total)
LogEx.logDebug("clean_size", "total=$total")
})
finish()
}
cleanFiles(adapter.child.flatten())
}
}
}
private fun initData() {
lifecycleScope.launch(Dispatchers.IO) {
val screenFiles = MediaHelps.findScreenshotsFiles(this@ScreenshotCleanActivity)
val total = screenFiles.values.toList().flatten().sumOf { it.length }
val value = Utils.getSizeArray(total)
withContext(Dispatchers.Main) {
binding.tvSize.text = value[0]
binding.tvUnit.text = value[1]
adapter.setData(screenFiles.keys.toList(), screenFiles.values.toList())
adapter.expandAllGroup()
binding.ivEmpty.visibility = if (screenFiles.isEmpty()) View.VISIBLE else View.GONE
binding.llBottom.visibility = if (screenFiles.isEmpty()) View.GONE else View.VISIBLE
}
}
}
private fun updateView(isSelection: Boolean) {
binding.ivSelectAll.setImageResource(if (isSelectAll) R.mipmap.ic_check_s else R.mipmap.ic_check_n)
binding.tvDelete.setBackgroundResource(if (isSelection) R.drawable.gradient_26 else R.drawable.btn_not_clickable)
val delete = getString(R.string.delete)
val text = if (total > 0) "${delete} (${Utils.getSize(total)})" else delete
binding.tvDelete.text = text
}
}
internal class MediaImageSpanLookup(
private val spanCount: Int,
private val expandableAdapter: ExpandableAdapter<*>
) :
GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
val viewType = expandableAdapter.getItemViewType(position)
return if (expandableAdapter.isGroup(viewType)) spanCount else 1
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.databinding.ItemChildScreenshotCleanBinding
import com.base.pdfviewerscannerwhite.databinding.ItemGroupScreenshotCleanBinding
import com.base.pdfviewerscannerwhite.mix.AppHelps.dpToPx
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import pokercc.android.expandablerecyclerview.ExpandableAdapter
class ScreenshotCleanAdapter(
var group: List<String>,
var child: List<List<FileBean>>
) : ExpandableAdapter<ExpandableAdapter.ViewHolder>() {
constructor() : this(emptyList(), emptyList())
private val _selectionData = MutableLiveData<List<FileBean>>()
val selectionData: LiveData<List<FileBean>> = _selectionData
fun setData(group: List<String>, child: List<List<FileBean>>) {
this.group = group
this.child = child
notifyDataSetChanged()
}
override fun getChildCount(groupPosition: Int): Int {
if (child.isEmpty() || child.size <= groupPosition) {
return 0
}
return child[groupPosition].size
}
override fun getGroupCount(): Int {
return group.size
}
override fun onCreateChildViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(viewGroup.context)
val view = ItemChildScreenshotCleanBinding.inflate(inflater, viewGroup, false)
return ChildViewHolder(view)
}
override fun onCreateGroupViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(viewGroup.context)
val view = ItemGroupScreenshotCleanBinding.inflate(inflater, viewGroup, false)
return GroupViewHolder(view)
}
override fun onGroupViewHolderExpandChange(
holder: ViewHolder,
groupPosition: Int,
animDuration: Long,
expand: Boolean
) {
}
override fun onBindGroupViewHolder(
holder: ViewHolder,
groupPosition: Int,
expand: Boolean,
payloads: List<Any>
) {
holder as GroupViewHolder
holder.binding.tvName.text = group[groupPosition]
holder.binding.viewBg.visibility = if (groupPosition == 0) View.GONE else View.VISIBLE
holder.itemView.setOnClickListener { }
}
override fun onBindChildViewHolder(
holder: ViewHolder,
groupPosition: Int,
childPosition: Int,
payloads: List<Any>
) {
holder as ChildViewHolder
val data = child[groupPosition][childPosition]
val width = holder.itemView.context.resources.displayMetrics.widthPixels
var margin = holder.itemView.context.dpToPx(44).toInt()
val imageWidth = (width - margin) / 3
holder.binding.ivImage.layoutParams.width = imageWidth
holder.binding.ivImage.layoutParams.height = imageWidth
margin = holder.itemView.context.dpToPx(15).toInt()
if (childPosition % 3 == 0) {
holder.itemView.setPadding(margin, 0, 0, 0)
} else if (childPosition % 3 == 2) {
holder.itemView.setPadding(0, 0, margin, 0)
} else {
holder.itemView.setPadding(margin / 2, 0, margin / 2, 0)
}
val round = holder.itemView.context.dpToPx(8).toInt()
val options = RequestOptions().transform(CenterCrop(), RoundedCorners(round))
Glide.with(holder.itemView.context)
.load(data.path)
.apply(options)
.into(holder.binding.ivImage)
holder.binding.tvSize.text = Utils.getSize(data.length)
holder.binding.ivSelect.setImageResource(if (data.isSelected) R.mipmap.ic_check_s else R.mipmap.ic_check_n)
holder.binding.clContainer.setOnClickListener {
data.isSelected = !data.isSelected
holder.binding.ivSelect.setImageResource(if (data.isSelected) R.mipmap.ic_check_s else R.mipmap.ic_check_n)
_selectionData.value = child.flatten()
}
}
}
private class ChildViewHolder(val binding: ItemChildScreenshotCleanBinding) :
ExpandableAdapter.ViewHolder(binding.root)
private class GroupViewHolder(val binding: ItemGroupScreenshotCleanBinding) :
ExpandableAdapter.ViewHolder(binding.root)
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.activity.addCallback
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.bean.SimilarBean
import com.base.pdfviewerscannerwhite.databinding.ActivitySimilarPhotosBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.Semaphore
class SimilarPhotosActivity : BaseActivity2() {
companion object {
fun start(context: Context) {
val intent = Intent(context, SimilarPhotosActivity::class.java)
context.startActivity(intent)
}
}
private lateinit var adapter: SimilarPhotosAdapter
private val semaphore = Semaphore(20)
private var isScanning = true
private val binding by lazy {
ActivitySimilarPhotosBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initView()
initData()
showAnimationAd(LottieType.SIMILAR_PHOTOS) {
if (isScanning) {
// loadingDialog = DialogHelps.showLoadingDialog(this)
}
}
AdmobHelps.showNativeAd(this, completed = {
binding.adNative.setNativeAd(it)
}, maxCompleted = { nativeAdLoader, nativeMaxAd ->
binding.adNative.setNativeAd(nativeAdLoader, nativeMaxAd)
})
onBackPressedDispatcher.addCallback {
handleBackPressed(this@SimilarPhotosActivity)
}
}
private fun initView() {
adapter = SimilarPhotosAdapter()
adapter.callback = object : BaseAdapter.OnClickCallback<SimilarBean> {
override fun onClicked(view: View, position: Int, item: SimilarBean) {
val list = getAllList(adapter.list)
val total = list.filter { it.isSelect }.sumOf { it.size }
showSelection(total > 0, total)
}
}
binding.rvPhoto.adapter = adapter
binding.rvPhoto.layoutManager = LinearLayoutManager(this)
binding.flBack.setOnClickListener { backPressed() }
binding.ivSelect.setOnCheckedChangeListener { _, isChecked ->
setSelectAll(isChecked, adapter.list)
}
binding.tvDelete.setOnClickListener {
val list = getAllList(adapter.list).filter { it.isSelect }
if (list.isEmpty()) return@setOnClickListener
val total = list.sumOf { it.size }
DialogHelps.showDeleteDialog(this, binding.tvDelete) {
showAnimationAd {
}
deleteFiles(list)
}
}
}
private fun initData() {
lifecycleScope.launch(Dispatchers.IO) {
val images = MediaHelps.findImageFiles(this@SimilarPhotosActivity)
val similarBeans = images.mapNotNull { value ->
lifecycleScope.async(Dispatchers.IO) {
semaphore.acquire() // 请求许可
try {
ImageHelpers.createImage(value.path)
} finally {
semaphore.release() // 释放许可
}
}.await()
}
// val similarBeans = deferredSimilarBeans.mapNotNull { it.await() }
val hasSame = mutableSetOf<String>()
similarBeans.forEach { similar ->
if (hasSame.contains(similar.url)) return@forEach
hasSame.add(similar.url)
similar.items.add(similar)
similarBeans.forEach {
if (!hasSame.contains(it.url) && ImageHelpers.similarCondition(similar, it)) {
hasSame.add(it.url)
similar.items.add(it)
}
}
}
val list = similarBeans.filter { it.items.size > 1 }
withContext(Dispatchers.Main) {
isScanning = false
loadingDialog?.dismiss()
setSelectAll(true, list)
}
}
}
private fun setSelectAll(isSelected: Boolean, list: List<SimilarBean>) {
binding.ivSelect.isSelected = isSelected
list.forEach { similar ->
similar.items.forEachIndexed { index, similarBean ->
similarBean.isSelect = if (index > 0) isSelected else false
}
}
adapter.submitList(list)
val total = getAllList(adapter.list).filter { it.isSelect }.sumOf { it.size }
showSelection(isSelected && list.isNotEmpty(), total)
}
private fun deleteFiles(list: List<SimilarBean>) {
lifecycleScope.launch(Dispatchers.IO) {
val paths = list.map { it.url }.toTypedArray()
paths.forEach { FileHelps.deleteFile(it) }
withContext(Dispatchers.Main) {
MediaHelps.updateMedia(applicationContext, paths)
}
}
}
private fun showSelection(isSelection: Boolean, total: Long) {
binding.tvDelete.setBackgroundResource(if (isSelection) R.drawable.gradient else R.drawable.gradient_not_clickable)
val delete = getString(R.string.delete)
binding.tvDelete.text =
if (total > 0) "${delete} (${Utils.getFormatSize(total)})" else delete
}
private fun getAllList(beans: List<SimilarBean>): MutableList<SimilarBean> {
val list = mutableListOf<SimilarBean>()
beans.forEach { it.items.forEach { list.add(it) } }
return list
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.GridLayoutManager
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.bean.SimilarBean
import com.base.pdfviewerscannerwhite.databinding.ItemSimilarPhotosBinding
import com.base.pdfviewerscannerwhite.mix.AppHelps.dpToPx
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
class SimilarPhotosAdapter(
val data: List<SimilarBean> = emptyList()
) : BaseAdapter<SimilarBean, ItemSimilarPhotosBinding>(data) {
override fun getViewBinding(
layoutInflater: LayoutInflater,
parent: ViewGroup
): ItemSimilarPhotosBinding {
return ItemSimilarPhotosBinding.inflate(layoutInflater, parent, false)
}
override fun bind(
holder: BaseViewHolder<ItemSimilarPhotosBinding>,
position: Int,
item: SimilarBean
) {
val total = item.items.filter { it.isSelect }.sumOf { it.size }
holder.binding.tvSize.text = Utils.getFormatSize(total)
val width = holder.itemView.context.resources.displayMetrics.widthPixels
val margin = holder.itemView.context.dpToPx(45).toInt()
val imageWidth = (width - margin) / 4
val largePhotoWidth = imageWidth * 2
val largePhotoHeight = (largePhotoWidth * 0.92).toInt()
holder.binding.ivFirstPhoto.layoutParams.width = largePhotoWidth
holder.binding.ivFirstPhoto.layoutParams.height = largePhotoHeight
val round = holder.itemView.context.dpToPx(8).toInt()
val options = RequestOptions().transform(CenterCrop(), RoundedCorners(round))
Glide.with(holder.itemView.context)
.load(item.items[0].url)
.override(largePhotoWidth, largePhotoHeight)
.apply(options)
.into(holder.binding.ivFirstPhoto)
holder.binding.ivSelect.setImageResource(if (item.items[0].isSelect) R.mipmap.ic_check_s_photo else R.mipmap.ic_check_n_photo)
holder.binding.ivFirstPhoto.setOnClickListener {
item.items[0].isSelect = !item.items[0].isSelect
holder.binding.ivSelect.setImageResource(if (item.items[0].isSelect) R.mipmap.ic_check_s_photo else R.mipmap.ic_check_n_photo)
setItemClick(item, item.items[0], 0, holder)
}
val largePhotos = item.items.take(5)
val largeAdapter = SimilarPhotosChildAdapter(largePhotos.subList(1, largePhotos.size))
setItemClick(item, largeAdapter, holder)
holder.binding.rvLargePhoto.layoutManager = GridLayoutManager(holder.itemView.context, 2, GridLayoutManager.VERTICAL, false)
holder.binding.rvLargePhoto.adapter = largeAdapter
val photos = if (item.items.size > 4) item.items.subList(5, item.items.size) else mutableListOf()
val adapter = SimilarPhotosChildAdapter(photos)
setItemClick(item, adapter, holder)
holder.binding.rvPhoto.layoutManager = GridLayoutManager(holder.itemView.context, 4, GridLayoutManager.VERTICAL, false)
holder.binding.rvPhoto.adapter = adapter
}
private fun setItemClick(
item: SimilarBean,
adapter: SimilarPhotosChildAdapter,
holder: BaseViewHolder<ItemSimilarPhotosBinding>
) {
adapter.callback = object : OnClickCallback<SimilarBean> {
override fun onClicked(view: View, position: Int, data: SimilarBean) {
setItemClick(item, data, position, holder)
}
}
}
private fun setItemClick(
item: SimilarBean,
data: SimilarBean,
position: Int,
holder: BaseViewHolder<ItemSimilarPhotosBinding>
) {
val total = item.items.filter { it.isSelect }.sumOf { it.size }
holder.binding.tvSize.text = Utils.getFormatSize(total)
callback?.onClicked(holder.itemView, position, data)
}
}
package com.base.pdfviewerscannerwhite.mix
import android.view.LayoutInflater
import android.view.ViewGroup
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.bean.SimilarBean
import com.base.pdfviewerscannerwhite.databinding.ItemSimilarPhotosChildBinding
import com.base.pdfviewerscannerwhite.mix.AppHelps.dpToPx
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
class SimilarPhotosChildAdapter(
val data: List<SimilarBean>
) : BaseAdapter<SimilarBean, ItemSimilarPhotosChildBinding>(data) {
override fun getViewBinding(
layoutInflater: LayoutInflater,
parent: ViewGroup
): ItemSimilarPhotosChildBinding {
return ItemSimilarPhotosChildBinding.inflate(layoutInflater, parent, false)
}
override fun bind(
holder: BaseViewHolder<ItemSimilarPhotosChildBinding>,
position: Int,
item: SimilarBean
) {
val width = holder.itemView.context.resources.displayMetrics.widthPixels
val margin = holder.itemView.context.dpToPx(45).toInt()
val imageWidth = (width - margin) / 4
holder.binding.ivPhoto.layoutParams.width = imageWidth
holder.binding.ivPhoto.layoutParams.height = (imageWidth * 0.91).toInt()
holder.binding.ivSelect.setImageResource(if (item.isSelect) R.mipmap.ic_check_s_photo else R.mipmap.ic_check_n)
holder.itemView.setOnClickListener {
item.isSelect = !item.isSelect
holder.binding.ivSelect.setImageResource(if (item.isSelect) R.mipmap.ic_check_s_photo else R.mipmap.ic_check_n)
callback?.onClicked(it, position, item)
}
val round = holder.itemView.context.dpToPx(8).toInt()
val options = RequestOptions().transform(CenterCrop(), RoundedCorners(round))
Glide.with(holder.itemView.context)
.load(item.url)
.apply(options)
.into(holder.binding.ivPhoto)
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.content.Context
import android.content.SharedPreferences
import com.base.pdfviewerscannerwhite.helper.MyApplication
import org.json.JSONObject
class SpUtils private constructor(context: Context) {
private val sharedPreferences: SharedPreferences = context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)
// 通用的保存数据方法
fun <T> put(key: String, value: T) {
when (value) {
is String -> putString(key, value)
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Boolean -> putBoolean(key, value)
is Float -> putFloat(key, value)
else -> throw IllegalArgumentException("Unsupported type")
}
}
// 通用的获取数据方法
fun <T> get(key: String, defaultValue: T): T {
return when (defaultValue) {
is String -> getString(key, defaultValue)
is Int -> getInt(key, defaultValue)
is Long -> getLong(key, defaultValue)
is Boolean -> getBoolean(key, defaultValue)
is Float -> getFloat(key, defaultValue)
else -> throw IllegalArgumentException("Unsupported type")
} as T
}
// 保存字符串
fun putString(key: String, value: String) {
sharedPreferences.edit().putString(getKey(key), value).apply()
}
// 获取字符串
fun getString(key: String, defaultValue: String = ""): String {
return sharedPreferences.getString(getKey(key), defaultValue) ?: defaultValue
}
// 保存整数
fun putInt(key: String, value: Int) {
sharedPreferences.edit().putInt(getKey(key), value).apply()
}
// 获取整数
fun getInt(key: String, defaultValue: Int = 0): Int {
return sharedPreferences.getInt(getKey(key), defaultValue)
}
// 保存长整数
fun putLong(key: String, value: Long) {
sharedPreferences.edit().putLong(getKey(key), value).apply()
}
// 获取长整数
fun getLong(key: String, defaultValue: Long = 0): Long {
return sharedPreferences.getLong(getKey(key), defaultValue)
}
// 保存布尔值
fun putBoolean(key: String, value: Boolean) {
sharedPreferences.edit().putBoolean(getKey(key), value).apply()
}
// 获取布尔值
fun getBoolean(key: String, defaultValue: Boolean = false): Boolean {
return sharedPreferences.getBoolean(getKey(key), defaultValue)
}
// 保存浮点数
fun putFloat(key: String, value: Float) {
sharedPreferences.edit().putFloat(getKey(key), value).apply()
}
// 获取浮点数
fun getFloat(key: String, defaultValue: Float = 0f): Float {
return sharedPreferences.getFloat(getKey(key), defaultValue)
}
// 删除键值对
fun remove(key: String) {
sharedPreferences.edit().remove(getKey(key)).apply()
}
// 清除所有数据
fun clear() {
sharedPreferences.edit().clear().apply()
}
// 检查键是否存在
fun contains(key: String): Boolean {
return sharedPreferences.contains(getKey(key))
}
// 保存 JSON 对象
fun saveJsonObjectToSp(json: String) {
try {
if (json.isEmpty()) return
val jsonObject = JSONObject(json)
sharedPreferences.edit().apply {
jsonObject.keys().forEachRemaining { key ->
val value = jsonObject.get(key)
val newKey = getKey(key)
when (value) {
is String -> putString(newKey, value)
is Int -> putInt(newKey, value)
is Long -> putLong(newKey, value)
is Boolean -> putBoolean(newKey, value)
else -> putString(newKey, value.toString())
}
}
apply()
}
} catch (_:Exception) { }
}
fun <T> getValue(key: String, default: T): T {
val newKey = getKey(key)
val value = get(newKey, default)
return value
}
private fun getKey(key: String): String = "${MyApplication.context.packageName}_$key"
companion object {
@Volatile
private var instance: SpUtils? = null
// 单例模式获取实例
fun getInstance(context: Context = MyApplication.context): SpUtils {
return instance ?: synchronized(this) {
instance ?: SpUtils(context).also {
instance = it
}
}
}
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.app.Activity
import android.graphics.Color
import android.view.View
import android.view.WindowManager
import androidx.annotation.ColorInt
import androidx.core.view.WindowCompat
object StatusBarHelps {
fun immersiveStatusBar(activity: Activity, @ColorInt statusBarColor: Int, isDark: Boolean) {
activity.window.apply {
// 透明状态栏
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
this.statusBarColor = statusBarColor
if (isDark) {
decorView.systemUiVisibility =
decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else{
decorView.systemUiVisibility =
decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
}
}
fun setDark(activity: Activity, isDark: Boolean) {
activity.window.apply {
if (isDark) {
decorView.systemUiVisibility =
decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
decorView.systemUiVisibility =
decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
}
}
fun immersive(activity: Activity, @ColorInt statusBarColor: Int) {
immersiveStatusBar(activity, statusBarColor, false)
}
fun immersive(activity: Activity) {
// 设置状态栏透明
activity.window.statusBarColor = Color.TRANSPARENT
WindowCompat.setDecorFitsSystemWindows(activity.window, false)
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.content.Context
import java.math.BigDecimal
import java.math.RoundingMode
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
object Utils {
fun getFormatSize(bytes: Number): String {
val value: Double
val unit: String
if (bytes.toDouble() < 1024) {
value = bytes.toDouble()
unit = " B"
} else if (bytes.toDouble() < 1024 * 1024) {
value = bytes.toDouble() / 1024
unit = " KB"
} else if (bytes.toDouble() < 1024 * 1024 * 1024) {
value = bytes.toDouble() / (1024 * 1024)
unit = " MB"
} else {
value = bytes.toDouble() / (1024 * 1024 * 1024)
unit = " GB"
}
val formattedNumber = String.format("%.2f", value)
return formattedNumber + unit
}
fun getSize(bytes: Long): String {
if (bytes == 0.toLong()) return "0 B"
val value: Double
val unit: String
if (bytes.toDouble() < 1024) {
value = bytes.toDouble()
unit = " B"
} else if (bytes.toDouble() < 1024 * 1024) {
value = bytes.toDouble() / 1024
unit = " KB"
} else if (bytes.toDouble() < 1024 * 1024 * 1024) {
value = bytes.toDouble() / (1024 * 1024)
unit = " MB"
} else {
value = bytes.toDouble() / (1024 * 1024 * 1024)
unit = " GB"
}
val formattedNumber = String.format("%.1f", value)
return formattedNumber + unit
}
fun getSizeArray(bytes: Long): Array<String> {
val value = getSize(bytes)
val size = value.substring(0, value.indexOf(" "))
val unit = value.substring(value.indexOf(" ") + 1)
return arrayOf(size, unit)
}
fun Long.toDateString(): String {
val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
return formatter.format(Date(this))
}
fun Context.toDate(): String {
val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
return formatter.format(Date())
}
fun getCurrentMinute(): Int {
val calendar = Calendar.getInstance()
return calendar.get(Calendar.MINUTE)
}
fun calculate(dividend: Int, divisor: Int): String {
val bigDecimalDividend = BigDecimal(dividend)
val bigDecimalDivisor = BigDecimal(divisor)
// 执行除法操作,并保留一位小数,不四舍五入
val result = bigDecimalDividend.divide(bigDecimalDivisor, 1, RoundingMode.DOWN)
// 将结果转换回Double
return result.toString()
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.activity.addCallback
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.databinding.ActivityWhatsappCleanBinding
import com.base.pdfviewerscannerwhite.mix.MineTypeHelps.TYPE_AUDIO
import com.base.pdfviewerscannerwhite.mix.MineTypeHelps.TYPE_IMAGE
import com.base.pdfviewerscannerwhite.mix.MineTypeHelps.TYPE_VIDEO
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.Locale
class WhatsappCleanActivity : BaseActivity2() {
companion object {
fun start(context: Context) {
val intent = Intent(context, WhatsappCleanActivity::class.java)
context.startActivity(intent)
}
}
private val binding by lazy {
ActivityWhatsappCleanBinding.inflate(layoutInflater)
}
private lateinit var videoAdapter: WhatsappCleanAdapter
private lateinit var imageAdapter: WhatsappCleanAdapter
private var videoList = listOf<FileBean>()
private var imageList = listOf<FileBean>()
private var audioList = listOf<FileBean>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initView()
initData()
showAnimationAd(LottieType.WHATSAPP)
onBackPressedDispatcher.addCallback {
handleBackPressed(this@WhatsappCleanActivity)
}
}
private fun initView() {
videoAdapter = WhatsappCleanAdapter(TYPE_VIDEO)
binding.rvVideo.adapter = videoAdapter
binding.rvVideo.layoutManager = GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false)
imageAdapter = WhatsappCleanAdapter(TYPE_IMAGE)
binding.rvImage.adapter = imageAdapter
binding.rvImage.layoutManager = GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false)
binding.viewVideo.setOnClickListener {
toDetail(videoList, videoAdapter.type, getString(R.string.video_messages))
}
binding.viewImage.setOnClickListener {
toDetail(imageList, imageAdapter.type, getString(R.string.image_messages))
}
binding.flBack.setOnClickListener { backPressed() }
binding.clVideoMessage.setOnClickListener { toDetail(videoList, videoAdapter.type, getString(R.string.video_messages)) }
binding.clImageMessage.setOnClickListener { toDetail(imageList, imageAdapter.type, getString(R.string.image_messages)) }
binding.llAudioMessage.setOnClickListener { toDetail(audioList, TYPE_AUDIO, getString(R.string.audio_messages)) }
}
private fun initData() {
lifecycleScope.launch(Dispatchers.IO) {
val filesDeferred = async { FileHelps.findWhatsappFiles() }
val files = filesDeferred.await()
videoList = files.filter { file ->
MineTypeHelps.videos.any { suffix -> file.name.lowercase(Locale.ROOT).endsWith(suffix) }
}
imageList = files.filter { file ->
MineTypeHelps.images.any { suffix -> file.name.lowercase(Locale.ROOT).endsWith(suffix) }
}
audioList = files.filter { file ->
MineTypeHelps.audios.any { suffix -> file.name.lowercase(Locale.ROOT).endsWith(suffix) }
}
val videoSize = videoList.sumOf { it.length }
val imageSize = imageList.sumOf { it.length }
val audioSize = audioList.sumOf { it.length }
val total = videoSize + imageSize + audioSize
val value = Utils.getSizeArray(total)
withContext(Dispatchers.Main) {
binding.tvSize.text = value[0]
binding.tvUnit.text = value[1]
binding.tvVideoSize.text = Utils.getSize(videoSize)
binding.tvImageSize.text = Utils.getSize(imageSize)
binding.tvAudioSize.text = Utils.getSize(audioSize)
binding.tvNoVideo.visibility = if (videoList.isEmpty()) View.VISIBLE else View.GONE
binding.tvNoImage.visibility = if (imageList.isEmpty()) View.VISIBLE else View.GONE
val textColor = if (videoList.isEmpty()) getColor(R.color.color_aeb4bd) else getColor(R.color.color_0592ee)
binding.tvVideoSize.setTextColor(textColor)
binding.tvImageSize.setTextColor(textColor)
binding.tvAudioSize.setTextColor(textColor)
if (audioList.isNotEmpty()) {
val foundVoiceMessage = getString(R.string.found_voice_message).replace("3", "${audioList.size}")
binding.tvNoAudio.text = foundVoiceMessage
}
videoAdapter.updateData(videoList)
imageAdapter.updateData(imageList)
}
}
}
private fun toDetail(list: List<FileBean>, type: Int, title: String) {
if (list.isEmpty()) return
list.forEach { it.isSelected = false }
WhatsappCleanDetailActivity.start(this, list, type, title)
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.annotation.SuppressLint
import android.media.tv.TvTrackInfo.TYPE_VIDEO
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.base.pdfviewerscannerwhite.databinding.ItemWhatsappCleanBinding
import com.base.pdfviewerscannerwhite.mix.AppHelps.dpToPx
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
class WhatsappCleanAdapter(
var data: List<FileBean>,
val type: Int = TYPE_VIDEO
) : BaseAdapter<FileBean, ItemWhatsappCleanBinding>(data.take(3)) {
constructor(type: Int = TYPE_VIDEO) : this(emptyList(), type)
fun updateData(data: List<FileBean>) {
this.data = data
submitList(data.take(3))
}
override fun getViewBinding(
layoutInflater: LayoutInflater,
parent: ViewGroup
): ItemWhatsappCleanBinding {
return ItemWhatsappCleanBinding.inflate(layoutInflater, parent, false)
}
@SuppressLint("SetTextI18n")
override fun bind(
holder: BaseViewHolder<ItemWhatsappCleanBinding>,
position: Int,
item: FileBean
) {
val width = holder.itemView.context.resources.displayMetrics.widthPixels
val margin = holder.itemView.context.dpToPx(128).toInt()
val imageWidth = (width - margin) / 3
holder.binding.ivImage.layoutParams.width = imageWidth
holder.binding.ivImage.layoutParams.height = imageWidth
val round = holder.itemView.context.dpToPx(8).toInt()
val options = RequestOptions().transform(CenterCrop(), RoundedCorners(round))
Glide.with(holder.itemView.context)
.load(item.path)
.apply(options)
.into(holder.binding.ivImage)
holder.binding.tvSize.text = ""
holder.binding.ivVideo.visibility = if (type == TYPE_VIDEO) View.VISIBLE else View.GONE
if (data.size > 3 && position == itemCount - 1) {
holder.binding.tvSize.text = "+${data.size - itemCount}"
} else {
holder.binding.ivShadow.visibility = View.GONE
}
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.mix
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.activity.addCallback
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.databinding.ActivityWhatsappCleanDetailBinding
class WhatsappCleanDetailActivity : BaseActivity2() {
companion object {
var list = listOf<FileBean>()
var type = 0
var titleName = ""
fun start(context: Context, list: List<FileBean>, type: Int, titleName: String) {
Companion.list = list
Companion.type = type
Companion.titleName = titleName
val intent = Intent(context, WhatsappCleanDetailActivity::class.java)
context.startActivity(intent)
}
}
private val binding by lazy {
ActivityWhatsappCleanDetailBinding.inflate(layoutInflater)
}
private lateinit var adapter: CleanDetailAdapter
private var total: Long = 0
private var isSelectAll = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initView()
onBackPressedDispatcher.addCallback {
handleBackPressed(this@WhatsappCleanDetailActivity)
}
}
private fun initView() {
adapter = CleanDetailAdapter(list, type)
adapter.callback = object : BaseAdapter.OnClickCallback<FileBean> {
override fun onClicked(view: View, position: Int, item: FileBean) {
if (item.isSelected) total += item.length
else total -= item.length
isSelectAll = adapter.list.stream().allMatch(FileBean::isSelected)
updateView(total > 0)
}
}
binding.rvWhatsapp.adapter = adapter
binding.rvWhatsapp.layoutManager = if (type == 2) {
LinearLayoutManager(this)
} else {
GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false)
}
if (titleName.isNotEmpty()) binding.tvTitle.text = titleName
binding.flBack.setOnClickListener { backPressed() }
binding.llSelectAll.setOnClickListener {
isSelectAll = !isSelectAll
val list = adapter.list
list.forEach { it.isSelected = isSelectAll }
adapter.submitList(list)
total = list.filter { it.isSelected }.sumOf { it.length }
updateView(isSelectAll)
}
binding.tvDelete.setOnClickListener {
if (total <= 0) return@setOnClickListener
DialogHelps.showDeleteDialog(this, binding.tvDelete) {
showAnimationAd {
}
cleanFiles(adapter.list)
}
}
}
private fun updateView(isSelection: Boolean) {
binding.ivSelectAll.setImageResource(if (isSelectAll) R.mipmap.ic_check_s else R.mipmap.ic_check_n)
binding.tvDelete.setBackgroundResource(if (isSelection) R.drawable.gradient_26 else R.drawable.btn_not_clickable)
val value = Utils.getSizeArray(total)
binding.tvSize.text = value[0]
binding.tvUnit.text = value[1]
}
}
\ No newline at end of file
package com.base.pdfviewerscannerwhite.ui.main
import android.view.View
import androidx.recyclerview.widget.RecyclerView.ViewHolder
class CommonViewHolder(view: View) : ViewHolder(view)
\ No newline at end of file
package com.base.pdfviewerscannerwhite.ui.main
import android.content.Context
import android.view.ViewGroup
import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.bean.FunctionUIBean
import com.base.pdfviewerscannerwhite.databinding.ItemFunctionBinding
import com.base.pdfviewerscannerwhite.utils.XmlEx.inflate
import com.chad.library.adapter4.BaseQuickAdapter
class FunctionAdapter : BaseQuickAdapter<FunctionUIBean, CommonViewHolder>() {
private val TAG = "FunctionAdapter"
var itemClick: ((key: String) -> Unit)? = null
override fun onBindViewHolder(holder: CommonViewHolder, position: Int, item: FunctionUIBean?) {
item ?: return
val binding = ItemFunctionBinding.bind(holder.itemView)
binding.iv.setImageResource(item.icon)
binding.tvDesc.text = item.desc
binding.root.setOnClickListener {
itemClick?.invoke(item.key)
}
}
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): CommonViewHolder {
return CommonViewHolder(R.layout.item_function.inflate(parent))
}
}
\ No newline at end of file
...@@ -8,8 +8,10 @@ import com.base.pdfviewerscannerwhite.R ...@@ -8,8 +8,10 @@ import com.base.pdfviewerscannerwhite.R
import com.base.pdfviewerscannerwhite.ads.admob.AdmobNativeUtils import com.base.pdfviewerscannerwhite.ads.admob.AdmobNativeUtils
import com.base.pdfviewerscannerwhite.bean.ConstObject.FUNCTION_APP_PROCESS import com.base.pdfviewerscannerwhite.bean.ConstObject.FUNCTION_APP_PROCESS
import com.base.pdfviewerscannerwhite.bean.ConstObject.FUNCTION_SCAM_JUNK import com.base.pdfviewerscannerwhite.bean.ConstObject.FUNCTION_SCAM_JUNK
import com.base.pdfviewerscannerwhite.bean.ConstObject.FUNCTION_SCREEN_SHORT
import com.base.pdfviewerscannerwhite.databinding.ActivityResultBinding import com.base.pdfviewerscannerwhite.databinding.ActivityResultBinding
import com.base.pdfviewerscannerwhite.helper.BaseActivity import com.base.pdfviewerscannerwhite.helper.BaseActivity
import com.base.pdfviewerscannerwhite.mix.LargeFileCleanActivity
import com.base.pdfviewerscannerwhite.ui.appprocess.AppProcessLoadingActivity import com.base.pdfviewerscannerwhite.ui.appprocess.AppProcessLoadingActivity
import com.base.pdfviewerscannerwhite.ui.cleanjunk.ScanJunkActivity import com.base.pdfviewerscannerwhite.ui.cleanjunk.ScanJunkActivity
import com.base.pdfviewerscannerwhite.utils.BarUtils import com.base.pdfviewerscannerwhite.utils.BarUtils
...@@ -60,6 +62,24 @@ class ResultActivity : BaseActivity<ActivityResultBinding>() { ...@@ -60,6 +62,24 @@ class ResultActivity : BaseActivity<ActivityResultBinding>() {
binding.tvSize.text = "Cleaned up ${s.toFormatSize(1)}" binding.tvSize.text = "Cleaned up ${s.toFormatSize(1)}"
} }
} }
FUNCTION_SCREEN_SHORT -> {
binding.tvTitle.text = getString(R.string.screenshot_clean)
val s = intent.extras?.getLong("clean_size") ?: 0L
if (s != 0L) {
binding.tvSize.text = "Cleaned up ${s.toFormatSize(1)}"
}
binding.tvFunction.text = getString(R.string.large_file_clean)
binding.ivFunction.setImageResource(R.mipmap.icon_file_tool)
binding.tvFunctionDesc.text = getString(R.string.find_your_large_file_to_clean_it)
binding.tvFunctionBtn.text = getString(R.string.scan_now)
binding.tvFunctionBtn.setOnClickListener {
startActivity(Intent(this, LargeFileCleanActivity::class.java))
finish()
}
}
} }
......
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#2372FC" android:state_checked="true" />
<item android:color="#D7DBE4" android:state_checked="false" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_f0f0f0" />
<corners android:radius="16dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!-- <solid android:color="#EDFF7A" />-->
<size
android:width="25dp"
android:height="25dp" />
<!-- 边缘全透明 -->
<stroke
android:width="8dp"
android:color="#00000000" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<!-- <solid android:color="#2372FD" />-->
<size android:height="25dp" />
<corners android:radius="30dp" />
</shape>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFC9C9C9" />
<corners android:radius="26dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="4dp" />
<solid android:color="@color/white" />
<stroke android:width="1dp" android:color="@color/color_aeb4bd" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="@color/color_8eb0ff"
android:endColor="@color/color_6473f8"
android:angle="0"
android:centerX="0.5"
android:centerY="0.5" />
<corners android:radius="37dp" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="@color/color_ff6400"
android:endColor="@color/color_ff8200"
android:angle="0"
android:centerX="0.5"
android:centerY="0.5" />
<corners android:radius="37dp" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="@color/color_ff6400"
android:endColor="@color/color_ff8200"
android:angle="0"
android:centerX="0.5"
android:centerY="0.5" />
<corners android:radius="4dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="@color/color_8eb0ff"
android:endColor="@color/color_6473f8"
android:angle="270"
android:centerX="0.5"
android:centerY="0.5" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#EBEDFF"
android:endColor="#EBEDFF"
android:angle="0"
android:centerX="0.5"
android:centerY="0.5" />
<corners android:radius="50dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="37dp" />
<solid android:color="@android:color/transparent" />
<stroke android:width="1dp" android:color="@color/color_ebebeb" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_66000000" />
<corners
android:bottomLeftRadius="8dp"
android:bottomRightRadius="8dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_66000000" />
<corners android:radius="8dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners android:radius="12dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners android:radius="16dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners
android:bottomLeftRadius="15dp"
android:bottomRightRadius="15dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners
android:topLeftRadius="15dp"
android:topRightRadius="15dp" />
</shape>
\ No newline at end of file
This diff is collapsed.
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
</FrameLayout> </FrameLayout>
<TextView <TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:includeFontPadding="false" android:includeFontPadding="false"
......
This diff is collapsed.
<?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="match_parent"
android:background="@color/color_f7fafa">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/gradient_background"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="6dp"
android:paddingBottom="6dp"
app:layout_constraintTop_toTopOf="parent">
<FrameLayout
android:id="@+id/fl_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="11dp"
android:padding="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/icon_return_bar_nor" />
</FrameLayout>
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/similar_photos"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/fl_back"
app:layout_constraintStart_toEndOf="@id/fl_back"
app:layout_constraintTop_toTopOf="@id/fl_back" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_select"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
app:layout_constraintTop_toBottomOf="@id/cl_top">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="13dp"
android:layout_marginStart="15dp"
android:text="@string/auto_select"
android:textColor="@color/black"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/iv_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
android:thumb="@drawable/bg_switch_thumb_ffffff"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:thumbTint="@color/white"
app:track="@drawable/bg_switch_track"
app:trackTint="@color/switch_track_selector" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.base.pdfviewerscannerwhite.mix.NativeView
android:id="@+id/ad_native"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="10dp"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/cl_select" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_photo"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="20dp"
android:scrollbars="none"
app:layout_constraintBottom_toTopOf="@id/tv_delete"
app:layout_constraintTop_toBottomOf="@id/ad_native" />
<TextView
android:id="@+id/tv_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="30dp"
android:layout_marginBottom="55dp"
android:background="@drawable/gradient_not_clickable"
android:gravity="center"
android:paddingVertical="12dp"
android:text="@string/delete"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent" />
<include
android:id="@+id/animation"
layout="@layout/custom_animation"
android:visibility="gone" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<?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"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_photo"
android:layout_width="80dp"
android:layout_height="73dp"
android:layout_marginTop="4dp"
android:src="@color/color_9b9595"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginEnd="8dp"
android:src="@mipmap/ic_check_n_photo"
app:layout_constraintTop_toTopOf="@id/iv_photo"
app:layout_constraintEnd_toEndOf="@id/iv_photo" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment