图片删除功能基本完成

This commit is contained in:
2026-04-24 16:49:01 +08:00
parent 873ceed140
commit 21140fb22b
14 changed files with 699 additions and 19 deletions

View File

@@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<application <application
android:allowBackup="true" android:allowBackup="true"
@@ -41,6 +42,10 @@
android:name=".ImageViewerActivity" android:name=".ImageViewerActivity"
android:label="@string/app_name"/> android:label="@string/app_name"/>
<activity
android:name=".TrashActivity"
android:label="@string/trash"/>
<meta-data <meta-data
android:name="android.max_aspect" android:name="android.max_aspect"
android:value="4.0"/> android:value="4.0"/>

View File

@@ -49,9 +49,10 @@ public class AlbumActivity extends AppCompatActivity {
adapter.setOnImageClickListener(new OnImageClickListener() { adapter.setOnImageClickListener(new OnImageClickListener() {
@Override @Override
public void onImageClick(int position, ArrayList<Uri> urls) { public void onImageClick(int position, ArrayList<Uri> urls, ArrayList<String> paths) {
Intent intent = new Intent(AlbumActivity.this, ImageViewerActivity.class); Intent intent = new Intent(AlbumActivity.this, ImageViewerActivity.class);
intent.putParcelableArrayListExtra(ImageViewerActivity.EXTRA_IMAGE_URLS, urls); intent.putParcelableArrayListExtra(ImageViewerActivity.EXTRA_IMAGE_URLS, urls);
intent.putStringArrayListExtra(ImageViewerActivity.EXTRA_POSITIONS, paths);
intent.putExtra(ImageViewerActivity.EXTRA_POSITION, position); intent.putExtra(ImageViewerActivity.EXTRA_POSITION, position);
startActivity(intent); startActivity(intent);
} }
@@ -90,6 +91,7 @@ public class AlbumActivity extends AppCompatActivity {
private void loadImages() { private void loadImages() {
ArrayList<Uri> imageUrls = new ArrayList<>(); ArrayList<Uri> imageUrls = new ArrayList<>();
ArrayList<String> imagePaths = new ArrayList<>();
ContentResolver contentResolver = getContentResolver(); ContentResolver contentResolver = getContentResolver();
Uri collection = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI; Uri collection = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
@@ -106,6 +108,7 @@ public class AlbumActivity extends AppCompatActivity {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(android.provider.MediaStore.Images.Media._ID)); long id = cursor.getLong(cursor.getColumnIndexOrThrow(android.provider.MediaStore.Images.Media._ID));
Uri contentUri = Uri.withAppendedPath(collection, String.valueOf(id)); Uri contentUri = Uri.withAppendedPath(collection, String.valueOf(id));
imageUrls.add(contentUri); imageUrls.add(contentUri);
imagePaths.add(path);
} }
} }
} }
@@ -114,7 +117,7 @@ public class AlbumActivity extends AppCompatActivity {
if (imageUrls.isEmpty()) { if (imageUrls.isEmpty()) {
Toast.makeText(this, R.string.no_images_found, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.no_images_found, Toast.LENGTH_SHORT).show();
} }
adapter.setData(imageUrls); adapter.setData(imageUrls, imagePaths);
} }
@Override @Override
@@ -133,4 +136,12 @@ public class AlbumActivity extends AppCompatActivity {
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override
protected void onResume() {
super.onResume();
if (checkPermission()) {
loadImages();
}
}
} }

View File

