├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── xml │ │ │ ├── file_paths.xml │ │ │ ├── image_widget_info.xml │ │ │ ├── data_extraction_rules.xml │ │ │ └── root_preferences.xml │ │ ├── drawable │ │ │ ├── divider.xml │ │ │ ├── arrow_left.xml │ │ │ ├── arrow_right.xml │ │ │ ├── activity_main_background.xml │ │ │ ├── ic_close.xml │ │ │ ├── ic_delete_image.xml │ │ │ ├── ic_undo.xml │ │ │ ├── ic_refresh.xml │ │ │ ├── ic_launcher_foreground.xml │ │ │ ├── dialog_background.xml │ │ │ ├── ic_image.xml │ │ │ ├── ic_info.xml │ │ │ └── ic_settings.xml │ │ ├── values-v31 │ │ │ ├── colors.xml │ │ │ ├── themes.xml │ │ │ └── styles.xml │ │ ├── values-night-v31 │ │ │ ├── colors.xml │ │ │ └── themes.xml │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── values │ │ │ ├── attrs.xml │ │ │ ├── dimens.xml │ │ │ ├── colors.xml │ │ │ ├── arrays.xml │ │ │ ├── themes.xml │ │ │ ├── styles.xml │ │ │ └── strings.xml │ │ ├── drawable-v21 │ │ │ ├── app_widget_background.xml │ │ │ └── app_widget_inner_view_background.xml │ │ ├── xml-v31 │ │ │ └── image_widget_info.xml │ │ ├── menu │ │ │ └── menu_main_activity.xml │ │ ├── drawable-v31 │ │ │ └── dialog_background.xml │ │ ├── drawable-night-v31 │ │ │ └── dialog_background.xml │ │ ├── layout │ │ │ ├── activity_settings2.xml │ │ │ ├── layout_view_pager_item.xml │ │ │ ├── activity_image.xml │ │ │ ├── layout_deleted_snackbar_buttons.xml │ │ │ ├── layout_main_view_pager_item.xml │ │ │ ├── image_widget.xml │ │ │ ├── activity_main_multi_window.xml │ │ │ └── activity_main.xml │ │ ├── values-en │ │ │ ├── arrays.xml │ │ │ └── strings.xml │ │ └── values-v21 │ │ │ └── styles.xml │ │ ├── java │ │ └── com │ │ │ └── sjk │ │ │ └── deleterecentpictures │ │ │ ├── entity │ │ │ ├── DeletedImageInfoEntity.kt │ │ │ ├── ImageDetailEntity.kt │ │ │ └── ImageInfoEntity.kt │ │ │ ├── utils │ │ │ ├── TimeUtil.kt │ │ │ ├── ClipboardUtil.kt │ │ │ ├── DensityUtil.kt │ │ │ ├── ApkUtil.kt │ │ │ ├── TransformUtil.kt │ │ │ ├── ShellUtil.kt │ │ │ ├── BigImageHelperImpl.kt │ │ │ ├── AlertDialogUtil.kt │ │ │ ├── ShortcutUtil.kt │ │ │ ├── PermissionUtil.kt │ │ │ ├── QRCodeUtil.kt │ │ │ └── FileUtil.kt │ │ │ ├── common │ │ │ ├── Const.kt │ │ │ ├── Switch.kt │ │ │ ├── Event.kt │ │ │ ├── GlobalData.kt │ │ │ ├── RecentImages.kt │ │ │ ├── ActivityManager.kt │ │ │ ├── BaseActivity.kt │ │ │ ├── Input.kt │ │ │ ├── ImageLoadManager.kt │ │ │ ├── Logger.kt │ │ │ ├── App.kt │ │ │ ├── DataSource.kt │ │ │ └── RecycleBinManager.kt │ │ │ ├── service │ │ │ ├── QuickSettingOpenService.kt │ │ │ └── ImageWidgetService.kt │ │ │ └── activity │ │ │ ├── image │ │ │ ├── ImageActivity.kt │ │ │ └── ImageActivityViewPagerAdapter.kt │ │ │ ├── main │ │ │ ├── ScrollButtonManager.kt │ │ │ └── MainActivityViewPagerAdapter.kt │ │ │ └── common │ │ │ └── ImageLongClickDialog.kt │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── docs └── images │ └── previews │ ├── 1.png │ ├── 2.png │ ├── 3.png │ └── 4.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── dictionaries │ ├── admin.xml │ ├── win10.xml │ └── sjk.xml ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── compiler.xml ├── kotlinc.xml ├── migrations.xml ├── deploymentTargetDropDown.xml ├── vcs.xml ├── inspectionProfiles │ └── Project_Default.xml ├── git_toolbox_prj.xml ├── aws.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml └── intellij-javadocs-4.0.1.xml ├── .gitignore ├── gradle.properties ├── README.md ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | rootProject.name='DeleteRecentPictures' 3 | -------------------------------------------------------------------------------- /docs/images/previews/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/docs/images/previews/1.png -------------------------------------------------------------------------------- /docs/images/previews/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/docs/images/previews/2.png -------------------------------------------------------------------------------- /docs/images/previews/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/docs/images/previews/3.png -------------------------------------------------------------------------------- /docs/images/previews/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/docs/images/previews/4.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1045290202/DeleteRecentPictures/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /.idea/dictionaries/admin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | infos 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/dictionaries/win10.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | dcim 5 | deleterecentpictures 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-v31/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @android:color/system_accent1_0 4 | @android:color/system_accent1_300 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/entity/DeletedImageInfoEntity.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.entity 2 | 3 | import java.io.File 4 | 5 | data class DeletedImageInfoEntity( 6 | val oldFile: File, 7 | val newFile: File, 8 | val info: ImageInfoEntity?, 9 | ) -------------------------------------------------------------------------------- /app/src/main/res/values-night-v31/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @android:color/system_accent1_800 4 | @android:color/system_accent1_200 5 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | /app/release/ 16 | /.idea/ 17 | /.kotlin 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 280dp 3 | 4 | 8 | 0dp 9 | 10 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/entity/ImageDetailEntity.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.entity 2 | 3 | data class ImageDetailEntity( 4 | val data: String, 5 | val dateAdded: Long, 6 | val dateModified: Long, 7 | val displayName: String, 8 | val mimeType: String, 9 | val size: Long, 10 | val width: Int, 11 | val height: Int, 12 | ) -------------------------------------------------------------------------------- /.idea/dictionaries/sjk.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | baos 5 | charlist 6 | constraintlayout 7 | coolapk 8 | coolmarket 9 | deleterecent 10 | fileprovider 11 | logd 12 | msgs 13 | tencent 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/app_widget_background.xml: -------------------------------------------------------------------------------- 1 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/app_widget_inner_view_background.xml: -------------------------------------------------------------------------------- 1 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/utils/TimeUtil.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.utils 2 | 3 | import java.text.DateFormat 4 | import java.util.* 5 | 6 | object TimeUtil { 7 | 8 | /** 9 | * 将时间戳格式化为系统默认格式 10 | * @param timestamp 时间戳 11 | */ 12 | fun formatTimestampToSystemDefaultFormat(timestamp: Long): String { 13 | val dateFormat = DateFormat.getDateTimeInstance() 14 | return dateFormat.format(Date(timestamp)) 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/arrow_left.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/arrow_right.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/xml/image_widget_info.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/activity_main_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values-night-v31/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 10 | -------------------------------------------------------------------------------- /.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/common/Const.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.common 2 | 3 | import com.sjk.deleterecentpictures.R 4 | 5 | object Const { 6 | const val DEFAULT_NUMBER_OF_PICTURES = 10 7 | val IMAGE_LONG_CLICK_DIALOG_ITEMS: Array = arrayOf( 8 | R.string.open_method, 9 | R.string.share, 10 | R.string.barcode_or_qrcode_recognition 11 | ) 12 | // 自动滚动的时间间隔 13 | const val AUTO_SCROLL_INTERVAL = 300L 14 | // 自动滚动的延迟时间 15 | const val AUTO_SCROLL_DELAY = 1000L 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete_image.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/common/Switch.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.common 2 | 3 | object Switch { 4 | const val ENABLE_LOG: Boolean = true 5 | val ENABLE_LOG_LEVELS: Map = mapOf( 6 | LoggerLevelEnum.VERBOSE to true, 7 | LoggerLevelEnum.DEBUG to true, 8 | LoggerLevelEnum.INFO to true, 9 | LoggerLevelEnum.WARN to true, 10 | LoggerLevelEnum.ERROR to true, 11 | LoggerLevelEnum.LOG to true 12 | ) 13 | const val ENABLE_LOG_TIME = true 14 | } -------------------------------------------------------------------------------- /app/src/main/res/values-v31/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/common/Event.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.common 2 | 3 | 4 | class Event { 5 | private val events: MutableMap) -> Unit> = mutableMapOf() 6 | 7 | fun addEventListener(event: String, callback: (args: Array) -> Unit) { 8 | this.events[event] = callback 9 | } 10 | 11 | fun removeEventListener(event: String) { 12 | this.events.remove(event) 13 | } 14 | 15 | fun fireEvent(event: String, vararg args: Any?) { 16 | this.events[event]?.invoke(args.toList().toTypedArray()) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/xml-v31/image_widget_info.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/common/GlobalData.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.common 2 | 3 | object GlobalData { 4 | private const val TAG: String = "GlobalData" 5 | private var data: MutableMap = mutableMapOf() 6 | 7 | 8 | fun setData(key: String, value: Any?) { 9 | this.data[key] = value 10 | } 11 | 12 | fun getData(key: String, default: Any?): Any? { 13 | if (this.data[key] == null) { 14 | return default 15 | } 16 | return this.data[key] 17 | } 18 | 19 | fun removeData(key: String) { 20 | this.data.remove(key) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/utils/ClipboardUtil.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.utils 2 | 3 | import android.content.ClipData 4 | import android.content.ClipboardManager 5 | import android.content.Context 6 | import com.sjk.deleterecentpictures.R 7 | import com.sjk.deleterecentpictures.common.App 8 | 9 | object ClipboardUtil { 10 | fun setText(text: String) { 11 | val clipboard = App.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager 12 | val clip = ClipData.newPlainText(App.applicationContext.resources.getString(R.string.app_name), text) 13 | clipboard.setPrimaryClip(clip) 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/utils/DensityUtil.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.utils 2 | 3 | import android.content.Context 4 | 5 | object DensityUtil { 6 | /** 7 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 8 | */ 9 | fun dip2px(context: Context, dpValue: Float): Int { 10 | val scale = context.resources.displayMetrics.density 11 | return (dpValue * scale + 0.5f).toInt() 12 | } 13 | 14 | /** 15 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp 16 | */ 17 | fun px2dip(context: Context, pxValue: Float): Int { 18 | val scale = context.resources.displayMetrics.density 19 | return (pxValue / scale + 0.5f).toInt() 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_undo.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ?attr/colorPrimary 4 | ?attr/colorPrimaryDark 5 | ?attr/colorAccent 6 | ?attr/colorSecondary 7 | 8 | #FFFFFF 9 | #64b5f6 10 | #FFE1F5FE 11 | #FF81D4FA 12 | #FF039BE5 13 | #FF01579B 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_refresh.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/dialog_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v31/dialog_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/aws.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 17 | 18 | 19 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-night-v31/dialog_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 整个外置存储目录 5 | 截图目录 6 | 自定义 7 | 8 | 9 | 10 | sdCard 11 | screenshots 12 | customize 13 | 14 | 15 | 16 | 1 17 | 2 18 | 19 | 20 | 21 | 修改时间(倒序) 22 | 添加时间(倒序) 23 | 24 | 25 | 26 | 1 27 | 2 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 17 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_settings2.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/entity/ImageInfoEntity.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.entity 2 | 3 | import android.net.Uri 4 | import com.flyjingfish.openimagelib.beans.OpenImageUrl 5 | import com.flyjingfish.openimagelib.enums.MediaType 6 | 7 | data class ImageInfoEntity( 8 | val path: String? = null, 9 | @Transient val uri: Uri? = null, 10 | val id: Long? = null, 11 | val dateAdded: Long? = null, 12 | val dateModified: Long? = null, 13 | val mimeType: String? = null, 14 | ) : OpenImageUrl { 15 | override fun getImageUrl(): String { 16 | return this.path ?: "" 17 | } 18 | 19 | override fun getVideoUrl(): String { 20 | return "" 21 | } 22 | 23 | override fun getCoverImageUrl(): String { 24 | return "" 25 | } 26 | 27 | override fun getType(): MediaType { 28 | return MediaType.IMAGE 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/res/values-en/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The entire external storage directory 5 | Screenshot directory 6 | Customize 7 | 8 | 9 | 10 | sdCard 11 | screenshots 12 | customize 13 | 14 | 15 | 16 | 1 17 | 2 18 | 19 | 20 | 21 | Modification time (reverse order) 22 | Add time (reverse order) 23 | 24 | 25 | 26 | 1 27 | 2 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_image.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/utils/ApkUtil.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.utils 2 | 3 | import android.content.Context 4 | import android.content.pm.PackageManager 5 | import android.os.Build 6 | import android.text.TextUtils 7 | 8 | object ApkUtil { 9 | fun checkApkExist(context: Context?, apkPackageName: String?): Boolean { 10 | return if (TextUtils.isEmpty(apkPackageName)) { 11 | false 12 | } else try { 13 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 14 | context?.packageManager?.getApplicationInfo(apkPackageName!!, PackageManager.MATCH_UNINSTALLED_PACKAGES) 15 | } else { 16 | context?.packageManager?.getApplicationInfo(apkPackageName!!, PackageManager.GET_UNINSTALLED_PACKAGES) 17 | } 18 | true 19 | } catch (e: PackageManager.NameNotFoundException) { 20 | false 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_view_pager_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 16 | 17 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/utils/TransformUtil.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * 已废弃 3 | */ 4 | 5 | package com.sjk.deleterecentpictures.utils 6 | 7 | import android.graphics.Bitmap 8 | import android.graphics.BitmapFactory 9 | import java.io.ByteArrayOutputStream 10 | 11 | 12 | object TransformUtil { 13 | fun filePath2Bitmap(imagePath: String?): Bitmap? { 14 | val options = BitmapFactory.Options() 15 | options.inPreferredConfig = Bitmap.Config.RGB_565 16 | val bitmap = BitmapFactory.decodeFile(imagePath, options) 17 | 18 | val baos = ByteArrayOutputStream() 19 | val quality = 50 20 | bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos) 21 | bitmap.recycle() 22 | val bytes = baos.toByteArray() 23 | return BitmapFactory.decodeByteArray(bytes, 0, bytes.size) 24 | 25 | // val options = BitmapFactory.Options() 26 | // options.inPreferredConfig = Bitmap.Config.RGB_565 27 | // return BitmapFactory.decodeFile(imagePath, options) 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/common/RecentImages.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.common 2 | 3 | import com.sjk.deleterecentpictures.entity.ImageInfoEntity 4 | 5 | object RecentImages { 6 | val imageInfos: MutableList = ArrayList() 7 | var currentImageInfoIndex: Int = 0 8 | 9 | val currentImageInfo: ImageInfoEntity? 10 | get() { 11 | if (this.currentImageInfoIndex >= this.imageInfos.size || this.currentImageInfoIndex < 0) { 12 | return null 13 | } 14 | 15 | return this.imageInfos[this.currentImageInfoIndex] 16 | } 17 | 18 | val currentImagePath: String? 19 | get() { 20 | return this.currentImageInfo?.path 21 | } 22 | 23 | val imageChecks: MutableList = ArrayList() 24 | 25 | fun clearImagePaths() { 26 | this.imageInfos.clear() 27 | } 28 | 29 | fun resetCurrentImagePathIndex() { 30 | this.currentImageInfoIndex = 0 31 | } 32 | 33 | fun clearImageChecks() { 34 | this.imageChecks.clear() 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/common/ActivityManager.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.common 2 | 3 | import android.app.Activity 4 | import java.util.Stack 5 | 6 | object ActivityManager { 7 | 8 | // 活动栈 9 | private val activityStack: Stack = Stack() 10 | 11 | val currentActivity: Activity? 12 | get() { 13 | return this.activityStack.lastElement() 14 | } 15 | 16 | /** 17 | * 将活动压入栈中 18 | */ 19 | fun push(activity: Activity) { 20 | this.activityStack.push(activity) 21 | } 22 | 23 | /** 24 | * 将活动从栈中弹出 25 | */ 26 | fun pop() { 27 | this.activityStack.pop() 28 | } 29 | 30 | /** 31 | * 将活动从栈中移除 32 | */ 33 | fun remove(activity: Activity) { 34 | this.activityStack.remove(activity) 35 | } 36 | 37 | /** 38 | * 结束指定的活动 39 | */ 40 | fun finish(activity: Activity) { 41 | activity.finish() 42 | } 43 | 44 | /** 45 | * 结束所有活动 46 | */ 47 | fun finishAll() { 48 | for (activity in this.activityStack) { 49 | activity?.finish() 50 | } 51 | } 52 | 53 | 54 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | android.nonTransitiveRClass=false 21 | android.nonFinalResIds=false 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/utils/ShellUtil.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.utils 2 | 3 | import com.sjk.deleterecentpictures.common.logE 4 | 5 | object ShellUtil { 6 | const val TAG: String = "ShellUtil" 7 | 8 | fun execDeleteFile(filePath: String): Boolean { 9 | return this.exec("rm -r $filePath") 10 | } 11 | 12 | fun exec(command: String?): Boolean { 13 | if (command == null) { 14 | return false 15 | } 16 | 17 | // var bufferedReader: BufferedReader? = null 18 | val runtime = Runtime.getRuntime() 19 | return try { 20 | //Process中封装了返回的结果和执行错误的结果 21 | val process = runtime.exec(command) 22 | /*bufferedReader = BufferedReader(InputStreamReader(process.inputStream)) 23 | val stringBuffer = StringBuffer() 24 | val buff = CharArray(1024) 25 | var ch = 0 26 | while (bufferedReader.read(buff).also { ch = it } != -1) { 27 | stringBuffer.append(buff, 0, ch) 28 | }*/ 29 | true 30 | } catch (e: Exception) { 31 | logE(TAG, e.stackTraceToString()) 32 | false 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/common/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.common 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | 6 | open class BaseActivity : AppCompatActivity() { 7 | 8 | override fun onCreate(savedInstanceState: Bundle?) { 9 | super.onCreate(savedInstanceState) 10 | App.activityManager.push(this) 11 | } 12 | 13 | override fun onResume() { 14 | super.onResume() 15 | } 16 | 17 | override fun onDestroy() { 18 | super.onDestroy() 19 | App.activityManager.remove(this) 20 | } 21 | 22 | protected fun getOutput(): Output { 23 | return App.output 24 | } 25 | 26 | protected fun getInput(): Input { 27 | return App.input 28 | } 29 | 30 | protected fun getDataSource(): DataSource { 31 | return App.dataSource 32 | } 33 | 34 | protected fun getGlobalData(key: String, default: Any?): Any? { 35 | return App.globalData.getData(key, default) 36 | } 37 | 38 | protected fun setGlobalData(key: String, value: Any?) { 39 | App.globalData.setData(key, value) 40 | } 41 | 42 | protected fun removeGlobalData(key: String) { 43 | App.globalData.removeData(key) 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/utils/BigImageHelperImpl.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.utils 2 | 3 | import android.content.Context 4 | import android.graphics.drawable.Drawable 5 | import com.bumptech.glide.request.target.CustomTarget 6 | import com.bumptech.glide.request.transition.Transition 7 | import com.flyjingfish.openimagelib.listener.BigImageHelper 8 | import com.flyjingfish.openimagelib.listener.OnLoadBigImageListener 9 | import com.sjk.deleterecentpictures.common.App 10 | 11 | 12 | class BigImageHelperImpl : BigImageHelper { 13 | override fun loadImage( 14 | context: Context?, 15 | imageUrl: String?, 16 | onLoadBigImageListener: OnLoadBigImageListener? 17 | ) { 18 | if (context == null || imageUrl == null) { 19 | onLoadBigImageListener?.onLoadImageFailed() 20 | return 21 | } 22 | App.imageLoadManger.createLoadImage(context, imageUrl) 23 | .into(object : CustomTarget() { 24 | override fun onResourceReady( 25 | drawable: Drawable, 26 | transition: Transition? 27 | ) { 28 | onLoadBigImageListener?.onLoadImageSuccess(drawable, imageUrl) 29 | } 30 | 31 | override fun onLoadCleared(placeholder: Drawable?) {} 32 | }) 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_deleted_snackbar_buttons.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/common/Input.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.common 2 | 3 | import com.sjk.deleterecentpictures.R 4 | 5 | object Input { 6 | fun setCurrentImagePathIndex(index: Int) { 7 | App.recentImages.currentImageInfoIndex = index 8 | } 9 | 10 | fun setAllImageChecksFalse() { 11 | App.recentImages.imageChecks.run { 12 | for (index in this.indices) { 13 | this[index] = false 14 | } 15 | } 16 | } 17 | 18 | fun copyCurrentImagePath(): Boolean { 19 | if (App.dataSource.getCurrentImageInfo()?.path == null) { 20 | App.output.showToast(App.resources.getString(R.string.no_path)) 21 | return false 22 | } 23 | 24 | App.clipboardUtil.setText(App.dataSource.getCurrentImageInfo()!!.path!!) 25 | App.output.showToast(App.resources.getString(R.string.copied)) 26 | return true 27 | } 28 | 29 | fun copyCurrentImageName(): Boolean { 30 | if (App.dataSource.getCurrentImageInfo()?.path == null) { 31 | App.output.showToast(App.resources.getString(R.string.no_path)) 32 | return false 33 | } 34 | 35 | App.clipboardUtil.setText(App.dataSource.getFileNameByPath(App.dataSource.getCurrentImageInfo())!!) 36 | App.output.showToast(App.resources.getString(R.string.copied)) 37 | return true 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 | 20 | 21 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 29 | 30 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/service/QuickSettingOpenService.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.service 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.PendingIntent 5 | import android.content.Intent 6 | import android.os.Build 7 | import android.service.quicksettings.TileService 8 | import androidx.annotation.RequiresApi 9 | import com.sjk.deleterecentpictures.activity.main.MainActivity 10 | 11 | @RequiresApi(api = Build.VERSION_CODES.N) 12 | class QuickSettingOpenService : TileService() { 13 | private var intent: Intent? = null 14 | 15 | // 当用户从Edit栏添加到快速设定中调用 16 | override fun onTileAdded() {} 17 | 18 | // 当用户从快速设定栏中移除的时候调用 19 | override fun onTileRemoved() {} 20 | 21 | // 点击的时候 22 | 23 | @SuppressLint("StartActivityAndCollapseDeprecated") 24 | override fun onClick() { 25 | this.intent = Intent(this, MainActivity::class.java) 26 | this.intent!!.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 27 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 28 | val pendingIntent = 29 | PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) 30 | this.startActivityAndCollapse(pendingIntent) 31 | } else { 32 | @Suppress("DEPRECATION") 33 | this.startActivityAndCollapse(this.intent) 34 | } 35 | } 36 | 37 | // 打开下拉菜单的时候调用,当快速设置按钮并没有在编辑栏拖到设置栏中不会调用 38 | // 在TleAdded之后会调用一次 39 | override fun onStartListening() {} 40 | 41 | // 关闭下拉菜单的时候调用,当快速设置按钮并没有在编辑栏拖到设置栏中不会调用 42 | // 在onTileRemoved移除之前也会调用移除 43 | override fun onStopListening() {} 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/utils/AlertDialogUtil.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.utils 2 | 3 | import android.view.View 4 | import android.widget.AdapterView 5 | import android.widget.TextView 6 | import androidx.annotation.IdRes 7 | import androidx.appcompat.app.AlertDialog 8 | 9 | object AlertDialogUtil { 10 | 11 | /** 12 | * 允许弹窗文本选择 13 | */ 14 | fun enableMessageSelection( 15 | alertDialog: AlertDialog, 16 | @IdRes messageTextViewId: Int = android.R.id.message, 17 | ) { 18 | alertDialog.window?.findViewById(messageTextViewId)?.setTextIsSelectable(true) 19 | } 20 | 21 | /** 22 | * 禁止弹窗文本选择 23 | */ 24 | fun disableMessageSelection( 25 | alertDialog: AlertDialog, 26 | @IdRes messageTextViewId: Int = android.R.id.message, 27 | ) { 28 | alertDialog.window?.findViewById(messageTextViewId)?.setTextIsSelectable(false) 29 | } 30 | 31 | /** 32 | * 禁止点击弹窗列表项时自动关闭弹窗,同时覆盖默认的点击事件 33 | */ 34 | fun disableAutoDismissWhenItemClick( 35 | alertDialog: AlertDialog, 36 | onItemClickListener: ((adapterView: AdapterView<*>, view: View, i: Int, l: Long) -> Unit)?, 37 | ) { 38 | alertDialog.listView.setOnItemClickListener { adapterView, view, i, l -> 39 | onItemClickListener?.invoke(adapterView, view, i, l) 40 | } 41 | } 42 | 43 | /** 44 | * 禁止点击弹窗列表项时自动关闭弹窗,同时覆盖默认的点击事件 45 | */ 46 | fun disableAutoDismissWhenItemClick( 47 | alertDialog: AlertDialog, 48 | ) { 49 | alertDialog.listView.setOnItemClickListener { _, _, _, _ -> 50 | 51 | } 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /app/src/main/res/values-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | 17 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/utils/ShortcutUtil.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * 已废弃 3 | */ 4 | 5 | package com.sjk.deleterecentpictures.utils 6 | 7 | import android.app.PendingIntent 8 | import android.content.Context 9 | import android.content.Intent 10 | import android.content.pm.ShortcutInfo 11 | import android.content.pm.ShortcutManager 12 | import android.os.Build 13 | import com.sjk.deleterecentpictures.R 14 | import com.sjk.deleterecentpictures.activity.settings.SettingsActivity 15 | 16 | object ShortcutUtil { 17 | fun createLauncherShortcut(context: Context) { 18 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 19 | val shortcutManager = context.getSystemService(ShortcutManager::class.java) 20 | ?: return 21 | if (shortcutManager.isRequestPinShortcutSupported) { 22 | val intent = Intent(context, SettingsActivity::class.java) 23 | intent.action = Intent.ACTION_VIEW 24 | val pinShortcutInfo = ShortcutInfo.Builder(context, "delete-directly-shortcut") 25 | .setShortLabel(context.getString(R.string.delete_the_latest_pictures_directly)) 26 | .setIntent(intent) 27 | .build() 28 | val pinnedShortcutCallbackIntent = shortcutManager.createShortcutResultIntent(pinShortcutInfo) 29 | val successCallback = PendingIntent.getBroadcast(context, 0, 30 | pinnedShortcutCallbackIntent, PendingIntent.FLAG_IMMUTABLE) 31 | shortcutManager.requestPinShortcut(pinShortcutInfo, 32 | successCallback.intentSender) 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 删除最近图片 2 | 3 | ![GitHub](https://img.shields.io/github/license/1045290202/DeleteRecentPictures) 4 | ![GitHub release (latest by date)](https://img.shields.io/github/v/release/1045290202/DeleteRecentPictures) 5 | ![GitHub latest downloads](https://img.shields.io/github/downloads/1045290202/DeleteRecentPictures/latest/total) 6 | ![GitHub all downloads](https://img.shields.io/github/downloads/1045290202/DeleteRecentPictures/total) 7 | ![GitHub stars](https://img.shields.io/github/stars/1045290202/DeleteRecentPictures?style=social) 8 | 9 | 删除手机里最近的图片,包括但不限于截图、照片等,只要图片被系统媒体扫描后就能显示。解决不小心保存图片、截屏、拍照等问题。建议配合悬浮球、侧边栏等类型的APP使用,或者利用状态栏中的快速磁贴打开。 10 | 11 | ### 支持系统 12 | 13 | Android 6.0(SdkVersion 23)及以上 14 | 15 | ### 功能 16 | 17 | + 支持图片切换 18 | + 支持大图模式查看 19 | + 支持查看图片信息 20 | + 支持用其他应用打开以及分享 21 | + 支持二维码识别 22 | + 多选删除 23 | + 允许自定义查找路径 24 | + 支持GIF播放和超大图查看 25 | + 支持从快速设置磁贴 26 | + 支持暗色模式 27 | + 使用Material You,支持Monet取色 28 | + 支持在最近运行任务中隐藏 29 | + 【实验性】支持多窗口模式(小窗和分屏等)下特有的布局 30 | + 【实验性】支持临时的撤销删除功能 31 | 32 | ### 预览图 33 | 34 | 1.png 35 | 2.png 36 | 3.png 37 | 4.png 38 | 39 | ### 下载 40 | 41 | [https://github.com/1045290202/DeleteRecentPictures/releases/latest](https://github.com/1045290202/DeleteRecentPictures/releases/latest) 42 | 43 | ### 开源库 44 | 45 | + [ZoomImage](https://github.com/panpf/zoomimage) 46 | + [ZXing](https://github.com/zxing/zxing) 47 | + [RikkaX](https://github.com/RikkaApps/RikkaX) 48 | + [Apache Commons IO](https://github.com/apache/commons-io) 49 | + [OpenImage](https://github.com/FlyJingFish/OpenImage) 50 | 51 | ------- 52 | 53 | 在酷安上关注开发者: *[@来一斤BUG](https://www.coolapk.com/u/458995)* 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/utils/PermissionUtil.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.utils 2 | 3 | import android.Manifest 4 | import android.app.Activity 5 | import android.content.Intent 6 | import android.content.pm.PackageManager 7 | import android.os.Build 8 | import android.os.Environment 9 | import android.provider.Settings 10 | import androidx.annotation.RequiresApi 11 | import androidx.core.app.ActivityCompat 12 | import androidx.core.net.toUri 13 | import com.sjk.deleterecentpictures.common.App 14 | 15 | object PermissionUtil { 16 | private val PERMISSIONS_STORAGE_V29 = arrayOf( 17 | Manifest.permission.READ_EXTERNAL_STORAGE, 18 | Manifest.permission.WRITE_EXTERNAL_STORAGE 19 | ) 20 | 21 | /** 22 | * 检查是否完整授予存储权限 23 | */ 24 | fun Activity.checkPermissionGranted(): Boolean { 25 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 26 | return Environment.isExternalStorageManager() 27 | } else { 28 | PERMISSIONS_STORAGE_V29.forEach { 29 | if (checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED) { 30 | return false 31 | } 32 | } 33 | return true 34 | } 35 | } 36 | 37 | /** 38 | * 依据不同安卓版本申请对应存储权限 39 | */ 40 | fun Activity.requestPermission() { 41 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 42 | requestPermissionV30() 43 | } else { 44 | requestPermissionV29() 45 | } 46 | } 47 | 48 | /** 49 | * API30+ 所有文件权限申请 50 | */ 51 | @RequiresApi(Build.VERSION_CODES.R) 52 | fun Activity.requestPermissionV30() { 53 | val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) 54 | intent.data = "package:${App.applicationContext.packageName}".toUri() 55 | startActivity(intent) 56 | } 57 | 58 | /** 59 | * API29- 读写存储权限申请 60 | */ 61 | fun Activity.requestPermissionV29() { 62 | ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE_V29, 0) 63 | } 64 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/common/ImageLoadManager.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.common 2 | 3 | import android.content.Context 4 | import android.graphics.drawable.Drawable 5 | import android.net.Uri 6 | import com.bumptech.glide.Glide 7 | import com.bumptech.glide.RequestBuilder 8 | import com.github.panpf.zoomimage.ZoomImageView 9 | import com.github.panpf.zoomimage.subsampling.ImageSource 10 | import com.github.panpf.zoomimage.subsampling.fromContent 11 | import com.sjk.deleterecentpictures.entity.ImageInfoEntity 12 | 13 | interface ILoadImageListener { 14 | fun onLoadImageSuccess(drawable: Drawable, imageUrl: String) 15 | fun onLoadImageFailed() 16 | } 17 | 18 | object ImageLoadManager { 19 | 20 | /** 21 | * 加载图片到图片控件 22 | */ 23 | fun loadImageToImageView( 24 | context: Context, 25 | imageInfo: ImageInfoEntity, 26 | imageView: ZoomImageView?, 27 | useSubsampling: Boolean = true 28 | ) { 29 | if (imageView == null || imageInfo.uri == null) { 30 | return 31 | } 32 | 33 | if (useSubsampling) { 34 | // 设置子采样图片源 35 | imageView.setSubsamplingImage(ImageSource.fromContent(context, imageInfo.uri)) 36 | } 37 | // 利用 Glide 加载较模糊的缩略图 38 | createLoadImage(context, imageInfo.uri) 39 | .into(imageView) 40 | } 41 | 42 | /** 43 | * 清除图片控件的图片 44 | */ 45 | fun clearImageView(context: Context, imageView: ZoomImageView?) { 46 | if (imageView == null) { 47 | return 48 | } 49 | 50 | Glide.with(context) 51 | .clear(imageView) 52 | 53 | // imageView.setSubsamplingImage() 54 | } 55 | 56 | fun createLoadImage( 57 | context: Context, 58 | imageUrl: String? 59 | ): RequestBuilder { 60 | return Glide.with(context) 61 | .load(imageUrl) 62 | .skipMemoryCache(false) 63 | } 64 | 65 | fun createLoadImage( 66 | context: Context, 67 | imageUrl: Uri? 68 | ): RequestBuilder { 69 | return Glide.with(context) 70 | .load(imageUrl) 71 | .skipMemoryCache(false) 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | apply plugin: "kotlin-android" 3 | 4 | android { 5 | compileSdk 36 6 | defaultConfig { 7 | applicationId "com.sjk.deleterecentpictures" 8 | minSdkVersion 23 9 | targetSdk 36 10 | versionCode 53 11 | versionName "3.3.1" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled true 16 | proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" 17 | } 18 | 19 | debug { 20 | // 添加调试后缀 21 | applicationIdSuffix ".debug" 22 | versionNameSuffix "-debug" 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility = JavaVersion.VERSION_21 27 | targetCompatibility = JavaVersion.VERSION_21 28 | } 29 | buildscript { 30 | 31 | } 32 | namespace "com.sjk.deleterecentpictures" 33 | buildFeatures { 34 | viewBinding true 35 | buildConfig false 36 | } 37 | lint { 38 | abortOnError false 39 | checkReleaseBuilds false 40 | } 41 | } 42 | 43 | repositories { 44 | maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } 45 | mavenCentral() 46 | } 47 | 48 | configurations.configureEach { 49 | exclude group: "androidx.appcompat", module: "appcompat" 50 | } 51 | 52 | dependencies { 53 | implementation fileTree(dir: "libs", include: ["*.jar"]) 54 | // implementation "androidx.appcompat:appcompat:1.6.1" 55 | // implementation "androidx.constraintlayout:constraintlayout:2.1.4" 56 | // testImplementation "junit:junit:4.12" 57 | // androidTestImplementation "androidx.test.ext:junit:1.1.1" 58 | // androidTestImplementation "androidx.test.espresso:espresso-core:3.2.0" 59 | // implementation "com.google.android.material:material:1.9.0" 60 | 61 | //AndPermission 62 | // implementation "com.yanzhenjie:permission:2.0.3" 63 | // implementation "com.github.chrisbanes:PhotoView:2.3.0" 64 | implementation "androidx.preference:preference-ktx:1.2.1" 65 | implementation "androidx.core:core-ktx:1.16.0" 66 | implementation "androidx.annotation:annotation-jvm:1.9.1" 67 | //noinspection GradleDependency 68 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 69 | // implementation "com.zxy.android:tiny:1.1.0" 70 | // implementation "com.davemorrissey.labs:subsampling-scale-image-view:3.10.0" 71 | // implementation "pl.droidsonroids.gif:android-gif-drawable:1.2.22" 72 | implementation "com.google.zxing:core:3.5.3" 73 | // implementation "com.github.piasy:BigImageViewer:1.8.1" 74 | // implementation "com.github.piasy:GlideImageLoader:1.8.1" 75 | // implementation "com.github.piasy:GlideImageViewFactory:1.8.1" 76 | implementation "dev.rikka.rikkax.appcompat:appcompat:1.6.1" 77 | implementation "dev.rikka.rikkax.material:material-preference:2.0.0" 78 | implementation "io.github.panpf.zoomimage:zoomimage-view-glide:1.3.0" 79 | implementation "io.github.flyjingfish:openimage-full:2.4.7" 80 | implementation "commons-io:commons-io:2.20.0" 81 | implementation "com.github.bumptech.glide:glide:4.16.0" 82 | implementation "com.squareup.okhttp3:okhttp:5.1.0" 83 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/utils/QRCodeUtil.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Description: 二维码工具类,使用了zxing库 3 | */ 4 | 5 | package com.sjk.deleterecentpictures.utils 6 | 7 | import android.graphics.Bitmap 8 | import android.graphics.BitmapFactory 9 | import com.google.zxing.* 10 | import com.google.zxing.common.GlobalHistogramBinarizer 11 | import com.google.zxing.common.HybridBinarizer 12 | import java.io.FileInputStream 13 | import java.util.* 14 | 15 | 16 | object QRCodeUtil { 17 | private val hints: Map = EnumMap(DecodeHintType::class.java) 18 | 19 | /** 20 | * 二维码解码 21 | */ 22 | fun decodeQRCode(filePath: String?): String? { 23 | return this.decodeQRCode(this.getDecodeAbleBitmap(filePath)) 24 | } 25 | 26 | /** 27 | * 二维码解码 28 | */ 29 | fun decodeQRCode(bitmap: Bitmap?): String? { 30 | if (bitmap == null) { 31 | return null 32 | } 33 | 34 | val width = bitmap.width 35 | val height = bitmap.height 36 | val pixels = IntArray(width * height) 37 | bitmap.getPixels(pixels, 0, width, 0, 0, width, height) 38 | 39 | var result: Result? 40 | val source: RGBLuminanceSource? 41 | var invertedSource: InvertedLuminanceSource? = null 42 | return try { 43 | source = RGBLuminanceSource(width, height, pixels) 44 | result = this.decodeQRCodeWithHybridBinarizer(source) 45 | if (result == null) { 46 | invertedSource = InvertedLuminanceSource(source) 47 | result = this.decodeQRCodeWithHybridBinarizer(invertedSource) 48 | } 49 | if (result == null) { 50 | result = this.decodeQRCodeWithGlobalHistogramBinarizer(source) 51 | } 52 | if (result == null) { 53 | result = this.decodeQRCodeWithGlobalHistogramBinarizer(invertedSource!!) 54 | } 55 | result?.text 56 | } catch (e: Exception) { 57 | e.printStackTrace() 58 | null 59 | } 60 | } 61 | 62 | private fun decodeQRCodeWithHybridBinarizer(source: LuminanceSource): Result? { 63 | return try { 64 | val binarizer = HybridBinarizer(source) 65 | val binaryBitmap = BinaryBitmap(binarizer) 66 | MultiFormatReader().decode(binaryBitmap, this.hints) 67 | } catch (e: Exception) { 68 | e.printStackTrace() 69 | null 70 | } 71 | } 72 | 73 | private fun decodeQRCodeWithGlobalHistogramBinarizer(source: LuminanceSource): Result? { 74 | return try { 75 | val binarizer = GlobalHistogramBinarizer(source) 76 | val binaryBitmap = BinaryBitmap(binarizer) 77 | MultiFormatReader().decode(binaryBitmap, this.hints) 78 | } catch (e: Exception) { 79 | e.printStackTrace() 80 | null 81 | } 82 | } 83 | 84 | private fun getDecodeAbleBitmap(filePath: String?): Bitmap? { 85 | return try { 86 | val fileInputStream = FileInputStream(filePath) 87 | BitmapFactory.decodeStream(fileInputStream) 88 | } catch (e: Exception) { 89 | null 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/activity/image/ImageActivity.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.activity.image 2 | 3 | import android.os.Build 4 | import android.os.Bundle 5 | import android.view.View 6 | import android.view.WindowManager.LayoutParams 7 | import androidx.activity.addCallback 8 | import androidx.viewpager2.widget.ViewPager2 9 | import com.sjk.deleterecentpictures.R 10 | import com.sjk.deleterecentpictures.common.BaseActivity 11 | 12 | class ImageActivity : BaseActivity() { 13 | private val viewPagerAdapter = ImageActivityViewPagerAdapter() 14 | private lateinit var viewPager: ViewPager2 15 | 16 | companion object { 17 | private const val TAG = "ImageActivity" 18 | } 19 | 20 | override fun onCreate(savedInstanceState: Bundle?) { 21 | super.onCreate(savedInstanceState) 22 | 23 | this.setFullScreen() 24 | this.setContentView(R.layout.activity_image) 25 | 26 | this.init() 27 | 28 | this.onBackPressedDispatcher.addCallback { 29 | if (this@ImageActivity.viewPagerAdapter.isCurrentScaleOne()) { 30 | this@ImageActivity.supportFinishAfterTransition() 31 | return@addCallback 32 | } 33 | this@ImageActivity.viewPagerAdapter.resetImageScaleWithAnimation(this@ImageActivity) 34 | } 35 | } 36 | 37 | private fun init() { 38 | // val imagePath: String? = this.getGlobalData("currentImagePath", null) as String? 39 | this.viewPagerAdapter.imageInfos = this.getDataSource().getRecentImageInfos() 40 | this.viewPager = this.findViewById(R.id.viewPager) 41 | this.viewPager.adapter = this.viewPagerAdapter 42 | this.viewPager.setCurrentItem(this.getDataSource().getCurrentImageInfoIndex(), false) 43 | this.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { 44 | override fun onPageSelected(position: Int) { 45 | super.onPageSelected(position) 46 | this@ImageActivity.getInput().setCurrentImagePathIndex(position) 47 | 48 | if (this@ImageActivity.getDataSource().getRecentImageInfos().isEmpty()) { 49 | this@ImageActivity.getInput().setCurrentImagePathIndex(0) 50 | return 51 | } 52 | } 53 | }) 54 | } 55 | 56 | private fun setFullScreen() { 57 | this.window.clearFlags( 58 | LayoutParams.FLAG_TRANSLUCENT_STATUS or 59 | LayoutParams.FLAG_TRANSLUCENT_NAVIGATION 60 | ) // 允许页面可以拉伸到顶部状态栏并且定义顶部状态栏透名 61 | this.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or 62 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or // 设置全屏显示 63 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE 64 | this.window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) 65 | // window.setStatusBarColor(Color.TRANSPARENT); //设置状态栏为透明 66 | // window.navigationBarColor = Color.parseColor("#44000000") //设置虚拟键为透明 67 | 68 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // 强制在屏幕安全区域显示内容(刘海屏等等) 69 | val lp = this.window.attributes 70 | lp.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS 71 | this.window.attributes = lp 72 | } 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_main_view_pager_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 20 | 21 | 28 | 29 | 34 | 35 | 40 | 41 | 42 | 43 | 49 | 50 | 57 | 58 | 63 | 64 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /app/src/main/res/layout/image_widget.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 14 | 15 | 24 | 25 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 49 | 50 |