Compare commits

...

21 Commits

Author SHA1 Message Date
ZhanGSKen 26301c68fa <mygallery>APK 15.0.20 release Publish. 2026-08-23 08:47:32 +08:00
BigPickle 9f0d8fbffb 已置顶相册长按菜单新增“置顶”功能,支持再次置顶到最前
PinnedAlbumDbHelper 新增 repinAlbumToFront():将当前相册集
排序号置为1,其余置顶记录整体后移一位;并新增
normalizeSortOrders() 按现有顺序把所有排序号重排为连续的
1..n,修正数据库中可能出现的序号空洞或重复。
AlbumAdapter 中已置顶相册的弹出菜单扩展为「取消置顶」「置顶」
两项,点击「置顶」即重新置顶到最前并刷新列表。
2026-08-23 08:44:07 +08:00
ZhanGSKen c270ef9d1f 更新源码维护人员联系信息。 2026-08-23 08:27:02 +08:00
ZhanGSKen 3b92af52f6 <mygallery>APK 15.0.19 release Publish. 2026-08-23 08:17:44 +08:00
BigPickle 6fb6fac2ba 修复封面剪裁窗口空白的包名残留问题
模块更名为 mygallery 后,activity_crop.xml 中自定义视图
仍引用旧包 cc.winboll.studio.gallery,导致 CropActivity
布局膨胀失败、剪裁窗口显示空白。现统一更正为
cc.winboll.studio.mygallery 包名引用,并同步更新
ACTION_COVER_UPDATED 广播 action 常量。
2026-08-23 08:10:38 +08:00
ZhanGSKen 04cd87842a 更新基础类库引用 2026-08-23 07:49:39 +08:00
ZhanGSKen 1b86e70069 更新应用介绍和资源链接 2026-08-23 07:40:42 +08:00
ZhanGSKen 13f1aa9e1f 更新应用默认显示名称 2026-08-23 07:22:25 +08:00
ZhanGSKen 675fa60670 设置应用图标配置规则,区分beta图标stage图标。 2026-08-23 07:19:10 +08:00
ZhanGSKen 0383f7c72d 更新java源码应用模块文件夹名称gallery为mygallery。 2026-08-23 07:03:06 +08:00
ZhanGSKen 800ba03d0f 更新应用配置模块名称为mygallery 2026-08-23 06:57:53 +08:00
ZhanGSKen 6aa19195f6 更换应用模块gallery为mygallery 2026-08-23 06:53:40 +08:00
ZhanGSKen 3c84c8205a 添加新应用图标资源 2026-08-23 06:49:53 +08:00
ZhanGSKen 0c348db4f7 <gallery>APK 15.0.18 release Publish. 2026-07-20 19:20:48 +08:00
MiMo a9f3c9fa81 feat(gallery): 实现置顶相册排序功能
1. 数据库升级
   - PinnedAlbumDbHelper 版本升级至 2
   - 新增 sort_order 字段存储排序号
   - 自动迁移旧数据并分配排序号

2. 置顶排序逻辑
   - 新置顶相册排序号设为1,其他相册排序号+1
   - 取消置顶时,后续相册排序号自动-1
   - 使用事务确保数据一致性

3. 列表加载与刷新
   - 加载相册时按排序号由小到大排列
   - 刷新列表时保持排序号顺序显示
   - 使用 LinkedHashMap 保持插入顺序

4. 调试日志
   - 置顶操作后输出所有相册排序号
   - 取消置顶时输出原排序号