@@ -13,18 +13,20 @@ import java.util.ArrayList;
public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder> { public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder> {
private ArrayList<Uri> imageUrls = new ArrayList<>(); private ArrayList<Uri> imageUrls = new ArrayList<>();
private ArrayList<String> imagePaths = new ArrayList<>();
private OnImageClickListener listener; private OnImageClickListener listener;
public interface OnImageClickListener { public interface OnImageClickListener {
void onImageClick(int position, ArrayList<Uri> urls); void onImageClick(int position, ArrayList<Uri> urls, ArrayList<String> paths);
} }
public void setOnImageClickListener(OnImageClickListener listener) { public void setOnImageClickListener(OnImageClickListener listener) {
this.listener = listener; this.listener = listener;
} }
public void setData(ArrayList<Uri> urls) { public void setData(ArrayList<Uri> urls, ArrayList<String> paths) {
this.imageUrls = urls; this.imageUrls = urls;
this.imagePaths = paths;
notifyDataSetChanged(); notifyDataSetChanged();
} }
@@ -44,11 +46,12 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
.into(holder.imageView); .into(holder.imageView);
final ArrayList<Uri> urls = imageUrls; final ArrayList<Uri> urls = imageUrls;
final ArrayList<String> paths = imagePaths;
holder.imageView.setOnClickListener(new OnClickListener() { holder.imageView.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (listener != null) { if (listener != null) {
listener.onImageClick(position, urls); listener.onImageClick(position, urls, paths);
} }
} }
}); });

View File

