├── .gitignore ├── .idea ├── aws.xml ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── deploymentTargetDropDown.xml ├── dictionaries │ ├── admin.xml │ ├── sjk.xml │ └── win10.xml ├── git_toolbox_prj.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── intellij-javadocs-4.0.1.xml ├── jarRepositories.xml ├── kotlinc.xml ├── migrations.xml ├── misc.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── sjk │ │ └── deleterecentpictures │ │ ├── activity │ │ ├── common │ │ │ └── ImageLongClickDialog.kt │ │ ├── image │ │ │ ├── ImageActivity.kt │ │ │ └── ImageActivityViewPagerAdapter.kt │ │ ├── main │ │ │ ├── MainActivity.kt │ │ │ ├── MainActivityViewPagerAdapter.kt │ │ │ └── ScrollButtonManager.kt │ │ └── settings │ │ │ └── SettingsActivity.kt │ │ ├── bean │ │ ├── DeletedImageInfoBean.kt │ │ ├── ImageDetailBean.kt │ │ └── ImageInfoBean.kt │ │ ├── common │ │ ├── ActivityManager.kt │ │ ├── App.kt │ │ ├── BaseActivity.kt │ │ ├── Const.kt │ │ ├── DataSource.kt │ │ ├── Event.kt │ │ ├── GlobalData.kt │ │ ├── ImageLoadManager.kt │ │ ├── Input.kt │ │ ├── Logger.kt │ │ ├── Output.kt │ │ ├── RecentImages.kt │ │ ├── RecycleBinManager.kt │ │ └── Switch.kt │ │ ├── service │ │ ├── ImageWidgetService.kt │ │ └── QuickSettingOpenService.kt │ │ ├── utils │ │ ├── AlertDialogUtil.kt │ │ ├── ApkUtil.kt │ │ ├── ClipboardUtil.kt │ │ ├── DensityUtil.kt │ │ ├── FileUtil.kt │ │ ├── ImageScannerUtil.kt │ │ ├── PermissionUtil.kt │ │ ├── QRCodeUtil.kt │ │ ├── ShellUtil.kt │ │ ├── ShortcutUtil.kt │ │ ├── TimeUtil.kt │ │ └── TransformUtil.kt │ │ └── widget │ │ └── ImageWidget.kt │ └── res │ ├── drawable-night-v31 │ └── dialog_background.xml │ ├── drawable-v21 │ ├── app_widget_background.xml │ └── app_widget_inner_view_background.xml │ ├── drawable-v31 │ └── dialog_background.xml │ ├── drawable │ ├── activity_main_background.xml │ ├── arrow_left.xml │ ├── arrow_right.xml │ ├── dialog_background.xml │ ├── divider.xml │ ├── ic_close.xml │ ├── ic_delete_image.xml │ ├── ic_image.xml │ ├── ic_info.xml │ ├── ic_launcher_foreground.xml │ ├── ic_refresh.xml │ ├── ic_settings.xml │ └── ic_undo.xml │ ├── layout │ ├── activity_image.xml │ ├── activity_main.xml │ ├── activity_main_multi_window.xml │ ├── activity_settings2.xml │ ├── image_widget.xml │ ├── layout_deleted_snackbar_buttons.xml │ ├── layout_main_view_pager_item.xml │ └── layout_view_pager_item.xml │ ├── menu │ └── menu_main_activity.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── 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 │ ├── values-en │ ├── arrays.xml │ └── strings.xml │ ├── values-night-v31 │ ├── colors.xml │ └── themes.xml │ ├── values-v21 │ └── styles.xml │ ├── values-v31 │ ├── colors.xml │ ├── styles.xml │ └── themes.xml │ ├── values │ ├── arrays.xml │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ ├── styles.xml │ └── themes.xml │ ├── xml-v31 │ └── image_widget_info.xml │ └── xml │ ├── data_extraction_rules.xml │ ├── file_paths.xml │ ├── image_widget_info.xml │ └── root_preferences.xml ├── build.gradle ├── docs └── images │ └── previews │ ├── 1.png │ ├── 2.png │ ├── 3.png │ └── 4.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.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 | -------------------------------------------------------------------------------- /.idea/aws.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 17 | 18 | 19 | 21 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 12 | 13 | 20 | 21 | 25 | 26 | 32 | 33 | 34 | 36 | 37 | 38 | 61 | 62 | 68 | 69 | 78 | 79 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/dictionaries/admin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | infos 5 | 6 | 7 | -------------------------------------------------------------------------------- /.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/dictionaries/win10.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | dcim 5 | deleterecentpictures 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/intellij-javadocs-4.0.1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UPDATE 6 | false 7 | true 8 | 9 | FIELD 10 | METHOD 11 | TYPE 12 | 13 | 14 | DEFAULT 15 | PUBLIC 16 | PROTECTED 17 | 18 | 19 | 20 | 21 | 22 | ^.*(public|protected|private)*.+interface\s+\w+.* 23 | /**\n 24 | * The interface ${name}.\n 25 | <#if element.typeParameters?has_content> * \n 26 | </#if> 27 | <#list element.typeParameters as typeParameter> 28 | * @param <${typeParameter.name}> the type parameter\n 29 | </#list> 30 | */ 31 | 32 | 33 | ^.*(public|protected|private)*.+enum\s+\w+.* 34 | /**\n 35 | * The enum ${name}.\n 36 | */ 37 | 38 | 39 | ^.*(public|protected|private)*.+class\s+\w+.* 40 | /**\n 41 | * The type ${name}.\n 42 | <#if element.typeParameters?has_content> * \n 43 | </#if> 44 | <#list element.typeParameters as typeParameter> 45 | * @param <${typeParameter.name}> the type parameter\n 46 | </#list> 47 | */ 48 | 49 | 50 | .+ 51 | /**\n 52 | * The type ${name}.\n 53 | */ 54 | 55 | 56 | 57 | 58 | .+ 59 | /**\n 60 | * Instantiates a new ${name}.\n 61 | <#if element.parameterList.parameters?has_content> 62 | *\n 63 | </#if> 64 | <#list element.parameterList.parameters as parameter> 65 | * @param ${parameter.name} the ${paramNames[parameter.name]}\n 66 | </#list> 67 | <#if element.throwsList.referenceElements?has_content> 68 | *\n 69 | </#if> 70 | <#list element.throwsList.referenceElements as exception> 71 | * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n 72 | </#list> 73 | */ 74 | 75 | 76 | 77 | 78 | ^.*(public|protected|private)*\s*.*(\w(\s*<.+>)*)+\s+get\w+\s*\(.*\).+ 79 | /**\n 80 | * Gets ${partName}.\n 81 | <#if element.typeParameters?has_content> * \n 82 | </#if> 83 | <#list element.typeParameters as typeParameter> 84 | * @param <${typeParameter.name}> the type parameter\n 85 | </#list> 86 | <#if element.parameterList.parameters?has_content> 87 | *\n 88 | </#if> 89 | <#list element.parameterList.parameters as parameter> 90 | * @param ${parameter.name} the ${paramNames[parameter.name]}\n 91 | </#list> 92 | <#if isNotVoid> 93 | *\n 94 | * @return the ${partName}\n 95 | </#if> 96 | <#if element.throwsList.referenceElements?has_content> 97 | *\n 98 | </#if> 99 | <#list element.throwsList.referenceElements as exception> 100 | * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n 101 | </#list> 102 | */ 103 | 104 | 105 | ^.*(public|protected|private)*\s*.*(void|\w(\s*<.+>)*)+\s+set\w+\s*\(.*\).+ 106 | /**\n 107 | * Sets ${partName}.\n 108 | <#if element.typeParameters?has_content> * \n 109 | </#if> 110 | <#list element.typeParameters as typeParameter> 111 | * @param <${typeParameter.name}> the type parameter\n 112 | </#list> 113 | <#if element.parameterList.parameters?has_content> 114 | *\n 115 | </#if> 116 | <#list element.parameterList.parameters as parameter> 117 | * @param ${parameter.name} the ${paramNames[parameter.name]}\n 118 | </#list> 119 | <#if isNotVoid> 120 | *\n 121 | * @return the ${partName}\n 122 | </#if> 123 | <#if element.throwsList.referenceElements?has_content> 124 | *\n 125 | </#if> 126 | <#list element.throwsList.referenceElements as exception> 127 | * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n 128 | </#list> 129 | */ 130 | 131 | 132 | ^.*((public\s+static)|(static\s+public))\s+void\s+main\s*\(\s*String\s*(\[\s*\]|\.\.\.)\s+\w+\s*\).+ 133 | /**\n 134 | * The entry point of application.\n 135 | 136 | <#if element.parameterList.parameters?has_content> 137 | *\n 138 | </#if> 139 | * @param ${element.parameterList.parameters[0].name} the input arguments\n 140 | <#if element.throwsList.referenceElements?has_content> 141 | *\n 142 | </#if> 143 | <#list element.throwsList.referenceElements as exception> 144 | * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n 145 | </#list> 146 | */ 147 | 148 | 149 | .+ 150 | /**\n 151 | * ${name}<#if isNotVoid> ${return}</#if>.\n 152 | <#if element.typeParameters?has_content> * \n 153 | </#if> 154 | <#list element.typeParameters as typeParameter> 155 | * @param <${typeParameter.name}> the type parameter\n 156 | </#list> 157 | <#if element.parameterList.parameters?has_content> 158 | *\n 159 | </#if> 160 | <#list element.parameterList.parameters as parameter> 161 | * @param ${parameter.name} the ${paramNames[parameter.name]}\n 162 | </#list> 163 | <#if isNotVoid> 164 | *\n 165 | * @return the ${return}\n 166 | </#if> 167 | <#if element.throwsList.referenceElements?has_content> 168 | *\n 169 | </#if> 170 | <#list element.throwsList.referenceElements as exception> 171 | * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n 172 | </#list> 173 | */ 174 | 175 | 176 | 177 | 178 | ^.*(public|protected|private)*.+static.*(\w\s\w)+.+ 179 | /**\n 180 | * The constant ${element.getName()}.\n 181 | */ 182 | 183 | 184 | ^.*(public|protected|private)*.*(\w\s\w)+.+ 185 | /**\n 186 | <#if element.parent.isInterface()> 187 | * The constant ${element.getName()}.\n 188 | <#else> 189 | * The ${name}.\n 190 | </#if> */ 191 | 192 | 193 | .+ 194 | /**\n 195 | <#if element.parent.isEnum()> 196 | *${name} ${typeName}.\n 197 | <#else> 198 | * The ${name}.\n 199 | </#if>*/ 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | true 7 | 8 | true 9 | true 10 | 11 | 12 | 37 | 59 | 60 | 61 | 62 | 63 | 64 | 66 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /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 | Android 6.0(SdkVersion 23)及以上 13 | 14 | ### 功能 15 | + 支持图片切换 16 | + 支持大图模式查看 17 | + 支持查看图片信息 18 | + 支持用其他应用打开以及分享 19 | + 支持二维码识别 20 | + 多选删除 21 | + 允许自定义查找路径 22 | + 支持GIF播放和超大图查看 23 | + 支持从快速设置磁贴 24 | + 支持暗色模式 25 | + 使用Material You,支持Monet取色 26 | + 支持在最近运行任务中隐藏 27 | + 【实验性】支持多窗口模式(小窗和分屏等)下特有的布局 28 | + 【实验性】支持临时的撤销删除功能 29 | 30 | ### 预览图 31 | 1.png 32 | 2.png 33 | 3.png 34 | 4.png 35 | 36 | ### 下载 37 | [https://github.com/1045290202/DeleteRecentPictures/releases/latest](https://github.com/1045290202/DeleteRecentPictures/releases/latest) 38 | 39 | ### 开源库 40 | + [ZoomImage](https://github.com/panpf/zoomimage) 41 | + [ZXing](https://github.com/zxing/zxing) 42 | + [RikkaX](https://github.com/RikkaApps/RikkaX) 43 | + [Apache Commons IO](https://github.com/apache/commons-io) 44 | 45 | ------- 46 | 47 | 在酷安上关注开发者: *[@来一斤BUG](https://www.coolapk.com/u/458995)* 48 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | apply plugin: "kotlin-android" 3 | 4 | android { 5 | compileSdk 35 6 | defaultConfig { 7 | applicationId "com.sjk.deleterecentpictures" 8 | minSdkVersion 23 9 | targetSdkVersion 35 10 | versionCode 51 11 | versionName "3.2.6" 12 | // testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled true 17 | proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" 18 | } 19 | } 20 | compileOptions { 21 | sourceCompatibility = JavaVersion.VERSION_21 22 | targetCompatibility = JavaVersion.VERSION_21 23 | } 24 | buildscript { 25 | 26 | } 27 | lintOptions { 28 | checkReleaseBuilds false 29 | abortOnError false 30 | } 31 | namespace "com.sjk.deleterecentpictures" 32 | buildFeatures { 33 | viewBinding true 34 | buildConfig true 35 | } 36 | } 37 | 38 | repositories { 39 | maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } 40 | mavenCentral() 41 | } 42 | 43 | configurations.configureEach { 44 | exclude group: "androidx.appcompat", module: "appcompat" 45 | } 46 | 47 | dependencies { 48 | implementation fileTree(dir: "libs", include: ["*.jar"]) 49 | // implementation "androidx.appcompat:appcompat:1.6.1" 50 | // implementation "androidx.constraintlayout:constraintlayout:2.1.4" 51 | // testImplementation "junit:junit:4.12" 52 | // androidTestImplementation "androidx.test.ext:junit:1.1.1" 53 | // androidTestImplementation "androidx.test.espresso:espresso-core:3.2.0" 54 | // implementation "com.google.android.material:material:1.9.0" 55 | 56 | //AndPermission 57 | // implementation "com.yanzhenjie:permission:2.0.3" 58 | // implementation "com.github.chrisbanes:PhotoView:2.3.0" 59 | implementation "androidx.preference:preference-ktx:1.2.1" 60 | implementation "androidx.core:core-ktx:1.15.0" 61 | implementation "androidx.annotation:annotation-jvm:1.9.1" 62 | //noinspection GradleDependency 63 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 64 | // implementation "com.zxy.android:tiny:1.1.0" 65 | // implementation "com.davemorrissey.labs:subsampling-scale-image-view:3.10.0" 66 | // implementation "pl.droidsonroids.gif:android-gif-drawable:1.2.22" 67 | implementation "com.google.zxing:core:3.5.3" 68 | // implementation "com.github.piasy:BigImageViewer:1.8.1" 69 | // implementation "com.github.piasy:GlideImageLoader:1.8.1" 70 | // implementation "com.github.piasy:GlideImageViewFactory:1.8.1" 71 | 72 | implementation "dev.rikka.rikkax.appcompat:appcompat:1.6.1" 73 | implementation "dev.rikka.rikkax.material:material-preference:2.0.0" 74 | 75 | implementation "io.github.panpf.zoomimage:zoomimage-view-glide:1.1.1" 76 | 77 | implementation "commons-io:commons-io:2.16.1" 78 | } -------------------------------------------------------------------------------- /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/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | 11 | 14 | 15 | 16 | 30 | 36 | 42 | 43 | 44 | 45 | 46 | 47 | 55 | 62 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 95 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/activity/common/ImageLongClickDialog.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.activity.common 2 | 3 | import android.app.Activity 4 | import android.content.DialogInterface 5 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 6 | import com.sjk.deleterecentpictures.R 7 | import com.sjk.deleterecentpictures.common.App 8 | import com.sjk.deleterecentpictures.common.logD 9 | 10 | class ImageLongClickDialog(activityContext: Activity, filePath: String?) { 11 | 12 | private val activityContext: Activity 13 | private val filePath: String? 14 | 15 | companion object { 16 | const val TAG: String = "ImageLongClickDialog" 17 | 18 | fun build( 19 | activityContext: Activity? = App.activityManager.currentActivity, 20 | filePath: String? 21 | ): ImageLongClickDialog? { 22 | if (activityContext == null) { 23 | return null 24 | } 25 | return ImageLongClickDialog(activityContext, filePath) 26 | } 27 | } 28 | 29 | init { 30 | this.activityContext = activityContext 31 | this.filePath = filePath 32 | } 33 | 34 | fun show() { 35 | val itemStrings: Array = 36 | App.const.IMAGE_LONG_CLICK_DIALOG_ITEMS.map { App.resources.getString(it) } 37 | .toTypedArray() 38 | val alertDialog = MaterialAlertDialogBuilder(this.activityContext) 39 | .setTitle(App.resources.getString(R.string.choose_your_action)) 40 | .setItems(itemStrings) { dialogInterface: DialogInterface, i: Int -> 41 | this.onImageLongClickDialogItemClick(dialogInterface, i, this.filePath) 42 | } 43 | .setNegativeButton(App.resources.getString(R.string.cancel)) { dialog: DialogInterface, _: Int -> dialog.cancel() } 44 | .create() 45 | alertDialog.window?.setBackgroundDrawableResource(R.drawable.dialog_background) 46 | alertDialog.show() 47 | } 48 | 49 | private fun onImageLongClickDialogItemClick( 50 | dialogInterface: DialogInterface, 51 | i: Int, 52 | filePath: String? 53 | ) { 54 | logD(TAG, "点击item $i") 55 | when (i) { 56 | 0 -> { 57 | if (!App.output.openByOtherApp(filePath)) { 58 | App.output.showToast(App.resources.getString(R.string.failed_to_invoke_open_method)) 59 | } 60 | } 61 | 62 | 1 -> { 63 | if (!App.output.shareToOtherApp(filePath)) { 64 | App.output.showToast(App.resources.getString(R.string.failed_to_invoke_sharing_method)) 65 | } 66 | } 67 | 68 | 2 -> { 69 | val discern = Thread { 70 | val content: String? = App.qrCodeUtil.decodeQRCode(filePath) 71 | 72 | App.activityManager.currentActivity?.runOnUiThread { 73 | if (content == null) { 74 | App.output.showToast(App.resources.getString(R.string.barcode_or_qrcode_not_found)) 75 | return@runOnUiThread 76 | } 77 | val alertDialog = MaterialAlertDialogBuilder(this.activityContext) 78 | .setTitle(App.resources.getString(R.string.recognized_content)) 79 | .setMessage("$content") 80 | .setNegativeButton(App.resources.getString(R.string.cancel)) { dialogInterface: DialogInterface, i: Int -> 81 | dialogInterface.cancel() 82 | } 83 | .setNeutralButton(App.resources.getString(R.string.copy)) { dialogInterface: DialogInterface, i: Int -> 84 | App.clipboardUtil.setText(content) 85 | App.output.showToast(App.resources.getString(R.string.copied)) 86 | } 87 | .setPositiveButton(App.resources.getString(R.string.open_with_browser)) { dialogInterface: DialogInterface, i: Int -> 88 | if (!App.output.openLinkWithBrowser(content)) { 89 | App.output.showToast(App.resources.getString(R.string.open_failed)) 90 | } 91 | } 92 | .create() 93 | 94 | alertDialog.window?.setBackgroundDrawableResource(R.drawable.dialog_background) 95 | alertDialog.show() 96 | App.alertDialogUtil.enableMessageSelection(alertDialog) 97 | } 98 | } 99 | discern.start() 100 | } 101 | 102 | else -> { 103 | 104 | } 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /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.OnBackPressedCallback 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(this, object : OnBackPressedCallback(true) { 29 | override fun handleOnBackPressed() { 30 | if (this@ImageActivity.viewPagerAdapter.isCurrentScaleOne()) { 31 | this@ImageActivity.supportFinishAfterTransition() 32 | return 33 | } 34 | this@ImageActivity.viewPagerAdapter.resetImageScaleWithAnimation() 35 | } 36 | }) 37 | } 38 | 39 | private fun init() { 40 | // val imagePath: String? = this.getGlobalData("currentImagePath", null) as String? 41 | this.viewPagerAdapter.imageInfos = this.getDataSource().getRecentImageInfos() 42 | this.viewPager = this.findViewById(R.id.viewPager) 43 | this.viewPager.adapter = viewPagerAdapter 44 | this.viewPager.setCurrentItem(this.getDataSource().getCurrentImageInfoIndex(), false) 45 | this.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { 46 | override fun onPageSelected(position: Int) { 47 | super.onPageSelected(position) 48 | this@ImageActivity.getInput().setCurrentImagePathIndex(position) 49 | 50 | if (this@ImageActivity.getDataSource().getRecentImageInfos().size == 0) { 51 | this@ImageActivity.getInput().setCurrentImagePathIndex(0) 52 | return 53 | } 54 | } 55 | }) 56 | } 57 | 58 | private fun setFullScreen() { 59 | this.window.clearFlags( 60 | LayoutParams.FLAG_TRANSLUCENT_STATUS or 61 | LayoutParams.FLAG_TRANSLUCENT_NAVIGATION 62 | ) // 允许页面可以拉伸到顶部状态栏并且定义顶部状态栏透名 63 | this.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or 64 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or // 设置全屏显示 65 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE 66 | this.window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) 67 | // window.setStatusBarColor(Color.TRANSPARENT); //设置状态栏为透明 68 | // window.navigationBarColor = Color.parseColor("#44000000") //设置虚拟键为透明 69 | 70 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // 强制在屏幕安全区域显示内容(刘海屏等等) 71 | val lp = this.window.attributes 72 | lp.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS 73 | this.window.attributes = lp 74 | } 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/activity/image/ImageActivityViewPagerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.activity.image 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.github.panpf.zoomimage.ZoomImageView 8 | import com.github.panpf.zoomimage.util.IntOffsetCompat 9 | import com.sjk.deleterecentpictures.R 10 | import com.sjk.deleterecentpictures.bean.ImageInfoBean 11 | import com.sjk.deleterecentpictures.common.App 12 | import kotlinx.coroutines.DelicateCoroutinesApi 13 | import kotlinx.coroutines.GlobalScope 14 | import kotlinx.coroutines.launch 15 | 16 | 17 | class ImageActivityViewPagerAdapter : 18 | RecyclerView.Adapter() { 19 | private val viewPagerViewHolders: MutableList = ArrayList() 20 | private val attachedViewHolders: MutableSet = mutableSetOf() 21 | var imageInfos: List = ArrayList() 22 | 23 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewPagerViewHolder { 24 | val viewPagerViewHolder = ViewPagerViewHolder( 25 | LayoutInflater.from(parent.context) 26 | .inflate(R.layout.layout_view_pager_item, parent, false) 27 | ) 28 | viewPagerViewHolders.add(viewPagerViewHolder) 29 | return viewPagerViewHolder 30 | } 31 | 32 | override fun onBindViewHolder( 33 | holder: ViewPagerViewHolder, 34 | position: Int, 35 | ) { 36 | holder.imageInfo = imageInfos[position] 37 | } 38 | 39 | override fun onViewDetachedFromWindow(holder: ViewPagerViewHolder) { 40 | super.onViewDetachedFromWindow(holder) 41 | 42 | this.attachedViewHolders.remove(holder) 43 | App.imageLoadManger.clearImageView(App.applicationContext, holder.imageView) 44 | } 45 | 46 | override fun onViewAttachedToWindow(holder: ViewPagerViewHolder) { 47 | super.onViewAttachedToWindow(holder) 48 | 49 | this.attachedViewHolders.add(holder) 50 | if (holder.imageInfo?.uri == null) { 51 | return 52 | } 53 | App.imageLoadManger.loadImageToImageView( 54 | App.applicationContext, 55 | holder.imageInfo!!, 56 | holder.imageView, 57 | ) 58 | // holder.imageView.setImageURI(holder.imageInfo?.uri) 59 | } 60 | 61 | override fun getItemCount(): Int { 62 | return this.imageInfos.size 63 | } 64 | 65 | fun isCurrentScaleOne(): Boolean { 66 | for (it in this@ImageActivityViewPagerAdapter.attachedViewHolders) { 67 | // 判断x就行了,暂时没有xy缩放不一致的情况 68 | if (it.imageView.zoomable.transformState.value.scaleX != 1f) { 69 | return false 70 | } 71 | } 72 | return true 73 | } 74 | 75 | @OptIn(DelicateCoroutinesApi::class) 76 | fun resetImageScaleWithAnimation() { 77 | GlobalScope.launch { 78 | this@ImageActivityViewPagerAdapter.attachedViewHolders.forEach { 79 | it.imageView.zoomable.scale( 80 | 1f, 81 | IntOffsetCompat.Zero, 82 | true, 83 | // ZoomAnimationSpec(400), 84 | ) 85 | } 86 | } 87 | } 88 | 89 | } 90 | 91 | class ViewPagerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 92 | val imageView: ZoomImageView = itemView.findViewById(R.id.imageView) 93 | val preventClickLeftView: View = itemView.findViewById(R.id.preventClickLeftView) 94 | val preventClickRightView: View = itemView.findViewById(R.id.preventClickRightView) 95 | var imageInfo: ImageInfoBean? = null 96 | 97 | init { 98 | this.imageView.scrollBar = null 99 | this.imageView.setOnLongClickListener { 100 | App.output.showImageLongClickDialog(this.imageInfo!!.path) 101 | 102 | return@setOnLongClickListener true 103 | } 104 | this.imageView.setOnClickListener { 105 | (this.itemView.context as ImageActivity).onBackPressedDispatcher.onBackPressed() 106 | } 107 | // this.preventClickLeftView.setOnClickListener { 108 | // 109 | // } 110 | this.preventClickLeftView.setOnLongClickListener { 111 | return@setOnLongClickListener true 112 | } 113 | // this.preventClickRightView.setOnClickListener { 114 | // 115 | // } 116 | this.preventClickRightView.setOnLongClickListener { 117 | return@setOnLongClickListener true 118 | } 119 | // 120 | // val gestureDetector = 121 | // GestureDetector(itemView.context, object : GestureDetector.SimpleOnGestureListener() { 122 | //// override fun onFling( 123 | //// e1: MotionEvent?, 124 | //// e2: MotionEvent, 125 | //// velocityX: Float, 126 | //// velocityY: Float 127 | //// ): Boolean { 128 | //// App.output.showToast("onFling$velocityY") 129 | //// imageView.post { 130 | //// if (velocityX > 0) { 131 | //// (itemView.context as ImageActivity).onBackPressedDispatcher.onBackPressed() 132 | //// } 133 | //// } 134 | //// return super.onFling(e1, e2, velocityX, velocityY) 135 | //// } 136 | // 137 | // override fun onScroll( 138 | // e1: MotionEvent?, 139 | // e2: MotionEvent, 140 | // distanceX: Float, 141 | // distanceY: Float 142 | // ): Boolean { 143 | // if (imageView.zoomable.transformState.value.scaleX != 1f) { 144 | // return super.onScroll(e1, e2, distanceX, distanceY) 145 | // } 146 | // // 判断是不是双指 147 | // if (e1!!.pointerCount >= 2) { 148 | // return super.onScroll(e1, e2, distanceX, distanceY) 149 | // } 150 | // App.output.showToast("滑动$distanceY") 151 | // imageView.post { 152 | // if (distanceY > 0) { 153 | // (itemView.context as ImageActivity).onBackPressedDispatcher.onBackPressed() 154 | // } 155 | // } 156 | // return true 157 | // } 158 | // }) 159 | // 160 | // this.imageView.setOnTouchListener { _, event -> 161 | // gestureDetector.onTouchEvent(event) 162 | // false 163 | // } 164 | } 165 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sjk/deleterecentpictures/activity/main/MainActivityViewPagerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.sjk.deleterecentpictures.activity.main 2 | 3 | import android.content.Intent 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.Button 8 | import android.widget.CompoundButton 9 | import androidx.core.app.ActivityOptionsCompat 10 | import androidx.recyclerview.widget.RecyclerView 11 | import com.github.panpf.zoomimage.ZoomImageView 12 | import com.google.android.material.checkbox.MaterialCheckBox 13 | import com.sjk.deleterecentpictures.R 14 | import com.sjk.deleterecentpictures.activity.image.ImageActivity 15 | import com.sjk.deleterecentpictures.bean.ImageInfoBean 16 | import com.sjk.deleterecentpictures.common.App 17 | import com.sjk.deleterecentpictures.common.Event 18 | 19 | class MainActivityViewPagerAdapter(val mainActivity: MainActivity) : 20 | RecyclerView.Adapter() { 21 | 22 | companion object { 23 | private const val TAG = "MainActivityViewPagerAdapter" 24 | lateinit var instance: MainActivityViewPagerAdapter 25 | } 26 | 27 | private var viewPagerViewHolders: MutableList = ArrayList() 28 | var imageInfos: MutableList = ArrayList() 29 | var imageChecks: MutableList = ArrayList() 30 | val event: Event = App.newEvent 31 | 32 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewPagerViewHolder { 33 | instance = this 34 | val view = LayoutInflater.from(parent.context) 35 | .inflate(R.layout.layout_main_view_pager_item, parent, false) 36 | val viewPagerViewHolder = ViewPagerViewHolder(this.mainActivity, view) 37 | viewPagerViewHolders.add(viewPagerViewHolder) 38 | return viewPagerViewHolder 39 | } 40 | 41 | override fun onBindViewHolder(holder: ViewPagerViewHolder, position: Int) { 42 | if (this.imageChecks.size > 0) { 43 | holder.isChecked = if (position < this.imageInfos.size) this.imageChecks[position] else false 44 | } 45 | holder.imageInfo = if (position < this.imageInfos.size) { 46 | this.imageInfos[position] 47 | } else { 48 | null 49 | } 50 | } 51 | 52 | override fun onViewDetachedFromWindow(holder: ViewPagerViewHolder) { 53 | super.onViewDetachedFromWindow(holder) 54 | 55 | App.imageLoadManger.clearImageView(App.applicationContext, holder.imageView) 56 | } 57 | 58 | override fun onViewAttachedToWindow(holder: ViewPagerViewHolder) { 59 | super.onViewAttachedToWindow(holder) 60 | 61 | if (holder.imageInfo?.uri == null) { 62 | holder.checkBox.visibility = View.GONE 63 | holder.imageView.visibility = View.GONE 64 | holder.emptyView.visibility = View.VISIBLE 65 | return 66 | } 67 | holder.checkBox.visibility = View.VISIBLE 68 | holder.checkBox.isChecked = holder.isChecked 69 | holder.imageView.visibility = View.VISIBLE 70 | holder.emptyView.visibility = View.GONE 71 | App.imageLoadManger.loadImageToImageView( 72 | App.applicationContext, 73 | holder.imageInfo!!, 74 | holder.imageView, 75 | false, 76 | ) 77 | } 78 | 79 | override fun getItemCount(): Int { 80 | return this.imageInfos.size + 1 81 | } 82 | 83 | fun setHolderChecked(position: Int, isChecked: Boolean) { 84 | this.viewPagerViewHolders[position].run { 85 | this.checkBox.isChecked = isChecked 86 | this.isChecked = isChecked 87 | } 88 | } 89 | 90 | fun setAllHolderChecked(isChecked: Boolean) { 91 | this.viewPagerViewHolders.forEachIndexed { index: Int, viewPagerViewHolder: ViewPagerViewHolder -> 92 | this.setHolderChecked(index, isChecked) 93 | } 94 | } 95 | 96 | } 97 | 98 | class ViewPagerViewHolder(val mainActivity: MainActivity, itemView: View) : RecyclerView.ViewHolder(itemView) { 99 | val checkBox: MaterialCheckBox = itemView.findViewById(R.id.checkbox) 100 | val imageView: ZoomImageView = itemView.findViewById(R.id.imageView) 101 | val emptyView: View = itemView.findViewById(R.id.emptyView) 102 | val detailsButton: Button = itemView.findViewById(R.id.imageDetailsButton) 103 | private val openImageActivityButton = 104 | itemView.findViewById