diff --git a/app/build.gradle b/app/build.gradle
index b3a5152..cc8b112 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -17,10 +17,20 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+
+ buildFeatures {
+ viewBinding true
+ }
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'com.google.android.material:material:1.0.0'
+ implementation 'androidx.recyclerview:recyclerview:1.1.0'
+ implementation 'androidx.cardview:cardview:1.0.0'
+ implementation 'androidx.viewpager:viewpager:1.0.0'
+ implementation 'com.github.bumptech.glide:glide:4.12.0'
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 63a620e..bd6fbab 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,9 @@
xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.gallery">
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/cc/winboll/gallery/Album.java b/app/src/main/java/cc/winboll/gallery/Album.java
new file mode 100644
index 0000000..f4a51f4
--- /dev/null
+++ b/app/src/main/java/cc/winboll/gallery/Album.java
@@ -0,0 +1,22 @@
+package cc.winboll.gallery;
+
+import android.net.Uri;
+
+public class Album {
+ private String name;
+ private String path;
+ private Uri coverUri;
+ private int imageCount;
+
+ public Album(String name, String path, Uri coverUri, int imageCount) {
+ this.name = name;
+ this.path = path;
+ this.coverUri = coverUri;
+ this.imageCount = imageCount;
+ }
+
+ public String getName() { return name; }
+ public String getPath() { return path; }
+ public Uri getCoverUri() { return coverUri; }
+ public int getImageCount() { return imageCount; }
+}
\ No newline at end of file
diff --git a/app/src/main/java/cc/winboll/gallery/AlbumActivity.java b/app/src/main/java/cc/winboll/gallery/AlbumActivity.java
new file mode 100644
index 0000000..49c87b6
--- /dev/null
+++ b/app/src/main/java/cc/winboll/gallery/AlbumActivity.java
@@ -0,0 +1,131 @@
+package cc.winboll.gallery;
+
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import java.util.ArrayList;
+
+public class AlbumActivity extends AppCompatActivity {
+ private static final int PERMISSION_REQUEST_CODE = 101;
+ public static final String EXTRA_ALBUM_PATH = "album_path";
+ public static final String EXTRA_ALBUM_NAME = "album_name";
+ private RecyclerView recyclerView;
+ private ImageAdapter adapter;
+ private String albumPath;
+ private String albumName;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ albumPath = getIntent().getStringExtra(EXTRA_ALBUM_PATH);
+ albumName = getIntent().getStringExtra(EXTRA_ALBUM_NAME);
+
+ getSupportActionBar().setTitle(albumName);
+
+ recyclerView = findViewById(R.id.recycler_view);
+ recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
+ adapter = new ImageAdapter();
+ recyclerView.setAdapter(adapter);
+
+ adapter.setOnImageClickListener((position, urls) -> {
+ Intent intent = new Intent(this, ImageViewerActivity.class);
+ intent.putParcelableArrayListExtra(ImageViewerActivity.EXTRA_IMAGE_URLS, urls);
+ intent.putExtra(ImageViewerActivity.EXTRA_POSITION, position);
+ startActivity(intent);
+ });
+
+ if (checkPermission()) {
+ loadImages();
+ } else {
+ requestPermission();
+ }
+ }
+
+ private boolean checkPermission() {
+ return ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private void requestPermission() {
+ ActivityCompat.requestPermissions(this,
+ new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE},
+ PERMISSION_REQUEST_CODE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == PERMISSION_REQUEST_CODE) {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ loadImages();
+ } else {
+ Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ private void loadImages() {
+ ArrayList imageUrls = new ArrayList<>();
+ ContentResolver contentResolver = getContentResolver();
+ Uri collection = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+
+ String selection = android.provider.MediaStore.Images.Media.DATA + " LIKE ?";
+ String[] selectionArgs = new String[]{albumPath + "%"};
+ String sortOrder = android.provider.MediaStore.Images.Media.DATE_ADDED + " DESC";
+
+ try (Cursor cursor = contentResolver.query(collection, null, selection, selectionArgs, sortOrder)) {
+ if (cursor != null) {
+ int dataColumn = cursor.getColumnIndexOrThrow(android.provider.MediaStore.Images.Media.DATA);
+ while (cursor.moveToNext()) {
+ String path = cursor.getString(dataColumn);
+ if (path != null && path.startsWith(albumPath + "/")) {
+ long id = cursor.getLong(cursor.getColumnIndexOrThrow(android.provider.MediaStore.Images.Media._ID));
+ Uri contentUri = Uri.withAppendedPath(collection, String.valueOf(id));
+ imageUrls.add(contentUri);
+ }
+ }
+ }
+ }
+
+ if (imageUrls.isEmpty()) {
+ Toast.makeText(this, R.string.no_images_found, Toast.LENGTH_SHORT).show();
+ }
+ adapter.setData(imageUrls);
+ }
+
+ @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);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/cc/winboll/gallery/AlbumAdapter.java b/app/src/main/java/cc/winboll/gallery/AlbumAdapter.java
new file mode 100644
index 0000000..56c7e74
--- /dev/null
+++ b/app/src/main/java/cc/winboll/gallery/AlbumAdapter.java
@@ -0,0 +1,71 @@
+package cc.winboll.gallery;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import com.bumptech.glide.Glide;
+import java.util.ArrayList;
+
+public class AlbumAdapter extends RecyclerView.Adapter {
+ private ArrayList albums = new ArrayList<>();
+ private OnAlbumClickListener listener;
+
+ public interface OnAlbumClickListener {
+ void onAlbumClick(Album album);
+ }
+
+ public void setOnAlbumClickListener(OnAlbumClickListener listener) {
+ this.listener = listener;
+ }
+
+ public void setData(ArrayList albums) {
+ this.albums = albums;
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_album, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ Album album = albums.get(position);
+ Glide.with(holder.coverImage.getContext())
+ .load(album.getCoverUri())
+ .centerCrop()
+ .into(holder.coverImage);
+ holder.albumName.setText(album.getName());
+ holder.imageCount.setText(album.getImageCount() + " photos");
+
+ holder.itemView.setOnClickListener(v -> {
+ if (listener != null) {
+ listener.onAlbumClick(album);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return albums.size();
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ ImageView coverImage;
+ TextView albumName;
+ TextView imageCount;
+ ViewHolder(View itemView) {
+ super(itemView);
+ coverImage = itemView.findViewById(R.id.album_cover);
+ albumName = itemView.findViewById(R.id.album_name);
+ imageCount = itemView.findViewById(R.id.image_count);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/cc/winboll/gallery/ImageAdapter.java b/app/src/main/java/cc/winboll/gallery/ImageAdapter.java
new file mode 100644
index 0000000..ff39fa7
--- /dev/null
+++ b/app/src/main/java/cc/winboll/gallery/ImageAdapter.java
@@ -0,0 +1,65 @@
+package cc.winboll.gallery;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import com.bumptech.glide.Glide;
+import java.util.ArrayList;
+
+public class ImageAdapter extends RecyclerView.Adapter {
+ private ArrayList imageUrls = new ArrayList<>();
+ private OnImageClickListener listener;
+
+ public interface OnImageClickListener {
+ void onImageClick(int position, ArrayList urls);
+ }
+
+ public void setOnImageClickListener(OnImageClickListener listener) {
+ this.listener = listener;
+ }
+
+ public void setData(ArrayList urls) {
+ this.imageUrls = urls;
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_gallery, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ Glide.with(holder.imageView.getContext())
+ .load(imageUrls.get(position))
+ .centerCrop()
+ .into(holder.imageView);
+
+ holder.imageView.setOnClickListener(v -> {
+ if (listener != null) {
+ listener.onImageClick(position, imageUrls);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return imageUrls.size();
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ ImageView imageView;
+ ViewHolder(View itemView) {
+ super(itemView);
+ imageView = itemView.findViewById(R.id.image);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/cc/winboll/gallery/ImagePagerAdapter.java b/app/src/main/java/cc/winboll/gallery/ImagePagerAdapter.java
new file mode 100644
index 0000000..047ed97
--- /dev/null
+++ b/app/src/main/java/cc/winboll/gallery/ImagePagerAdapter.java
@@ -0,0 +1,52 @@
+package cc.winboll.gallery;
+
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import androidx.annotation.NonNull;
+import androidx.viewpager.widget.PagerAdapter;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import java.util.ArrayList;
+
+public class ImagePagerAdapter extends PagerAdapter {
+ private ArrayList imageUrls;
+
+ public ImagePagerAdapter(ArrayList imageUrls) {
+ this.imageUrls = imageUrls;
+ }
+
+ @Override
+ public int getCount() {
+ return imageUrls.size();
+ }
+
+ @NonNull
+ @Override
+ public Object instantiateItem(@NonNull ViewGroup container, int position) {
+ View view = LayoutInflater.from(container.getContext())
+ .inflate(R.layout.item_image_pager, container, false);
+ ImageView imageView = view.findViewById(R.id.image);
+
+ Glide.with(imageView.getContext())
+ .load(imageUrls.get(position))
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .skipMemoryCache(true)
+ .into(imageView);
+
+ container.addView(view);
+ return view;
+ }
+
+ @Override
+ public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
+ container.removeView((View) object);
+ }
+
+ @Override
+ public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
+ return view == object;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/cc/winboll/gallery/ImageViewerActivity.java b/app/src/main/java/cc/winboll/gallery/ImageViewerActivity.java
new file mode 100644
index 0000000..39fee1f
--- /dev/null
+++ b/app/src/main/java/cc/winboll/gallery/ImageViewerActivity.java
@@ -0,0 +1,73 @@
+package cc.winboll.gallery;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageButton;
+import androidx.viewpager.widget.ViewPager;
+import java.util.ArrayList;
+
+public class ImageViewerActivity extends Activity implements ViewPager.OnPageChangeListener {
+ public static final String EXTRA_IMAGE_URLS = "image_urls";
+ public static final String EXTRA_POSITION = "position";
+
+ private ArrayList imageUrls;
+ private int currentPosition;
+ private ViewPager viewPager;
+ private View toolbar;
+ private ImageButton btnBack;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ setContentView(R.layout.activity_image_viewer);
+
+ imageUrls = getIntent().getParcelableArrayListExtra(EXTRA_IMAGE_URLS);
+ currentPosition = getIntent().getIntExtra(EXTRA_POSITION, 0);
+
+ viewPager = findViewById(R.id.view_pager);
+ toolbar = findViewById(R.id.toolbar);
+ btnBack = findViewById(R.id.btn_back);
+
+ ImagePagerAdapter adapter = new ImagePagerAdapter(imageUrls);
+ viewPager.setAdapter(adapter);
+ viewPager.setCurrentItem(currentPosition);
+ viewPager.addOnPageChangeListener(this);
+
+ btnBack.setOnClickListener(v -> finish());
+
+ viewPager.setOnClickListener(v -> toggleToolbar());
+ }
+
+ private void toggleToolbar() {
+ if (toolbar.getVisibility() == View.VISIBLE) {
+ toolbar.setVisibility(View.GONE);
+ } else {
+ toolbar.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ currentPosition = position;
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {}
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
+
+ @Override
+ public void onBackPressed() {
+ if (viewPager.getCurrentItem() > 0) {
+ viewPager.setCurrentItem(viewPager.getCurrentItem() - 1);
+ } else {
+ super.onBackPressed();
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/cc/winboll/gallery/MainActivity.java b/app/src/main/java/cc/winboll/gallery/MainActivity.java
index 6a3e996..bf51ae1 100644
--- a/app/src/main/java/cc/winboll/gallery/MainActivity.java
+++ b/app/src/main/java/cc/winboll/gallery/MainActivity.java
@@ -1,19 +1,169 @@
package cc.winboll.gallery;
+import android.Manifest;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
import android.os.Bundle;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import java.io.File;
+import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
-
+ private static final int PERMISSION_REQUEST_CODE = 100;
+ private RecyclerView recyclerView;
+ private AlbumAdapter adapter;
+ private Preferences prefs;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
-
- Toolbar toolbar=(Toolbar)findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ prefs = new Preferences(this);
+
+ recyclerView = findViewById(R.id.recycler_view);
+ recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
+ adapter = new AlbumAdapter();
+ recyclerView.setAdapter(adapter);
+
+ adapter.setOnAlbumClickListener(album -> {
+ Intent intent = new Intent(this, AlbumActivity.class);
+ intent.putExtra(AlbumActivity.EXTRA_ALBUM_PATH, album.getPath());
+ intent.putExtra(AlbumActivity.EXTRA_ALBUM_NAME, album.getName());
+ startActivity(intent);
+ });
+
+ if (checkPermission()) {
+ loadAlbums();
+ } else {
+ requestPermission();
+ }
+ }
+
+ private boolean checkPermission() {
+ return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private void requestPermission() {
+ ActivityCompat.requestPermissions(this,
+ new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ PERMISSION_REQUEST_CODE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == PERMISSION_REQUEST_CODE) {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ loadAlbums();
+ } else {
+ Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ private void loadAlbums() {
+ String folderPath = prefs.getFolderPath();
+ File baseFolder = new File(folderPath);
+
+ if (!baseFolder.exists() || !baseFolder.isDirectory()) {
+ folderPath = Environment.getExternalStorageDirectory() + "/DCIM";
+ baseFolder = new File(folderPath);
+ if (!baseFolder.exists()) {
+ folderPath = Environment.getExternalStorageDirectory() + "/Pictures";
+ baseFolder = new File(folderPath);
+ }
+ }
+
+ ArrayList albums = new ArrayList<>();
+
+ File[] subfolders = baseFolder.listFiles(File::isDirectory);
+ if (subfolders != null) {
+ for (File subfolder : subfolders) {
+ ArrayList images = getImagesInFolder(subfolder.getAbsolutePath());
+ if (!images.isEmpty()) {
+ Uri latestImage = images.get(0);
+ albums.add(new Album(subfolder.getName(), subfolder.getAbsolutePath(), latestImage, images.size()));
+ }
+ }
+ }
+
+ if (albums.isEmpty()) {
+ Toast.makeText(this, R.string.no_images_found, Toast.LENGTH_SHORT).show();
+ }
+ adapter.setData(albums);
+ }
+
+ private ArrayList getImagesInFolder(String folderPath) {
+ ArrayList imageUrls = new ArrayList<>();
+ ContentResolver contentResolver = getContentResolver();
+ Uri collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+
+ String selection = MediaStore.Images.Media.DATA + " LIKE ?";
+ String[] selectionArgs = new String[]{folderPath + "/%"};
+ String sortOrder = MediaStore.Images.Media.DATE_ADDED + " DESC";
+
+ try (Cursor cursor = contentResolver.query(collection, null, selection, selectionArgs, sortOrder)) {
+ if (cursor != null) {
+ int dataColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
+ while (cursor.moveToNext()) {
+ String path = cursor.getString(dataColumn);
+ if (path != null) {
+ long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
+ Uri contentUri = Uri.withAppendedPath(collection, String.valueOf(id));
+ imageUrls.add(contentUri);
+ }
+ }
+ }
+ }
+ return imageUrls;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ int id = item.getItemId();
+ if (id == R.id.action_settings) {
+ startActivity(new Intent(this, SettingsActivity.class));
+ return true;
+ } else if (id == R.id.action_refresh) {
+ if (checkPermission()) {
+ loadAlbums();
+ }
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
}
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (checkPermission()) {
+ loadAlbums();
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/cc/winboll/gallery/Preferences.java b/app/src/main/java/cc/winboll/gallery/Preferences.java
new file mode 100644
index 0000000..f9ce948
--- /dev/null
+++ b/app/src/main/java/cc/winboll/gallery/Preferences.java
@@ -0,0 +1,28 @@
+package cc.winboll.gallery;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+public class Preferences {
+ private static final String PREF_NAME = "gallery_prefs";
+ private static final String KEY_FOLDER_PATH = "folder_path";
+ private static final String DEFAULT_PATH = "/storage/emulated/0/DCIM";
+
+ public static String getDefaultPath() {
+ return DEFAULT_PATH;
+ }
+
+ private final SharedPreferences prefs;
+
+ public Preferences(Context context) {
+ prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+ }
+
+ public String getFolderPath() {
+ return prefs.getString(KEY_FOLDER_PATH, DEFAULT_PATH);
+ }
+
+ public void setFolderPath(String path) {
+ prefs.edit().putString(KEY_FOLDER_PATH, path).apply();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/cc/winboll/gallery/SettingsActivity.java b/app/src/main/java/cc/winboll/gallery/SettingsActivity.java
new file mode 100644
index 0000000..bd94ddd
--- /dev/null
+++ b/app/src/main/java/cc/winboll/gallery/SettingsActivity.java
@@ -0,0 +1,38 @@
+package cc.winboll.gallery;
+
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import androidx.appcompat.app.AppCompatActivity;
+
+public class SettingsActivity extends AppCompatActivity {
+ private Preferences prefs;
+ private EditText editFolderPath;
+ private TextView textCurrentPath;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_settings);
+
+ prefs = new Preferences(this);
+
+ editFolderPath = findViewById(R.id.edit_folder_path);
+ textCurrentPath = findViewById(R.id.text_current_path);
+ Button btnSave = findViewById(R.id.btn_save);
+
+ String currentPath = prefs.getFolderPath();
+ editFolderPath.setText(currentPath);
+ textCurrentPath.setText("Current: " + currentPath);
+
+ btnSave.setOnClickListener(v -> {
+ String newPath = editFolderPath.getText().toString().trim();
+ if (!newPath.isEmpty()) {
+ prefs.setFolderPath(newPath);
+ textCurrentPath.setText("Current: " + newPath);
+ }
+ finish();
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_back.xml b/app/src/main/res/drawable/ic_back.xml
new file mode 100644
index 0000000..0cdf891
--- /dev/null
+++ b/app/src/main/res/drawable/ic_back.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_image_viewer.xml b/app/src/main/res/layout/activity_image_viewer.xml
new file mode 100644
index 0000000..8ffd344
--- /dev/null
+++ b/app/src/main/res/layout/activity_image_viewer.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 9e910fa..ccb42b7 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,22 +1,28 @@
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
-
-
-
+
+
+
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
new file mode 100644
index 0000000..24d2676
--- /dev/null
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_album.xml b/app/src/main/res/layout/item_album.xml
new file mode 100644
index 0000000..a73ebb1
--- /dev/null
+++ b/app/src/main/res/layout/item_album.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_gallery.xml b/app/src/main/res/layout/item_gallery.xml
new file mode 100644
index 0000000..c9a4c77
--- /dev/null
+++ b/app/src/main/res/layout/item_gallery.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_image_pager.xml b/app/src/main/res/layout/item_image_pager.xml
new file mode 100644
index 0000000..7ef0e81
--- /dev/null
+++ b/app/src/main/res/layout/item_image_pager.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..a2b736c
--- /dev/null
+++ b/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,15 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 479769a..f91a689 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -3,4 +3,6 @@
#009688
#00796B
#FF9800
+ #000000
+ #FFFFFF
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 41cb790..e3b00e5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,4 +1,11 @@
Gallery
-
+ Refresh
+ Settings
+ Settings
+ Folder Path
+ Enter folder path
+ Save
+ Cancel
+ No images found
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..249e583
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..dfbabe2
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
\ No newline at end of file
diff --git a/gradlew b/gradlew
old mode 100644
new mode 100755
diff --git a/local.properties b/local.properties
index f4fcdb0..f68e558 100644
--- a/local.properties
+++ b/local.properties
@@ -7,4 +7,4 @@
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
-#sdk.dir=
\ No newline at end of file
+sdk.dir=/home/jian/Android/Sdk
\ No newline at end of file