@@ -1,9 +1,11 @@
package cc.winboll.gallery; package cc.winboll.gallery;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.provider.MediaStore;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
@@ -11,13 +13,16 @@ import android.view.View.OnTouchListener;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.ImageButton; import android.widget.ImageButton;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
public class ImageViewerActivity extends Activity implements ViewPager.OnPageChangeListener { public class ImageViewerActivity extends Activity implements ViewPager.OnPageChangeListener {
public static final String EXTRA_IMAGE_URLS = "image_urls"; public static final String EXTRA_IMAGE_URLS = "image_urls";
public static final String EXTRA_POSITIONS = "image_positions";
public static final String EXTRA_POSITION = "position"; public static final String EXTRA_POSITION = "position";
private ArrayList<Uri> imageUrls; private ArrayList<Uri> imageUrls;
private ArrayList<String> imagePaths;
private int currentPosition; private int currentPosition;
private ViewPager viewPager; private ViewPager viewPager;
private View toolbar; private View toolbar;
@@ -25,6 +30,7 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
private ImageButton btnDelete; private ImageButton btnDelete;
private ImageButton btnShare; private ImageButton btnShare;
private GestureDetector gestureDetector; private GestureDetector gestureDetector;
private TrashManager trashManager;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -34,8 +40,11 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
setContentView(R.layout.activity_image_viewer); setContentView(R.layout.activity_image_viewer);
imageUrls = getIntent().getParcelableArrayListExtra(EXTRA_IMAGE_URLS); imageUrls = getIntent().getParcelableArrayListExtra(EXTRA_IMAGE_URLS);
imagePaths = getIntent().getStringArrayListExtra(EXTRA_POSITIONS);
currentPosition = getIntent().getIntExtra(EXTRA_POSITION, 0); currentPosition = getIntent().getIntExtra(EXTRA_POSITION, 0);
trashManager = new TrashManager(this);
viewPager = findViewById(R.id.view_pager); viewPager = findViewById(R.id.view_pager);
toolbar = findViewById(R.id.toolbar); toolbar = findViewById(R.id.toolbar);
btnBack = findViewById(R.id.btn_back); btnBack = findViewById(R.id.btn_back);
@@ -72,7 +81,7 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
btnDelete.setOnClickListener(new View.OnClickListener() { btnDelete.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
deleteCurrentImage(); showDeleteDialog();
} }
}); });
@@ -92,23 +101,89 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
} }
} }
private void deleteCurrentImage() { private void showDeleteDialog() {
if (currentPosition >= 0 && currentPosition < imageUrls.size()) { new AlertDialog.Builder(this)
Uri imageUri = imageUrls.get(currentPosition); .setMessage("Delete to trash?")
getContentResolver().delete(imageUri, null, null); .setPositiveButton("Yes", new android.content.DialogInterface.OnClickListener() {
imageUrls.remove(currentPosition); @Override
if (imageUrls.isEmpty()) { public void onClick(android.content.DialogInterface dialog, int which) {
finish(); moveToTrash();
} else { }
if (currentPosition >= imageUrls.size()) { })
currentPosition = imageUrls.size() - 1; .setNegativeButton("No", null)
.show();
}
private void moveToTrash() {
if (currentPosition >= 0 && currentPosition < imageUrls.size()) {
String imagePath = "";
if (imagePaths != null && currentPosition < imagePaths.size()) {
imagePath = imagePaths.get(currentPosition);
} else {
imagePath = getPathFromUri(imageUrls.get(currentPosition));
}
Uri imageUri = imageUrls.get(currentPosition);
long result = -1;
if (!imagePath.isEmpty()) {
result = trashManager.addToTrash(imagePath);
}
if (result > 0) {
try {
getContentResolver().delete(imageUri, null, null);
} catch (Exception e) {
e.printStackTrace();
}
android.widget.Toast.makeText(this, "Moved to trash", android.widget.Toast.LENGTH_SHORT).show();
removeCurrentImage();
} else {
try {
int deleted = getContentResolver().delete(imageUri, null, null);
android.widget.Toast.makeText(this, "Deleted: " + deleted, android.widget.Toast.LENGTH_SHORT).show();
removeCurrentImage();
} catch (Exception e) {
e.printStackTrace();
removeCurrentImage();
} }
viewPager.setAdapter(new ImagePagerAdapter(imageUrls));
viewPager.setCurrentItem(currentPosition);
} }
} }
} }
private void removeCurrentImage() {
imageUrls.remove(currentPosition);
if (imagePaths != null) {
imagePaths.remove(currentPosition);
}
if (imageUrls.isEmpty()) {
finish();
} else {
if (currentPosition >= imageUrls.size()) {
currentPosition = imageUrls.size() - 1;
}
viewPager.setAdapter(new ImagePagerAdapter(imageUrls));
viewPager.setCurrentItem(currentPosition);
}
}
private String getPathFromUri(Uri uri) {
String[] projection = { MediaStore.Images.Media.DATA };
android.database.Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
return cursor.getString(columnIndex);
}
} finally {
cursor.close();
}
}
return uri.getPath();
}
private void shareCurrentImage() { private void shareCurrentImage() {
if (currentPosition >= 0 && currentPosition < imageUrls.size()) { if (currentPosition >= 0 && currentPosition < imageUrls.size()) {
Uri imageUri = imageUrls.get(currentPosition); Uri imageUri = imageUrls.get(currentPosition);

View File

@@ -6,9 +6,11 @@ import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.provider.Settings;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.Toast; import android.widget.Toast;
@@ -27,6 +29,7 @@ import cc.winboll.gallery.AlbumAdapter.OnAlbumClickListener;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CODE = 100; private static final int PERMISSION_REQUEST_CODE = 100;
private static final int MANAGE_PERMISSION_REQUEST_CODE = 101;
private RecyclerView recyclerView; private RecyclerView recyclerView;
private AlbumAdapter adapter; private AlbumAdapter adapter;
private Preferences prefs; private Preferences prefs;
@@ -56,14 +59,49 @@ public class MainActivity extends AppCompatActivity {
} }
}); });
checkAndRequestPermissions();
}
private void checkAndRequestPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, MANAGE_PERMISSION_REQUEST_CODE);
} catch (Exception e) {
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivityForResult(intent, MANAGE_PERMISSION_REQUEST_CODE);
}
return;
}
}
if (checkPermission()) { if (checkPermission()) {
loadAlbums(); loadAlbums();
} else { } else {
requestPermission(); requestPermission();
} }
} }
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == MANAGE_PERMISSION_REQUEST_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (Environment.isExternalStorageManager()) {
loadAlbums();
} else {
Toast.makeText(this, "Permission required", Toast.LENGTH_SHORT).show();
}
}
}
}
private boolean checkPermission() { private boolean checkPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return Environment.isExternalStorageManager();
}
return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED; == PackageManager.PERMISSION_GRANTED;
} }
@@ -162,6 +200,9 @@ public class MainActivity extends AppCompatActivity {
if (id == R.id.action_settings) { if (id == R.id.action_settings) {
startActivity(new Intent(this, SettingsActivity.class)); startActivity(new Intent(this, SettingsActivity.class));
return true; return true;
} else if (id == R.id.action_trash) {
startActivity(new Intent(this, TrashActivity.class));
return true;
} else if (id == R.id.action_refresh) { } else if (id == R.id.action_refresh) {
if (checkPermission()) { if (checkPermission()) {
loadAlbums(); loadAlbums();

View File

@@ -0,0 +1,186 @@
package cc.winboll.gallery;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.io.File;
import java.util.ArrayList;
public class TrashActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CODE = 102;
private RecyclerView recyclerView;
private TrashAdapter adapter;
private TrashManager trashManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("Trash");
trashManager = new TrashManager(this);
recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
adapter = new TrashAdapter();
recyclerView.setAdapter(adapter);
adapter.setOnTrashClickListener(new TrashAdapter.OnTrashClickListener() {
@Override
public void onRestoreClick(int position) {
restoreImage(position);
}
@Override
public void onDeleteClick(int position) {
permanentlyDelete(position);
}
});
if (checkPermission()) {
loadTrash();
} else {
requestPermission();
}
}
private boolean checkPermission() {
return ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED;
}
private void requestPermission() {
ActivityCompat.requestPermissions(this,
new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE},
PERMISSION_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
loadTrash();
} else {
Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
}
}
}
private void loadTrash() {
Cursor cursor = trashManager.getTrashList();
ArrayList<TrashItem> items = new ArrayList<TrashItem>();
ArrayList<Uri> uris = new ArrayList<Uri>();
String trashPath = TrashDbHelper.getTrashPath();
File trashDir = new File(trashPath);
if (cursor != null && cursor.getCount() > 0) {
cursor.moveToFirst();
do {
try {
long id = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
String fileName = cursor.getString(cursor.getColumnIndexOrThrow("file_name"));
String originalPath = cursor.getString(cursor.getColumnIndexOrThrow("original_path"));
String originalFolder = cursor.getString(cursor.getColumnIndexOrThrow("original_folder"));
TrashItem item = new TrashItem();
item.id = id;
item.fileName = fileName;
item.originalPath = originalPath;
item.originalFolder = originalFolder;
File trashFile = new File(trashDir, fileName);
if (trashFile.exists()) {
items.add(item);
uris.add(Uri.fromFile(trashFile));
}
} catch (Exception e) {
e.printStackTrace();
}
} while (cursor.moveToNext());
cursor.close();
}
adapter.setData(items, uris);
if (items.isEmpty()) {
Toast.makeText(this, "Trash is empty", Toast.LENGTH_SHORT).show();
}
}
private void restoreImage(int position) {
long id = adapter.getItemId(position);
String fileName = adapter.getFileName(position);
String originalPath = adapter.getOriginalPath(position);
if (trashManager.restore(id, fileName, originalPath)) {
Toast.makeText(this, "Image restored", Toast.LENGTH_SHORT).show();
loadTrash();
} else {
Toast.makeText(this, "Restore failed", Toast.LENGTH_SHORT).show();
}
}
private void permanentlyDelete(int position) {
long id = adapter.getItemId(position);
String fileName = adapter.getFileName(position);
if (trashManager.deletePermanently(id, fileName)) {
Toast.makeText(this, "Image deleted", Toast.LENGTH_SHORT).show();
loadTrash();
} else {
Toast.makeText(this, "Delete failed", Toast.LENGTH_SHORT).show();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_trash, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.action_clear_trash) {
trashManager.clearTrash();
Toast.makeText(this, "Trash cleared", Toast.LENGTH_SHORT).show();
loadTrash();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onResume() {
super.onResume();
if (checkPermission()) {
loadTrash();
}
}
public static class TrashItem {
public long id;
public String fileName;
public String originalPath;
public String originalFolder;
}
}

View File

@@ -0,0 +1,107 @@
package cc.winboll.gallery;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.ArrayList;
public class TrashAdapter extends RecyclerView.Adapter<TrashAdapter.ViewHolder> {
private ArrayList<TrashActivity.TrashItem> trashItems = new ArrayList<TrashActivity.TrashItem>();
private ArrayList<Uri> imageUrls = new ArrayList<Uri>();
private OnTrashClickListener listener;
public interface OnTrashClickListener {
void onRestoreClick(int position);
void onDeleteClick(int position);
}
public void setOnTrashClickListener(OnTrashClickListener listener) {
this.listener = listener;
}
public void setData(ArrayList<TrashActivity.TrashItem> items, ArrayList<Uri> uris) {
this.trashItems = items;
this.imageUrls = uris;
notifyDataSetChanged();
}
public long getItemId(int position) {
if (position >= 0 && position < trashItems.size()) {
return trashItems.get(position).id;
}
return -1;
}
public String getFileName(int position) {
if (position >= 0 && position < trashItems.size()) {
return trashItems.get(position).fileName;
}
return "";
}
public String getOriginalPath(int position) {
if (position >= 0 && position < trashItems.size()) {
return trashItems.get(position).originalPath;
}
return "";
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_trash, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
if (position < imageUrls.size()) {
Glide.with(holder.imageView.getContext())
.load(imageUrls.get(position))
.centerCrop()
.into(holder.imageView);
}
holder.btnRestore.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onRestoreClick(position);
}
}
});
holder.btnDelete.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onDeleteClick(position);
}
}
});
}
@Override
public int getItemCount() {
return trashItems.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
ImageView btnRestore;
ImageView btnDelete;
ViewHolder(View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.image);
btnRestore = itemView.findViewById(R.id.btn_restore);
btnDelete = itemView.findViewById(R.id.btn_delete);
}
}
}

