Compare commits
16 Commits
gallery-v1
...
gallery-v1
| Author | SHA1 | Date | |
|---|---|---|---|
| 262d35fb4e | |||
| 5b0bb599bb | |||
| 3955de100d | |||
| 7456db1729 | |||
| 226cbf43fe | |||
| 4a267d5606 | |||
| 4d0d8c4d59 | |||
| 9a43267b63 | |||
| c2618672bb | |||
| 6d9bd175f6 | |||
| ffbecaa31d | |||
| e26df437c5 | |||
| 248fd9d8d8 | |||
| 5b631710a9 | |||
| cda85feddd | |||
| ecad4a7913 |
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Sat Apr 25 05:49:42 HKT 2026
|
#Sun Apr 26 11:32:37 HKT 2026
|
||||||
stageCount=1
|
stageCount=6
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.0
|
baseVersion=15.0
|
||||||
publishVersion=15.0.0
|
publishVersion=15.0.5
|
||||||
buildCount=0
|
buildCount=0
|
||||||
baseBetaVersion=15.0.1
|
baseBetaVersion=15.0.6
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import android.net.Uri;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
@@ -16,6 +17,7 @@ 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 java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
@@ -30,6 +32,8 @@ public class AlbumActivity extends AppCompatActivity {
|
|||||||
private ImageAdapter adapter;
|
private ImageAdapter adapter;
|
||||||
private String albumPath;
|
private String albumPath;
|
||||||
private String albumName;
|
private String albumName;
|
||||||
|
private FloatingActionButton fabScrollTop;
|
||||||
|
private Preferences prefs;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -45,11 +49,39 @@ public class AlbumActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
getSupportActionBar().setTitle(albumName);
|
getSupportActionBar().setTitle(albumName);
|
||||||
|
|
||||||
|
prefs = new Preferences(this);
|
||||||
|
|
||||||
recyclerView = findViewById(R.id.recycler_view);
|
recyclerView = findViewById(R.id.recycler_view);
|
||||||
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
|
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
|
||||||
adapter = new ImageAdapter();
|
adapter = new ImageAdapter();
|
||||||
|
adapter.setContext(this);
|
||||||
|
adapter.setAlbumPath(albumPath);
|
||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
|
fabScrollTop = findViewById(R.id.fab_scroll_top);
|
||||||
|
fabScrollTop.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
adapter.setOnImageClickListener(new OnImageClickListener() {
|
adapter.setOnImageClickListener(new OnImageClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onImageClick(int position, ArrayList<Uri> urls, ArrayList<String> paths) {
|
public void onImageClick(int position, ArrayList<Uri> urls, ArrayList<String> paths) {
|
||||||
@@ -92,6 +124,20 @@ public class AlbumActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getSortOrder(int sortMode) {
|
||||||
|
switch (sortMode) {
|
||||||
|
case Preferences.SORT_TIME_ASC:
|
||||||
|
return android.provider.MediaStore.Images.Media.DATE_ADDED + " ASC";
|
||||||
|
case Preferences.SORT_NAME_DESC:
|
||||||
|
return android.provider.MediaStore.Images.Media.DISPLAY_NAME + " DESC";
|
||||||
|
case Preferences.SORT_NAME_ASC:
|
||||||
|
return android.provider.MediaStore.Images.Media.DISPLAY_NAME + " ASC";
|
||||||
|
case Preferences.SORT_TIME_DESC:
|
||||||
|
default:
|
||||||
|
return android.provider.MediaStore.Images.Media.DATE_ADDED + " DESC";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void loadImages() {
|
private void loadImages() {
|
||||||
LogUtils.d(TAG, "loadImages");
|
LogUtils.d(TAG, "loadImages");
|
||||||
ArrayList<Uri> imageUrls = new ArrayList<>();
|
ArrayList<Uri> imageUrls = new ArrayList<>();
|
||||||
@@ -101,7 +147,8 @@ public class AlbumActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
String selection = android.provider.MediaStore.Images.Media.DATA + " LIKE ?";
|
String selection = android.provider.MediaStore.Images.Media.DATA + " LIKE ?";
|
||||||
String[] selectionArgs = new String[]{albumPath + "%"};
|
String[] selectionArgs = new String[]{albumPath + "%"};
|
||||||
String sortOrder = android.provider.MediaStore.Images.Media.DATE_ADDED + " DESC";
|
int sortMode = prefs.getAlbumSortMode();
|
||||||
|
String sortOrder = getSortOrder(sortMode);
|
||||||
|
|
||||||
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) {
|
||||||
@@ -125,19 +172,59 @@ public class AlbumActivity extends AppCompatActivity {
|
|||||||
adapter.setData(imageUrls, imagePaths);
|
adapter.setData(imageUrls, imagePaths);
|
||||||
LogUtils.d(TAG, "Loaded " + imageUrls.size() + " images");
|
LogUtils.d(TAG, "Loaded " + imageUrls.size() + " images");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
getMenuInflater().inflate(R.menu.menu_main, menu);
|
getMenuInflater().inflate(R.menu.menu_album, menu);
|
||||||
|
int sortMode = prefs.getAlbumSortMode();
|
||||||
|
int menuId = getSortMenuId(sortMode);
|
||||||
|
MenuItem item = menu.findItem(menuId);
|
||||||
|
if (item != null) {
|
||||||
|
item.setChecked(true);
|
||||||
|
}
|
||||||
|
MenuItem sortItem = menu.findItem(R.id.action_sort);
|
||||||
|
if (sortItem != null && sortItem.getSubMenu() != null) {
|
||||||
|
sortItem.getSubMenu().setGroupCheckable(0, true, true);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getSortMenuId(int sortMode) {
|
||||||
|
switch (sortMode) {
|
||||||
|
case Preferences.SORT_TIME_ASC:
|
||||||
|
return R.id.sort_time_asc;
|
||||||
|
case Preferences.SORT_NAME_DESC:
|
||||||
|
return R.id.sort_name_desc;
|
||||||
|
case Preferences.SORT_NAME_ASC:
|
||||||
|
return R.id.sort_name_asc;
|
||||||
|
case Preferences.SORT_TIME_DESC:
|
||||||
|
default:
|
||||||
|
return R.id.sort_time_desc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
if (item.getItemId() == R.id.action_refresh) {
|
int itemId = item.getItemId();
|
||||||
if (checkPermission()) {
|
if (itemId == R.id.sort_time_desc) {
|
||||||
loadImages();
|
prefs.setAlbumSortMode(Preferences.SORT_TIME_DESC);
|
||||||
}
|
item.setChecked(true);
|
||||||
|
loadImages();
|
||||||
|
return true;
|
||||||
|
} else if (itemId == R.id.sort_time_asc) {
|
||||||
|
prefs.setAlbumSortMode(Preferences.SORT_TIME_ASC);
|
||||||
|
item.setChecked(true);
|
||||||
|
loadImages();
|
||||||
|
return true;
|
||||||
|
} else if (itemId == R.id.sort_name_desc) {
|
||||||
|
prefs.setAlbumSortMode(Preferences.SORT_NAME_DESC);
|
||||||
|
item.setChecked(true);
|
||||||
|
loadImages();
|
||||||
|
return true;
|
||||||
|
} else if (itemId == R.id.sort_name_asc) {
|
||||||
|
prefs.setAlbumSortMode(Preferences.SORT_NAME_ASC);
|
||||||
|
item.setChecked(true);
|
||||||
|
loadImages();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
@@ -149,5 +236,8 @@ public class AlbumActivity extends AppCompatActivity {
|
|||||||
if (checkPermission()) {
|
if (checkPermission()) {
|
||||||
loadImages();
|
loadImages();
|
||||||
}
|
}
|
||||||
|
if (adapter != null) {
|
||||||
|
adapter.refreshBg();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ import android.provider.MediaStore;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
|
import android.view.View.OnLongClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -13,6 +14,8 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
|
||||||
@@ -20,6 +23,22 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
|
|||||||
public static final String TAG = "AlbumAdapter";
|
public static final String TAG = "AlbumAdapter";
|
||||||
private ArrayList<Album> albums = new ArrayList<>();
|
private ArrayList<Album> albums = new ArrayList<>();
|
||||||
private OnAlbumClickListener listener;
|
private OnAlbumClickListener listener;
|
||||||
|
private Preferences prefs;
|
||||||
|
private int bgType = 0;
|
||||||
|
private PinnedAlbumDbHelper pinnedDbHelper;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public interface OnAlbumClickListener {
|
public interface OnAlbumClickListener {
|
||||||
void onAlbumClick(Album album);
|
void onAlbumClick(Album album);
|
||||||
@@ -30,10 +49,89 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setData(ArrayList<Album> albums) {
|
public void setData(ArrayList<Album> albums) {
|
||||||
this.albums = albums;
|
this.albums = sortAlbums(albums);
|
||||||
LogUtils.d(TAG, "setData: " + albums.size() + " albums");
|
LogUtils.d(TAG, "setData: " + albums.size() + " albums");
|
||||||
notifyDataSetChanged();
|
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();
|
||||||
|
pinnedDbHelper = PinnedAlbumDbHelper.getInstance(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshBg() {
|
||||||
|
if (prefs != null) {
|
||||||
|
bgType = prefs.getBgType();
|
||||||
|
}
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshPinned() {
|
||||||
|
if (pinnedDbHelper != null && albums != null && !albums.isEmpty()) {
|
||||||
|
ArrayList<Album> pinned = new ArrayList<>();
|
||||||
|
ArrayList<Album> unpinned = new ArrayList<>();
|
||||||
|
for (Album album : albums) {
|
||||||
|
if (pinnedDbHelper.isPinned(album.getPath())) {
|
||||||
|
pinned.add(album);
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showContextMenu(View view, final Album album) {
|
||||||
|
android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(view.getContext());
|
||||||
|
builder.setTitle(album.getName());
|
||||||
|
boolean isPinned = pinnedDbHelper != null && pinnedDbHelper.isPinned(album.getPath());
|
||||||
|
String[] items = isPinned ? new String[]{"取消置顶"} : new String[]{"置顶"};
|
||||||
|
builder.setItems(items, new android.content.DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(android.content.DialogInterface dialog, int which) {
|
||||||
|
if (pinnedDbHelper != null) {
|
||||||
|
if (which == 0) {
|
||||||
|
if (isPinned) {
|
||||||
|
pinnedDbHelper.unpinAlbum(album.getPath());
|
||||||
|
} else {
|
||||||
|
pinnedDbHelper.pinAlbum(album.getPath());
|
||||||
|
}
|
||||||
|
refreshPinned();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
@@ -48,6 +146,19 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
|
|||||||
final Album album = albums.get(position);
|
final Album album = albums.get(position);
|
||||||
LogUtils.d(TAG, "bind: " + album.getName() + ", cover=" + album.getCoverUri());
|
LogUtils.d(TAG, "bind: " + album.getName() + ", cover=" + album.getCoverUri());
|
||||||
|
|
||||||
|
holder.coverImage.setBackgroundResource(getBgRes());
|
||||||
|
|
||||||
|
boolean isPinned = pinnedDbHelper != null && pinnedDbHelper.isPinned(album.getPath());
|
||||||
|
holder.pinIcon.setVisibility(isPinned ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View v) {
|
||||||
|
showContextMenu(holder.itemView, album);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
holder.albumName.setText(album.getName());
|
holder.albumName.setText(album.getName());
|
||||||
holder.imageCount.setText(album.getImageCount() + " photos");
|
holder.imageCount.setText(album.getImageCount() + " photos");
|
||||||
|
|
||||||
@@ -105,11 +216,13 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
|
|||||||
|
|
||||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
ImageView coverImage;
|
ImageView coverImage;
|
||||||
|
ImageView pinIcon;
|
||||||
TextView albumName;
|
TextView albumName;
|
||||||
TextView imageCount;
|
TextView imageCount;
|
||||||
ViewHolder(View itemView) {
|
ViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
coverImage = itemView.findViewById(R.id.album_cover);
|
coverImage = itemView.findViewById(R.id.album_cover);
|
||||||
|
pinIcon = itemView.findViewById(R.id.pin_icon);
|
||||||
albumName = itemView.findViewById(R.id.album_name);
|
albumName = itemView.findViewById(R.id.album_name);
|
||||||
imageCount = itemView.findViewById(R.id.image_count);
|
imageCount = itemView.findViewById(R.id.image_count);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
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 AlbumCoverDbHelper extends SQLiteOpenHelper {
|
||||||
|
public static final String TAG = "AlbumCoverDbHelper";
|
||||||
|
private static final String DB_NAME = "album_cover.db";
|
||||||
|
private static final int DB_VERSION = 1;
|
||||||
|
private static final String TABLE_NAME = "album_covers";
|
||||||
|
private static final String COLUMN_ALBUM_PATH = "album_path";
|
||||||
|
private static final String COLUMN_IMAGE_PATH = "image_path";
|
||||||
|
|
||||||
|
private static final String SQL_CREATE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||||
|
+ COLUMN_ALBUM_PATH + " TEXT PRIMARY KEY, "
|
||||||
|
+ COLUMN_IMAGE_PATH + " TEXT)";
|
||||||
|
|
||||||
|
private static AlbumCoverDbHelper dbHelper;
|
||||||
|
|
||||||
|
public static AlbumCoverDbHelper getInstance(Context context) {
|
||||||
|
if (dbHelper == null) {
|
||||||
|
dbHelper = new AlbumCoverDbHelper(context.getApplicationContext());
|
||||||
|
}
|
||||||
|
return dbHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlbumCoverDbHelper(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 setCover(String albumPath, String imagePath) {
|
||||||
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(COLUMN_ALBUM_PATH, albumPath);
|
||||||
|
values.put(COLUMN_IMAGE_PATH, imagePath);
|
||||||
|
db.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
||||||
|
LogUtils.d(TAG, "setCover: album=" + albumPath + ", image=" + imagePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCover(String albumPath) {
|
||||||
|
SQLiteDatabase db = getReadableDatabase();
|
||||||
|
Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_IMAGE_PATH},
|
||||||
|
COLUMN_ALBUM_PATH + " = ?", new String[]{albumPath}, null, null, null);
|
||||||
|
String coverPath = null;
|
||||||
|
if (cursor != null) {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
coverPath = cursor.getString(0);
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return coverPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearCover(String albumPath) {
|
||||||
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
|
db.delete(TABLE_NAME, COLUMN_ALBUM_PATH + " = ?", new String[]{albumPath});
|
||||||
|
LogUtils.d(TAG, "clearCover: " + albumPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package cc.winboll.studio.gallery;
|
package cc.winboll.studio.gallery;
|
||||||
|
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -10,6 +11,8 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
|
||||||
@@ -18,6 +21,24 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
|
|||||||
private ArrayList<Uri> imageUrls = new ArrayList<>();
|
private ArrayList<Uri> imageUrls = new ArrayList<>();
|
||||||
private ArrayList<String> imagePaths = new ArrayList<>();
|
private ArrayList<String> imagePaths = new ArrayList<>();
|
||||||
private OnImageClickListener listener;
|
private OnImageClickListener listener;
|
||||||
|
private int bgType = 0;
|
||||||
|
private Preferences prefs;
|
||||||
|
private PinnedImageDbHelper pinnedDbHelper;
|
||||||
|
private AlbumCoverDbHelper coverDbHelper;
|
||||||
|
private String albumPath;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public interface OnImageClickListener {
|
public interface OnImageClickListener {
|
||||||
void onImageClick(int position, ArrayList<Uri> urls, ArrayList<String> paths);
|
void onImageClick(int position, ArrayList<Uri> urls, ArrayList<String> paths);
|
||||||
@@ -30,10 +51,93 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
|
|||||||
public void setData(ArrayList<Uri> urls, ArrayList<String> paths) {
|
public void setData(ArrayList<Uri> urls, ArrayList<String> paths) {
|
||||||
this.imageUrls = urls;
|
this.imageUrls = urls;
|
||||||
this.imagePaths = paths;
|
this.imagePaths = paths;
|
||||||
|
sortPinnedFirst();
|
||||||
LogUtils.d(TAG, "setData: " + urls.size() + " images");
|
LogUtils.d(TAG, "setData: " + urls.size() + " images");
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sortPinnedFirst() {
|
||||||
|
if (pinnedDbHelper == null || imagePaths.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ArrayList<Uri> pinnedUrls = new ArrayList<>();
|
||||||
|
ArrayList<String> pinnedPaths = new ArrayList<>();
|
||||||
|
ArrayList<Uri> unpinnedUrls = new ArrayList<>();
|
||||||
|
ArrayList<String> unpinnedPaths = new ArrayList<>();
|
||||||
|
for (int i = 0; i < imagePaths.size(); i++) {
|
||||||
|
String path = imagePaths.get(i);
|
||||||
|
if (pinnedDbHelper.isPinned(path)) {
|
||||||
|
pinnedUrls.add(imageUrls.get(i));
|
||||||
|
pinnedPaths.add(path);
|
||||||
|
} else {
|
||||||
|
unpinnedUrls.add(imageUrls.get(i));
|
||||||
|
unpinnedPaths.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imageUrls.clear();
|
||||||
|
imagePaths.clear();
|
||||||
|
imageUrls.addAll(pinnedUrls);
|
||||||
|
imageUrls.addAll(unpinnedUrls);
|
||||||
|
imagePaths.addAll(pinnedPaths);
|
||||||
|
imagePaths.addAll(unpinnedPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContext(android.content.Context context) {
|
||||||
|
prefs = new Preferences(context);
|
||||||
|
bgType = prefs.getBgType();
|
||||||
|
pinnedDbHelper = PinnedImageDbHelper.getInstance(context);
|
||||||
|
coverDbHelper = AlbumCoverDbHelper.getInstance(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlbumPath(String albumPath) {
|
||||||
|
this.albumPath = albumPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshBg() {
|
||||||
|
if (prefs != null) {
|
||||||
|
bgType = prefs.getBgType();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshPinned() {
|
||||||
|
if (pinnedDbHelper == null || imagePaths.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sortPinnedFirst();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showContextMenu(View view, final int position) {
|
||||||
|
final String imagePath = imagePaths.get(position);
|
||||||
|
android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(view.getContext());
|
||||||
|
builder.setTitle("Image");
|
||||||
|
boolean isPinned = pinnedDbHelper != null && pinnedDbHelper.isPinned(imagePath);
|
||||||
|
String[] items = isPinned ? new String[]{"取消置顶", "设置为封面"} : new String[]{"置顶", "设置为封面"};
|
||||||
|
builder.setItems(items, new android.content.DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(android.content.DialogInterface dialog, int which) {
|
||||||
|
if (which == 0) {
|
||||||
|
if (pinnedDbHelper != null) {
|
||||||
|
if (isPinned) {
|
||||||
|
pinnedDbHelper.unpinImage(imagePath);
|
||||||
|
} else {
|
||||||
|
pinnedDbHelper.pinImage(imagePath);
|
||||||
|
}
|
||||||
|
refreshPinned();
|
||||||
|
}
|
||||||
|
} else if (which == 1) {
|
||||||
|
if (coverDbHelper != null && albumPath != null) {
|
||||||
|
coverDbHelper.setCover(albumPath, imagePath);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
@@ -44,6 +148,8 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
|
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
|
||||||
|
holder.imageView.setBackgroundResource(getBgRes());
|
||||||
|
|
||||||
Glide.with(holder.imageView.getContext())
|
Glide.with(holder.imageView.getContext())
|
||||||
.load(imageUrls.get(position))
|
.load(imageUrls.get(position))
|
||||||
.centerCrop()
|
.centerCrop()
|
||||||
@@ -51,7 +157,15 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
|
|||||||
|
|
||||||
final ArrayList<Uri> urls = imageUrls;
|
final ArrayList<Uri> urls = imageUrls;
|
||||||
final ArrayList<String> paths = imagePaths;
|
final ArrayList<String> paths = imagePaths;
|
||||||
holder.imageView.setOnClickListener(new OnClickListener() {
|
final String imagePath = imagePaths.get(position);
|
||||||
|
|
||||||
|
boolean isPinned = pinnedDbHelper != null && pinnedDbHelper.isPinned(imagePath);
|
||||||
|
holder.pinIcon.setVisibility(isPinned ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
boolean isCover = coverDbHelper != null && albumPath != null && imagePath.equals(coverDbHelper.getCover(albumPath));
|
||||||
|
holder.coverIcon.setVisibility(isCover ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
holder.itemView.setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
@@ -59,6 +173,14 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View v) {
|
||||||
|
showContextMenu(holder.itemView, position);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -68,9 +190,13 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
|
|||||||
|
|
||||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
ImageView imageView;
|
ImageView imageView;
|
||||||
|
ImageView pinIcon;
|
||||||
|
ImageView coverIcon;
|
||||||
ViewHolder(View itemView) {
|
ViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
imageView = itemView.findViewById(R.id.image);
|
imageView = itemView.findViewById(R.id.image);
|
||||||
|
pinIcon = itemView.findViewById(R.id.pin_icon);
|
||||||
|
coverIcon = itemView.findViewById(R.id.cover_icon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,10 +16,25 @@ 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) {
|
public ImagePagerAdapter(ArrayList<Uri> imageUrls, int bgType) {
|
||||||
this.imageUrls = imageUrls;
|
this.imageUrls = imageUrls;
|
||||||
LogUtils.d(TAG, "ImagePagerAdapter created with " + imageUrls.size() + " images");
|
this.bgType = bgType;
|
||||||
|
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
|
||||||
@@ -32,6 +47,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());
|
||||||
ImageView imageView = view.findViewById(R.id.image);
|
ImageView imageView = view.findViewById(R.id.image);
|
||||||
|
|
||||||
Glide.with(imageView.getContext())
|
Glide.with(imageView.getContext())
|
||||||
|
|||||||
@@ -32,8 +32,12 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
|
|||||||
private ImageButton btnBack;
|
private ImageButton btnBack;
|
||||||
private ImageButton btnDelete;
|
private ImageButton btnDelete;
|
||||||
private ImageButton btnShare;
|
private ImageButton btnShare;
|
||||||
|
private ImageButton btnInfo;
|
||||||
|
private ImageButton btnBg;
|
||||||
|
private int bgType = 0;
|
||||||
private GestureDetector gestureDetector;
|
private GestureDetector gestureDetector;
|
||||||
private TrashManager trashManager;
|
private TrashManager trashManager;
|
||||||
|
private Preferences prefs;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -48,14 +52,20 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
|
|||||||
currentPosition = getIntent().getIntExtra(EXTRA_POSITION, 0);
|
currentPosition = getIntent().getIntExtra(EXTRA_POSITION, 0);
|
||||||
|
|
||||||
trashManager = new TrashManager(this);
|
trashManager = new TrashManager(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);
|
||||||
btnBack = findViewById(R.id.btn_back);
|
btnBack = findViewById(R.id.btn_back);
|
||||||
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);
|
||||||
|
btnBg = findViewById(R.id.btn_bg);
|
||||||
|
|
||||||
ImagePagerAdapter adapter = new ImagePagerAdapter(imageUrls);
|
applyBg();
|
||||||
|
|
||||||
|
ImagePagerAdapter adapter = new ImagePagerAdapter(imageUrls, bgType);
|
||||||
viewPager.setAdapter(adapter);
|
viewPager.setAdapter(adapter);
|
||||||
viewPager.setCurrentItem(currentPosition);
|
viewPager.setCurrentItem(currentPosition);
|
||||||
viewPager.addOnPageChangeListener(this);
|
viewPager.addOnPageChangeListener(this);
|
||||||
@@ -95,6 +105,20 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
|
|||||||
shareCurrentImage();
|
shareCurrentImage();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
btnInfo.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
showImageInfo();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnBg.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
switchBg();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toggleToolbar() {
|
private void toggleToolbar() {
|
||||||
@@ -105,6 +129,52 @@ 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?")
|
||||||
@@ -169,7 +239,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));
|
viewPager.setAdapter(new ImagePagerAdapter(imageUrls, bgType));
|
||||||
viewPager.setCurrentItem(currentPosition);
|
viewPager.setCurrentItem(currentPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,6 +271,155 @@ public class ImageViewerActivity extends Activity implements ViewPager.OnPageCha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showImageInfo() {
|
||||||
|
if (currentPosition < 0 || currentPosition >= imageUrls.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String imagePath = "";
|
||||||
|
if (imagePaths != null && currentPosition < imagePaths.size()) {
|
||||||
|
imagePath = imagePaths.get(currentPosition);
|
||||||
|
} else {
|
||||||
|
imagePath = getPathFromUri(imageUrls.get(currentPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
File imageFile = new File(imagePath);
|
||||||
|
if (!imageFile.exists()) {
|
||||||
|
imageFile = new File(imagePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
android.widget.LinearLayout layout = new android.widget.LinearLayout(this);
|
||||||
|
layout.setOrientation(android.widget.LinearLayout.VERTICAL);
|
||||||
|
layout.setPadding(48, 32, 48, 32);
|
||||||
|
|
||||||
|
android.widget.TextView labelPath = new android.widget.TextView(this);
|
||||||
|
labelPath.setText("Path:");
|
||||||
|
labelPath.setTextColor(getColor(android.R.color.darker_gray));
|
||||||
|
labelPath.setTextSize(14);
|
||||||
|
labelPath.setTypeface(null, android.graphics.Typeface.BOLD);
|
||||||
|
layout.addView(labelPath);
|
||||||
|
|
||||||
|
android.widget.TextView valuePath = new android.widget.TextView(this);
|
||||||
|
valuePath.setText(imagePath);
|
||||||
|
valuePath.setTextColor(getColor(android.R.color.black));
|
||||||
|
valuePath.setTextSize(14);
|
||||||
|
valuePath.setTextIsSelectable(true);
|
||||||
|
layout.addView(valuePath);
|
||||||
|
|
||||||
|
if (imageFile.exists()) {
|
||||||
|
long sizeBytes = imageFile.length();
|
||||||
|
String size;
|
||||||
|
if (sizeBytes < 1024) {
|
||||||
|
size = sizeBytes + " B";
|
||||||
|
} else if (sizeBytes < 1024 * 1024) {
|
||||||
|
size = String.format("%.2f KB", sizeBytes / 1024.0);
|
||||||
|
} else {
|
||||||
|
size = String.format("%.2f MB", sizeBytes / (1024.0 * 1024.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
android.widget.TextView labelSize = new android.widget.TextView(this);
|
||||||
|
labelSize.setText("Size:");
|
||||||
|
labelSize.setTextColor(getColor(android.R.color.darker_gray));
|
||||||
|
labelSize.setTextSize(14);
|
||||||
|
labelSize.setTypeface(null, android.graphics.Typeface.BOLD);
|
||||||
|
layout.addView(labelSize);
|
||||||
|
|
||||||
|
android.widget.TextView valueSize = new android.widget.TextView(this);
|
||||||
|
valueSize.setText(size);
|
||||||
|
valueSize.setTextColor(getColor(android.R.color.black));
|
||||||
|
valueSize.setTextSize(14);
|
||||||
|
valueSize.setTextIsSelectable(true);
|
||||||
|
layout.addView(valueSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
android.graphics.BitmapFactory.Options options = new android.graphics.BitmapFactory.Options();
|
||||||
|
options.inJustDecodeBounds = true;
|
||||||
|
android.graphics.BitmapFactory.decodeFile(imagePath, options);
|
||||||
|
if (options.outWidth > 0 && options.outHeight > 0) {
|
||||||
|
android.widget.TextView labelPixels = new android.widget.TextView(this);
|
||||||
|
labelPixels.setText("Pixels:");
|
||||||
|
labelPixels.setTextColor(getColor(android.R.color.darker_gray));
|
||||||
|
labelPixels.setTextSize(14);
|
||||||
|
labelPixels.setTypeface(null, android.graphics.Typeface.BOLD);
|
||||||
|
layout.addView(labelPixels);
|
||||||
|
|
||||||
|
android.widget.TextView valuePixels = new android.widget.TextView(this);
|
||||||
|
valuePixels.setText(options.outWidth + " x " + options.outHeight);
|
||||||
|
valuePixels.setTextColor(getColor(android.R.color.black));
|
||||||
|
valuePixels.setTextSize(14);
|
||||||
|
valuePixels.setTextIsSelectable(true);
|
||||||
|
layout.addView(valuePixels);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.e(TAG, "get pixels error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String[] projection = {
|
||||||
|
MediaStore.Images.Media.DATE_ADDED,
|
||||||
|
MediaStore.Images.Media.DATE_MODIFIED,
|
||||||
|
MediaStore.Images.Media.DATE_TAKEN
|
||||||
|
};
|
||||||
|
android.database.Cursor cursor = getContentResolver().query(
|
||||||
|
imageUrls.get(currentPosition), projection, null, null, null);
|
||||||
|
if (cursor != null) {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
int dateAddedCol = cursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED);
|
||||||
|
int dateTakenCol = cursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN);
|
||||||
|
|
||||||
|
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
if (dateTakenCol >= 0) {
|
||||||
|
long dateTaken = cursor.getLong(dateTakenCol);
|
||||||
|
if (dateTaken > 0) {
|
||||||
|
android.widget.TextView labelTaken = new android.widget.TextView(this);
|
||||||
|
labelTaken.setText("Date Taken:");
|
||||||
|
labelTaken.setTextColor(getColor(android.R.color.darker_gray));
|
||||||
|
labelTaken.setTextSize(14);
|
||||||
|
labelTaken.setTypeface(null, android.graphics.Typeface.BOLD);
|
||||||
|
layout.addView(labelTaken);
|
||||||
|
|
||||||
|
android.widget.TextView valueTaken = new android.widget.TextView(this);
|
||||||
|
valueTaken.setText(sdf.format(new java.util.Date(dateTaken)));
|
||||||
|
valueTaken.setTextColor(getColor(android.R.color.black));
|
||||||
|
valueTaken.setTextSize(14);
|
||||||
|
valueTaken.setTextIsSelectable(true);
|
||||||
|
layout.addView(valueTaken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dateAddedCol >= 0) {
|
||||||
|
long dateAdded = cursor.getLong(dateAddedCol);
|
||||||
|
if (dateAdded > 0) {
|
||||||
|
android.widget.TextView labelAdded = new android.widget.TextView(this);
|
||||||
|
labelAdded.setText("Date Added:");
|
||||||
|
labelAdded.setTextColor(getColor(android.R.color.darker_gray));
|
||||||
|
labelAdded.setTextSize(14);
|
||||||
|
labelAdded.setTypeface(null, android.graphics.Typeface.BOLD);
|
||||||
|
layout.addView(labelAdded);
|
||||||
|
|
||||||
|
android.widget.TextView valueAdded = new android.widget.TextView(this);
|
||||||
|
valueAdded.setText(sdf.format(new java.util.Date(dateAdded * 1000)));
|
||||||
|
valueAdded.setTextColor(getColor(android.R.color.black));
|
||||||
|
valueAdded.setTextSize(14);
|
||||||
|
valueAdded.setTextIsSelectable(true);
|
||||||
|
layout.addView(valueAdded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.e(TAG, "get date error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle("Image Info")
|
||||||
|
.setView(layout)
|
||||||
|
.setPositiveButton("OK", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPageSelected(int position) {
|
public void onPageSelected(int position) {
|
||||||
currentPosition = position;
|
currentPosition = position;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import android.provider.MediaStore;
|
|||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
@@ -22,6 +23,7 @@ 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.libappbase.LogUtils;
|
||||||
import cc.winboll.studio.libappbase.LogActivity;
|
import cc.winboll.studio.libappbase.LogActivity;
|
||||||
@@ -37,6 +39,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
private RecyclerView recyclerView;
|
private RecyclerView recyclerView;
|
||||||
private AlbumAdapter adapter;
|
private AlbumAdapter adapter;
|
||||||
private Preferences prefs;
|
private Preferences prefs;
|
||||||
|
private FloatingActionButton fabScrollTop;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -52,8 +55,33 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
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);
|
||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
|
fabScrollTop = findViewById(R.id.fab_scroll_top);
|
||||||
|
fabScrollTop.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
adapter.setOnAlbumClickListener(new OnAlbumClickListener() {
|
adapter.setOnAlbumClickListener(new OnAlbumClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onAlbumClick(Album album) {
|
public void onAlbumClick(Album album) {
|
||||||
@@ -131,16 +159,16 @@ 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 = Environment.getExternalStorageDirectory() + "/DCIM";
|
folderPath = Preferences.getDefaultPath();
|
||||||
baseFolder = new File(folderPath);
|
baseFolder = new File(folderPath);
|
||||||
LogUtils.d(TAG, "try DCIM: " + baseFolder.getAbsolutePath() + ", exists=" + baseFolder.exists());
|
LogUtils.d(TAG, "try default: " + baseFolder.getAbsolutePath() + ", exists=" + baseFolder.exists());
|
||||||
if (!baseFolder.exists()) {
|
if (!baseFolder.exists()) {
|
||||||
folderPath = Environment.getExternalStorageDirectory() + "/Pictures";
|
folderPath = Environment.getExternalStorageDirectory() + "/Pictures";
|
||||||
baseFolder = new File(folderPath);
|
baseFolder = new File(folderPath);
|
||||||
@@ -148,6 +176,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AlbumCoverDbHelper coverDbHelper = AlbumCoverDbHelper.getInstance(this);
|
||||||
ArrayList<Album> albums = new ArrayList<>();
|
ArrayList<Album> albums = new ArrayList<>();
|
||||||
|
|
||||||
FileFilter directoryFilter = new FileFilter() {
|
FileFilter directoryFilter = new FileFilter() {
|
||||||
@@ -161,11 +190,26 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
if (subfolders != null) {
|
if (subfolders != null) {
|
||||||
for (File subfolder : subfolders) {
|
for (File subfolder : subfolders) {
|
||||||
LogUtils.d(TAG, "scanning folder: " + subfolder.getName());
|
LogUtils.d(TAG, "scanning folder: " + subfolder.getName());
|
||||||
ArrayList<Uri> images = getImagesInFolder(subfolder.getAbsolutePath());
|
String albumPath = subfolder.getAbsolutePath();
|
||||||
if (!images.isEmpty()) {
|
String coverPath = coverDbHelper.getCover(albumPath);
|
||||||
Uri latestImage = images.get(0);
|
Uri coverUri = null;
|
||||||
albums.add(new Album(subfolder.getName(), subfolder.getAbsolutePath(), latestImage, images.size()));
|
if (coverPath != null) {
|
||||||
LogUtils.d(TAG, "album added: " + subfolder.getName() + ", " + images.size() + " images");
|
coverUri = getUriFromPath(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()) {
|
||||||
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,7 +221,24 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
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) {
|
||||||
|
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) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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();
|
||||||
@@ -244,6 +305,10 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
scanMediaStore();
|
scanMediaStore();
|
||||||
loadAlbums();
|
loadAlbums();
|
||||||
}
|
}
|
||||||
|
if (adapter != null) {
|
||||||
|
adapter.refreshBg();
|
||||||
|
adapter.refreshPinned();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scanMediaStore() {
|
private void scanMediaStore() {
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
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 PinnedImageDbHelper extends SQLiteOpenHelper {
|
||||||
|
public static final String TAG = "PinnedImageDbHelper";
|
||||||
|
private static final String DB_NAME = "pinned_image.db";
|
||||||
|
private static final int DB_VERSION = 1;
|
||||||
|
private static final String TABLE_NAME = "pinned_images";
|
||||||
|
private static final String COLUMN_PATH = "image_path";
|
||||||
|
|
||||||
|
private static final String SQL_CREATE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||||
|
+ COLUMN_PATH + " TEXT PRIMARY KEY)";
|
||||||
|
|
||||||
|
private static PinnedImageDbHelper dbHelper;
|
||||||
|
|
||||||
|
public static PinnedImageDbHelper getInstance(Context context) {
|
||||||
|
if (dbHelper == null) {
|
||||||
|
dbHelper = new PinnedImageDbHelper(context.getApplicationContext());
|
||||||
|
}
|
||||||
|
return dbHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PinnedImageDbHelper(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 pinImage(String imagePath) {
|
||||||
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(COLUMN_PATH, imagePath);
|
||||||
|
db.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_IGNORE);
|
||||||
|
LogUtils.d(TAG, "pinImage: " + imagePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unpinImage(String imagePath) {
|
||||||
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
|
db.delete(TABLE_NAME, COLUMN_PATH + " = ?", new String[]{imagePath});
|
||||||
|
LogUtils.d(TAG, "unpinImage: " + imagePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPinned(String imagePath) {
|
||||||
|
SQLiteDatabase db = getReadableDatabase();
|
||||||
|
Cursor cursor = db.query(TABLE_NAME, null, COLUMN_PATH + " = ?",
|
||||||
|
new String[]{imagePath}, 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,16 @@ public class Preferences {
|
|||||||
public static final String TAG = "Preferences";
|
public static final String TAG = "Preferences";
|
||||||
private static final String PREF_NAME = "gallery_prefs";
|
private static final String PREF_NAME = "gallery_prefs";
|
||||||
private static final String KEY_FOLDER_PATH = "folder_path";
|
private static final String KEY_FOLDER_PATH = "folder_path";
|
||||||
private static final String DEFAULT_PATH = "/storage/emulated/0/DCIM";
|
private static final String KEY_BG_TYPE = "bg_type";
|
||||||
|
private static final String KEY_ALBUM_SORT_MODE = "album_sort_mode";
|
||||||
|
private static final int DEFAULT_BG_TYPE = 0;
|
||||||
|
private static final int DEFAULT_SORT_MODE = 0;
|
||||||
|
private static final String DEFAULT_PATH = "/storage/emulated/0/Pictures/Gallery/owner";
|
||||||
|
|
||||||
|
public static final int SORT_TIME_DESC = 0;
|
||||||
|
public static final int SORT_TIME_ASC = 1;
|
||||||
|
public static final int SORT_NAME_DESC = 2;
|
||||||
|
public static final int SORT_NAME_ASC = 3;
|
||||||
|
|
||||||
public static String getDefaultPath() {
|
public static String getDefaultPath() {
|
||||||
return DEFAULT_PATH;
|
return DEFAULT_PATH;
|
||||||
@@ -30,4 +39,22 @@ public class Preferences {
|
|||||||
LogUtils.d(TAG, "setFolderPath: " + path);
|
LogUtils.d(TAG, "setFolderPath: " + path);
|
||||||
prefs.edit().putString(KEY_FOLDER_PATH, path).apply();
|
prefs.edit().putString(KEY_FOLDER_PATH, path).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getBgType() {
|
||||||
|
return prefs.getInt(KEY_BG_TYPE, DEFAULT_BG_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBgType(int type) {
|
||||||
|
LogUtils.d(TAG, "setBgType: " + type);
|
||||||
|
prefs.edit().putInt(KEY_BG_TYPE, type).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAlbumSortMode() {
|
||||||
|
return prefs.getInt(KEY_ALBUM_SORT_MODE, DEFAULT_SORT_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlbumSortMode(int mode) {
|
||||||
|
LogUtils.d(TAG, "setAlbumSortMode: " + mode);
|
||||||
|
prefs.edit().putInt(KEY_ALBUM_SORT_MODE, mode).apply();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
5
gallery/src/main/res/drawable/bg_black.xml
Normal file
5
gallery/src/main/res/drawable/bg_black.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#000000"/>
|
||||||
|
</shape>
|
||||||
30
gallery/src/main/res/drawable/bg_checkerboard.xml
Normal file
30
gallery/src/main/res/drawable/bg_checkerboard.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="10dp"
|
||||||
|
android:height="10dp"
|
||||||
|
android:viewportWidth="10"
|
||||||
|
android:viewportHeight="10">
|
||||||
|
<path
|
||||||
|
android:fillColor="#808080"
|
||||||
|
android:pathData="M0,0h1v1h-1z M2,0h1v1h-1z M4,0h1v1h-1z M6,0h1v1h-1z M8,0h1v1h-1z
|
||||||
|
M1,1h1v1h-1z M3,1h1v1h-1z M5,1h1v1h-1z M7,1h1v1h-1z M9,1h1v1h-1z
|
||||||
|
M0,2h1v1h-1z M2,2h1v1h-1z M4,2h1v1h-1z M6,2h1v1h-1z M8,2h1v1h-1z
|
||||||
|
M1,3h1v1h-1z M3,3h1v1h-1z M5,3h1v1h-1z M7,3h1v1h-1z M9,3h1v1h-1z
|
||||||
|
M0,4h1v1h-1z M2,4h1v1h-1z M4,4h1v1h-1z M6,4h1v1h-1z M8,4h1v1h-1z
|
||||||
|
M1,5h1v1h-1z M3,5h1v1h-1z M5,5h1v1h-1z M7,5h1v1h-1z M9,5h1v1h-1z
|
||||||
|
M0,6h1v1h-1z M2,6h1v1h-1z M4,6h1v1h-1z M6,6h1v1h-1z M8,6h1v1h-1z
|
||||||
|
M1,7h1v1h-1z M3,7h1v1h-1z M5,7h1v1h-1z M7,7h1v1h-1z M9,7h1v1h-1z
|
||||||
|
M0,8h1v1h-1z M2,8h1v1h-1z M4,8h1v1h-1z M6,8h1v1h-1z M8,8h1v1h-1z
|
||||||
|
M1,9h1v1h-1z M3,9h1v1h-1z M5,9h1v1h-1z M7,9h1v1h-1z M9,9h1v1h-1z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M1,0h1v1h-1z M3,0h1v1h-1z M5,0h1v1h-1z M7,0h1v1h-1z M9,0h1v1h-1z
|
||||||
|
M0,1h1v1h-1z M2,1h1v1h-1z M4,1h1v1h-1z M6,1h1v1h-1z M8,1h1v1h-1z
|
||||||
|
M1,2h1v1h-1z M3,2h1v1h-1z M5,2h1v1h-1z M7,2h1v1h-1z M9,2h1v1h-1z
|
||||||
|
M0,3h1v1h-1z M2,3h1v1h-1z M4,3h1v1h-1z M6,3h1v1h-1z M8,3h1v1h-1z
|
||||||
|
M1,4h1v1h-1z M3,4h1v1h-1z M5,4h1v1h-1z M7,4h1v1h-1z M9,4h1v1h-1z
|
||||||
|
M0,5h1v1h-1z M2,5h1v1h-1z M4,5h1v1h-1z M6,5h1v1h-1z M8,5h1v1h-1z
|
||||||
|
M1,6h1v1h-1z M3,6h1v1h-1z M5,6h1v1h-1z M7,6h1v1h-1z M9,6h1v1h-1z
|
||||||
|
M0,7h1v1h-1z M2,7h1v1h-1z M4,7h1v1h-1z M6,7h1v1h-1z M8,7h1v1h-1z
|
||||||
|
M1,8h1v1h-1z M3,8h1v1h-1z M5,8h1v1h-1z M7,8h1v1h-1z M9,8h1v1h-1z
|
||||||
|
M0,9h1v1h-1z M2,9h1v1h-1z M4,9h1v1h-1z M6,9h1v1h-1z M8,9h1v1h-1z"/>
|
||||||
|
</vector>
|
||||||
8
gallery/src/main/res/drawable/bg_circle_white.xml
Normal file
8
gallery/src/main/res/drawable/bg_circle_white.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="#CCFFFFFF"/>
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#000000"/>
|
||||||
|
</shape>
|
||||||
5
gallery/src/main/res/drawable/bg_white.xml
Normal file
5
gallery/src/main/res/drawable/bg_white.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#FFFFFF"/>
|
||||||
|
</shape>
|
||||||
10
gallery/src/main/res/drawable/ic_arrow_up.xml
Normal file
10
gallery/src/main/res/drawable/ic_arrow_up.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
|
||||||
|
</vector>
|
||||||
12
gallery/src/main/res/drawable/ic_bg.xml
Normal file
12
gallery/src/main/res/drawable/ic_bg.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M4,4h7v7h-7z M13,4h7v7h-7z M4,13h7v7h-7z M13,13h7v7h-7z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#808080"
|
||||||
|
android:pathData="M11,4h2v7h-2z M4,11h7v2h-7z M13,11h7v2h-7z M11,13h2v7h-2z M13,4h2v7h-2z M4,13h7v2h-7z"/>
|
||||||
|
</vector>
|
||||||
13
gallery/src/main/res/drawable/ic_cover.xml
Normal file
13
gallery/src/main/res/drawable/ic_cover.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:pathData="M21,3H3C1.9,3 1,3.9 1,5v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V5C23,3.9 22.1,3 21,3zM21,19H3V5h18V19z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:pathData="M9,12l2,2l4,-4l1.5,1.5L11,17l-3,-3z"/>
|
||||||
|
</vector>
|
||||||
9
gallery/src/main/res/drawable/ic_info.xml
Normal file
9
gallery/src/main/res/drawable/ic_info.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
|
||||||
|
</vector>
|
||||||
9
gallery/src/main/res/drawable/ic_pin.xml
Normal file
9
gallery/src/main/res/drawable/ic_pin.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M16,12V4H17V2H7V4H8V12L6,14V16H11.5V22H12.5V16H18V14L16,12Z"/>
|
||||||
|
</vector>
|
||||||
10
gallery/src/main/res/drawable/ic_sort.xml
Normal file
10
gallery/src/main/res/drawable/ic_sort.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M3,18h6v-2H3v2zM3,6v2h18V6H3zM3,13h12v-2H3v2z"/>
|
||||||
|
</vector>
|
||||||
@@ -44,6 +44,22 @@
|
|||||||
android:src="@drawable/ic_share"
|
android:src="@drawable/ic_share"
|
||||||
android:contentDescription="Share"/>
|
android:contentDescription="Share"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btn_info"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_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"
|
||||||
|
|||||||
@@ -1,28 +1,45 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<FrameLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
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:orientation="vertical">
|
||||||
|
|
||||||
</LinearLayout>
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fab_scroll_top"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:src="@drawable/ic_arrow_up"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:backgroundTint="@color/colorAccent"
|
||||||
|
app:tint="@color/white"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
@@ -9,8 +9,8 @@
|
|||||||
android:id="@+id/album_cover"
|
android:id="@+id/album_cover"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="120dp"
|
android:layout_height="120dp"
|
||||||
android:scaleType="centerCrop"
|
android:layout_margin="2dp"
|
||||||
android:background="@color/black"/>
|
android:scaleType="centerCrop"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/album_name"
|
android:id="@+id/album_name"
|
||||||
@@ -24,6 +24,17 @@
|
|||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:ellipsize="end"/>
|
android:ellipsize="end"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/pin_icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="6dp"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:padding="3dp"
|
||||||
|
android:src="@drawable/ic_pin"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/image_count"
|
android:id="@+id/image_count"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -12,4 +12,30 @@
|
|||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:background="@color/black"/>
|
android:background="@color/black"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/pin_icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="top|end"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
android:background="@drawable/bg_circle_white"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:src="@drawable/ic_pin"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:tint="@color/black"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/cover_icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
android:background="@drawable/bg_circle_white"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:src="@drawable/ic_cover"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:tint="@color/black"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"/>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
@@ -2,8 +2,7 @@
|
|||||||
<FrameLayout
|
<FrameLayout
|
||||||
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="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:background="@color/black">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/image"
|
android:id="@+id/image"
|
||||||
|
|||||||
26
gallery/src/main/res/menu/menu_album.xml
Normal file
26
gallery/src/main/res/menu/menu_album.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_sort"
|
||||||
|
android:icon="@drawable/ic_sort"
|
||||||
|
android:title="排序"
|
||||||
|
app:showAsAction="ifRoom">
|
||||||
|
<menu>
|
||||||
|
<group android:checkableBehavior="single">
|
||||||
|
<item
|
||||||
|
android:id="@+id/sort_time_desc"
|
||||||
|
android:title="时间倒序"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/sort_time_asc"
|
||||||
|
android:title="时间正序"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/sort_name_desc"
|
||||||
|
android:title="名称倒序"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/sort_name_asc"
|
||||||
|
android:title="名称正序"/>
|
||||||
|
</group>
|
||||||
|
</menu>
|
||||||
|
</item>
|
||||||
|
</menu>
|
||||||
Reference in New Issue
Block a user