diff --git a/gallery/build.properties b/gallery/build.properties index 3d42bc2..7b66d2f 100644 --- a/gallery/build.properties +++ b/gallery/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun Apr 26 09:00:16 CST 2026 +#Sun Apr 26 09:40:02 CST 2026 stageCount=4 libraryProject= baseVersion=15.0 publishVersion=15.0.3 -buildCount=11 +buildCount=22 baseBetaVersion=15.0.4 diff --git a/gallery/src/main/java/cc/winboll/studio/gallery/ImageAdapter.java b/gallery/src/main/java/cc/winboll/studio/gallery/ImageAdapter.java index 21f2882..cb1badd 100644 --- a/gallery/src/main/java/cc/winboll/studio/gallery/ImageAdapter.java +++ b/gallery/src/main/java/cc/winboll/studio/gallery/ImageAdapter.java @@ -1,5 +1,6 @@ package cc.winboll.studio.gallery; +import android.content.DialogInterface; import android.net.Uri; import android.view.LayoutInflater; import android.view.View; @@ -10,6 +11,8 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import cc.winboll.studio.libappbase.LogUtils; @@ -20,6 +23,7 @@ public class ImageAdapter extends RecyclerView.Adapter private OnImageClickListener listener; private int bgType = 0; private Preferences prefs; + private PinnedImageDbHelper pinnedDbHelper; private int getBgRes() { switch (bgType) { @@ -45,13 +49,60 @@ public class ImageAdapter extends RecyclerView.Adapter public void setData(ArrayList urls, ArrayList paths) { this.imageUrls = urls; this.imagePaths = paths; + sortPinnedFirst(); LogUtils.d(TAG, "setData: " + urls.size() + " images"); notifyDataSetChanged(); } + private void sortPinnedFirst() { + if (pinnedDbHelper == null || imagePaths.isEmpty()) { + return; + } + ArrayList pinnedPaths = new ArrayList<>(); + ArrayList unpinnedPaths = new ArrayList<>(); + for (String path : imagePaths) { + if (pinnedDbHelper.isPinned(path)) { + pinnedPaths.add(path); + } else { + unpinnedPaths.add(path); + } + } + Comparator pathComparator = new Comparator() { + @Override + public int compare(String p1, String p2) { + return p1.compareToIgnoreCase(p2); + } + }; + Collections.sort(pinnedPaths, pathComparator); + Collections.sort(unpinnedPaths, pathComparator); + + ArrayList newUrls = new ArrayList<>(); + ArrayList newPaths = new ArrayList<>(); + for (String path : pinnedPaths) { + int index = imagePaths.indexOf(path); + if (index >= 0) { + newUrls.add(imageUrls.get(index)); + newPaths.add(path); + } + } + for (String path : unpinnedPaths) { + int index = imagePaths.indexOf(path); + if (index >= 0) { + newUrls.add(imageUrls.get(index)); + newPaths.add(path); + } + } + + imageUrls.clear(); + imagePaths.clear(); + imageUrls.addAll(newUrls); + imagePaths.addAll(newPaths); + } + public void setContext(android.content.Context context) { prefs = new Preferences(context); bgType = prefs.getBgType(); + pinnedDbHelper = PinnedImageDbHelper.getInstance(context); } public void refreshBg() { @@ -60,7 +111,37 @@ public class ImageAdapter extends RecyclerView.Adapter notifyDataSetChanged(); } } - + + public void refreshPinned() { + if (pinnedDbHelper == null || imagePaths.isEmpty()) { + return; + } + sortPinnedFirst(); + notifyDataSetChanged(); + } + + private void showContextMenu(View view, final int position) { + final String imagePath = imagePaths.get(position); + android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(view.getContext()); + builder.setTitle("Image"); + boolean isPinned = pinnedDbHelper != null && pinnedDbHelper.isPinned(imagePath); + String[] items = isPinned ? new String[]{"取消置顶"} : new String[]{"置顶"}; + builder.setItems(items, new android.content.DialogInterface.OnClickListener() { + @Override + public void onClick(android.content.DialogInterface dialog, int which) { + if (pinnedDbHelper != null && which == 0) { + if (isPinned) { + pinnedDbHelper.unpinImage(imagePath); + } else { + pinnedDbHelper.pinImage(imagePath); + } + refreshPinned(); + } + } + }); + builder.show(); + } + @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { @@ -80,7 +161,12 @@ public class ImageAdapter extends RecyclerView.Adapter final ArrayList urls = imageUrls; final ArrayList paths = imagePaths; - holder.imageView.setOnClickListener(new OnClickListener() { + final String imagePath = imagePaths.get(position); + + boolean isPinned = pinnedDbHelper != null && pinnedDbHelper.isPinned(imagePath); + holder.pinIcon.setVisibility(isPinned ? View.VISIBLE : View.GONE); + + holder.itemView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (listener != null) { @@ -88,6 +174,14 @@ public class ImageAdapter extends RecyclerView.Adapter } } }); + + holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + showContextMenu(holder.itemView, position); + return true; + } + }); } @Override @@ -97,9 +191,11 @@ public class ImageAdapter extends RecyclerView.Adapter static class ViewHolder extends RecyclerView.ViewHolder { ImageView imageView; + ImageView pinIcon; ViewHolder(View itemView) { super(itemView); imageView = itemView.findViewById(R.id.image); + pinIcon = itemView.findViewById(R.id.pin_icon); } } } \ No newline at end of file diff --git a/gallery/src/main/java/cc/winboll/studio/gallery/PinnedAlbumDbHelper.java b/gallery/src/main/java/cc/winboll/studio/gallery/PinnedAlbumDbHelper.java index cbe60cc..6dc35e9 100644 --- a/gallery/src/main/java/cc/winboll/studio/gallery/PinnedAlbumDbHelper.java +++ b/gallery/src/main/java/cc/winboll/studio/gallery/PinnedAlbumDbHelper.java @@ -9,7 +9,7 @@ import cc.winboll.studio.libappbase.LogUtils; public class PinnedAlbumDbHelper extends SQLiteOpenHelper { public static final String TAG = "PinnedAlbumDbHelper"; - private static final String DB_NAME = "gallery.db"; + private static final String DB_NAME = "pinned_album.db"; private static final int DB_VERSION = 1; private static final String TABLE_NAME = "pinned_albums"; private static final String COLUMN_PATH = "album_path"; diff --git a/gallery/src/main/java/cc/winboll/studio/gallery/PinnedImageDbHelper.java b/gallery/src/main/java/cc/winboll/studio/gallery/PinnedImageDbHelper.java new file mode 100644 index 0000000..460d6e9 --- /dev/null +++ b/gallery/src/main/java/cc/winboll/studio/gallery/PinnedImageDbHelper.java @@ -0,0 +1,78 @@ +package cc.winboll.studio.gallery; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import cc.winboll.studio.libappbase.LogUtils; + +public class PinnedImageDbHelper extends SQLiteOpenHelper { + public static final String TAG = "PinnedImageDbHelper"; + private static final String DB_NAME = "pinned_image.db"; + private static final int DB_VERSION = 1; + private static final String TABLE_NAME = "pinned_images"; + private static final String COLUMN_PATH = "image_path"; + + private static final String SQL_CREATE = "CREATE TABLE " + TABLE_NAME + " (" + + COLUMN_PATH + " TEXT PRIMARY KEY)"; + + private static PinnedImageDbHelper dbHelper; + + public static PinnedImageDbHelper getInstance(Context context) { + if (dbHelper == null) { + dbHelper = new PinnedImageDbHelper(context.getApplicationContext()); + } + return dbHelper; + } + + public PinnedImageDbHelper(Context context) { + super(context, DB_NAME, null, DB_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(SQL_CREATE); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); + onCreate(db); + } + + public void pinImage(String imagePath) { + SQLiteDatabase db = getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(COLUMN_PATH, imagePath); + db.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_IGNORE); + LogUtils.d(TAG, "pinImage: " + imagePath); + } + + public void unpinImage(String imagePath) { + SQLiteDatabase db = getWritableDatabase(); + db.delete(TABLE_NAME, COLUMN_PATH + " = ?", new String[]{imagePath}); + LogUtils.d(TAG, "unpinImage: " + imagePath); + } + + public boolean isPinned(String imagePath) { + SQLiteDatabase db = getReadableDatabase(); + Cursor cursor = db.query(TABLE_NAME, null, COLUMN_PATH + " = ?", + new String[]{imagePath}, null, null, null); + boolean pinned = cursor.getCount() > 0; + cursor.close(); + return pinned; + } + + public String[] getPinnedPaths() { + SQLiteDatabase db = getReadableDatabase(); + Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_PATH}, null, null, null, null, null); + String[] paths = new String[cursor.getCount()]; + int i = 0; + while (cursor.moveToNext()) { + paths[i++] = cursor.getString(0); + } + cursor.close(); + return paths; + } +} \ No newline at end of file diff --git a/gallery/src/main/res/layout/item_gallery.xml b/gallery/src/main/res/layout/item_gallery.xml index c9a4c77..21d64b4 100644 --- a/gallery/src/main/res/layout/item_gallery.xml +++ b/gallery/src/main/res/layout/item_gallery.xml @@ -12,4 +12,15 @@ android:scaleType="centerCrop" android:background="@color/black"/> + + \ No newline at end of file