View File

@@ -0,0 +1,73 @@
package cc.winboll.gallery;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Environment;
import java.io.File;
public class TrashDbHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "trash.db";
private static final int DB_VERSION = 1;
private static final String TABLE_NAME = "trash_items";
private static final String COL_ID = "_id";
private static final String COL_FILE_NAME = "file_name";
private static final String COL_ORIGINAL_PATH = "original_path";
private static final String COL_ORIGINAL_FOLDER = "original_folder";
private static final String COL_DELETE_TIME = "delete_time";
public TrashDbHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_NAME + " (" +
COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COL_FILE_NAME + " TEXT, " +
COL_ORIGINAL_PATH + " TEXT, " +
COL_ORIGINAL_FOLDER + " TEXT, " +
COL_DELETE_TIME + " INTEGER)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
public long insert(String fileName, String originalPath, String originalFolder) {
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COL_FILE_NAME, fileName);
values.put(COL_ORIGINAL_PATH, originalPath);
values.put(COL_ORIGINAL_FOLDER, originalFolder);
values.put(COL_DELETE_TIME, System.currentTimeMillis());
return db.insert(TABLE_NAME, null, values);
}
public Cursor getAll() {
SQLiteDatabase db = getReadableDatabase();
return db.query(TABLE_NAME, null, null, null, null, null, COL_DELETE_TIME + " DESC");
}
public int delete(long id) {
SQLiteDatabase db = getWritableDatabase();
return db.delete(TABLE_NAME, COL_ID + "=?", new String[]{String.valueOf(id)});
}
public void clear() {
SQLiteDatabase db = getWritableDatabase();
db.delete(TABLE_NAME, null, null);
}
public static String getTrashPath() {
File trashDir = new File(Environment.getExternalStorageDirectory(), ".Trash");
if (!trashDir.exists()) {
trashDir.mkdirs();
}
return trashDir.getAbsolutePath();
}
}

