diff --git a/gallery/build.properties b/gallery/build.properties index 6b74c3a..4449b5c 100644 --- a/gallery/build.properties +++ b/gallery/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun Apr 26 11:32:37 HKT 2026 +#Sun Apr 26 23:43:34 CST 2026 stageCount=6 libraryProject= baseVersion=15.0 publishVersion=15.0.5 -buildCount=0 +buildCount=51 baseBetaVersion=15.0.6 diff --git a/gallery/src/main/java/cc/winboll/studio/gallery/AlbumActivity.java b/gallery/src/main/java/cc/winboll/studio/gallery/AlbumActivity.java index 90181b7..a9ccac2 100644 --- a/gallery/src/main/java/cc/winboll/studio/gallery/AlbumActivity.java +++ b/gallery/src/main/java/cc/winboll/studio/gallery/AlbumActivity.java @@ -240,4 +240,12 @@ private String getSortOrder(int sortMode) { adapter.refreshBg(); } } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == 100 && resultCode == RESULT_OK) { + loadImages(); + } + } } \ No newline at end of file diff --git a/gallery/src/main/java/cc/winboll/studio/gallery/AlbumAdapter.java b/gallery/src/main/java/cc/winboll/studio/gallery/AlbumAdapter.java index 70d6157..2a23ac2 100644 --- a/gallery/src/main/java/cc/winboll/studio/gallery/AlbumAdapter.java +++ b/gallery/src/main/java/cc/winboll/studio/gallery/AlbumAdapter.java @@ -26,6 +26,8 @@ public class AlbumAdapter extends RecyclerView.Adapter private Preferences prefs; private int bgType = 0; private PinnedAlbumDbHelper pinnedDbHelper; + private OnCoverSizeListener coverSizeListener; + private boolean coverSizeReported = false; private int getBgRes() { switch (bgType) { @@ -43,10 +45,21 @@ public class AlbumAdapter extends RecyclerView.Adapter public interface OnAlbumClickListener { void onAlbumClick(Album album); } - + + public interface OnCoverSizeListener { + void onCoverSize(int width, int height); + } + + public static final int DEFAULT_COVER_WIDTH = 240; + public static final int DEFAULT_COVER_HEIGHT = 120; + public void setOnAlbumClickListener(OnAlbumClickListener listener) { this.listener = listener; } + + public void setOnCoverSizeListener(OnCoverSizeListener listener) { + this.coverSizeListener = listener; + } public void setData(ArrayList albums) { this.albums = sortAlbums(albums); @@ -84,6 +97,10 @@ public class AlbumAdapter extends RecyclerView.Adapter notifyDataSetChanged(); } + public void refreshCover() { + notifyDataSetChanged(); + } + public void refreshPinned() { if (pinnedDbHelper != null && albums != null && !albums.isEmpty()) { ArrayList pinned = new ArrayList<>(); @@ -162,6 +179,29 @@ private void showContextMenu(View view, final Album album) { holder.albumName.setText(album.getName()); holder.imageCount.setText(album.getImageCount() + " photos"); + holder.itemView.post(new Runnable() { + @Override + public void run() { + if (!coverSizeReported && prefs != null) { + int savedWidth = prefs.getCoverWidth(); + if (savedWidth == AlbumAdapter.DEFAULT_COVER_WIDTH) { + int width = holder.coverImage.getWidth(); + int height = holder.coverImage.getHeight(); + if (width > 0 && height > 0) { + prefs.setCoverWidth(width); + prefs.setCoverHeight(height); + float ratio = (float) width / height; + prefs.setCoverRatio(ratio); + coverSizeReported = true; + if (coverSizeListener != null) { + coverSizeListener.onCoverSize(width, height); + } + } + } + } + } + }); + holder.itemView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { @@ -176,37 +216,22 @@ private void showContextMenu(View view, final Album album) { String uriString = coverUri.toString(); LogUtils.d(TAG, "uri scheme: " + coverUri.getScheme() + ", path: " + uriString); - // For content:// URIs, try to get the actual file path - if ("content".equals(coverUri.getScheme())) { - try { - android.database.Cursor cursor = holder.coverImage.getContext().getContentResolver() - .query(coverUri, new String[]{MediaStore.Images.Media.DATA}, null, null, null); - if (cursor != null && cursor.moveToFirst()) { - int dataColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); - String filePath = cursor.getString(dataColumn); - cursor.close(); - if (filePath != null) { - File actualFile = new File(filePath); - LogUtils.d(TAG, "actual file: " + actualFile.getAbsolutePath() + ", exists=" + actualFile.exists()); - // Use file path instead of content URI for better compatibility - Glide.with(holder.coverImage.getContext()) - .load(actualFile) - .centerCrop() - .into(holder.coverImage); - return; - } - } - } catch (Exception e) { - LogUtils.e(TAG, "query error: " + e.getMessage()); + if ("file".equals(coverUri.getScheme())) { + File coverFile = new File(coverUri.getPath()); + if (coverFile.exists()) { + Glide.with(holder.coverImage.getContext()) + .load(coverFile) + .fitCenter() + .into(holder.coverImage); + return; } } + + Glide.with(holder.coverImage.getContext()) + .load(coverUri) + .fitCenter() + .into(holder.coverImage); } - - // Fallback to content URI - Glide.with(holder.coverImage.getContext()) - .load(coverUri) - .centerCrop() - .into(holder.coverImage); } @Override diff --git a/gallery/src/main/java/cc/winboll/studio/gallery/AlbumCoverDbHelper.java b/gallery/src/main/java/cc/winboll/studio/gallery/AlbumCoverDbHelper.java index a945bde..3a74d25 100644 --- a/gallery/src/main/java/cc/winboll/studio/gallery/AlbumCoverDbHelper.java +++ b/gallery/src/main/java/cc/winboll/studio/gallery/AlbumCoverDbHelper.java @@ -10,14 +10,16 @@ import cc.winboll.studio.libappbase.LogUtils; public class AlbumCoverDbHelper extends SQLiteOpenHelper { public static final String TAG = "AlbumCoverDbHelper"; private static final String DB_NAME = "album_cover.db"; - private static final int DB_VERSION = 1; + private static final int DB_VERSION = 2; private static final String TABLE_NAME = "album_covers"; private static final String COLUMN_ALBUM_PATH = "album_path"; private static final String COLUMN_IMAGE_PATH = "image_path"; + private static final String COLUMN_CROP_PATH = "crop_path"; private static final String SQL_CREATE = "CREATE TABLE " + TABLE_NAME + " (" + COLUMN_ALBUM_PATH + " TEXT PRIMARY KEY, " - + COLUMN_IMAGE_PATH + " TEXT)"; + + COLUMN_IMAGE_PATH + " TEXT, " + + COLUMN_CROP_PATH + " TEXT)"; private static AlbumCoverDbHelper dbHelper; @@ -39,8 +41,24 @@ public class AlbumCoverDbHelper extends SQLiteOpenHelper { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); - onCreate(db); + if (oldVersion < 2) { + try { + db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + COLUMN_CROP_PATH + " TEXT"); + } catch (Exception e) { + db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); + onCreate(db); + } + } + } + + public void setCoverWithCrop(String albumPath, String imagePath, String cropPath) { + SQLiteDatabase db = getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(COLUMN_ALBUM_PATH, albumPath); + values.put(COLUMN_IMAGE_PATH, imagePath); + values.put(COLUMN_CROP_PATH, cropPath); + db.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE); + LogUtils.d(TAG, "setCoverWithCrop: album=" + albumPath + ", image=" + imagePath + ", crop=" + cropPath); } public void setCover(String albumPath, String imagePath) { @@ -54,21 +72,60 @@ public class AlbumCoverDbHelper extends SQLiteOpenHelper { public String getCover(String albumPath) { SQLiteDatabase db = getReadableDatabase(); - Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_IMAGE_PATH}, + Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_CROP_PATH, COLUMN_IMAGE_PATH}, COLUMN_ALBUM_PATH + " = ?", new String[]{albumPath}, null, null, null); String coverPath = null; if (cursor != null) { if (cursor.moveToFirst()) { coverPath = cursor.getString(0); + if (coverPath == null) { + coverPath = cursor.getString(1); + } } cursor.close(); } return coverPath; } + public String getCropPath(String albumPath) { + SQLiteDatabase db = getReadableDatabase(); + Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_CROP_PATH}, + COLUMN_ALBUM_PATH + " = ?", new String[]{albumPath}, null, null, null); + String cropPath = null; + if (cursor != null) { + if (cursor.moveToFirst()) { + cropPath = cursor.getString(0); + } + cursor.close(); + } + return cropPath; + } + + public String getOriginalImagePath(String albumPath) { + SQLiteDatabase db = getReadableDatabase(); + Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_IMAGE_PATH}, + COLUMN_ALBUM_PATH + " = ?", new String[]{albumPath}, null, null, null); + String imagePath = null; + if (cursor != null) { + if (cursor.moveToFirst()) { + imagePath = cursor.getString(0); + } + cursor.close(); + } + return imagePath; + } + public void clearCover(String albumPath) { SQLiteDatabase db = getWritableDatabase(); - db.delete(TABLE_NAME, COLUMN_ALBUM_PATH + " = ?", new String[]{albumPath}); + ContentValues values = new ContentValues(); + values.putNull(COLUMN_CROP_PATH); + db.update(TABLE_NAME, values, COLUMN_ALBUM_PATH + " = ?", new String[]{albumPath}); LogUtils.d(TAG, "clearCover: " + albumPath); } + + public void deleteCover(String albumPath) { + SQLiteDatabase db = getWritableDatabase(); + db.delete(TABLE_NAME, COLUMN_ALBUM_PATH + " = ?", new String[]{albumPath}); + LogUtils.d(TAG, "deleteCover: " + albumPath); + } } \ No newline at end of file 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 a4a621b..d607637 100644 --- a/gallery/src/main/java/cc/winboll/studio/gallery/ImageAdapter.java +++ b/gallery/src/main/java/cc/winboll/studio/gallery/ImageAdapter.java @@ -1,6 +1,7 @@ package cc.winboll.studio.gallery; import android.content.DialogInterface; +import android.content.Intent; import android.net.Uri; import android.view.LayoutInflater; import android.view.View; @@ -94,6 +95,14 @@ public class ImageAdapter extends RecyclerView.Adapter this.albumPath = albumPath; } + public int getCropWidth() { + return prefs != null ? prefs.getCoverWidth() : 240; + } + + public int getCropHeight() { + return prefs != null ? prefs.getCoverHeight() : 120; + } + public void refreshBg() { if (prefs != null) { bgType = prefs.getBgType(); @@ -111,16 +120,38 @@ public class ImageAdapter extends RecyclerView.Adapter private void showContextMenu(View view, final int position) { final String imagePath = imagePaths.get(position); + final Uri imageUri = imageUrls.get(position); + final boolean[] isPinned = {pinnedDbHelper != null && pinnedDbHelper.isPinned(imagePath)}; + final boolean[] isCover = {false}; + if (coverDbHelper != null && albumPath != null) { + String originalPath = coverDbHelper.getOriginalImagePath(albumPath); + isCover[0] = originalPath != null && originalPath.equals(imagePath); + } + 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[]{"置顶", "设置为封面"}; + + String[] items; + if (isPinned[0]) { + if (isCover[0]) { + items = new String[]{"取消置顶", "调整封面", "取消封面"}; + } else { + items = new String[]{"取消置顶", "设置为封面"}; + } + } else { + if (isCover[0]) { + items = new String[]{"置顶", "调整封面", "取消封面"}; + } else { + items = new String[]{"置顶", "设置为封面"}; + } + } + builder.setItems(items, new android.content.DialogInterface.OnClickListener() { @Override public void onClick(android.content.DialogInterface dialog, int which) { if (which == 0) { if (pinnedDbHelper != null) { - if (isPinned) { + if (isPinned[0]) { pinnedDbHelper.unpinImage(imagePath); } else { pinnedDbHelper.pinImage(imagePath); @@ -129,7 +160,17 @@ public class ImageAdapter extends RecyclerView.Adapter } } else if (which == 1) { if (coverDbHelper != null && albumPath != null) { - coverDbHelper.setCover(albumPath, imagePath); + Intent cropIntent = new Intent(view.getContext(), CropActivity.class); + cropIntent.putExtra(CropActivity.EXTRA_IMAGE_URI, imageUri); + cropIntent.putExtra(CropActivity.EXTRA_IMAGE_PATH, imagePath); + cropIntent.putExtra(CropActivity.EXTRA_ALBUM_PATH, albumPath); + cropIntent.putExtra(CropActivity.EXTRA_CROP_WIDTH, getCropWidth()); + cropIntent.putExtra(CropActivity.EXTRA_CROP_HEIGHT, getCropHeight()); + ((AlbumActivity) view.getContext()).startActivityForResult(cropIntent, 100); + } + } else if (which == 2) { + if (coverDbHelper != null && albumPath != null && isCover[0]) { + coverDbHelper.deleteCover(albumPath); notifyDataSetChanged(); } } @@ -162,7 +203,11 @@ public class ImageAdapter extends RecyclerView.Adapter boolean isPinned = pinnedDbHelper != null && pinnedDbHelper.isPinned(imagePath); holder.pinIcon.setVisibility(isPinned ? View.VISIBLE : View.GONE); - boolean isCover = coverDbHelper != null && albumPath != null && imagePath.equals(coverDbHelper.getCover(albumPath)); + boolean isCover = false; + if (coverDbHelper != null && albumPath != null) { + String originalPath = coverDbHelper.getOriginalImagePath(albumPath); + isCover = originalPath != null && originalPath.equals(imagePath); + } holder.coverIcon.setVisibility(isCover ? View.VISIBLE : View.GONE); holder.itemView.setOnClickListener(new OnClickListener() { diff --git a/gallery/src/main/java/cc/winboll/studio/gallery/Preferences.java b/gallery/src/main/java/cc/winboll/studio/gallery/Preferences.java index d5e70e3..5746194 100644 --- a/gallery/src/main/java/cc/winboll/studio/gallery/Preferences.java +++ b/gallery/src/main/java/cc/winboll/studio/gallery/Preferences.java @@ -10,8 +10,14 @@ public class Preferences { private static final String KEY_FOLDER_PATH = "folder_path"; private static final String KEY_BG_TYPE = "bg_type"; private static final String KEY_ALBUM_SORT_MODE = "album_sort_mode"; + private static final String KEY_COVER_WIDTH = "cover_width"; + private static final String KEY_COVER_HEIGHT = "cover_height"; + private static final String KEY_COVER_RATIO = "cover_ratio"; private static final int DEFAULT_BG_TYPE = 0; private static final int DEFAULT_SORT_MODE = 0; + private static final int DEFAULT_COVER_WIDTH = 240; + private static final int DEFAULT_COVER_HEIGHT = 120; + private static final float DEFAULT_COVER_RATIO = 2.0f; private static final String DEFAULT_PATH = "/storage/emulated/0/Pictures/Gallery/owner"; public static final int SORT_TIME_DESC = 0; @@ -57,4 +63,28 @@ public class Preferences { LogUtils.d(TAG, "setAlbumSortMode: " + mode); prefs.edit().putInt(KEY_ALBUM_SORT_MODE, mode).apply(); } + + public int getCoverWidth() { + return prefs.getInt(KEY_COVER_WIDTH, DEFAULT_COVER_WIDTH); + } + + public void setCoverWidth(int width) { + prefs.edit().putInt(KEY_COVER_WIDTH, width).apply(); + } + + public int getCoverHeight() { + return prefs.getInt(KEY_COVER_HEIGHT, DEFAULT_COVER_HEIGHT); + } + + public void setCoverHeight(int height) { + prefs.edit().putInt(KEY_COVER_HEIGHT, height).apply(); + } + + public float getCoverRatio() { + return prefs.getFloat(KEY_COVER_RATIO, DEFAULT_COVER_RATIO); + } + + public void setCoverRatio(float ratio) { + prefs.edit().putFloat(KEY_COVER_RATIO, ratio).apply(); + } } \ No newline at end of file