Compare commits

..

13 Commits

Author SHA1 Message Date
74ab06448f feat: 添加剪裁背景相关组件及版本更新
- 新增BackgroundRadioButton自定义单选按钮组件
- 新增CustomApplicationBackground应用背景管理类
- 更新build.properties版本构建号(0->19)
- 完善封面剪裁背景修改功能相关基础设施
2026-05-02 10:49:36 +08:00
6cf5ac2034 feat: 添加CropBackgroundUtils工具类及封面剪裁背景修改功能
- 创建CropBackgroundUtils工具类,实现与BackgroundUtils类似的背景管理功能
- 在封面剪裁窗口(CropActivity)添加"修改剪裁背景颜色"按钮
- 按钮点击后弹出Toast提示信息
- 使用独立的偏好设置存储(crop_background_prefs)
2026-05-02 10:48:28 +08:00
a2884122aa 实现主窗口背景颜色修改功能。 2026-05-02 10:40:22 +08:00
03ae37dc91 fix: BackgroundUtils初始化时自动保存配置至SP
- initFromResource 和 initFromColor 增加自动调用 saveToPreferences()
- 优化 initFromPreferences 默认逻辑,避免重复调用保存方法
2026-05-02 10:32:58 +08:00
aea9f1d745 feat: 增强BackgroundUtils属性获取功能
- 新增 getAttributeValueType() 函数,返回当前属性值类型
- 新增 getResId() 和 getColor() 公共函数,用于获取具体的属性值
2026-05-02 10:15:03 +08:00
0786d69ad6 refactor: 调整主窗口背景设置逻辑与布局透明度
- MainActivity: 将背景设置逻辑从 onCreate 移至 onPostCreate
- activity_main.xml: RecyclerView 背景颜色修改为透明
2026-05-02 10:05:26 +08:00
1585ff7eed feat: 重构背景管理系统,移除选择背景对话框并添加BackgroundUtils工具类
- 移除 BgSelectorDialog 对话框及 dialog_bg_selector 布局文件
- 移除 ImageViewerActivity 中的背景选择按钮及相关方法(switchBg/applyBg)
- 移除 CropActivity 中的背景颜色预览视图及 showBgDialog 方法
- 移除 ImagePagerAdapter 中的 bgType 参数,背景固定为黑色
- 新增 BackgroundUtils 单例工具类,支持:
  - 通过资源ID或颜色值创建背景Drawable
  - DrawableType枚举记录创建方式
  - SharedPreferences持久化存储背景配置
  - 首次启动默认使用绿色背景并保存
- 在 GlobalWinBoLLApplication 中初始化BackgroundUtils
- 在 MainActivity 中应用BackgroundUtils设置的背景
- 主窗口菜单添加修改背景颜色选项
2026-05-02 09:54:48 +08:00
91b2b1b480 处理AIDE Pro编译提示问题 2026-05-02 02:53:05 +08:00
bef3f3ce81 <gallery>APK 15.0.15 release Publish. 2026-05-01 21:09:33 +08:00
c0da46e0fd 为preview_image添加单击事件,随机改变容器背景色
- 为preview_image的父级LinearLayout添加id标识(preview_image_container)
- 实现单击preview_image时随机生成颜色并改变容器背景
- 添加必要的import语句(LinearLayout, Random)
2026-05-01 21:05:23 +08:00
72ca11a1af 剪裁信息对话框,预览图片时添加一个边框。 2026-05-01 20:52:33 +08:00
5decb2f8d9 <gallery>APK 15.0.14 release Publish. 2026-05-01 10:29:10 +08:00
29e7cfe985 refactor: 抽象背景选择对话框为独立的BgSelectorDialog类
- 新增BgSelectorDialog对话框类,继承Dialog
- 新增dialog_bg_selector.xml布局文件定义对话框视图
- 重构ImageViewerActivity.switchBg()使用新的BgSelectorDialog
- 重构CropActivity.showBgDialog()使用新的BgSelectorDialog
- 统一两个Activity的背景选择交互方式
2026-05-01 10:25:45 +08:00
15 changed files with 504 additions and 267 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Fri May 01 09:21:07 HKT 2026 #Sat May 02 10:32:03 CST 2026
stageCount=14 stageCount=16
libraryProject= libraryProject=
baseVersion=15.0 baseVersion=15.0
publishVersion=15.0.13 publishVersion=15.0.15
buildCount=0 buildCount=19
baseBetaVersion=15.0.14 baseBetaVersion=15.0.16

View File

