Compare commits
5 Commits
gallery-v1
...
gallery-v1
| Author | SHA1 | Date | |
|---|---|---|---|
| 7456db1729 | |||
| 226cbf43fe | |||
| 4a267d5606 | |||
| 4d0d8c4d59 | |||
| 9a43267b63 |
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Sat Apr 25 20:05:35 HKT 2026
|
#Sun Apr 26 10:16:07 HKT 2026
|
||||||
stageCount=4
|
stageCount=5
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.0
|
baseVersion=15.0
|
||||||
publishVersion=15.0.3
|
publishVersion=15.0.4
|
||||||
buildCount=0
|
buildCount=0
|
||||||
baseBetaVersion=15.0.4
|
baseBetaVersion=15.0.5
|
||||||
|
|||||||
@@ -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 RecyclerView.OnScrollListener scrollListener;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -51,6 +55,31 @@ public class AlbumActivity extends AppCompatActivity {
|
|||||||
adapter.setContext(this);
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scrollListener = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
recyclerView.addOnScrollListener(scrollListener);
|
||||||
|
|
||||||
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) {
|
||||||
@@ -127,23 +156,6 @@ public class AlbumActivity extends AppCompatActivity {
|
|||||||
LogUtils.d(TAG, "Loaded " + imageUrls.size() + " images");
|
LogUtils.d(TAG, "Loaded " + imageUrls.size() + " images");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
getMenuInflater().inflate(R.menu.menu_main, menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
|
||||||
if (item.getItemId() == R.id.action_refresh) {
|
|
||||||
if (checkPermission()) {
|
|
||||||
loadImages();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -22,6 +25,7 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
|
|||||||
private OnAlbumClickListener listener;
|
private OnAlbumClickListener listener;
|
||||||
private Preferences prefs;
|
private Preferences prefs;
|
||||||
private int bgType = 0;
|
private int bgType = 0;
|
||||||
|
private PinnedAlbumDbHelper pinnedDbHelper;
|
||||||
|
|
||||||
private int getBgRes() {
|
private int getBgRes() {
|
||||||
switch (bgType) {
|
switch (bgType) {
|
||||||
@@ -45,23 +49,90 @@ 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) {
|
public void setContext(android.content.Context context) {
|
||||||
prefs = new Preferences(context);
|
prefs = new Preferences(context);
|
||||||
bgType = prefs.getBgType();
|
bgType = prefs.getBgType();
|
||||||
|
pinnedDbHelper = PinnedAlbumDbHelper.getInstance(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshBg() {
|
public void refreshBg() {
|
||||||
if (prefs != null) {
|
if (prefs != null) {
|
||||||
bgType = prefs.getBgType();
|
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();
|
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
|
||||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
@@ -77,6 +148,17 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
|
|||||||
|
|
||||||
holder.coverImage.setBackgroundResource(getBgRes());
|
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");
|
||||||
|
|
||||||
@@ -134,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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -20,6 +23,7 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
|
|||||||
private OnImageClickListener listener;
|
private OnImageClickListener listener;
|
||||||
private int bgType = 0;
|
private int bgType = 0;
|
||||||
private Preferences prefs;
|
private Preferences prefs;
|
||||||
|
private PinnedImageDbHelper pinnedDbHelper;
|
||||||
|
|
||||||
private int getBgRes() {
|
private int getBgRes() {
|
||||||
switch (bgType) {
|
switch (bgType) {
|
||||||
@@ -45,13 +49,60 @@ 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<String> pinnedPaths = new ArrayList<>();
|
||||||
|
ArrayList<String> unpinnedPaths = new ArrayList<>();
|
||||||
|
for (String path : imagePaths) {
|
||||||
|
if (pinnedDbHelper.isPinned(path)) {
|
||||||
|
pinnedPaths.add(path);
|
||||||
|
} else {
|
||||||
|
unpinnedPaths.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Comparator<String> pathComparator = new Comparator<String>() {
|
||||||
|
@Override
|
||||||
|
public int compare(String p1, String p2) {
|
||||||
|
return p1.compareToIgnoreCase(p2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Collections.sort(pinnedPaths, pathComparator);
|
||||||
|
Collections.sort(unpinnedPaths, pathComparator);
|
||||||
|
|
||||||
|
ArrayList<Uri> newUrls = new ArrayList<>();
|
||||||
|
ArrayList<String> newPaths = new ArrayList<>();
|
||||||
|
for (String path : pinnedPaths) {
|
||||||
|
int index = imagePaths.indexOf(path);
|
||||||
|
if (index >= 0) {
|
||||||
|
newUrls.add(imageUrls.get(index));
|
||||||
|
newPaths.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String path : unpinnedPaths) {
|
||||||
|
int index = imagePaths.indexOf(path);
|
||||||
|
if (index >= 0) {
|
||||||
|
newUrls.add(imageUrls.get(index));
|
||||||
|
newPaths.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imageUrls.clear();
|
||||||
|
imagePaths.clear();
|
||||||
|
imageUrls.addAll(newUrls);
|
||||||
|
imagePaths.addAll(newPaths);
|
||||||
|
}
|
||||||
|
|
||||||
public void setContext(android.content.Context context) {
|
public void setContext(android.content.Context context) {
|
||||||
prefs = new Preferences(context);
|
prefs = new Preferences(context);
|
||||||
bgType = prefs.getBgType();
|
bgType = prefs.getBgType();
|
||||||
|
pinnedDbHelper = PinnedImageDbHelper.getInstance(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshBg() {
|
public void refreshBg() {
|
||||||
@@ -61,6 +112,36 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (pinnedDbHelper != null && which == 0) {
|
||||||
|
if (isPinned) {
|
||||||
|
pinnedDbHelper.unpinImage(imagePath);
|
||||||
|
} else {
|
||||||
|
pinnedDbHelper.pinImage(imagePath);
|
||||||
|
}
|
||||||
|
refreshPinned();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
@@ -80,7 +161,12 @@ 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);
|
||||||
|
|
||||||
|
holder.itemView.setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
@@ -88,6 +174,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
|
||||||
@@ -97,9 +191,11 @@ 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;
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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) {
|
||||||
@@ -55,6 +58,30 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
adapter.setContext(this);
|
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) {
|
||||||
@@ -247,6 +274,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
adapter.refreshBg();
|
adapter.refreshBg();
|
||||||
|
adapter.refreshPinned();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
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>
|
||||||
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>
|
||||||
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>
|
||||||
@@ -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,17 @@
|
|||||||
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"/>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
Reference in New Issue
Block a user