diff --git a/gallery/build.properties b/gallery/build.properties index 2f3e898..369bf23 100644 --- a/gallery/build.properties +++ b/gallery/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Fri Apr 24 20:49:06 GMT 2026 +#Sat Apr 25 05:39:21 CST 2026 stageCount=0 libraryProject= baseVersion=15.0 publishVersion=15.0.0 -buildCount=4 +buildCount=14 baseBetaVersion=15.0.1 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 4087a60..e078ec3 100644 --- a/gallery/src/main/java/cc/winboll/studio/gallery/AlbumAdapter.java +++ b/gallery/src/main/java/cc/winboll/studio/gallery/AlbumAdapter.java @@ -1,5 +1,7 @@ package cc.winboll.studio.gallery; +import android.net.Uri; +import android.provider.MediaStore; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -9,6 +11,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; +import java.io.File; import java.util.ArrayList; import cc.winboll.studio.libappbase.LogUtils; @@ -43,10 +46,8 @@ public class AlbumAdapter extends RecyclerView.Adapter @Override public void onBindViewHolder(@NonNull ViewHolder holder, final int position) { final Album album = albums.get(position); - Glide.with(holder.coverImage.getContext()) - .load(album.getCoverUri()) - .centerCrop() - .into(holder.coverImage); + LogUtils.d(TAG, "bind: " + album.getName() + ", cover=" + album.getCoverUri()); + holder.albumName.setText(album.getName()); holder.imageCount.setText(album.getImageCount() + " photos"); @@ -58,6 +59,43 @@ public class AlbumAdapter extends RecyclerView.Adapter } } }); + + Uri coverUri = album.getCoverUri(); + if (coverUri != null) { + 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()); + } + } + } + + // 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/MainActivity.java b/gallery/src/main/java/cc/winboll/studio/gallery/MainActivity.java index 86dec88..a808698 100644 --- a/gallery/src/main/java/cc/winboll/studio/gallery/MainActivity.java +++ b/gallery/src/main/java/cc/winboll/studio/gallery/MainActivity.java @@ -5,6 +5,7 @@ import android.content.ContentResolver; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; +import android.media.MediaScannerConnection; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -26,6 +27,7 @@ import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogActivity; import java.io.File; import java.io.FileFilter; +import java.io.FilenameFilter; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { @@ -133,13 +135,16 @@ public class MainActivity extends AppCompatActivity { LogUtils.d(TAG, "loadAlbums"); String folderPath = prefs.getFolderPath(); File baseFolder = new File(folderPath); + LogUtils.d(TAG, "baseFolder: " + baseFolder.getAbsolutePath() + ", exists=" + baseFolder.exists()); if (!baseFolder.exists() || !baseFolder.isDirectory()) { folderPath = Environment.getExternalStorageDirectory() + "/DCIM"; baseFolder = new File(folderPath); + LogUtils.d(TAG, "try DCIM: " + baseFolder.getAbsolutePath() + ", exists=" + baseFolder.exists()); if (!baseFolder.exists()) { folderPath = Environment.getExternalStorageDirectory() + "/Pictures"; baseFolder = new File(folderPath); + LogUtils.d(TAG, "try Pictures: " + baseFolder.getAbsolutePath() + ", exists=" + baseFolder.exists()); } } @@ -152,12 +157,15 @@ public class MainActivity extends AppCompatActivity { } }; File[] subfolders = baseFolder.listFiles(directoryFilter); + LogUtils.d(TAG, "subfolders: " + (subfolders != null ? subfolders.length : 0)); if (subfolders != null) { for (File subfolder : subfolders) { + LogUtils.d(TAG, "scanning folder: " + subfolder.getName()); ArrayList images = getImagesInFolder(subfolder.getAbsolutePath()); if (!images.isEmpty()) { Uri latestImage = images.get(0); albums.add(new Album(subfolder.getName(), subfolder.getAbsolutePath(), latestImage, images.size())); + LogUtils.d(TAG, "album added: " + subfolder.getName() + ", " + images.size() + " images"); } } } @@ -179,19 +187,24 @@ public class MainActivity extends AppCompatActivity { String[] selectionArgs = new String[]{folderPath + "/%"}; String sortOrder = MediaStore.Images.Media.DATE_ADDED + " DESC"; + LogUtils.d(TAG, "getImagesInFolder: " + folderPath); + try (Cursor cursor = contentResolver.query(collection, null, selection, selectionArgs, sortOrder)) { if (cursor != null) { + LogUtils.d(TAG, "cursor count: " + cursor.getCount()); int dataColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); while (cursor.moveToNext()) { String path = cursor.getString(dataColumn); if (path != null) { long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)); Uri contentUri = Uri.withAppendedPath(collection, String.valueOf(id)); + LogUtils.d(TAG, "image: id=" + id + ", path=" + path); imageUrls.add(contentUri); } } } } + LogUtils.d(TAG, "found " + imageUrls.size() + " images"); return imageUrls; } @@ -228,7 +241,50 @@ public class MainActivity extends AppCompatActivity { protected void onResume() { super.onResume(); if (checkPermission()) { + scanMediaStore(); loadAlbums(); } } + + private void scanMediaStore() { + String folderPath = prefs.getFolderPath(); + File baseFolder = new File(folderPath); + if (baseFolder.exists() && baseFolder.isDirectory()) { + File[] subfolders = baseFolder.listFiles(new FileFilter() { + @Override + public boolean accept(File file) { + return file.isDirectory(); + } + }); + if (subfolders != null) { + ArrayList paths = new ArrayList<>(); + for (File subfolder : subfolders) { + File[] images = subfolder.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + String lower = name.toLowerCase(); + return lower.endsWith(".jpg") || lower.endsWith(".jpeg") + || lower.endsWith(".png") || lower.endsWith(".gif") + || lower.endsWith(".webp") || lower.endsWith(".bmp"); + } + }); + if (images != null) { + for (File img : images) { + paths.add(img.getAbsolutePath()); + } + } + } + if (!paths.isEmpty()) { + LogUtils.d(TAG, "scanning " + paths.size() + " files to MediaStore"); + String[] pathArray = paths.toArray(new String[0]); + MediaScannerConnection.scanFile(this, pathArray, null, new MediaScannerConnection.OnScanCompletedListener() { + @Override + public void onScanCompleted(String path, Uri uri) { + LogUtils.d(TAG, "scanCompleted: " + path + " -> " + uri); + } + }); + } + } + } + } } diff --git a/gallery/src/main/java/cc/winboll/studio/gallery/TrashManager.java b/gallery/src/main/java/cc/winboll/studio/gallery/TrashManager.java index 2c8bc35..92a527b 100644 --- a/gallery/src/main/java/cc/winboll/studio/gallery/TrashManager.java +++ b/gallery/src/main/java/cc/winboll/studio/gallery/TrashManager.java @@ -1,8 +1,11 @@ package cc.winboll.studio.gallery; +import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; +import android.media.MediaScannerConnection; import android.net.Uri; +import android.os.Build; import android.provider.MediaStore; import java.io.File; import java.util.UUID; @@ -50,25 +53,58 @@ public class TrashManager { public boolean restore(long id, String fileName, String originalPath) { LogUtils.i(TAG, "restore: " + fileName + " -> " + originalPath); File trashFile = new File(TrashDbHelper.getTrashPath(), fileName); + LogUtils.d(TAG, "trashFile exists: " + trashFile.exists() + ", path: " + trashFile.getAbsolutePath()); if (!trashFile.exists()) { + LogUtils.e(TAG, "trashFile not exists: " + trashFile.getAbsolutePath()); return false; } File originalFolder = new File(originalPath).getParentFile(); + LogUtils.d(TAG, "originalFolder: " + originalFolder + ", exists: " + (originalFolder != null && originalFolder.exists())); if (originalFolder != null && !originalFolder.exists()) { - originalFolder.mkdirs(); + boolean created = originalFolder.mkdirs(); + LogUtils.d(TAG, "mkdirs result: " + created + ", path: " + originalFolder.getAbsolutePath()); } File originalFile = new File(originalPath); String restoreName = originalFile.getName(); File restoreFile = new File(originalFolder, restoreName); + LogUtils.d(TAG, "restoreFile: " + restoreFile.getAbsolutePath() + ", exists: " + restoreFile.exists()); - if (trashFile.renameTo(restoreFile)) { + boolean renameResult = trashFile.renameTo(restoreFile); + LogUtils.d(TAG, "renameTo result: " + renameResult); + + if (renameResult) { dbHelper.delete(id); LogUtils.i(TAG, "Restored: " + fileName); + scanMedia(restoreFile.getAbsolutePath()); return true; } + // Try copy + delete if rename failed + LogUtils.i(TAG, "renameTo failed, trying copy + delete"); + try { + java.io.InputStream in = new java.io.FileInputStream(trashFile); + java.io.OutputStream out = new java.io.FileOutputStream(restoreFile); + byte[] buffer = new byte[4096]; + int len; + while ((len = in.read(buffer)) > 0) { + out.write(buffer, 0, len); + } + in.close(); + out.close(); + boolean deleted = trashFile.delete(); + LogUtils.d(TAG, "copy+delete result: " + deleted); + if (deleted) { + dbHelper.delete(id); + LogUtils.i(TAG, "Restored (copy): " + fileName); + scanMedia(restoreFile.getAbsolutePath()); + return true; + } + } catch (Exception e) { + LogUtils.e(TAG, "copy failed: " + e.getMessage()); + } + LogUtils.e(TAG, "Failed to restore: " + fileName); return false; } @@ -109,4 +145,14 @@ public class TrashManager { } return ".jpg"; } + + private void scanMedia(String filePath) { + LogUtils.d(TAG, "scanMedia: " + filePath); + MediaScannerConnection.scanFile(context, new String[]{filePath}, null, new android.media.MediaScannerConnection.OnScanCompletedListener() { + @Override + public void onScanCompleted(String path, Uri uri) { + LogUtils.d(TAG, "scanCompleted: " + path + " -> " + uri); + } + }); + } } \ No newline at end of file diff --git a/gradlew b/gradlew old mode 100644 new mode 100755