diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index debe689..b37c3f0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
+
+
+
diff --git a/app/src/main/java/cc/winboll/gallery/AlbumActivity.java b/app/src/main/java/cc/winboll/gallery/AlbumActivity.java
index 0bfa1a3..4d723e9 100644
--- a/app/src/main/java/cc/winboll/gallery/AlbumActivity.java
+++ b/app/src/main/java/cc/winboll/gallery/AlbumActivity.java
@@ -49,9 +49,10 @@ public class AlbumActivity extends AppCompatActivity {
adapter.setOnImageClickListener(new OnImageClickListener() {
@Override
- public void onImageClick(int position, ArrayList urls) {
+ public void onImageClick(int position, ArrayList urls, ArrayList paths) {
Intent intent = new Intent(AlbumActivity.this, ImageViewerActivity.class);
intent.putParcelableArrayListExtra(ImageViewerActivity.EXTRA_IMAGE_URLS, urls);
+ intent.putStringArrayListExtra(ImageViewerActivity.EXTRA_POSITIONS, paths);
intent.putExtra(ImageViewerActivity.EXTRA_POSITION, position);
startActivity(intent);
}
@@ -90,6 +91,7 @@ public class AlbumActivity extends AppCompatActivity {
private void loadImages() {
ArrayList imageUrls = new ArrayList<>();
+ ArrayList imagePaths = new ArrayList<>();
ContentResolver contentResolver = getContentResolver();
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));
Uri contentUri = Uri.withAppendedPath(collection, String.valueOf(id));
imageUrls.add(contentUri);
+ imagePaths.add(path);
}
}
}
@@ -114,7 +117,7 @@ public class AlbumActivity extends AppCompatActivity {
if (imageUrls.isEmpty()) {
Toast.makeText(this, R.string.no_images_found, Toast.LENGTH_SHORT).show();
}
- adapter.setData(imageUrls);
+ adapter.setData(imageUrls, imagePaths);
}
@Override
@@ -133,4 +136,12 @@ public class AlbumActivity extends AppCompatActivity {
}
return super.onOptionsItemSelected(item);
}
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (checkPermission()) {
+ loadImages();
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/cc/winboll/gallery/ImageAdapter.java b/app/src/main/java/cc/winboll/gallery/ImageAdapter.java
index 7d448af..d52cefc 100644
--- a/app/src/main/java/cc/winboll/gallery/ImageAdapter.java
+++ b/app/src/main/java/cc/winboll/gallery/ImageAdapter.java
@@ -13,18 +13,20 @@ import java.util.ArrayList;
public class ImageAdapter extends RecyclerView.Adapter {
private ArrayList imageUrls = new ArrayList<>();
+ private ArrayList imagePaths = new ArrayList<>();
private OnImageClickListener listener;
public interface OnImageClickListener {
- void onImageClick(int position, ArrayList urls);
+ void onImageClick(int position, ArrayList urls, ArrayList paths);
}
public void setOnImageClickListener(OnImageClickListener listener) {
this.listener = listener;
}
- public void setData(ArrayList urls) {
+ public void setData(ArrayList urls, ArrayList paths) {
this.imageUrls = urls;
+ this.imagePaths = paths;
notifyDataSetChanged();
}
@@ -44,11 +46,12 @@ public class ImageAdapter extends RecyclerView.Adapter
.into(holder.imageView);
final ArrayList urls = imageUrls;
+ final ArrayList paths = imagePaths;
holder.imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
- listener.onImageClick(position, urls);
+ listener.onImageClick(position, urls, paths);
}
}
});
diff --git a/app/src/main/java/cc/winboll/gallery/ImageViewerActivity.java b/app/src/main/java/cc/winboll/gallery/ImageViewerActivity.java
index c52358f..97ef06c 100644
--- a/app/src/main/java/cc/winboll/gallery/ImageViewerActivity.java
+++ b/app/src/main/java/cc/winboll/gallery/ImageViewerActivity.java
@@ -1,9 +1,11 @@
package cc.winboll.gallery;
import android.app.Activity;
+import android.app.AlertDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.provider.MediaStore;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
@@ -11,13 +13,16 @@ import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.ImageButton;
import androidx.viewpager.widget.ViewPager;
+import java.io.File;
import java.util.ArrayList;
public class ImageViewerActivity extends Activity implements ViewPager.OnPageChangeListener {
public static final String EXTRA_IMAGE_URLS = "image_urls";
+ public static final String EXTRA_POSITIONS = "image_positions";
public static final String EXTRA_POSITION = "position";
private ArrayList imageUrls;
+ private ArrayList imagePaths;
private int currentPosition;
private ViewPager viewPager;
private View toolbar;
@@ -25,6 +30,7 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
private ImageButton btnDelete;
private ImageButton btnShare;
private GestureDetector gestureDetector;
+ private TrashManager trashManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -34,8 +40,11 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
setContentView(R.layout.activity_image_viewer);
imageUrls = getIntent().getParcelableArrayListExtra(EXTRA_IMAGE_URLS);
+ imagePaths = getIntent().getStringArrayListExtra(EXTRA_POSITIONS);
currentPosition = getIntent().getIntExtra(EXTRA_POSITION, 0);
+ trashManager = new TrashManager(this);
+
viewPager = findViewById(R.id.view_pager);
toolbar = findViewById(R.id.toolbar);
btnBack = findViewById(R.id.btn_back);
@@ -72,7 +81,7 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
btnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- deleteCurrentImage();
+ showDeleteDialog();
}
});
@@ -92,23 +101,89 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
}
}
- private void deleteCurrentImage() {
- if (currentPosition >= 0 && currentPosition < imageUrls.size()) {
- Uri imageUri = imageUrls.get(currentPosition);
- getContentResolver().delete(imageUri, null, null);
- imageUrls.remove(currentPosition);
- if (imageUrls.isEmpty()) {
- finish();
- } else {
- if (currentPosition >= imageUrls.size()) {
- currentPosition = imageUrls.size() - 1;
+ private void showDeleteDialog() {
+ new AlertDialog.Builder(this)
+ .setMessage("Delete to trash?")
+ .setPositiveButton("Yes", new android.content.DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(android.content.DialogInterface dialog, int which) {
+ moveToTrash();
+ }
+ })
+ .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() {
if (currentPosition >= 0 && currentPosition < imageUrls.size()) {
Uri imageUri = imageUrls.get(currentPosition);
diff --git a/app/src/main/java/cc/winboll/gallery/MainActivity.java b/app/src/main/java/cc/winboll/gallery/MainActivity.java
index 08b880e..03695ac 100644
--- a/app/src/main/java/cc/winboll/gallery/MainActivity.java
+++ b/app/src/main/java/cc/winboll/gallery/MainActivity.java
@@ -6,9 +6,11 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
+import android.provider.Settings;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
@@ -27,6 +29,7 @@ import cc.winboll.gallery.AlbumAdapter.OnAlbumClickListener;
public class MainActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CODE = 100;
+ private static final int MANAGE_PERMISSION_REQUEST_CODE = 101;
private RecyclerView recyclerView;
private AlbumAdapter adapter;
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()) {
loadAlbums();
} else {
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() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ return Environment.isExternalStorageManager();
+ }
return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED;
}
@@ -162,6 +200,9 @@ public class MainActivity extends AppCompatActivity {
if (id == R.id.action_settings) {
startActivity(new Intent(this, SettingsActivity.class));
return true;
+ } else if (id == R.id.action_trash) {
+ startActivity(new Intent(this, TrashActivity.class));
+ return true;
} else if (id == R.id.action_refresh) {
if (checkPermission()) {
loadAlbums();
diff --git a/app/src/main/java/cc/winboll/gallery/TrashActivity.java b/app/src/main/java/cc/winboll/gallery/TrashActivity.java
new file mode 100644
index 0000000..be1149f
--- /dev/null
+++ b/app/src/main/java/cc/winboll/gallery/TrashActivity.java
@@ -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 items = new ArrayList();
+ ArrayList uris = new ArrayList();
+
+ 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;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/cc/winboll/gallery/TrashAdapter.java b/app/src/main/java/cc/winboll/gallery/TrashAdapter.java
new file mode 100644
index 0000000..029fac2
--- /dev/null
+++ b/app/src/main/java/cc/winboll/gallery/TrashAdapter.java
@@ -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 {
+ private ArrayList trashItems = new ArrayList();
+ private ArrayList imageUrls = new ArrayList();
+ 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 items, ArrayList 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/cc/winboll/gallery/TrashDbHelper.java b/app/src/main/java/cc/winboll/gallery/TrashDbHelper.java
new file mode 100644
index 0000000..4fd0c5c
--- /dev/null
+++ b/app/src/main/java/cc/winboll/gallery/TrashDbHelper.java
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/cc/winboll/gallery/TrashManager.java b/app/src/main/java/cc/winboll/gallery/TrashManager.java
new file mode 100644
index 0000000..8f584d5
--- /dev/null
+++ b/app/src/main/java/cc/winboll/gallery/TrashManager.java
@@ -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";
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_restore.xml b/app/src/main/res/drawable/ic_restore.xml
new file mode 100644
index 0000000..f607ca2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_restore.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_trash.xml b/app/src/main/res/layout/item_trash.xml
new file mode 100644
index 0000000..1a5a89a
--- /dev/null
+++ b/app/src/main/res/layout/item_trash.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index a2b736c..9cfd845 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -2,6 +2,11 @@