mirror of
https://gitea.winboll.cc/ZhanGSKen/MyWinBoLL.git
synced 2026-08-14 21:47:11 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c348db4f7 | |||
| a9f3c9fa81 | |||
| ce01ecf138 | |||
| 9bc9cf3700 | |||
| e97c5fe050 | |||
| 14ed5d60c5 | |||
| dbd10b88d9 | |||
| a4acdce1de |
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Sat Jul 18 15:42:59 HKT 2026
|
||||
stageCount=17
|
||||
#Mon Jul 20 19:20:48 HKT 2026
|
||||
stageCount=19
|
||||
libraryProject=
|
||||
baseVersion=15.0
|
||||
publishVersion=15.0.16
|
||||
publishVersion=15.0.18
|
||||
buildCount=0
|
||||
baseBetaVersion=15.0.17
|
||||
baseBetaVersion=15.0.19
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/GalleryTheme"
|
||||
android:resizeableActivity="true"
|
||||
android:name=".GlobalWinBoLLApplication"
|
||||
android:name=".App"
|
||||
android:requestLegacyExternalStorage="true">
|
||||
|
||||
<activity
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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;
|
||||
@@ -14,8 +13,9 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.bumptech.glide.Glide;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
@@ -62,28 +62,11 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
|
||||
}
|
||||
|
||||
public void setData(ArrayList<Album> albums) {
|
||||
this.albums = sortAlbums(albums);
|
||||
this.albums = albums;
|
||||
LogUtils.d(TAG, "setData: " + albums.size() + " albums");
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private ArrayList<Album> sortAlbums(ArrayList<Album> list) {
|
||||
if (pinnedDbHelper == null || list == null || list.isEmpty()) {
|
||||
return list;
|
||||
}
|
||||
ArrayList<Album> pinned = new ArrayList<>();
|
||||
ArrayList<Album> unpinned = new ArrayList<>();
|
||||
for (Album album : list) {
|
||||
if (pinnedDbHelper.isPinned(album.getPath())) {
|
||||
pinned.add(album);
|
||||
} else {
|
||||
unpinned.add(album);
|
||||
}
|
||||
}
|
||||
pinned.addAll(unpinned);
|
||||
return pinned;
|
||||
}
|
||||
|
||||
public void setContext(android.content.Context context) {
|
||||
prefs = new Preferences(context);
|
||||
bgType = prefs.getBgType();
|
||||
@@ -103,23 +86,28 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
|
||||
|
||||
public void refreshPinned() {
|
||||
if (pinnedDbHelper != null && albums != null && !albums.isEmpty()) {
|
||||
ArrayList<Album> pinned = new ArrayList<>();
|
||||
ArrayList<Album> unpinned = new ArrayList<>();
|
||||
java.util.LinkedHashMap<String, Integer> pinnedPathMap = pinnedDbHelper.getPinnedPathsWithSortOrder();
|
||||
Set<String> pinnedPaths = pinnedPathMap.keySet();
|
||||
ArrayList<Album> pinned = new ArrayList<Album>();
|
||||
ArrayList<Album> unpinned = new ArrayList<Album>();
|
||||
|
||||
// 按照排序号顺序添加置顶相册
|
||||
for (String pinnedPath : pinnedPathMap.keySet()) {
|
||||
for (Album album : albums) {
|
||||
if (album.getPath().equals(pinnedPath)) {
|
||||
pinned.add(album);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加非置顶相册
|
||||
for (Album album : albums) {
|
||||
if (pinnedDbHelper.isPinned(album.getPath())) {
|
||||
pinned.add(album);
|
||||
} else {
|
||||
if (!pinnedPaths.contains(album.getPath())) {
|
||||
unpinned.add(album);
|
||||
}
|
||||
}
|
||||
Comparator<Album> nameComparator = new Comparator<Album>() {
|
||||
@Override
|
||||
public int compare(Album a1, Album a2) {
|
||||
return a1.getName().compareToIgnoreCase(a2.getName());
|
||||
}
|
||||
};
|
||||
Collections.sort(pinned, nameComparator);
|
||||
Collections.sort(unpinned, nameComparator);
|
||||
|
||||
albums.clear();
|
||||
albums.addAll(pinned);
|
||||
albums.addAll(unpinned);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package cc.winboll.studio.gallery;
|
||||
|
||||
import cc.winboll.studio.gallery.utils.BackgroundUtils;
|
||||
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||
import cc.winboll.studio.libappbase.CrashActivity;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.libappbase.utils.CrashHandleNotifyUtils;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/07/19
|
||||
* @Describe 应用全局入口类(继承 GlobalWinBoLLApplication)
|
||||
* 负责应用初始化、全局资源管理与生命周期回调处理,是整个应用的核心入口
|
||||
*/
|
||||
public class App extends GlobalApplication {
|
||||
|
||||
public static final String TAG = "App";
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
try {
|
||||
super.onCreate();
|
||||
|
||||
WinBoLLActivityManager.init(this);
|
||||
|
||||
BackgroundUtils.initFromPreferences(this);
|
||||
|
||||
ToastUtils.init(getApplicationContext());
|
||||
} catch (Throwable e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
pw.close();
|
||||
String stackTraceStr = sw.toString();
|
||||
CrashHandleNotifyUtils.handleUncaughtException(
|
||||
this,
|
||||
getPackageName(),
|
||||
stackTraceStr,
|
||||
CrashActivity.class
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
super.onTerminate();
|
||||
ToastUtils.release();
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package cc.winboll.studio.gallery;
|
||||
|
||||
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.gallery.utils.BackgroundUtils;
|
||||
|
||||
/**
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/04/24 15:23
|
||||
*/
|
||||
public class GlobalWinBoLLApplication extends GlobalApplication {
|
||||
|
||||
public static final String TAG = "GlobalWinBoLLApplication";
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
LogUtils.d(TAG, "onCreate");
|
||||
setIsDebugging(BuildConfig.DEBUG);
|
||||
//setIsDebugging(false);
|
||||
|
||||
WinBoLLActivityManager.init(this);
|
||||
|
||||
BackgroundUtils.initFromPreferences(this);
|
||||
|
||||
// 初始化 Toast 框架
|
||||
ToastUtils.init(this);
|
||||
// 设置 Toast 布局样式
|
||||
//ToastUtils.setView(R.layout.view_toast);
|
||||
//ToastUtils.setStyle(new WhiteToastStyle());
|
||||
//ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
|
||||
|
||||
//CrashHandler.getInstance().registerGlobal(this);
|
||||
//CrashHandler.getInstance().registerPart(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
super.onTerminate();
|
||||
LogUtils.d(TAG, "onTerminate");
|
||||
ToastUtils.release();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.Settings;
|
||||
import android.view.Menu;
|
||||
@@ -27,15 +29,22 @@ import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import cc.winboll.studio.gallery.AlbumAdapter.OnAlbumClickListener;
|
||||
import cc.winboll.studio.gallery.App;
|
||||
import cc.winboll.studio.gallery.utils.BackgroundUtils;
|
||||
import cc.winboll.studio.libappbase.LogActivity;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import com.a4455jkjh.colorpicker.ColorPickerDialog;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
public static final String TAG = "MainActivity";
|
||||
@@ -45,6 +54,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
private AlbumAdapter adapter;
|
||||
private Preferences prefs;
|
||||
private FloatingActionButton fabScrollTop;
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -70,37 +81,37 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
fabScrollTop = findViewById(R.id.fab_scroll_top);
|
||||
fabScrollTop.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
recyclerView.scrollToPosition(0);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
recyclerView.scrollToPosition(0);
|
||||
}
|
||||
});
|
||||
|
||||
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
GridLayoutManager layoutManager = (GridLayoutManager) recyclerView.getLayoutManager();
|
||||
if (layoutManager != null) {
|
||||
int firstVisible = layoutManager.findFirstVisibleItemPosition();
|
||||
if (firstVisible > 0) {
|
||||
fabScrollTop.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
fabScrollTop.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
GridLayoutManager layoutManager = (GridLayoutManager) recyclerView.getLayoutManager();
|
||||
if (layoutManager != null) {
|
||||
int firstVisible = layoutManager.findFirstVisibleItemPosition();
|
||||
if (firstVisible > 0) {
|
||||
fabScrollTop.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
fabScrollTop.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
adapter.setOnAlbumClickListener(new OnAlbumClickListener() {
|
||||
@Override
|
||||
public void onAlbumClick(Album album) {
|
||||
Intent intent = new Intent(MainActivity.this, AlbumActivity.class);
|
||||
intent.putExtra(AlbumActivity.EXTRA_ALBUM_PATH, album.getPath());
|
||||
intent.putExtra(AlbumActivity.EXTRA_ALBUM_NAME, album.getName());
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onAlbumClick(Album album) {
|
||||
Intent intent = new Intent(MainActivity.this, AlbumActivity.class);
|
||||
intent.putExtra(AlbumActivity.EXTRA_ALBUM_PATH, album.getPath());
|
||||
intent.putExtra(AlbumActivity.EXTRA_ALBUM_NAME, album.getName());
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
checkAndRequestPermissions();
|
||||
}
|
||||
@@ -152,13 +163,13 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private void requestPermission() {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
|
||||
PERMISSION_REQUEST_CODE);
|
||||
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
|
||||
PERMISSION_REQUEST_CODE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
@NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == PERMISSION_REQUEST_CODE) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
@@ -169,96 +180,125 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAlbums() {
|
||||
LogUtils.d(TAG, "loadAlbums");
|
||||
private void loadAlbums() {
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final ArrayList<Album> albums = loadAlbumsInBackground();
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (albums.isEmpty()) {
|
||||
Toast.makeText(MainActivity.this, R.string.no_images_found, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
adapter.setData(albums);
|
||||
LogUtils.d(TAG, "Loaded " + albums.size() + " albums");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ArrayList<Album> loadAlbumsInBackground() {
|
||||
LogUtils.d(TAG, "loadAlbumsInBackground");
|
||||
String folderPath = prefs.getFolderPath();
|
||||
File baseFolder = new File(folderPath);
|
||||
LogUtils.d(TAG, "baseFolder: " + baseFolder.getAbsolutePath() + ", exists=" + baseFolder.exists());
|
||||
|
||||
if (!baseFolder.exists() || !baseFolder.isDirectory()) {
|
||||
folderPath = Preferences.getDefaultPath();
|
||||
baseFolder = new File(folderPath);
|
||||
LogUtils.d(TAG, "try default: " + 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());
|
||||
}
|
||||
}
|
||||
|
||||
AlbumCoverDbHelper coverDbHelper = AlbumCoverDbHelper.getInstance(this);
|
||||
ArrayList<Album> albums = new ArrayList<>();
|
||||
PinnedAlbumDbHelper pinnedDbHelper = PinnedAlbumDbHelper.getInstance(this);
|
||||
|
||||
FileFilter directoryFilter = new FileFilter() {
|
||||
// 获取带排序号的置顶路径映射(已按排序号排序)
|
||||
java.util.LinkedHashMap<String, Integer> pinnedPathMap = pinnedDbHelper.getPinnedPathsWithSortOrder();
|
||||
Set<String> pinnedPaths = pinnedPathMap.keySet();
|
||||
|
||||
ArrayList<Album> albums = new ArrayList<Album>();
|
||||
|
||||
File[] subfolders = baseFolder.listFiles(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
return file.isDirectory();
|
||||
}
|
||||
};
|
||||
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());
|
||||
String albumPath = subfolder.getAbsolutePath();
|
||||
String coverPath = coverDbHelper.getCover(albumPath);
|
||||
LogUtils.d(TAG, "loadAlbums: album=" + albumPath + ", coverPath=" + coverPath);
|
||||
Uri coverUri = null;
|
||||
if (coverPath != null) {
|
||||
File coverFile = new File(coverPath);
|
||||
if (coverFile.exists()) {
|
||||
coverUri = Uri.fromFile(coverFile);
|
||||
LogUtils.d(TAG, "loadAlbums: cover from file=" + coverFile.getAbsolutePath());
|
||||
} else {
|
||||
coverUri = getUriFromPath(coverPath);
|
||||
LogUtils.d(TAG, "loadAlbums: cover from media store path=" + coverPath);
|
||||
}
|
||||
}
|
||||
if (coverUri == null) {
|
||||
ArrayList<Uri> images = getImagesInFolder(albumPath);
|
||||
if (!images.isEmpty()) {
|
||||
coverUri = images.get(0);
|
||||
}
|
||||
}
|
||||
ArrayList<Uri> allImages = getImagesInFolder(albumPath);
|
||||
if (coverUri == null && !allImages.isEmpty()) {
|
||||
coverUri = allImages.get(0);
|
||||
}
|
||||
if (coverUri != null || !allImages.isEmpty()) {
|
||||
if (coverUri == null && !allImages.isEmpty()) {
|
||||
coverUri = allImages.get(0);
|
||||
}
|
||||
int imageCount = allImages.size();
|
||||
albums.add(new Album(subfolder.getName(), albumPath, coverUri, imageCount));
|
||||
LogUtils.d(TAG, "album added: " + subfolder.getName() + ", " + imageCount + " images");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (albums.isEmpty()) {
|
||||
Toast.makeText(this, R.string.no_images_found, Toast.LENGTH_SHORT).show();
|
||||
LogUtils.i(TAG, "No albums found");
|
||||
// 按照排序号顺序添加置顶相册
|
||||
ArrayList<Album> pinned = new ArrayList<Album>();
|
||||
ArrayList<Album> unpinned = new ArrayList<Album>();
|
||||
|
||||
// 先按照pinnedPathMap的顺序(排序号顺序)添加置顶相册
|
||||
for (String pinnedPath : pinnedPathMap.keySet()) {
|
||||
for (Album album : albums) {
|
||||
if (album.getPath().equals(pinnedPath)) {
|
||||
pinned.add(album);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter.setData(albums);
|
||||
LogUtils.d(TAG, "Loaded " + albums.size() + " albums");
|
||||
|
||||
// 添加非置顶相册
|
||||
for (Album album : albums) {
|
||||
if (!pinnedPaths.contains(album.getPath())) {
|
||||
unpinned.add(album);
|
||||
}
|
||||
}
|
||||
|
||||
pinned.addAll(unpinned);
|
||||
return pinned;
|
||||
}
|
||||
|
||||
private Uri getUriFromPath(String path) {
|
||||
String[] projection = { MediaStore.Images.Media._ID };
|
||||
String selection = MediaStore.Images.Media.DATA + " = ?";
|
||||
String[] selectionArgs = { path };
|
||||
try (Cursor cursor = getContentResolver().query(
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
projection, selection, selectionArgs, null)) {
|
||||
if (cursor != null) {
|
||||
Cursor cursor = getContentResolver().query(
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
projection, selection, selectionArgs, null);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
|
||||
return Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, String.valueOf(id));
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ArrayList<Uri> getImagesInFolder(String folderPath) {
|
||||
ArrayList<Uri> imageUrls = new ArrayList<>();
|
||||
ArrayList<Uri> imageUrls = new ArrayList<Uri>();
|
||||
ContentResolver contentResolver = getContentResolver();
|
||||
Uri collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
|
||||
@@ -266,30 +306,32 @@ 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());
|
||||
Cursor cursor = contentResolver.query(collection, null, selection, selectionArgs, sortOrder);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
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);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
LogUtils.d(TAG, "found " + imageUrls.size() + " images");
|
||||
return imageUrls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.menu_main, menu);
|
||||
MenuItem debugItem = menu.findItem(R.id.action_debug);
|
||||
if (debugItem != null) {
|
||||
debugItem.setVisible(App.isDebugging());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -303,31 +345,26 @@ public class MainActivity extends AppCompatActivity {
|
||||
startActivity(intent);
|
||||
return true;
|
||||
} else if (id == R.id.action_change_bg_color) {
|
||||
//Toast.makeText(this, "修改背景颜色", Toast.LENGTH_SHORT).show();
|
||||
if (BackgroundUtils.DrawableType.COLOR == BackgroundUtils.getInstance().getDrawableType()) {
|
||||
|
||||
ColorPickerDialog dlg = new ColorPickerDialog(this, BackgroundUtils.getInstance().getColor());
|
||||
dlg.setOnColorChangedListener(new com.a4455jkjh.colorpicker.view.OnColorChangedListener() {
|
||||
|
||||
@Override
|
||||
public void beforeColorChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onColorChanged(int color) {
|
||||
BackgroundUtils.getInstance().initFromColor(MainActivity.this, color);
|
||||
View content = findViewById(android.R.id.content);
|
||||
if (content != null) {
|
||||
content.setBackground(BackgroundUtils.getInstance().getDrawable());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterColorChanged() {
|
||||
}
|
||||
});
|
||||
dlg.show();
|
||||
}
|
||||
if (BackgroundUtils.DrawableType.COLOR == BackgroundUtils.getInstance().getDrawableType()) {
|
||||
ColorPickerDialog dlg = new ColorPickerDialog(this, BackgroundUtils.getInstance().getColor());
|
||||
dlg.setOnColorChangedListener(new com.a4455jkjh.colorpicker.view.OnColorChangedListener() {
|
||||
@Override
|
||||
public void beforeColorChanged() {
|
||||
}
|
||||
@Override
|
||||
public void onColorChanged(int color) {
|
||||
BackgroundUtils.getInstance().initFromColor(MainActivity.this, color);
|
||||
View content = findViewById(android.R.id.content);
|
||||
if (content != null) {
|
||||
content.setBackground(BackgroundUtils.getInstance().getDrawable());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void afterColorChanged() {
|
||||
}
|
||||
});
|
||||
dlg.show();
|
||||
}
|
||||
return true;
|
||||
} else if (id == R.id.action_settings) {
|
||||
startActivity(new Intent(this, SettingsActivity.class));
|
||||
@@ -359,7 +396,6 @@ public class MainActivity extends AppCompatActivity {
|
||||
super.onResume();
|
||||
registerReceiver(coverUpdatedReceiver, new IntentFilter(Preferences.ACTION_COVER_UPDATED));
|
||||
if (checkPermission()) {
|
||||
scanMediaStore();
|
||||
loadAlbums();
|
||||
}
|
||||
if (adapter != null) {
|
||||
@@ -375,45 +411,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
unregisterReceiver(coverUpdatedReceiver);
|
||||
}
|
||||
|
||||
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<String> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,19 @@ import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
public class PinnedAlbumDbHelper extends SQLiteOpenHelper {
|
||||
public static final String TAG = "PinnedAlbumDbHelper";
|
||||
private static final String DB_NAME = "pinned_album.db";
|
||||
private static final int DB_VERSION = 1;
|
||||
private static final int DB_VERSION = 2;
|
||||
private static final String TABLE_NAME = "pinned_albums";
|
||||
private static final String COLUMN_PATH = "album_path";
|
||||
private static final String COLUMN_SORT_ORDER = "sort_order";
|
||||
|
||||
private static final String SQL_CREATE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
+ COLUMN_PATH + " TEXT PRIMARY KEY)";
|
||||
+ COLUMN_PATH + " TEXT PRIMARY KEY, "
|
||||
+ COLUMN_SORT_ORDER + " INTEGER DEFAULT 0)";
|
||||
|
||||
private static PinnedAlbumDbHelper dbHelper;
|
||||
|
||||
@@ -37,22 +40,87 @@ public class PinnedAlbumDbHelper 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) {
|
||||
// 创建新表
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME + "_new");
|
||||
db.execSQL("CREATE TABLE " + TABLE_NAME + "_new ("
|
||||
+ COLUMN_PATH + " TEXT PRIMARY KEY, "
|
||||
+ COLUMN_SORT_ORDER + " INTEGER DEFAULT 0)");
|
||||
|
||||
// 迁移旧数据,为现有记录分配排序号(按原始顺序)
|
||||
Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_PATH}, null, null, null, null, null);
|
||||
int sortOrder = 1;
|
||||
while (cursor.moveToNext()) {
|
||||
String path = cursor.getString(0);
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(COLUMN_PATH, path);
|
||||
values.put(COLUMN_SORT_ORDER, sortOrder++);
|
||||
db.insert(TABLE_NAME + "_new", null, values);
|
||||
}
|
||||
cursor.close();
|
||||
|
||||
// 删除旧表,重命名新表
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
|
||||
db.execSQL("ALTER TABLE " + TABLE_NAME + "_new RENAME TO " + TABLE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
public void pinAlbum(String albumPath) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(COLUMN_PATH, albumPath);
|
||||
db.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_IGNORE);
|
||||
LogUtils.d(TAG, "pinAlbum: " + albumPath);
|
||||
db.beginTransaction();
|
||||
try {
|
||||
// 将所有现有置顶数据的排序号加1
|
||||
db.execSQL("UPDATE " + TABLE_NAME + " SET " + COLUMN_SORT_ORDER + " = " + COLUMN_SORT_ORDER + " + 1");
|
||||
|
||||
// 插入新数据,排序号为1
|
||||
ContentValues insertValues = new ContentValues();
|
||||
insertValues.put(COLUMN_PATH, albumPath);
|
||||
insertValues.put(COLUMN_SORT_ORDER, 1);
|
||||
db.insertWithOnConflict(TABLE_NAME, null, insertValues, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
|
||||
// 查询所有置顶相册的排序号并输出调试信息
|
||||
Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_PATH, COLUMN_SORT_ORDER},
|
||||
null, null, null, null, COLUMN_SORT_ORDER + " ASC");
|
||||
StringBuilder sb = new StringBuilder("All pinned albums after pin:\n");
|
||||
while (cursor.moveToNext()) {
|
||||
String path = cursor.getString(0);
|
||||
int sortOrder = cursor.getInt(1);
|
||||
sb.append(" [").append(sortOrder).append("] ").append(path).append("\n");
|
||||
}
|
||||
cursor.close();
|
||||
LogUtils.d(TAG, sb.toString());
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
public void unpinAlbum(String albumPath) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
db.delete(TABLE_NAME, COLUMN_PATH + " = ?", new String[]{albumPath});
|
||||
LogUtils.d(TAG, "unpinAlbum: " + albumPath);
|
||||
db.beginTransaction();
|
||||
try {
|
||||
// 获取要取消置顶的相册的排序号
|
||||
Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_SORT_ORDER},
|
||||
COLUMN_PATH + " = ?", new String[]{albumPath}, null, null, null);
|
||||
if (cursor.moveToFirst()) {
|
||||
int sortNumber = cursor.getInt(0);
|
||||
cursor.close();
|
||||
|
||||
// 将排序号大于该排序号的记录减1
|
||||
db.execSQL("UPDATE " + TABLE_NAME + " SET " + COLUMN_SORT_ORDER + " = " + COLUMN_SORT_ORDER + " - 1 WHERE " + COLUMN_SORT_ORDER + " > ?", new Object[]{sortNumber});
|
||||
|
||||
// 删除该相册的置顶记录
|
||||
db.delete(TABLE_NAME, COLUMN_PATH + " = ?", new String[]{albumPath});
|
||||
|
||||
LogUtils.d(TAG, "unpinAlbum: " + albumPath + ", sort number was: " + sortNumber);
|
||||
} else {
|
||||
cursor.close();
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPinned(String albumPath) {
|
||||
@@ -66,7 +134,8 @@ public class PinnedAlbumDbHelper extends SQLiteOpenHelper {
|
||||
|
||||
public String[] getPinnedPaths() {
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_PATH}, null, null, null, null, null);
|
||||
Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_PATH}, null, null, null, null,
|
||||
COLUMN_SORT_ORDER + " ASC");
|
||||
String[] paths = new String[cursor.getCount()];
|
||||
int i = 0;
|
||||
while (cursor.moveToNext()) {
|
||||
@@ -75,4 +144,18 @@ public class PinnedAlbumDbHelper extends SQLiteOpenHelper {
|
||||
cursor.close();
|
||||
return paths;
|
||||
}
|
||||
|
||||
public LinkedHashMap<String, Integer> getPinnedPathsWithSortOrder() {
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_PATH, COLUMN_SORT_ORDER},
|
||||
null, null, null, null, COLUMN_SORT_ORDER + " ASC");
|
||||
LinkedHashMap<String, Integer> map = new LinkedHashMap<String, Integer>();
|
||||
while (cursor.moveToNext()) {
|
||||
String path = cursor.getString(0);
|
||||
int sortOrder = cursor.getInt(1);
|
||||
map.put(path, sortOrder);
|
||||
}
|
||||
cursor.close();
|
||||
return map;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user