@@ -3,7 +3,6 @@ package cc.winboll.studio.gallery;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.RectF; import android.graphics.RectF;
import android.graphics.drawable.GradientDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.content.DialogInterface; import android.content.DialogInterface;
@@ -19,6 +18,7 @@ import android.widget.ImageView;
import android.widget.ScrollView; import android.widget.ScrollView;
import android.widget.SeekBar; import android.widget.SeekBar;
import android.widget.TextView; import android.widget.TextView;
import android.widget.LinearLayout;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
@@ -26,6 +26,7 @@ import cc.winboll.studio.libappbase.LogUtils;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Random;
public class CropActivity extends AppCompatActivity { public class CropActivity extends AppCompatActivity {
public static final String TAG = "CropActivity"; public static final String TAG = "CropActivity";
@@ -72,15 +73,6 @@ public class CropActivity extends AppCompatActivity {
} }
}); });
final ImageView btnBg = findViewById(R.id.btn_color_pick);
btnBg.setImageResource(R.drawable.ic_bg);
btnBg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showBgDialog();
}
});
findViewById(R.id.btn_done).setOnClickListener(new View.OnClickListener() { findViewById(R.id.btn_done).setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@@ -95,6 +87,13 @@ public class CropActivity extends AppCompatActivity {
} }
}); });
findViewById(R.id.btn_change_bg).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(CropActivity.this, "修改剪裁背景颜色", Toast.LENGTH_SHORT).show();
}
});
zoomContainer = findViewById(R.id.zoom_container); zoomContainer = findViewById(R.id.zoom_container);
SeekBar seekBarZoom = findViewById(R.id.seekbar_zoom); SeekBar seekBarZoom = findViewById(R.id.seekbar_zoom);
@@ -116,11 +115,7 @@ public class CropActivity extends AppCompatActivity {
cropCanvasView = findViewById(R.id.crop_canvas_view); cropCanvasView = findViewById(R.id.crop_canvas_view);
final View colorView = findViewById(R.id.color_view);
final GradientDrawable colorDrawable = (GradientDrawable) colorView.getBackground();
cropCanvasView.setBackgroundType(bgType); cropCanvasView.setBackgroundType(bgType);
updateColorView(colorDrawable, bgType);
cropCanvasView.setOnBackgroundColorChangedListener(new CropCanvasView.OnBackgroundColorChangedListener() { cropCanvasView.setOnBackgroundColorChangedListener(new CropCanvasView.OnBackgroundColorChangedListener() {
@Override @Override
@@ -131,43 +126,6 @@ public class CropActivity extends AppCompatActivity {
loadImage(); loadImage();
} }
private void updateColorView(GradientDrawable drawable, int bgType) {
switch (bgType) {
case 0:
drawable.setColor(0xFF808080);
break;
case 1:
drawable.setColor(0xFFFFFFFF);
break;
case 2:
default:
drawable.setColor(0xFF000000);
break;
}
}
private void showBgDialog() {
final String[] bgNames = {"灰白相间", "全白", "全黑"};
final int[] bgDrawables = {R.drawable.bg_checkerboard, R.drawable.bg_white, R.drawable.bg_black};
final View colorView = findViewById(R.id.color_view);
final GradientDrawable colorDrawable = (GradientDrawable) colorView.getBackground();
final int currentBgType = cropCanvasView.getBackgroundType();
new AlertDialog.Builder(this)
.setTitle("选择背景")
.setSingleChoiceItems(bgNames, currentBgType, new android.content.DialogInterface.OnClickListener() {
@Override
public void onClick(android.content.DialogInterface dialog, int which) {
cropCanvasView.setBackgroundType(which);
prefs.setBgType(which);
updateColorView(colorDrawable, which);
dialog.dismiss();
}
})
.setNegativeButton("取消", null)
.show();
}
private void loadImage() { private void loadImage() {
try { try {
if (imagePath != null && new File(imagePath).exists()) { if (imagePath != null && new File(imagePath).exists()) {
@@ -315,10 +273,18 @@ public class CropActivity extends AppCompatActivity {
View dialogView = getLayoutInflater().inflate(R.layout.dialog_crop_info, null); View dialogView = getLayoutInflater().inflate(R.layout.dialog_crop_info, null);
TextView infoText = dialogView.findViewById(R.id.info_text); TextView infoText = dialogView.findViewById(R.id.info_text);
ImageView previewImage = dialogView.findViewById(R.id.preview_image); ImageView previewImage = dialogView.findViewById(R.id.preview_image);
final LinearLayout previewImageContainer = dialogView.findViewById(R.id.preview_image_container);
infoText.setText(info.toString()); infoText.setText(info.toString());
if (previewBitmap != null) { if (previewBitmap != null) {
previewImage.setImageBitmap(previewBitmap); previewImage.setImageBitmap(previewBitmap);
} }
previewImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int randomColor = 0xFF000000 | new Random().nextInt(0x00FFFFFF);
previewImageContainer.setBackgroundColor(randomColor);
}
});
AlertDialog dialog = new AlertDialog.Builder(this) AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle("裁剪信息") .setTitle("裁剪信息")
@@ -344,4 +310,4 @@ public class CropActivity extends AppCompatActivity {
originalBitmap = null; originalBitmap = null;
} }
} }
} }

View File

@@ -4,6 +4,7 @@ import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.gallery.utils.BackgroundUtils;
/** /**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com> * @Author 豆包&ZhanGSKen<zhangsken@qq.com>
@@ -23,6 +24,8 @@ public class GlobalWinBoLLApplication extends GlobalApplication {
WinBoLLActivityManager.init(this); WinBoLLActivityManager.init(this);
BackgroundUtils.initFromPreferences(this);
// 初始化 Toast 框架 // 初始化 Toast 框架
ToastUtils.init(this); ToastUtils.init(this);
// 设置 Toast 布局样式 // 设置 Toast 布局样式

View File

@@ -16,25 +16,10 @@ import cc.winboll.studio.libappbase.LogUtils;
public class ImagePagerAdapter extends PagerAdapter { public class ImagePagerAdapter extends PagerAdapter {
public static final String TAG = "ImagePagerAdapter"; public static final String TAG = "ImagePagerAdapter";
private ArrayList<Uri> imageUrls; private ArrayList<Uri> imageUrls;
private int bgType;
public ImagePagerAdapter(ArrayList<Uri> imageUrls, int bgType) { public ImagePagerAdapter(ArrayList<Uri> imageUrls) {
this.imageUrls = imageUrls; this.imageUrls = imageUrls;
this.bgType = bgType; LogUtils.d(TAG, "ImagePagerAdapter created with " + imageUrls.size() + " images");
LogUtils.d(TAG, "ImagePagerAdapter created with " + imageUrls.size() + " images, bgType=" + bgType);
}
private int getBgRes() {
switch (bgType) {
case 0:
return R.drawable.bg_checkerboard;
case 1:
return R.drawable.bg_white;
case 2:
return R.drawable.bg_black;
default:
return R.drawable.bg_checkerboard;
}
} }
@Override @Override
@@ -47,7 +32,7 @@ public class ImagePagerAdapter extends PagerAdapter {
public Object instantiateItem(@NonNull ViewGroup container, int position) { public Object instantiateItem(@NonNull ViewGroup container, int position) {
View view = LayoutInflater.from(container.getContext()) View view = LayoutInflater.from(container.getContext())
.inflate(R.layout.item_image_pager, container, false); .inflate(R.layout.item_image_pager, container, false);
view.setBackgroundResource(getBgRes()); view.setBackgroundResource(R.color.black);
ImageView imageView = view.findViewById(R.id.image); ImageView imageView = view.findViewById(R.id.image);
Glide.with(imageView.getContext()) Glide.with(imageView.getContext())

View File

@@ -34,9 +34,7 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
private ImageButton btnDelete; private ImageButton btnDelete;
private ImageButton btnShare; private ImageButton btnShare;
private ImageButton btnInfo; private ImageButton btnInfo;
private ImageButton btnBg;
private ImageButton btnGallery; private ImageButton btnGallery;
private int bgType = 0;
private GestureDetector gestureDetector; private GestureDetector gestureDetector;
private TrashManager trashManager; private TrashManager trashManager;
private Preferences prefs; private Preferences prefs;
@@ -55,7 +53,6 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
trashManager = new TrashManager(this); trashManager = new TrashManager(this);
prefs = new Preferences(this); prefs = new Preferences(this);
bgType = prefs.getBgType();
viewPager = findViewById(R.id.view_pager); viewPager = findViewById(R.id.view_pager);
toolbar = findViewById(R.id.toolbar); toolbar = findViewById(R.id.toolbar);
@@ -63,13 +60,10 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
btnDelete = findViewById(R.id.btn_delete); btnDelete = findViewById(R.id.btn_delete);
btnShare = findViewById(R.id.btn_share); btnShare = findViewById(R.id.btn_share);
btnInfo = findViewById(R.id.btn_info); btnInfo = findViewById(R.id.btn_info);
btnBg = findViewById(R.id.btn_bg);
btnGallery = findViewById(R.id.btn_gallery); btnGallery = findViewById(R.id.btn_gallery);
applyBg(); ImagePagerAdapter adapter = new ImagePagerAdapter(imageUrls);
ImagePagerAdapter adapter = new ImagePagerAdapter(imageUrls, bgType);
viewPager.setAdapter(adapter); viewPager.setAdapter(adapter);
viewPager.setCurrentItem(currentPosition); viewPager.setCurrentItem(currentPosition);
viewPager.addOnPageChangeListener(this); viewPager.addOnPageChangeListener(this);
@@ -117,13 +111,6 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
} }
}); });
btnBg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchBg();
}
});
btnGallery.setOnClickListener(new View.OnClickListener() { btnGallery.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@@ -147,52 +134,6 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
} }
} }
private void applyBg() {
int bgRes;
switch (bgType) {
case 0:
bgRes = R.drawable.bg_checkerboard;
break;
case 1:
bgRes = R.drawable.bg_white;
break;
case 2:
bgRes = R.drawable.bg_black;
break;
default:
bgRes = R.drawable.bg_checkerboard;
}
View container = findViewById(R.id.container);
if (container != null) {
container.setBackgroundResource(bgRes);
}
}
private void switchBg() {
final String[] bgNames = {"灰白相间", "全白", "全黑"};
final int[] bgResources = {R.drawable.bg_checkerboard, R.drawable.bg_white, R.drawable.bg_black};
new AlertDialog.Builder(this)
.setTitle("选择背景")
.setSingleChoiceItems(bgNames, bgType, new android.content.DialogInterface.OnClickListener() {
@Override
public void onClick(android.content.DialogInterface dialog, int which) {
bgType = which;
prefs.setBgType(which);
int currentItem = viewPager.getCurrentItem();
View container = findViewById(R.id.container);
if (container != null) {
container.setBackgroundResource(bgResources[which]);
}
viewPager.setAdapter(new ImagePagerAdapter(imageUrls, bgType));
viewPager.setCurrentItem(currentItem);
dialog.dismiss();
}
})
.setNegativeButton("取消", null)
.show();
}
private void showDeleteDialog() { private void showDeleteDialog() {
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setMessage("Delete to trash?") .setMessage("Delete to trash?")
@@ -257,7 +198,7 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
if (currentPosition >= imageUrls.size()) { if (currentPosition >= imageUrls.size()) {
currentPosition = imageUrls.size() - 1; currentPosition = imageUrls.size() - 1;
} }
viewPager.setAdapter(new ImagePagerAdapter(imageUrls, bgType)); viewPager.setAdapter(new ImagePagerAdapter(imageUrls));
viewPager.setCurrentItem(currentPosition); viewPager.setCurrentItem(currentPosition);
} }
} }

View File

@@ -26,10 +26,12 @@ import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import cc.winboll.studio.gallery.AlbumAdapter.OnAlbumClickListener; import cc.winboll.studio.gallery.AlbumAdapter.OnAlbumClickListener;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.gallery.utils.BackgroundUtils;
import cc.winboll.studio.libappbase.LogActivity; import cc.winboll.studio.libappbase.LogActivity;
import cc.winboll.studio.libappbase.LogUtils;
import com.a4455jkjh.colorpicker.ColorPickerDialog;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.FilenameFilter; import java.io.FilenameFilter;
@@ -49,55 +51,60 @@ public class MainActivity extends AppCompatActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
LogUtils.d(TAG, "onCreate"); LogUtils.d(TAG, "onCreate");
View content = findViewById(android.R.id.content);
if (content != null) {
content.setBackground(BackgroundUtils.getInstance().getDrawable());
}
Toolbar toolbar = findViewById(R.id.toolbar); Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
prefs = new Preferences(this); prefs = new Preferences(this);
recyclerView = findViewById(R.id.recycler_view); recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new GridLayoutManager(this, 2)); recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
adapter = new AlbumAdapter(); adapter = new AlbumAdapter();
adapter.setContext(this); adapter.setContext(this);
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
fabScrollTop = findViewById(R.id.fab_scroll_top); fabScrollTop = findViewById(R.id.fab_scroll_top);
fabScrollTop.setOnClickListener(new View.OnClickListener() { fabScrollTop.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
recyclerView.scrollToPosition(0); recyclerView.scrollToPosition(0);
} }
}); });
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override @Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy); super.onScrolled(recyclerView, dx, dy);
GridLayoutManager layoutManager = (GridLayoutManager) recyclerView.getLayoutManager(); GridLayoutManager layoutManager = (GridLayoutManager) recyclerView.getLayoutManager();
if (layoutManager != null) { if (layoutManager != null) {
int firstVisible = layoutManager.findFirstVisibleItemPosition(); int firstVisible = layoutManager.findFirstVisibleItemPosition();
if (firstVisible > 0) { if (firstVisible > 0) {
fabScrollTop.setVisibility(View.VISIBLE); fabScrollTop.setVisibility(View.VISIBLE);
} else { } else {
fabScrollTop.setVisibility(View.GONE); fabScrollTop.setVisibility(View.GONE);
} }
} }
} }
}); });
adapter.setOnAlbumClickListener(new OnAlbumClickListener() { adapter.setOnAlbumClickListener(new OnAlbumClickListener() {
@Override @Override
public void onAlbumClick(Album album) { public void onAlbumClick(Album album) {
Intent intent = new Intent(MainActivity.this, AlbumActivity.class); Intent intent = new Intent(MainActivity.this, AlbumActivity.class);
intent.putExtra(AlbumActivity.EXTRA_ALBUM_PATH, album.getPath()); intent.putExtra(AlbumActivity.EXTRA_ALBUM_PATH, album.getPath());
intent.putExtra(AlbumActivity.EXTRA_ALBUM_NAME, album.getName()); intent.putExtra(AlbumActivity.EXTRA_ALBUM_NAME, album.getName());
startActivity(intent); startActivity(intent);
} }
}); });
checkAndRequestPermissions(); checkAndRequestPermissions();
} }
private void checkAndRequestPermissions() { private void checkAndRequestPermissions() {
LogUtils.i(TAG, "checkAndRequestPermissions"); LogUtils.i(TAG, "checkAndRequestPermissions");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
@@ -113,14 +120,14 @@ public class MainActivity extends AppCompatActivity {
return; return;
} }
} }
if (checkPermission()) { if (checkPermission()) {
loadAlbums(); loadAlbums();
} else { } else {
requestPermission(); requestPermission();
} }
} }
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
@@ -134,7 +141,7 @@ public class MainActivity extends AppCompatActivity {
} }
} }
} }
private boolean checkPermission() { private boolean checkPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return Environment.isExternalStorageManager(); return Environment.isExternalStorageManager();
@@ -145,13 +152,13 @@ public class MainActivity extends AppCompatActivity {
private void requestPermission() { private void requestPermission() {
ActivityCompat.requestPermissions(this, ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
PERMISSION_REQUEST_CODE); PERMISSION_REQUEST_CODE);
} }
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) { @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) { if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
@@ -162,12 +169,12 @@ public class MainActivity extends AppCompatActivity {
} }
} }
private void loadAlbums() { private void loadAlbums() {
LogUtils.d(TAG, "loadAlbums"); LogUtils.d(TAG, "loadAlbums");
String folderPath = prefs.getFolderPath(); String folderPath = prefs.getFolderPath();
File baseFolder = new File(folderPath); File baseFolder = new File(folderPath);
LogUtils.d(TAG, "baseFolder: " + baseFolder.getAbsolutePath() + ", exists=" + baseFolder.exists()); LogUtils.d(TAG, "baseFolder: " + baseFolder.getAbsolutePath() + ", exists=" + baseFolder.exists());
if (!baseFolder.exists() || !baseFolder.isDirectory()) { if (!baseFolder.exists() || !baseFolder.isDirectory()) {
folderPath = Preferences.getDefaultPath(); folderPath = Preferences.getDefaultPath();
baseFolder = new File(folderPath); baseFolder = new File(folderPath);
@@ -178,10 +185,10 @@ private void loadAlbums() {
LogUtils.d(TAG, "try Pictures: " + baseFolder.getAbsolutePath() + ", exists=" + baseFolder.exists()); LogUtils.d(TAG, "try Pictures: " + baseFolder.getAbsolutePath() + ", exists=" + baseFolder.exists());
} }
} }
AlbumCoverDbHelper coverDbHelper = AlbumCoverDbHelper.getInstance(this); AlbumCoverDbHelper coverDbHelper = AlbumCoverDbHelper.getInstance(this);
ArrayList<Album> albums = new ArrayList<>(); ArrayList<Album> albums = new ArrayList<>();
FileFilter directoryFilter = new FileFilter() { FileFilter directoryFilter = new FileFilter() {
@Override @Override
public boolean accept(File file) { public boolean accept(File file) {
@@ -224,7 +231,7 @@ private void loadAlbums() {
} }
} }
} }
if (albums.isEmpty()) { if (albums.isEmpty()) {
Toast.makeText(this, R.string.no_images_found, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.no_images_found, Toast.LENGTH_SHORT).show();
LogUtils.i(TAG, "No albums found"); LogUtils.i(TAG, "No albums found");
@@ -232,14 +239,14 @@ private void loadAlbums() {
adapter.setData(albums); adapter.setData(albums);
LogUtils.d(TAG, "Loaded " + albums.size() + " albums"); LogUtils.d(TAG, "Loaded " + albums.size() + " albums");
} }
private Uri getUriFromPath(String path) { private Uri getUriFromPath(String path) {
String[] projection = { MediaStore.Images.Media._ID }; String[] projection = { MediaStore.Images.Media._ID };
String selection = MediaStore.Images.Media.DATA + " = ?"; String selection = MediaStore.Images.Media.DATA + " = ?";
String[] selectionArgs = { path }; String[] selectionArgs = { path };
try (Cursor cursor = getContentResolver().query( try (Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection, selection, selectionArgs, null)) { projection, selection, selectionArgs, null)) {
if (cursor != null) { if (cursor != null) {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)); long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
@@ -249,18 +256,18 @@ private void loadAlbums() {
} }
return null; return null;
} }
private ArrayList<Uri> getImagesInFolder(String folderPath) { private ArrayList<Uri> getImagesInFolder(String folderPath) {
ArrayList<Uri> imageUrls = new ArrayList<>(); ArrayList<Uri> imageUrls = new ArrayList<>();
ContentResolver contentResolver = getContentResolver(); ContentResolver contentResolver = getContentResolver();
Uri collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; Uri collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String selection = MediaStore.Images.Media.DATA + " LIKE ?"; String selection = MediaStore.Images.Media.DATA + " LIKE ?";
String[] selectionArgs = new String[]{folderPath + "/%"}; String[] selectionArgs = new String[]{folderPath + "/%"};
String sortOrder = MediaStore.Images.Media.DATE_ADDED + " DESC"; String sortOrder = MediaStore.Images.Media.DATE_ADDED + " DESC";
LogUtils.d(TAG, "getImagesInFolder: " + folderPath); LogUtils.d(TAG, "getImagesInFolder: " + folderPath);
try (Cursor cursor = contentResolver.query(collection, null, selection, selectionArgs, sortOrder)) { try (Cursor cursor = contentResolver.query(collection, null, selection, selectionArgs, sortOrder)) {
if (cursor != null) { if (cursor != null) {
LogUtils.d(TAG, "cursor count: " + cursor.getCount()); LogUtils.d(TAG, "cursor count: " + cursor.getCount());
@@ -295,6 +302,33 @@ private void loadAlbums() {
intent.addCategory(Intent.CATEGORY_APP_GALLERY); intent.addCategory(Intent.CATEGORY_APP_GALLERY);
startActivity(intent); startActivity(intent);
return true; 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();
}
return true;
} else if (id == R.id.action_settings) { } else if (id == R.id.action_settings) {
startActivity(new Intent(this, SettingsActivity.class)); startActivity(new Intent(this, SettingsActivity.class));
return true; return true;
@@ -310,7 +344,7 @@ private void loadAlbums() {
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
private BroadcastReceiver coverUpdatedReceiver = new BroadcastReceiver() { private BroadcastReceiver coverUpdatedReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
@@ -319,7 +353,7 @@ private void loadAlbums() {
} }
} }
}; };
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
@@ -334,35 +368,35 @@ private void loadAlbums() {
adapter.refreshCover(); adapter.refreshCover();
} }
} }
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
unregisterReceiver(coverUpdatedReceiver); unregisterReceiver(coverUpdatedReceiver);
} }
private void scanMediaStore() { private void scanMediaStore() {
String folderPath = prefs.getFolderPath(); String folderPath = prefs.getFolderPath();
File baseFolder = new File(folderPath); File baseFolder = new File(folderPath);
if (baseFolder.exists() && baseFolder.isDirectory()) { if (baseFolder.exists() && baseFolder.isDirectory()) {
File[] subfolders = baseFolder.listFiles(new FileFilter() { File[] subfolders = baseFolder.listFiles(new FileFilter() {
@Override @Override
public boolean accept(File file) { public boolean accept(File file) {
return file.isDirectory(); return file.isDirectory();
} }
}); });
if (subfolders != null) { if (subfolders != null) {
ArrayList<String> paths = new ArrayList<>(); ArrayList<String> paths = new ArrayList<>();
for (File subfolder : subfolders) { for (File subfolder : subfolders) {
File[] images = subfolder.listFiles(new FilenameFilter() { File[] images = subfolder.listFiles(new FilenameFilter() {
@Override @Override
public boolean accept(File dir, String name) { public boolean accept(File dir, String name) {
String lower = name.toLowerCase(); String lower = name.toLowerCase();
return lower.endsWith(".jpg") || lower.endsWith(".jpeg") return lower.endsWith(".jpg") || lower.endsWith(".jpeg")
|| lower.endsWith(".png") || lower.endsWith(".gif") || lower.endsWith(".png") || lower.endsWith(".gif")
|| lower.endsWith(".webp") || lower.endsWith(".bmp"); || lower.endsWith(".webp") || lower.endsWith(".bmp");
} }
}); });
if (images != null) { if (images != null) {
for (File img : images) { for (File img : images) {
paths.add(img.getAbsolutePath()); paths.add(img.getAbsolutePath());
@@ -373,11 +407,11 @@ private void loadAlbums() {
LogUtils.d(TAG, "scanning " + paths.size() + " files to MediaStore"); LogUtils.d(TAG, "scanning " + paths.size() + " files to MediaStore");
String[] pathArray = paths.toArray(new String[0]); String[] pathArray = paths.toArray(new String[0]);
MediaScannerConnection.scanFile(this, pathArray, null, new MediaScannerConnection.OnScanCompletedListener() { MediaScannerConnection.scanFile(this, pathArray, null, new MediaScannerConnection.OnScanCompletedListener() {
@Override @Override
public void onScanCompleted(String path, Uri uri) { public void onScanCompleted(String path, Uri uri) {
LogUtils.d(TAG, "scanCompleted: " + path + " -> " + uri); LogUtils.d(TAG, "scanCompleted: " + path + " -> " + uri);
} }
}); });
} }
} }
} }

View File

@@ -0,0 +1,129 @@
package cc.winboll.studio.gallery.utils;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.core.content.ContextCompat;
public class BackgroundUtils {
public enum DrawableType {
RESOURCE_ID,
COLOR
}
private static volatile BackgroundUtils instance;
private static final String PREF_NAME = "background_prefs";
private static final String KEY_TYPE = "bg_type";
private static final String KEY_RES_ID = "bg_res_id";
private static final String KEY_COLOR = "bg_color";
private Context context;
private Drawable drawable;
private DrawableType drawableType;
private int resId;
private int color;
private BackgroundUtils() {
}
public static BackgroundUtils getInstance() {
if (instance == null) {
synchronized (BackgroundUtils.class) {
if (instance == null) {
instance = new BackgroundUtils();
}
}
}
return instance;
}
public static BackgroundUtils initFromResource(Context context, @DrawableRes int resId) {
synchronized (BackgroundUtils.class) {
BackgroundUtils utils = getInstance();
utils.context = context.getApplicationContext();
utils.drawableType = DrawableType.RESOURCE_ID;
utils.resId = resId;
utils.drawable = ContextCompat.getDrawable(utils.context, resId);
utils.saveToPreferences();
return utils;
}
}
public static BackgroundUtils initFromColor(Context context, @ColorInt int color) {
synchronized (BackgroundUtils.class) {
BackgroundUtils utils = getInstance();
utils.context = context.getApplicationContext();
utils.drawableType = DrawableType.COLOR;
utils.color = color;
utils.drawable = new ColorDrawable(color);
utils.saveToPreferences();
return utils;
}
}
public static BackgroundUtils initFromPreferences(Context context) {
synchronized (BackgroundUtils.class) {
Context appContext = context.getApplicationContext();
SharedPreferences prefs = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
int type = prefs.getInt(KEY_TYPE, -1);
if (type == 0) {
int resId = prefs.getInt(KEY_RES_ID, 0);
if (resId != 0) {
return initFromResource(appContext, resId);
}
} else if (type == 1) {
int color = prefs.getInt(KEY_COLOR, Color.BLACK);
return initFromColor(appContext, color);
}
// 默认情况initFromColor 内部已经调用了 saveToPreferences()
return initFromColor(appContext, 0xFF00FF00);
}
}
public Drawable getDrawable() {
return drawable;
}
public DrawableType getDrawableType() {
return drawableType;
}
public DrawableType getAttributeValueType() {
return drawableType;
}
public int getResId() {
return resId;
}
public int getColor() {
return color;
}
public void saveToPreferences() {
if (context == null) return;
SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
if (drawableType == DrawableType.RESOURCE_ID) {
editor.putInt(KEY_TYPE, 0);
editor.putInt(KEY_RES_ID, resId);
editor.remove(KEY_COLOR);
} else {
editor.putInt(KEY_TYPE, 1);
editor.putInt(KEY_COLOR, color);
editor.remove(KEY_RES_ID);
}
editor.apply();
}
public static void clearPreferences(Context context) {
SharedPreferences prefs = context.getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
prefs.edit().clear().apply();
}
}

View File

@@ -0,0 +1,124 @@
package cc.winboll.studio.gallery.utils;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.core.content.ContextCompat;
public class CropBackgroundUtils {
public enum DrawableType {
RESOURCE_ID,
COLOR
}
private static volatile CropBackgroundUtils instance;
private static final String PREF_NAME = "crop_background_prefs";
private static final String KEY_TYPE = "crop_bg_type";
private static final String KEY_RES_ID = "crop_bg_res_id";
private static final String KEY_COLOR = "crop_bg_color";
private Context context;
private Drawable drawable;
private DrawableType drawableType;
private int resId;
private int color;
private CropBackgroundUtils() {
}
public static CropBackgroundUtils getInstance() {
if (instance == null) {
synchronized (CropBackgroundUtils.class) {
if (instance == null) {
instance = new CropBackgroundUtils();
}
}
}
return instance;
}
public static CropBackgroundUtils initFromResource(Context context, @DrawableRes int resId) {
synchronized (CropBackgroundUtils.class) {
CropBackgroundUtils utils = getInstance();
utils.context = context.getApplicationContext();
utils.drawableType = DrawableType.RESOURCE_ID;
utils.resId = resId;
utils.drawable = ContextCompat.getDrawable(utils.context, resId);
utils.saveToPreferences();
return utils;
}
}
public static CropBackgroundUtils initFromColor(Context context, @ColorInt int color) {
synchronized (CropBackgroundUtils.class) {
CropBackgroundUtils utils = getInstance();
utils.context = context.getApplicationContext();
utils.drawableType = DrawableType.COLOR;
utils.color = color;
utils.drawable = new ColorDrawable(color);
utils.saveToPreferences();
return utils;
}
}
public static CropBackgroundUtils initFromPreferences(Context context) {
synchronized (CropBackgroundUtils.class) {
Context appContext = context.getApplicationContext();
SharedPreferences prefs = appContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
int type = prefs.getInt(KEY_TYPE, -1);
if (type == 0) {
int resId = prefs.getInt(KEY_RES_ID, 0);
if (resId != 0) {
return initFromResource(appContext, resId);
}
} else if (type == 1) {
int color = prefs.getInt(KEY_COLOR, Color.BLACK);
return initFromColor(appContext, color);
}
return initFromColor(appContext, 0xFF00FF00);
}
}
public Drawable getDrawable() {
return drawable;
}
public DrawableType getDrawableType() {
return drawableType;
}
public int getResId() {
return resId;
}
public int getColor() {
return color;
}
public void saveToPreferences() {
if (context == null) return;
SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
if (drawableType == DrawableType.RESOURCE_ID) {
editor.putInt(KEY_TYPE, 0);
editor.putInt(KEY_RES_ID, resId);
editor.remove(KEY_COLOR);
} else {
editor.putInt(KEY_TYPE, 1);
editor.putInt(KEY_COLOR, color);
editor.remove(KEY_RES_ID);
}
editor.apply();
}
public static void clearPreferences(Context context) {
SharedPreferences prefs = context.getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
prefs.edit().clear().apply();
}
}

View File

@@ -0,0 +1,34 @@
package cc.winboll.studio.gallery.views;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.RadioButton;
public class BackgroundRadioButton extends RadioButton {
CustomApplicationBackground mCustomApplicationBackground;
public BackgroundRadioButton(Context context) {
super(context);
}
public BackgroundRadioButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BackgroundRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setupCustomApplicationBackground(Context context, int resId) {
mCustomApplicationBackground = new CustomApplicationBackground(context, resId);
}
public void setCustomApplicationBackground(CustomApplicationBackground customApplicationBackground) {
mCustomApplicationBackground = customApplicationBackground;
}
public CustomApplicationBackground getCustomApplicationBackground() {
return mCustomApplicationBackground;
}
}

View File

@@ -0,0 +1,29 @@
package cc.winboll.studio.gallery.views;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
public class CustomApplicationBackground {
Drawable mDrawable;
public CustomApplicationBackground(Drawable drawable) {
mDrawable = drawable;
}
public CustomApplicationBackground(int color) {
mDrawable = new ColorDrawable(color);
}
public CustomApplicationBackground(Context context, int resId) {
mDrawable = context.getDrawable(resId);
}
public Drawable getDrawable() {
return mDrawable;
}
public void setDrawable(Drawable drawable) {
mDrawable = drawable;
}
}

View File

@@ -33,30 +33,6 @@
android:gravity="center" android:gravity="center"
android:layout_marginEnd="8dp"/> android:layout_marginEnd="8dp"/>
<FrameLayout
android:id="@+id/color_icon_container"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="8dp"
android:background="@android:color/transparent">
<View
android:id="@+id/color_view"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center"
android:background="@drawable/bg_color_circle"/>
</FrameLayout>
<ImageView
android:id="@+id/btn_color_pick"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="5dp"
android:src="@drawable/ic_color_pick"
android:background="?attr/selectableItemBackgroundBorderless"/>
<View <View
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
@@ -70,6 +46,15 @@
android:src="@drawable/ic_info" android:src="@drawable/ic_info"
android:background="?attr/selectableItemBackgroundBorderless"/> android:background="?attr/selectableItemBackgroundBorderless"/>
<ImageView
android:id="@+id/btn_change_bg"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="5dp"
android:src="@drawable/ic_color_pick"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="修改剪裁背景颜色"/>
<ImageView <ImageView
android:id="@+id/btn_done" android:id="@+id/btn_done"
android:layout_width="40dp" android:layout_width="40dp"

View File

@@ -60,14 +60,6 @@
android:src="@drawable/ic_info" android:src="@drawable/ic_info"
android:contentDescription="Info"/> android:contentDescription="Info"/>
<ImageButton
android:id="@+id/btn_bg"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_bg"
android:contentDescription="Background"/>
<ImageButton <ImageButton
android:id="@+id/btn_delete" android:id="@+id/btn_delete"
android:layout_width="48dp" android:layout_width="48dp"

View File

@@ -27,7 +27,7 @@
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/black"/> android:background="@android:color/transparent"/>
</LinearLayout> </LinearLayout>

View File

@@ -1,26 +1,36 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp" android:padding="16dp"
android:background="@drawable/bg_dialog"> android:background="@drawable/bg_dialog"
android:gravity="center_horizontal">
<TextView <TextView
android:id="@+id/info_text" android:id="@+id/info_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="14sp" android:textSize="14sp"
android:fontFamily="monospace" android:fontFamily="monospace"
android:textColor="@android:color/white"/> android:textColor="@android:color/white"/>
<ImageView <LinearLayout
android:id="@+id/preview_image" android:id="@+id/preview_image_container"
android:layout_width="match_parent" android:orientation="horizontal"
android:layout_height="200dp" android:layout_width="match_parent"
android:layout_marginTop="16dp" android:layout_height="wrap_content"
android:scaleType="fitCenter" android:padding="2dp"
android:background="@android:color/black"/> android:background="#FFBBD505">
<ImageView
android:id="@+id/preview_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -8,6 +8,11 @@
android:icon="@drawable/ic_mi_gallery" android:icon="@drawable/ic_mi_gallery"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_change_bg_color"
android:title="修改背景颜色"
app:showAsAction="never"/>
<item <item
android:id="@+id/action_trash" android:id="@+id/action_trash"
android:title="@string/trash" android:title="@string/trash"