修改文件:
- PinnedAlbumDbHelper.java
- MainActivity.java
- AlbumAdapter.java
- build.properties
2026-07-20 19:15:59 +08:00
ZhanGSKen ce01ecf138 <gallery>APK 15.0.17 release Publish. 2026-07-19 15:10:50 +08:00
MiMo 9bc9cf3700 Gallery: 主窗口加载性能优化
- loadAlbums移至后台线程(ExecutorService+Handler),主线程不再阻塞
- 消除重复getImagesInFolder调用,MediaStore查询减少50%
- onResume移除scanMediaStore避免重复磁盘扫描
- pinned状态批量加载到Set,SQLite查询从3N次降为1次
- AlbumAdapter移除冗余sortAlbums,直接接收已排序数据
- Cursor改用try-finally确保关闭,移除冗余日志输出
2026-07-19 15:02:22 +08:00
ZhanGSKen e97c5fe050 转换旧有的应用基础类为libappbase的GlobalApplication基础类。 2026-07-19 14:13:22 +08:00
MiMo 14ed5d60c5 更正编译参数为最后发布的基准参数。 2026-07-19 13:54:17 +08:00
MiMo dbd10b88d9 合并手机端开发的分支文件,修复编译参数不同步问题。 2026-07-19 13:51:55 +08:00
MiMo a4acdce1de Gallery: 添加App入口类,调试菜单动态显隐
- 新增App类继承GlobalWinBoLLApplication,参照appbase添加崩溃处理
- Manifest application节点android:name切换为App类
- MainActivity菜单栏Debug Log项根据GlobalWinBoLLApplication.isDebugging()控制可见性
2026-07-19 13:46:50 +08:00
107 changed files with 540 additions and 378 deletions
@@ -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();
}
}
@@ -1,78 +0,0 @@
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 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 String TABLE_NAME = "pinned_albums";
private static final String COLUMN_PATH = "album_path";
private static final String SQL_CREATE = "CREATE TABLE " + TABLE_NAME + " ("
+ COLUMN_PATH + " TEXT PRIMARY KEY)";
private static PinnedAlbumDbHelper dbHelper;
public static PinnedAlbumDbHelper getInstance(Context context) {
if (dbHelper == null) {
dbHelper = new PinnedAlbumDbHelper(context.getApplicationContext());
}
return dbHelper;
}
public PinnedAlbumDbHelper(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 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);
}
public void unpinAlbum(String albumPath) {
SQLiteDatabase db = getWritableDatabase();
db.delete(TABLE_NAME, COLUMN_PATH + " = ?", new String[]{albumPath});
LogUtils.d(TAG, "unpinAlbum: " + albumPath);
}
public boolean isPinned(String albumPath) {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, null, COLUMN_PATH + " = ?",
new String[]{albumPath}, 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;
}
}
+3 -3
View File
@@ -1,7 +1,7 @@
# Gallery
# MyGallery
#### 介绍
云宝相册应用
我的相册应用
#### 软件架构
适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
@@ -18,7 +18,7 @@
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码 : ZhanGSKen(ZhanGSKen<zhangsken@188.com>)
3. 提交代码 : ZhanGSKen(ZhanGSKen<ZhanGSKen@QQ.COM>)
4. 新建 Pull Request
@@ -23,7 +23,7 @@ android {
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "cc.winboll.studio.gallery"
applicationId "cc.winboll.studio.mygallery"
minSdkVersion 26
// MIUI12
targetSdkVersion 30
@@ -108,12 +108,12 @@ dependencies {
implementation 'com.termux:termux-shared:0.118.0'
*/
// WinBoLL库 nexus.winboll.cc
api 'cc.winboll.studio:libaes:15.20.21'
api 'cc.winboll.studio:libappbase:15.20.34'
//api 'cc.winboll.studio:libaes:15.20.21'
//api 'cc.winboll.studio:libappbase:15.20.34'
// WinBoLL备用库 jitpack.io
//api 'com.github.ZhanGSKen:AES:aes-v15.15.7'
//api 'com.github.ZhanGSKen:APPBase:appbase-v15.15.4'
// jitpack.io
api 'com.github.ZhanGSKen:libappbase:appbase-v15.21.+'
api 'com.github.ZhanGSKen:libaes:aes-v15.21.+'
api fileTree(dir: 'libs', include: ['*.jar'])
}
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Sat Jul 18 15:42:59 HKT 2026
stageCount=17
#Sun Aug 23 08:47:32 HKT 2026
stageCount=21
libraryProject=
baseVersion=15.0
publishVersion=15.0.16
publishVersion=15.0.20
buildCount=0
baseBetaVersion=15.0.17
baseBetaVersion=15.0.21
@@ -2,7 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<application tools:replace="android:icon"
android:icon="@drawable/ic_my_gallery_beta">
<!-- Put flavor specific code here -->
@@ -2,5 +2,5 @@
<resources>
<!-- Put flavor specific strings here -->
<string name="app_name">Gallery+</string>
<string name="app_name">MyGallery+</string>
</resources>
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.studio.gallery">
package="cc.winboll.studio.mygallery">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
@@ -9,12 +9,12 @@
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:icon="@drawable/ic_my_gallery"
android:roundIcon="@drawable/ic_my_gallery"
android:label="@string/app_name"
android:theme="@style/GalleryTheme"
android:resizeableActivity="true"
android:name=".GlobalWinBoLLApplication"
android:name=".App"
android:requestLegacyExternalStorage="true">
<activity
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.os.Bundle;
import android.view.View;
@@ -33,18 +33,18 @@ public class AboutActivity extends AppCompatActivity {
private APPInfo genDefaultAppInfo() {
LogUtils.d(TAG, "genDefaultAppInfo() 调用");
String branchName = "gallery";
String branchName = "mygallery";
APPInfo appInfo = new APPInfo();
appInfo.setAppName("Gallery");
appInfo.setAppName("MyGallery");
appInfo.setAppIcon(R.drawable.ic_winboll);
appInfo.setAppDescription(getString(R.string.app_description));
appInfo.setAppGitName("Gallery");
appInfo.setAppGitName("MyGallery");
appInfo.setAppGitOwner("ZhanGSKen");
appInfo.setAppGitAPPBranch(branchName);
appInfo.setAppGitAPPSubProjectFolder(branchName);
appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=Gallery");
appInfo.setAppAPKName("Gallery");
appInfo.setAppAPKFolderName("Gallery");
appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=MyGallery");
appInfo.setAppAPKName("MyGallery");
appInfo.setAppAPKFolderName("MyGallery");
LogUtils.d(TAG, "genDefaultAppInfo: 应用信息已生成");
return appInfo;
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.net.Uri;
import cc.winboll.studio.libappbase.LogUtils;
@@ -22,4 +22,4 @@ public class Album {
public String getPath() { return path; }
public Uri getCoverUri() { return coverUri; }
public int getImageCount() { return imageCount; }
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.content.ContentResolver;
import android.content.Intent;
@@ -21,7 +21,7 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.gallery.ImageAdapter.OnImageClickListener;
import cc.winboll.studio.mygallery.ImageAdapter.OnImageClickListener;
public class AlbumActivity extends AppCompatActivity {
public static final String TAG = "AlbumActivity";
@@ -248,4 +248,4 @@ private String getSortOrder(int sortMode) {
loadImages();
}
}
}
}
@@ -1,7 +1,6 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
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);
@@ -131,7 +119,12 @@ private void showContextMenu(View view, final Album album) {
android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(view.getContext());
builder.setTitle(album.getName());
final boolean isPinned = pinnedDbHelper != null && pinnedDbHelper.isPinned(album.getPath());
String[] items = isPinned ? new String[]{"取消置顶"} : new String[]{"置顶"};
String[] items = null;
if (isPinned) {
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) {
@@ -142,8 +135,10 @@ private void showContextMenu(View view, final Album album) {
} else {
pinnedDbHelper.pinAlbum(album.getPath());
}
refreshPinned();
} else if (which == 1 && isPinned) {
pinnedDbHelper.repinAlbumToFront(album.getPath());
}
refreshPinned();
}
}
});
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.content.ContentValues;
import android.content.Context;
@@ -128,4 +128,4 @@ public class AlbumCoverDbHelper extends SQLiteOpenHelper {
db.delete(TABLE_NAME, COLUMN_ALBUM_PATH + " = ?", new String[]{albumPath});
LogUtils.d(TAG, "deleteCover: " + albumPath);
}
}
}
@@ -0,0 +1,52 @@
package cc.winboll.studio.mygallery;
import cc.winboll.studio.mygallery.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,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.content.Context;
import android.graphics.Bitmap;
@@ -747,4 +747,4 @@ public class CropCanvasView extends View {
public int getColorAt(float x, float y) {
return getImageColorAt(x, y);
}
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.content.Context;
import android.graphics.Canvas;
@@ -221,4 +221,4 @@ public class CropOverlayView extends View {
}
return -1;
}
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.content.DialogInterface;
import android.content.Intent;
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.net.Uri;
import android.view.LayoutInflater;
@@ -54,4 +54,4 @@ public class ImagePagerAdapter extends PagerAdapter {
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.app.Activity;
import android.app.AlertDialog;
@@ -394,4 +394,4 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
public void onBackPressed() {
finish();
}
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.Manifest;
import android.content.BroadcastReceiver;
@@ -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;
@@ -26,16 +28,23 @@ import androidx.core.app.ActivityCompat;
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.utils.BackgroundUtils;
import cc.winboll.studio.mygallery.AlbumAdapter.OnAlbumClickListener;
import cc.winboll.studio.mygallery.App;
import cc.winboll.studio.mygallery.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();
}
}
@@ -0,0 +1,218 @@
package cc.winboll.studio.mygallery;
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;
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 = 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_SORT_ORDER + " INTEGER DEFAULT 0)";
private static PinnedAlbumDbHelper dbHelper;
public static PinnedAlbumDbHelper getInstance(Context context) {
if (dbHelper == null) {
dbHelper = new PinnedAlbumDbHelper(context.getApplicationContext());
}
return dbHelper;
}
public PinnedAlbumDbHelper(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) {
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();
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.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 void repinAlbumToFront(String albumPath) {
SQLiteDatabase db = getWritableDatabase();
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();
if (sortNumber > 1) {
// 将排序号小于该记录的记录全部加1(整体后移)
db.execSQL("UPDATE " + TABLE_NAME + " SET " + COLUMN_SORT_ORDER + " = " + COLUMN_SORT_ORDER + " + 1 WHERE " + COLUMN_SORT_ORDER + " < ?", new Object[]{sortNumber});
// 该相册置顶到排序号1
ContentValues values = new ContentValues();
values.put(COLUMN_SORT_ORDER, 1);
db.update(TABLE_NAME, values, COLUMN_PATH + " = ?", new String[]{albumPath});
LogUtils.d(TAG, "repinAlbumToFront: " + albumPath + ", sort number was: " + sortNumber);
}
// 按当前顺序将排序号重排为连续的1..n
normalizeSortOrders(db);
// 查询所有置顶相册的排序号并输出调试信息
Cursor debugCursor = 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 repin:\n");
while (debugCursor.moveToNext()) {
sb.append(" [").append(debugCursor.getInt(1)).append("] ").append(debugCursor.getString(0)).append("\n");
}
debugCursor.close();
LogUtils.d(TAG, sb.toString());
} else {
cursor.close();
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
// 按排序号升序将所有记录重排为连续的1..n,修正可能出现的序号空洞或重复
private void normalizeSortOrders(SQLiteDatabase db) {
Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_PATH}, null, null, null, null,
COLUMN_SORT_ORDER + " ASC");
int sortOrder = 1;
while (cursor.moveToNext()) {
String path = cursor.getString(0);
db.execSQL("UPDATE " + TABLE_NAME + " SET " + COLUMN_SORT_ORDER + " = ? WHERE " + COLUMN_PATH + " = ?",
new Object[]{sortOrder++, path});
}
cursor.close();
}
public boolean isPinned(String albumPath) {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, null, COLUMN_PATH + " = ?",
new String[]{albumPath}, 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,
COLUMN_SORT_ORDER + " ASC");
String[] paths = new String[cursor.getCount()];
int i = 0;
while (cursor.moveToNext()) {
paths[i++] = cursor.getString(0);
}
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;
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.content.ContentValues;
import android.content.Context;
@@ -75,4 +75,4 @@ public class PinnedImageDbHelper extends SQLiteOpenHelper {
cursor.close();
return paths;
}
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.content.Context;
import android.content.SharedPreferences;
@@ -14,7 +14,7 @@ public class Preferences {
private static final String KEY_COVER_HEIGHT = "cover_height";
private static final String KEY_COVER_RATIO = "cover_ratio";
public static final String ACTION_COVER_UPDATED = "cc.winboll.studio.gallery.COVER_UPDATED";
public static final String ACTION_COVER_UPDATED = "cc.winboll.studio.mygallery.COVER_UPDATED";
private static final int DEFAULT_BG_TYPE = 0;
private static final int DEFAULT_SORT_MODE = 0;
@@ -90,4 +90,4 @@ public class Preferences {
public void setCoverRatio(float ratio) {
prefs.edit().putFloat(KEY_COVER_RATIO, ratio).apply();
}
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.os.Bundle;
import android.view.View;
@@ -43,4 +43,4 @@ public class SettingsActivity extends AppCompatActivity {
}
});
}
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.content.ContentResolver;
import android.content.Intent;
@@ -190,4 +190,4 @@ public class TrashActivity extends AppCompatActivity {
public String originalPath;
public String originalFolder;
}
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.net.Uri;
import android.view.LayoutInflater;
@@ -108,4 +108,4 @@ public class TrashAdapter extends RecyclerView.Adapter<TrashAdapter.ViewHolder>
btnDelete = itemView.findViewById(R.id.btn_delete);
}
}
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.content.ContentValues;
import android.content.Context;
@@ -75,4 +75,4 @@ public class TrashDbHelper extends SQLiteOpenHelper {
}
return trashDir.getAbsolutePath();
}
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.content.ContentResolver;
import android.content.Context;
@@ -155,4 +155,4 @@ public class TrashManager {
}
});
}
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery;
package cc.winboll.studio.mygallery;
import android.content.Context;
import android.graphics.Canvas;
@@ -166,4 +166,4 @@ public class ZoomContainerView extends FrameLayout {
}
return handled || super.onTouchEvent(event);
}
}
}
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery.dialog;
package cc.winboll.studio.mygallery.dialog;
import android.app.Dialog;
import android.content.Context;
@@ -6,7 +6,7 @@ import android.os.Bundle;
import android.view.WindowManager;
import android.widget.TextView;
import androidx.annotation.NonNull;
import cc.winboll.studio.gallery.R;
import cc.winboll.studio.mygallery.R;
/**
* 颜色表对话框
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery.utils;
package cc.winboll.studio.mygallery.utils;
import android.content.Context;
import android.content.SharedPreferences;
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery.utils;
package cc.winboll.studio.mygallery.utils;
import android.content.Context;
import android.content.SharedPreferences;
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery.views;
package cc.winboll.studio.mygallery.views;
import android.content.Context;
import android.util.AttributeSet;
@@ -1,4 +1,4 @@
package cc.winboll.studio.gallery.views;
package cc.winboll.studio.mygallery.views;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:clickable="true">
<item
android:left="0dp"
android:top="0dp"
android:right="0dp"
android:bottom="0dp"
android:drawable="@drawable/my_gallery_blankbackground"/>
</layer-list>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:clickable="true">
<item android:drawable="@drawable/ic_launcher_background"/>
<item
android:left="0dp"
android:top="0dp"
android:right="0dp"
android:bottom="0dp"
android:drawable="@drawable/my_gallery_blankbackground"/>
</layer-list>

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

@@ -73,19 +73,19 @@
android:layout_marginBottom="56dp"
android:fillViewport="true">
<cc.winboll.studio.gallery.ZoomContainerView
<cc.winboll.studio.mygallery.ZoomContainerView
android:id="@+id/zoom_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
<cc.winboll.studio.gallery.CropCanvasView
<cc.winboll.studio.mygallery.CropCanvasView
android:id="@+id/crop_canvas_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</cc.winboll.studio.gallery.ZoomContainerView>
</cc.winboll.studio.mygallery.ZoomContainerView>
</ScrollView>

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Some files were not shown because too many files have changed in this diff Show More