View File

@@ -0,0 +1,102 @@
package cc.winboll.gallery;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import java.io.File;
import java.util.UUID;
public class TrashManager {
private final Context context;
private final TrashDbHelper dbHelper;
public TrashManager(Context context) {
this.context = context;
this.dbHelper = new TrashDbHelper(context);
}
public long addToTrash(String imagePath) {
File sourceFile = new File(imagePath);
if (!sourceFile.exists()) {
return -1;
}
String uniqueId = UUID.randomUUID().toString();
String extension = getExtension(imagePath);
String newFileName = uniqueId + extension;
String trashPath = TrashDbHelper.getTrashPath();
File destFile = new File(trashPath, newFileName);
if (sourceFile.renameTo(destFile)) {
String originalFolder = sourceFile.getParent();
long result = dbHelper.insert(newFileName, imagePath, originalFolder);
return result;
}
return -1;
}
public Cursor getTrashList() {
return dbHelper.getAll();
}
public boolean restore(long id, String fileName, String originalPath) {
File trashFile = new File(TrashDbHelper.getTrashPath(), fileName);
if (!trashFile.exists()) {
return false;
}
File originalFolder = new File(originalPath).getParentFile();
if (originalFolder != null && !originalFolder.exists()) {
originalFolder.mkdirs();
}
File originalFile = new File(originalPath);
String restoreName = originalFile.getName();
File restoreFile = new File(originalFolder, restoreName);
if (trashFile.renameTo(restoreFile)) {
dbHelper.delete(id);
return true;
}
return false;
}
public boolean deletePermanently(long id, String fileName) {
File trashFile = new File(TrashDbHelper.getTrashPath(), fileName);
boolean deleted = trashFile.delete();
if (deleted) {
dbHelper.delete(id);
}
return deleted;
}
public void clearTrash() {
Cursor cursor = getTrashList();
if (cursor != null) {
while (cursor.moveToNext()) {
try {
int colIndex = cursor.getColumnIndexOrThrow("_id");
if (!cursor.isNull(colIndex)) {
String fileName = cursor.getString(cursor.getColumnIndexOrThrow("file_name"));
File trashFile = new File(TrashDbHelper.getTrashPath(), fileName);
trashFile.delete();
}
} catch (Exception e) {
e.printStackTrace();
}
}
cursor.close();
}
dbHelper.clear();
}
private String getExtension(String path) {
int lastDot = path.lastIndexOf('.');
if (lastDot > 0) {
return path.substring(lastDot);
}
return ".jpg";
}
}

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9H1l3.89,3.89 0.07,0.14L9,12H6c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,17.9 10.51,19 13,19c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08V8H12z"/>
</vector>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="2dp">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="120dp"
android:scaleType="centerCrop"
android:background="@color/black"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#80000000"
android:orientation="horizontal">
<ImageView
android:id="@+id/btn_restore"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_weight="1"
android:padding="8dp"
android:src="@drawable/ic_restore"
android:contentDescription="Restore"/>
<ImageView
android:id="@+id/btn_delete"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_weight="1"
android:padding="8dp"
android:src="@drawable/ic_delete"
android:contentDescription="Delete"/>
</LinearLayout>
</FrameLayout>

View File

@@ -2,6 +2,11 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_trash"
android:title="@string/trash"
app:showAsAction="never"/>
<item <item
android:id="@+id/action_settings" android:id="@+id/action_settings"
android:title="@string/settings" android:title="@string/settings"

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_trash"
android:title="@string/trash"
app:showAsAction="never"/>
<item
android:id="@+id/action_clear_trash"
android:title="@string/clear_trash"
app:showAsAction="never"/>
</menu>

View File

@@ -8,4 +8,10 @@
<string name="save">Save</string> <string name="save">Save</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="no_images_found">No images found</string> <string name="no_images_found">No images found</string>
<string name="trash">Trash</string>
<string name="clear_trash">Clear Trash</string>
<string name="delete_confirm">Delete to trash?</string>
<string name="restore_confirm">Restore to original folder?</string>
<string name="yes">Yes</string>
<string name="no">No</string>
</resources> </resources>