From 66452a26e3bfb0a95b0f1ec7be80ee96f856c198 Mon Sep 17 00:00:00 2001 From: kollerlukas Date: Sat, 11 Feb 2017 23:53:20 -0800 Subject: [PATCH] - added simple FileExplorer to copy, move and delete items - Bug fixes --- app/src/main/AndroidManifest.xml | 4 + .../us/koller/cameraroll/IntentReceiver.java | 1 + .../fileExplorer/RecyclerViewAdapter.java | 217 ++++++ .../fileExplorer/ViewHolder/FileHolder.java | 56 ++ .../item/ViewHolder/PhotoViewHolder.java | 7 +- .../us/koller/cameraroll/data/AlbumItem.java | 15 +- .../us/koller/cameraroll/data/File_POJO.java | 82 +++ .../data/FilesLoader/FilesLoader.java | 246 +++++++ .../MediaLoader/Loader/StorageLoader.java | 31 +- .../koller/cameraroll/ui/AlbumActivity.java | 3 +- .../cameraroll/ui/FileExplorerActivity.java | 633 ++++++++++++++++++ .../us/koller/cameraroll/ui/ItemActivity.java | 7 +- .../us/koller/cameraroll/ui/MainActivity.java | 25 +- .../koller/cameraroll/util/ItemViewUtil.java | 17 +- .../us/koller/cameraroll/util/SortUtil.java | 7 + .../res/layout/activity_file_explorer.xml | 17 + app/src/main/res/layout/file_cover.xml | 26 + .../main/res/layout/recycler_view_layout.xml | 3 +- app/src/main/res/menu/file_explorer.xml | 19 + app/src/main/res/menu/main.xml | 5 + app/src/main/res/values/strings.xml | 9 + app/src/main/res/values/styles.xml | 5 + 22 files changed, 1400 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/us/koller/cameraroll/adapter/fileExplorer/RecyclerViewAdapter.java create mode 100644 app/src/main/java/us/koller/cameraroll/adapter/fileExplorer/ViewHolder/FileHolder.java create mode 100644 app/src/main/java/us/koller/cameraroll/data/File_POJO.java create mode 100644 app/src/main/java/us/koller/cameraroll/data/FilesLoader/FilesLoader.java create mode 100644 app/src/main/java/us/koller/cameraroll/ui/FileExplorerActivity.java create mode 100644 app/src/main/res/layout/activity_file_explorer.xml create mode 100644 app/src/main/res/layout/file_cover.xml create mode 100644 app/src/main/res/menu/file_explorer.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b3648984..bba7322c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -35,6 +35,10 @@ android:theme="@style/Theme.CameraRoll.Translucent.AboutActivity" android:launchMode="singleTop"/> + + diff --git a/app/src/main/java/us/koller/cameraroll/IntentReceiver.java b/app/src/main/java/us/koller/cameraroll/IntentReceiver.java index abccd3b2..f6423148 100644 --- a/app/src/main/java/us/koller/cameraroll/IntentReceiver.java +++ b/app/src/main/java/us/koller/cameraroll/IntentReceiver.java @@ -60,6 +60,7 @@ public void viewPhoto(Intent intent) { .putExtra(ItemActivity.VIEW_ONLY, true) .putExtra(ItemActivity.ALBUM, album) .putExtra(ItemActivity.ITEM_POSITION, album.getAlbumItems().indexOf(albumItem)) + .putExtra(ItemActivity.FINISH_AFTER, true) .addFlags(intent.getFlags()); startActivity(view_photo); } diff --git a/app/src/main/java/us/koller/cameraroll/adapter/fileExplorer/RecyclerViewAdapter.java b/app/src/main/java/us/koller/cameraroll/adapter/fileExplorer/RecyclerViewAdapter.java new file mode 100644 index 00000000..b13b3290 --- /dev/null +++ b/app/src/main/java/us/koller/cameraroll/adapter/fileExplorer/RecyclerViewAdapter.java @@ -0,0 +1,217 @@ +package us.koller.cameraroll.adapter.fileExplorer; + +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.os.Handler; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import us.koller.cameraroll.R; +import us.koller.cameraroll.adapter.fileExplorer.ViewHolder.FileHolder; +import us.koller.cameraroll.data.Album; +import us.koller.cameraroll.data.AlbumItem; +import us.koller.cameraroll.data.File_POJO; +import us.koller.cameraroll.ui.ItemActivity; + +public class RecyclerViewAdapter extends RecyclerView.Adapter { + + public static final int NORMAL_MODE = 0; + public static final int SELECTOR_MODE = 1; + public static final int PICK_TARGET_MODE = 2; + + + public interface Callback { + void onSelectorModeEnter(); + + void onSelectorModeExit(File_POJO[] selected_items); + + void onItemSelected(int count); + + void onPickTargetModeEnter(); + + void onPickTargetModeExit(); + + void onDataChanged(); + } + + private File_POJO files; + + private int mode = NORMAL_MODE; + private boolean[] selected_items; + + private Callback callback; + + public RecyclerViewAdapter(Callback callback) { + this.callback = callback; + } + + public RecyclerViewAdapter setFiles(File_POJO files) { + this.files = files; + selected_items = new boolean[files.getChildren().size()]; + return this; + } + + public File_POJO getFiles() { + return files; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.file_cover, parent, false); + return new FileHolder(v); + } + + @Override + public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) { + final File_POJO file = files.getChildren().get(position); + + ((FileHolder) holder).setFile(file); + + ((FileHolder) holder).setSelected(selected_items[position]); + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (mode == SELECTOR_MODE) { + onItemSelect(file); + } else if (file.isMedia) { + + int index = file.getPath().lastIndexOf("/"); + String path = file.getPath().substring(0, index); + + //load Album + final Album album = new Album().setPath(path); + AlbumItem albumItem = AlbumItem.getInstance(holder.itemView.getContext(), file.getPath()); + if (albumItem != null) { + album.getAlbumItems() + .add(albumItem); + } + + //create intent + Intent intent = new Intent(holder.itemView.getContext(), ItemActivity.class) + .putExtra(ItemActivity.ALBUM_ITEM, albumItem) + .putExtra(ItemActivity.ALBUM, album) + .putExtra(ItemActivity.VIEW_ONLY, true) + .putExtra(ItemActivity.ITEM_POSITION, album.getAlbumItems().indexOf(albumItem)) + .putExtra(ItemActivity.FINISH_AFTER, false); + + try { + holder.itemView.getContext().startActivity(intent); + } catch (ActivityNotFoundException e) { + e.printStackTrace(); + } + } else { + //to keep the ripple animation + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + RecyclerViewAdapter.this.setFiles(file); + RecyclerViewAdapter.this.notifyDataSetChanged(); + + if (callback != null) { + callback.onDataChanged(); + } + } + }, 300); + } + } + }); + + holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + onItemSelect(file); + return true; + } + }); + + //clicking on folder icons also selects this item + holder.itemView.findViewById(R.id.folder_indicator).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + onItemSelect(file); + } + }); + } + + private void onItemSelect(File_POJO file) { + if (mode == NORMAL_MODE) { + mode = SELECTOR_MODE; + selected_items = new boolean[files.getChildren().size()]; + if (callback != null) { + callback.onSelectorModeEnter(); + } + } + + int position = files.getChildren().indexOf(file); + selected_items[position] = !selected_items[position]; + notifyItemChanged(position); + + if (callback != null) { + callback.onItemSelected(getSelectedCount()); + } + + checkSelectorMode(); + } + + public boolean isModeActive() { + return mode == SELECTOR_MODE; + } + + public int getMode() { + return mode; + } + + private int getSelectedCount() { + int selected_items_count = 0; + for (int i = 0; i < selected_items.length; i++) { + selected_items_count += selected_items[i] ? 1 : 0; + } + return selected_items_count; + } + + private void checkSelectorMode() { + int selected_items_count = getSelectedCount(); + if (selected_items_count == 0) { + cancelMode(); + } + } + + public void cancelMode() { + if (mode == SELECTOR_MODE) { + mode = NORMAL_MODE; + if (callback != null) { + File_POJO[] files = new File_POJO[getSelectedCount()]; + int index = 0; + for (int i = 0; i < selected_items.length; i++) { + if (selected_items[i]) { + files[index] = this.files.getChildren().get(i); + index++; + } + } + callback.onSelectorModeExit(files); + } + selected_items = new boolean[files.getChildren().size()]; + } else if (mode == PICK_TARGET_MODE) { + mode = NORMAL_MODE; + if (callback != null) { + callback.onPickTargetModeExit(); + } + } + notifyDataSetChanged(); + } + + public void pickTarget() { + mode = PICK_TARGET_MODE; + if (callback != null) { + callback.onPickTargetModeEnter(); + } + } + + @Override + public int getItemCount() { + return files.getChildren().size(); + } +} \ No newline at end of file diff --git a/app/src/main/java/us/koller/cameraroll/adapter/fileExplorer/ViewHolder/FileHolder.java b/app/src/main/java/us/koller/cameraroll/adapter/fileExplorer/ViewHolder/FileHolder.java new file mode 100644 index 00000000..28c22941 --- /dev/null +++ b/app/src/main/java/us/koller/cameraroll/adapter/fileExplorer/ViewHolder/FileHolder.java @@ -0,0 +1,56 @@ +package us.koller.cameraroll.adapter.fileExplorer.ViewHolder; + +import android.graphics.drawable.Drawable; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import java.io.File; + +import us.koller.cameraroll.R; +import us.koller.cameraroll.data.File_POJO; +import us.koller.cameraroll.util.MediaType; + +public class FileHolder extends RecyclerView.ViewHolder { + + private File_POJO file; + + public FileHolder(View itemView) { + super(itemView); + } + + public void setFile(File_POJO file) { + this.file = file; + + ImageView folderIndicator = (ImageView) itemView.findViewById(R.id.folder_indicator); + if (!file.isMedia) { + folderIndicator.setImageResource(R.drawable.ic_folder_white_24dp); + } else if (MediaType.isVideo(folderIndicator.getContext(), file.getPath())) { + folderIndicator.setImageResource(R.drawable.ic_videocam_white_24dp); + } else { + folderIndicator.setImageResource(R.drawable.ic_photo_white_24dp); + } + + TextView textView = (TextView) itemView.findViewById(R.id.text); + textView.setText(file.getName()); + } + + public void setSelected(boolean selected) { + itemView.setBackgroundColor( + ContextCompat.getColor(itemView.getContext(), + selected ? R.color.colorAccent_translucent : + android.R.color.transparent)); + + TextView textView = (TextView) itemView.findViewById(R.id.text); + textView.setTextColor(ContextCompat.getColor(itemView.getContext(), + selected ? R.color.grey_900_translucent : R.color.white_translucent1)); + + ImageView folderIndicator = (ImageView) itemView.findViewById(R.id.folder_indicator); + Drawable d = folderIndicator.getDrawable().mutate(); + d.setTint(ContextCompat.getColor(itemView.getContext(), + selected ? R.color.grey_900_translucent : R.color.white)); + folderIndicator.setImageDrawable(d); + } +} diff --git a/app/src/main/java/us/koller/cameraroll/adapter/item/ViewHolder/PhotoViewHolder.java b/app/src/main/java/us/koller/cameraroll/adapter/item/ViewHolder/PhotoViewHolder.java index 00d7ac15..9a95218f 100644 --- a/app/src/main/java/us/koller/cameraroll/adapter/item/ViewHolder/PhotoViewHolder.java +++ b/app/src/main/java/us/koller/cameraroll/adapter/item/ViewHolder/PhotoViewHolder.java @@ -1,6 +1,5 @@ package us.koller.cameraroll.adapter.item.ViewHolder; -import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; @@ -50,6 +49,12 @@ public void swapView(final boolean isReturning) { } private void bindImageView(View view, View transitionView) { + if (albumItem.error) { + transitionView.setVisibility(View.VISIBLE); + ItemViewUtil.bindTransitionView((ImageView) transitionView, albumItem); + return; + } + final SubsamplingScaleImageView imageView = (SubsamplingScaleImageView) view; final GestureDetector gestureDetector diff --git a/app/src/main/java/us/koller/cameraroll/data/AlbumItem.java b/app/src/main/java/us/koller/cameraroll/data/AlbumItem.java index 4749e9d1..c9727d1e 100644 --- a/app/src/main/java/us/koller/cameraroll/data/AlbumItem.java +++ b/app/src/main/java/us/koller/cameraroll/data/AlbumItem.java @@ -86,13 +86,16 @@ public long getDate(Activity context) { public Uri getUri(Context context) { Uri uri; if (!contentUri) { - File file = new File(getPath()); - uri = FileProvider.getUriForFile(context, - context.getApplicationContext().getPackageName() + ".provider", file); - } else { - uri = Uri.parse(getPath()); + try { + File file = new File(getPath()); + uri = FileProvider.getUriForFile(context, + context.getApplicationContext().getPackageName() + ".provider", file); + return uri; + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } } - return uri; + return Uri.parse(getPath()); } AlbumItem(Parcel parcel) { diff --git a/app/src/main/java/us/koller/cameraroll/data/File_POJO.java b/app/src/main/java/us/koller/cameraroll/data/File_POJO.java new file mode 100644 index 00000000..697f41c6 --- /dev/null +++ b/app/src/main/java/us/koller/cameraroll/data/File_POJO.java @@ -0,0 +1,82 @@ +package us.koller.cameraroll.data; + +import android.app.Activity; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; + +import us.koller.cameraroll.util.SortUtil; + +//simple POJO class +public class File_POJO + implements Parcelable, SortUtil.Sortable { + + private String path; + private ArrayList children; + public boolean isMedia; + + public File_POJO(String path, boolean isMedia) { + this.path = path; + this.isMedia = isMedia; + + children = new ArrayList<>(); + } + + public void addChild(File_POJO file) { + children.add(file); + } + + @Override + public String getName() { + String[] s = getPath().split("/"); + return s[s.length - 1]; + } + + @Override + public long getDate(Activity context) { + //not needed + return 0; + } + + public String getPath() { + return path; + } + + public ArrayList getChildren() { + return children; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeString(path); + parcel.writeString(String.valueOf(isMedia)); + File_POJO[] children = new File_POJO[this.children.size()]; + for (int k = 0; k < children.length; k++) { + children[k] = this.children.get(k); + } + parcel.writeTypedArray(children, 0); + } + + private File_POJO(Parcel parcel) { + path = parcel.readString(); + isMedia = Boolean.valueOf(parcel.readString()); + children = parcel.createTypedArrayList(CREATOR); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public File_POJO createFromParcel(Parcel parcel) { + return new File_POJO(parcel); + } + + @Override + public File_POJO[] newArray(int i) { + return new File_POJO[i]; + } + }; +} diff --git a/app/src/main/java/us/koller/cameraroll/data/FilesLoader/FilesLoader.java b/app/src/main/java/us/koller/cameraroll/data/FilesLoader/FilesLoader.java new file mode 100644 index 00000000..5260b808 --- /dev/null +++ b/app/src/main/java/us/koller/cameraroll/data/FilesLoader/FilesLoader.java @@ -0,0 +1,246 @@ +package us.koller.cameraroll.data.FilesLoader; + +import android.app.Activity; +import android.os.AsyncTask; +import android.os.Environment; +import android.os.Handler; +import android.util.Log; +import android.widget.Toast; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.Arrays; + +import us.koller.cameraroll.data.File_POJO; +import us.koller.cameraroll.data.MediaLoader.MediaLoader; +import us.koller.cameraroll.util.MediaType; +import us.koller.cameraroll.util.SortUtil; + +public class FilesLoader { + + public interface LoaderCallback { + void onMediaLoaded(File_POJO files); + + void timeout(); + + void needPermission(); + } + + public interface Callback { + void callback(File_POJO files); + } + + //option to set thread count; + //if set to -1 every dir in home dir get its own thread + private static final int THREAD_COUNT = -1; + + private ArrayList threads; + + //for timeout + private Handler handler; + private Runnable timeout; + + public void loadFiles(final Activity context, + final LoaderCallback callback) { + + if (!MediaLoader.checkPermission(context)) { + callback.needPermission(); + return; + } + + final long startTime = System.currentTimeMillis(); + + //handle timeout + handler = new Handler(); + timeout = new Runnable() { + @Override + public void run() { + Toast.makeText(context, "timeout", Toast.LENGTH_SHORT).show(); + callback.timeout(); + } + }; + handler.postDelayed(timeout, 5000); + + //load files from storage + AsyncTask.execute(new Runnable() { + @Override + public void run() { + searchStorage(context, new FilesLoader.Callback() { + @Override + public void callback(File_POJO files) { + //sort files by name + SortUtil.sortFiles(context, files); + + //done loading files from storage + callback.onMediaLoaded(files); + cancelTimeout(); + if (THREAD_COUNT == -1) { + Log.d("FilesLoader", "onMediaLoaded(): " + String.valueOf(System.currentTimeMillis() - startTime) + "; " + files.getChildren()); + } else { + Log.d("FilesLoader", "onMediaLoaded(" + String.valueOf(THREAD_COUNT) + + "): " + String.valueOf(System.currentTimeMillis() - startTime) + "; " + files.getChildren()); + } + } + }); + } + }); + } + + private void cancelTimeout() { + if (handler != null && timeout != null) { + handler.removeCallbacks(timeout); + } + } + + public void onDestroy() { + //cancel all threads when Activity is being destroyed + if (threads != null) { + for (int i = 0; i < threads.size(); i++) { + threads.get(i).cancel(); + } + } + cancelTimeout(); + } + + private void searchStorage(final Activity context, final FilesLoader.Callback callback) { + final File_POJO files = new File_POJO(Environment.getExternalStorageDirectory().toString(), false); + File dir = Environment.getExternalStorageDirectory(); //new File("/storage/emulated/0"); + File[] dirs = dir.listFiles(new FileFilter() { + @Override + public boolean accept(File file) { + return !file.getName().equals("Android"); + } + }); + + threads = new ArrayList<>(); + + Thread.Callback threadCallback = new Thread.Callback() { + @Override + public void done(Thread thread, + ArrayList filesToAdd) { + for (int i = 0; i < filesToAdd.size(); i++) { + files.addChild(filesToAdd.get(i)); + } + threads.remove(thread); + thread.cancel(); + if (threads.size() == 0) { + callback.callback(files); + threads = null; + } + } + }; + + if (THREAD_COUNT == -1) { + for (int i = 0; i < dirs.length; i++) { + final File[] threadFiles = {dirs[i]}; + Thread thread = new Thread(context, threadFiles, threadCallback); + thread.start(); + threads.add(thread); + } + } else { + //overhead is to big!! + final File[][] threadDirs = divideDirs(dirs); + + for (int i = 0; i < THREAD_COUNT; i++) { + final File[] threadFiles = threadDirs[i]; + Thread thread = new Thread(context, threadFiles, threadCallback); + thread.start(); + threads.add(thread); + } + } + } + + private File[][] divideDirs(File[] dirs) { + int[] threadDirs_sizes = new int[THREAD_COUNT]; + int rest = dirs.length % THREAD_COUNT; + for (int i = 0; i < threadDirs_sizes.length; i++) { + threadDirs_sizes[i] = dirs.length / THREAD_COUNT; + if (rest > 0) { + threadDirs_sizes[i]++; + rest--; + } + } + + Log.d("FilesLoader", Arrays.toString(threadDirs_sizes)); + + File[][] threadDirs = new File[THREAD_COUNT][dirs.length / THREAD_COUNT + 1]; + int index = 0; + for (int i = 0; i < THREAD_COUNT; i++) { + File[] threadDir = Arrays.copyOfRange(dirs, index, index + threadDirs_sizes[i]); + threadDirs[i] = threadDir; + index = index + threadDirs_sizes[i]; + } + + return threadDirs; + } + + private static class Thread extends java.lang.Thread { + + interface Callback { + void done(Thread thread, ArrayList files); + } + + private Activity context; + private Callback callback; + + private File[] dirs; + + Thread(Activity context, File[] dirs, Callback callback) { + this.context = context; + this.callback = callback; + this.dirs = dirs; + } + + @Override + public void run() { + super.run(); + + final ArrayList files = new ArrayList<>(); + + if (dirs != null) { + for (int i = 0; i < dirs.length; i++) { + File_POJO file_pojo = new File_POJO(dirs[i].getPath(), + MediaType.isMedia(context, dirs[i].getPath())); + recursivelySearchStorage(context, dirs[i], file_pojo); + files.add(file_pojo); + } + } + + if (callback != null) { + callback.done(this, files); + } + } + + private void recursivelySearchStorage(final Activity context, + final File file, + final File_POJO files) { + if (interrupted() || file == null) { + return; + } + + if (file.isFile()) { + return; + } + + java.io.File[] filesToSearch = file.listFiles(); + + for (int i = 0; i < filesToSearch.length; i++) { + boolean isMedia = MediaType.isMedia(context, filesToSearch[i].getPath()); + if (filesToSearch[i].isDirectory() || isMedia) { + File_POJO file_pojo = new File_POJO(filesToSearch[i].getPath(), isMedia); + files.addChild(file_pojo); + if (filesToSearch[i].isDirectory()) { + recursivelySearchStorage(context, filesToSearch[i], file_pojo); + } + } + } + } + + void cancel() { + context = null; + callback = null; + interrupt(); + } + } +} diff --git a/app/src/main/java/us/koller/cameraroll/data/MediaLoader/Loader/StorageLoader.java b/app/src/main/java/us/koller/cameraroll/data/MediaLoader/Loader/StorageLoader.java index 1821f4ec..518d9584 100644 --- a/app/src/main/java/us/koller/cameraroll/data/MediaLoader/Loader/StorageLoader.java +++ b/app/src/main/java/us/koller/cameraroll/data/MediaLoader/Loader/StorageLoader.java @@ -28,6 +28,10 @@ public class StorageLoader implements MediaLoader.Loader { private ArrayList threads; + //for timeout + private Handler handler; + private Runnable timeout; + @Override public void loadAlbums(final Activity context, final boolean hiddenFolders, final MediaLoader.LoaderCallback callback) { @@ -37,8 +41,8 @@ public void loadAlbums(final Activity context, final boolean hiddenFolders, final ArrayList albums = new ArrayList<>(); //handle timeout - final Handler handler = new Handler(); - final Runnable timeout = new Runnable() { + handler = new Handler(); + timeout = new Runnable() { @Override public void run() { Toast.makeText(context, "timeout", Toast.LENGTH_SHORT).show(); @@ -65,7 +69,7 @@ public void callback(ArrayList albums) { //done loading media from storage SortUtil.sortAlbums(context, albums, SortUtil.BY_NAME); callback.onMediaLoaded(albums); - handler.removeCallbacks(timeout); + cancelTimeout(); if (THREAD_COUNT == -1) { Log.d("StorageLoader", "onMediaLoaded(): " + String.valueOf(System.currentTimeMillis() - startTime)); } else { @@ -78,14 +82,23 @@ public void callback(ArrayList albums) { }); } + private void cancelTimeout() { + if (handler != null && timeout != null) { + handler.removeCallbacks(timeout); + } + } + @Override public void onDestroy() { - //cancel all mediaLoaderThreads when Activity is being destroyed + //cancel all threads when Activity is being destroyed if (threads != null) { for (int i = 0; i < threads.size(); i++) { - threads.get(i).cancel(); + if (threads.get(i) != null) { + threads.get(i).cancel(); + } } } + cancelTimeout(); } private void searchStorage(final Activity context, final ArrayList albums, final MediaLoader.Callback callback) { @@ -116,9 +129,9 @@ public void done(Thread thread, if (THREAD_COUNT == -1) { for (int i = 0; i < dirs.length; i++) { final File[] files = {dirs[i]}; - Thread mediaLoaderThread = new Thread(context, files, threadCallback); - mediaLoaderThread.start(); - threads.add(mediaLoaderThread); + Thread thread = new Thread(context, files, threadCallback); + thread.start(); + threads.add(thread); } } else { //overhead is to big!! @@ -226,7 +239,7 @@ private void recursivelySearchStorage(final Activity context, album.getAlbumItems() .add(albumItem); } - } else if (file.isDirectory()) { + } else if (files[i].isDirectory()) { recursivelySearchStorage(context, files[i], albums); } } diff --git a/app/src/main/java/us/koller/cameraroll/ui/AlbumActivity.java b/app/src/main/java/us/koller/cameraroll/ui/AlbumActivity.java index 870e5601..6b06b3b4 100644 --- a/app/src/main/java/us/koller/cameraroll/ui/AlbumActivity.java +++ b/app/src/main/java/us/koller/cameraroll/ui/AlbumActivity.java @@ -53,7 +53,8 @@ import us.koller.cameraroll.util.MediaType; import us.koller.cameraroll.util.Util; -public class AlbumActivity extends AppCompatActivity implements SwipeBackCoordinatorLayout.OnSwipeListener, RecyclerViewAdapter.Callback { +public class AlbumActivity extends AppCompatActivity + implements SwipeBackCoordinatorLayout.OnSwipeListener, RecyclerViewAdapter.Callback { public static final String ALBUM = "ALBUM"; public static final String VIEW_ALBUM = "VIEW_ALBUM"; diff --git a/app/src/main/java/us/koller/cameraroll/ui/FileExplorerActivity.java b/app/src/main/java/us/koller/cameraroll/ui/FileExplorerActivity.java new file mode 100644 index 00000000..a2fde61d --- /dev/null +++ b/app/src/main/java/us/koller/cameraroll/ui/FileExplorerActivity.java @@ -0,0 +1,633 @@ +package us.koller.cameraroll.ui; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.support.design.widget.Snackbar; +import android.support.graphics.drawable.AnimatedVectorDrawableCompat; +import android.support.v4.content.ContextCompat; +import android.support.v4.graphics.drawable.DrawableCompat; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.transition.Fade; +import android.transition.Slide; +import android.transition.TransitionSet; +import android.view.Gravity; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.WindowInsets; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.widget.TextView; +import android.widget.Toast; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import us.koller.cameraroll.R; +import us.koller.cameraroll.adapter.fileExplorer.RecyclerViewAdapter; +import us.koller.cameraroll.data.File_POJO; +import us.koller.cameraroll.data.FilesLoader.FilesLoader; +import us.koller.cameraroll.ui.widget.ParallaxImageView; +import us.koller.cameraroll.ui.widget.SwipeBackCoordinatorLayout; +import us.koller.cameraroll.util.ColorFade; +import us.koller.cameraroll.util.Util; + +public class FileExplorerActivity extends AppCompatActivity + implements SwipeBackCoordinatorLayout.OnSwipeListener, RecyclerViewAdapter.Callback { + + public static final String FILES = "FILES"; + public static final String CURRENT_FILE = "CURRENT_FILE"; + public static final String SELECTED_ITEMS = "SELECTED_ITEMS"; + + private File_POJO files; + + private FilesLoader loader; + + private RecyclerView recyclerView; + private RecyclerViewAdapter recyclerViewAdapter; + + private Menu menu; + + private FileAction fileAction; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_file_explorer); + + //load files + if (savedInstanceState != null + && savedInstanceState.containsKey(FILES)) { + files = savedInstanceState.getParcelable(FILES); + } else { + files = new File_POJO("", false); + } + + final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + toolbar.setBackgroundColor(ContextCompat.getColor(this, R.color.black_translucent2)); + toolbar.setNavigationIcon(AnimatedVectorDrawableCompat.create(this, R.drawable.back_to_cancel_animateable)); + setSupportActionBar(toolbar); + + //set Toolbar overflow icon color + Drawable drawable = toolbar.getOverflowIcon(); + if (drawable != null) { + drawable = DrawableCompat.wrap(drawable); + DrawableCompat.setTint(drawable.mutate(), + ContextCompat.getColor(this, R.color.grey_900_translucent)); + toolbar.setOverflowIcon(drawable); + } + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setTitle(getString(R.string.file_explorer)); + actionBar.setDisplayHomeAsUpEnabled(true); + } + + final ViewGroup rootView = (ViewGroup) findViewById(R.id.root_view); + if (rootView instanceof SwipeBackCoordinatorLayout) { + ((SwipeBackCoordinatorLayout) rootView).setOnSwipeListener(this); + } + + recyclerView = (RecyclerView) findViewById(R.id.recyclerView); + recyclerView.setTag(ParallaxImageView.RECYCLER_VIEW_TAG); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + if (savedInstanceState != null && savedInstanceState.containsKey(CURRENT_FILE)) { + recyclerViewAdapter = new RecyclerViewAdapter(this) + .setFiles((File_POJO) savedInstanceState.getParcelable(CURRENT_FILE)); + } else { + recyclerViewAdapter = new RecyclerViewAdapter(this) + .setFiles(files); + } + recyclerViewAdapter.notifyDataSetChanged(); + recyclerView.setAdapter(recyclerViewAdapter); + + //setting window insets manually + rootView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + @Override + public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) { + toolbar.setPadding(toolbar.getPaddingStart() /*+ insets.getSystemWindowInsetLeft()*/, + toolbar.getPaddingTop() + insets.getSystemWindowInsetTop(), + toolbar.getPaddingEnd() /*+ insets.getSystemWindowInsetRight()*/, + toolbar.getPaddingBottom()); + + ViewGroup.MarginLayoutParams toolbarParams + = (ViewGroup.MarginLayoutParams) toolbar.getLayoutParams(); + toolbarParams.leftMargin += insets.getSystemWindowInsetLeft(); + toolbarParams.rightMargin += insets.getSystemWindowInsetRight(); + toolbar.setLayoutParams(toolbarParams); + + recyclerView.setPadding(recyclerView.getPaddingStart() + insets.getSystemWindowInsetLeft(), + recyclerView.getPaddingTop() /*+ insets.getSystemWindowInsetTop()*/, + recyclerView.getPaddingEnd() + insets.getSystemWindowInsetRight(), + recyclerView.getPaddingBottom() + insets.getSystemWindowInsetBottom()); + + // clear this listener so insets aren't re-applied + rootView.setOnApplyWindowInsetsListener(null); + return insets.consumeSystemWindowInsets(); + } + }); + + //setting recyclerView top padding, so recyclerView starts below the toolbar + toolbar.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + recyclerView.setPadding(recyclerView.getPaddingStart(), + recyclerView.getPaddingTop() + toolbar.getHeight(), + recyclerView.getPaddingEnd(), + recyclerView.getPaddingBottom()); + + recyclerView.scrollToPosition(0); + + toolbar.getViewTreeObserver().removeOnPreDrawListener(this); + return false; + } + }); + + //needed to achieve transparent navBar + getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + + //load files + if (savedInstanceState == null) { + loadFiles(); + } + } + + public void loadFiles() { + final Snackbar snackbar = Snackbar.make(findViewById(R.id.root_view), + getString(R.string.loading), Snackbar.LENGTH_INDEFINITE); + Util.showSnackbar(snackbar); + + loader = new FilesLoader(); + loader.loadFiles(this, new FilesLoader.LoaderCallback() { + @Override + public void onMediaLoaded(final File_POJO files) { + runOnUiThread(new Runnable() { + @Override + public void run() { + loader.onDestroy(); + loader = null; + FileExplorerActivity.this.files = files; + if (recyclerViewAdapter != null) { + recyclerViewAdapter.setFiles(files); + recyclerViewAdapter.notifyDataSetChanged(); + onDataChanged(); + } + snackbar.dismiss(); + } + }); + } + + @Override + public void timeout() { + runOnUiThread(new Runnable() { + @Override + public void run() { + snackbar.dismiss(); + + final Snackbar snackbar = Snackbar.make(findViewById(R.id.root_view), + getString(R.string.loading), Snackbar.LENGTH_INDEFINITE); + snackbar.setAction(getString(R.string.retry), new View.OnClickListener() { + @Override + public void onClick(View view) { + loadFiles(); + } + }); + Util.showSnackbar(snackbar); + } + }); + } + + @Override + public void needPermission() { + runOnUiThread(new Runnable() { + @Override + public void run() { + snackbar.dismiss(); + } + }); + } + }); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putParcelable(FILES, files); + outState.putParcelable(CURRENT_FILE, recyclerViewAdapter.getFiles()); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.file_explorer, menu); + this.menu = menu; + //hide menu items; items are made visible, when a folder gets selected + for (int i = 0; i < menu.size(); i++) { + menu.getItem(i).setVisible(false); + } + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + if (recyclerViewAdapter.isModeActive() + || recyclerViewAdapter.getMode() == RecyclerViewAdapter.PICK_TARGET_MODE) { + recyclerViewAdapter.cancelMode(); + } else { + onBackPressed(); + } + break; + case R.id.paste: + recyclerViewAdapter.cancelMode(); + if (FileAction.action == FileAction.MOVE + | FileAction.action == FileAction.COPY) { + fileAction.execute(this, + recyclerViewAdapter.getFiles(), + new FileAction.Callback() { + @Override + public void done() { + loadFiles(); + } + }); + } + break; + case R.id.move: + FileAction.action = FileAction.MOVE; + recyclerViewAdapter.cancelMode(); + break; + case R.id.copy: + FileAction.action = FileAction.COPY; + recyclerViewAdapter.cancelMode(); + break; + case R.id.delete: + FileAction.action = FileAction.DELETE; + recyclerViewAdapter.cancelMode(); + break; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onBackPressed() { + if (recyclerViewAdapter.isModeActive()) { + recyclerViewAdapter.cancelMode(); + } else if (recyclerViewAdapter != null && files != null + && !recyclerViewAdapter.getFiles().getPath().equals(files.getPath())) { + recyclerViewAdapter.setFiles(files); + recyclerViewAdapter.notifyDataSetChanged(); + this.onDataChanged(); + } else { + super.onBackPressed(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + if (loader != null) { + loader.onDestroy(); + } + } + + @Override + public boolean canSwipeBack(int dir) { + return SwipeBackCoordinatorLayout.canSwipeBackForThisView(recyclerView, dir); + } + + @Override + public void onSwipeProcess(float percent) { + getWindow().getDecorView().setBackgroundColor(SwipeBackCoordinatorLayout.getBackgroundColor(percent)); + } + + @Override + public void onSwipeFinish(int dir) { + if (recyclerViewAdapter != null && files != null + && !recyclerViewAdapter.getFiles().getPath().equals(files.getPath())) { + recyclerViewAdapter.setFiles(files); + recyclerViewAdapter.notifyDataSetChanged(); + this.onDataChanged(); + } + getWindow().setReturnTransition(new TransitionSet() + .setOrdering(TransitionSet.ORDERING_TOGETHER) + .addTransition(new Slide(dir > 0 ? Gravity.TOP : Gravity.BOTTOM)) + .addTransition(new Fade()) + .setInterpolator(new AccelerateDecelerateInterpolator())); + onBackPressed(); + } + + @Override + public void onSelectorModeEnter() { + final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + toolbar.setTitleTextColor(ContextCompat.getColor(this, android.R.color.transparent)); + toolbar.setActivated(true); + toolbar.animate().translationY(0.0f).start(); + + Util.setDarkStatusBarIcons(findViewById(R.id.root_view)); + + ColorFade.fadeBackgroundColor(toolbar, + ContextCompat.getColor(this, R.color.black_translucent2), + ContextCompat.getColor(this, R.color.colorAccent)); + + ((Animatable) toolbar.getNavigationIcon()).start(); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + toolbar.setNavigationIcon(AnimatedVectorDrawableCompat + .create(FileExplorerActivity.this, R.drawable.cancel_to_back_vector_animateable)); + toolbar.setTitleTextColor(ContextCompat.getColor(FileExplorerActivity.this, R.color.grey_900_translucent)); + + //make menu items visible + for (int i = 0; i < menu.size(); i++) { + if (menu.getItem(i).getItemId() != R.id.paste) { + menu.getItem(i).setVisible(true); + } + } + } + }, 300); + } + + @Override + public void onSelectorModeExit(File_POJO[] selected_items) { + fileAction = new FileAction(selected_items); + if (FileAction.action == FileAction.DELETE) { + fileAction.execute(this, null, + new FileAction.Callback() { + @Override + public void done() { + loadFiles(); + } + }); + resetToolbar(); + } else if (FileAction.action == FileAction.MOVE + | FileAction.action == FileAction.COPY) { + recyclerViewAdapter.pickTarget(); + } else { + resetToolbar(); + } + } + + @Override + public void onItemSelected(int count) { + String title = String.valueOf(count) + (count > 1 ? + getString(R.string.items) : getString(R.string.item)); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + toolbar.setTitle(title); + } + + @Override + public void onPickTargetModeEnter() { + final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + if (fileAction != null) { + int count = fileAction.getFiles().length; + toolbar.setTitle(FileAction.getModeString(this) + " " + + String.valueOf(count) + + (count > 1 ? getString(R.string.items) : getString(R.string.item))); + } + + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + //hide menu items + for (int i = 0; i < menu.size(); i++) { + if (menu.getItem(i).getItemId() != R.id.paste) { + menu.getItem(i).setVisible(false); + } else { + menu.getItem(i).setVisible(true); + Drawable icon = menu.getItem(i).getIcon().mutate(); + icon.setTint(ContextCompat.getColor(FileExplorerActivity.this, + R.color.grey_900_translucent)); + menu.getItem(i).setIcon(icon); + } + } + } + }, 300); + } + + @Override + public void onPickTargetModeExit() { + resetToolbar(); + } + + @Override + public void onDataChanged() { + TextView emptyState = (TextView) findViewById(R.id.empty_state); + emptyState.setVisibility( + recyclerViewAdapter.getFiles().getChildren().size() == 0 ? + View.VISIBLE : View.GONE); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null && recyclerViewAdapter.getMode() != RecyclerViewAdapter.PICK_TARGET_MODE) { + actionBar.setTitle(recyclerViewAdapter.getFiles().getPath()); + } + } + + public void resetToolbar() { + final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + toolbar.setTitleTextColor(ContextCompat.getColor(this, android.R.color.transparent)); + toolbar.setActivated(false); + ColorFade.fadeBackgroundColor(toolbar, + ContextCompat.getColor(this, R.color.colorAccent), + ContextCompat.getColor(this, R.color.black_translucent2)); + toolbar.setTitle(recyclerViewAdapter.getFiles().getPath()); + + ((Animatable) toolbar.getNavigationIcon()).start(); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + toolbar.setNavigationIcon(AnimatedVectorDrawableCompat + .create(FileExplorerActivity.this, R.drawable.back_to_cancel_animateable)); + toolbar.setTitleTextColor(ContextCompat.getColor(FileExplorerActivity.this, R.color.white)); + + Util.setLightStatusBarIcons(findViewById(R.id.root_view)); + + //hide menu items + for (int i = 0; i < menu.size(); i++) { + menu.getItem(i).setVisible(false); + } + } + }, 300); + } + + public static class FileAction { + + public interface Callback { + void done(); + } + + static final int EMPTY = 0; + static final int MOVE = 1; + static final int COPY = 2; + static final int DELETE = 3; + + static int action = EMPTY; + + private File_POJO[] files; + + FileAction(File_POJO[] files) { + this.files = files; + } + + File_POJO[] getFiles() { + return files; + } + + void execute(final Activity context, final File_POJO target, final Callback callback) { + + if ((FileAction.action == FileAction.EMPTY) + || (target == null && action == FileAction.MOVE) + || (target == null && action == FileAction.COPY)) { + return; + } + + if (FileAction.action == FileAction.MOVE) { + if (target == null) { + return; + } + + int success_count = 0; + for (int i = 0; i < files.length; i++) { + boolean result = moveFile(files[i].getPath(), target.getPath()); + success_count += result ? 1 : 0; + if (result) { + context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, + Uri.parse(files[i].getPath()))); + context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, + Uri.parse(target.getPath()))); + } + } + + Toast.makeText(context, context.getString(R.string.successfully_moved) + + String.valueOf(success_count) + "/" + + String.valueOf(files.length), Toast.LENGTH_SHORT).show(); + + } else if (FileAction.action == FileAction.COPY) { + if (target == null) { + return; + } + + int success_count = 0; + for (int i = 0; i < files.length; i++) { + boolean result = copyFile(files[i].getPath(), target.getPath()); + success_count += result ? 1 : 0; + if (result) { + context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, + Uri.parse(files[i].getPath()))); + context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, + Uri.parse(target.getPath()))); + } + } + + Toast.makeText(context, context.getString(R.string.successfully_copied) + + String.valueOf(success_count) + "/" + + String.valueOf(files.length), Toast.LENGTH_SHORT).show(); + + } else if (FileAction.action == FileAction.DELETE) { + + int success_count = 0; + for (int i = 0; i < files.length; i++) { + boolean result = deleteFile(files[i].getPath()); + success_count += result ? 1 : 0; + if (result) { + context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, + Uri.parse(files[i].getPath()))); + } + } + + Toast.makeText(context, context.getString(R.string.successfully_deleted) + + String.valueOf(success_count) + "/" + + String.valueOf(files.length), Toast.LENGTH_SHORT).show(); + } + FileAction.action = FileAction.EMPTY; + + if (callback != null) { + callback.done(); + } + } + + private static boolean moveFile(String path, String destination) { + /*boolean result = copyFile(path, destination); + + //delete original file + result = result && deleteFile(path);*/ + + File file = new File(path); + return file.renameTo(new File(destination, file.getName())); + } + + private static boolean copyFile(String path, String destination) { + //create output directory if it doesn't exist + File dir = new File(destination); + if (!dir.exists()) { + dir.mkdirs(); + } + + try { + InputStream inputStream = new FileInputStream(path); + OutputStream outputStream = new FileOutputStream( + new File(destination, new File(path).getName())); + + byte[] buffer = new byte[1024]; + + int length; + //copy the file content in bytes + while ((length = inputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, length); + } + + inputStream.close(); + outputStream.close(); + + // write the output file + outputStream.flush(); + outputStream.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + + return true; + } + + private static boolean deleteFile(String path) { + File file = new File(path); + if (file.exists()) { + return file.delete(); + } + return false; + } + + static String getModeString(Context context) { + switch (action) { + case EMPTY: + return "empty"; + case MOVE: + return context.getString(R.string.move); + case COPY: + return context.getString(R.string.copy); + case DELETE: + return context.getString(R.string.delete); + } + return ""; + } + } +} diff --git a/app/src/main/java/us/koller/cameraroll/ui/ItemActivity.java b/app/src/main/java/us/koller/cameraroll/ui/ItemActivity.java index 00bc6cfb..3fd09d82 100644 --- a/app/src/main/java/us/koller/cameraroll/ui/ItemActivity.java +++ b/app/src/main/java/us/koller/cameraroll/ui/ItemActivity.java @@ -69,6 +69,7 @@ public class ItemActivity extends AppCompatActivity { public static final String ALBUM = "ALBUM"; public static final String ITEM_POSITION = "ITEM_POSITION"; public static final String VIEW_ONLY = "VIEW_ONLY"; + public static final String FINISH_AFTER = "FINISH_AFTER"; public static final String HIDDEN_ALBUMITEM = "HIDDEN_ALBUMITEM"; private static final String WAS_SYSTEM_UI_HIDDEN = "WAS_SYSTEM_UI_HIDDEN"; private static final String IMAGE_VIEW_SAVED_STATE = "IMAGE_VIEW_SAVED_STATE"; @@ -625,7 +626,11 @@ public interface Callback { public void onBackPressed() { showUI(false); if (view_only) { - this.finishAffinity(); + if (getIntent().getBooleanExtra(FINISH_AFTER, false)) { + this.finishAffinity(); + } else { + this.finish(); + } } else { ViewHolder viewHolder = ((ViewPagerAdapter) viewPager.getAdapter()).findViewHolderByTag(albumItem.getPath()); diff --git a/app/src/main/java/us/koller/cameraroll/ui/MainActivity.java b/app/src/main/java/us/koller/cameraroll/ui/MainActivity.java index 041faf61..af3fdf38 100644 --- a/app/src/main/java/us/koller/cameraroll/ui/MainActivity.java +++ b/app/src/main/java/us/koller/cameraroll/ui/MainActivity.java @@ -5,11 +5,13 @@ import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.content.ContextCompat; +import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; @@ -95,6 +97,15 @@ public void onClick(View view) { } }); + //set Toolbar overflow icon color + Drawable drawable = toolbar.getOverflowIcon(); + if (drawable != null) { + drawable = DrawableCompat.wrap(drawable); + DrawableCompat.setTint(drawable.mutate(), + ContextCompat.getColor(this, R.color.grey_900_translucent)); + toolbar.setOverflowIcon(drawable); + } + Util.setDarkStatusBarIcons(findViewById(R.id.root_view)); } @@ -240,7 +251,9 @@ public void timeout() { snackbar.setAction(getString(R.string.retry), new View.OnClickListener() { @Override public void onClick(View view) { - mediaLoader.onDestroy(); + if (mediaLoader != null) { + mediaLoader.onDestroy(); + } refreshPhotos(); snackbar.dismiss(); } @@ -267,8 +280,10 @@ public void needPermission() { public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.main, menu); - if (!pick_photos) { - menu.findItem(R.id.hiddenFolders).setChecked(hiddenFolders); + menu.findItem(R.id.hiddenFolders).setChecked(hiddenFolders); + if (pick_photos) { + menu.findItem(R.id.about).setVisible(false); + menu.findItem(R.id.file_explorer).setVisible(false); } return true; } @@ -284,6 +299,10 @@ public boolean onOptionsItemSelected(MenuItem item) { item.setChecked(hiddenFolders); refreshPhotos(); break; + case R.id.file_explorer: + startActivity(new Intent(this, FileExplorerActivity.class), + ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle()); + break; case R.id.about: startActivity(new Intent(this, AboutActivity.class), ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle()); diff --git a/app/src/main/java/us/koller/cameraroll/util/ItemViewUtil.java b/app/src/main/java/us/koller/cameraroll/util/ItemViewUtil.java index ab7f0432..a2988972 100644 --- a/app/src/main/java/us/koller/cameraroll/util/ItemViewUtil.java +++ b/app/src/main/java/us/koller/cameraroll/util/ItemViewUtil.java @@ -46,19 +46,10 @@ public static View bindSubsamplingImageView(SubsamplingScaleImageView imageView, } imageView.setOrientation(SubsamplingScaleImageView.ORIENTATION_USE_EXIF); - imageView.setMinimumDpi(160); - - if (!photo.contentUri) { - imageView.setImage(ImageSource.uri(photo.getPath()), imageViewState); - } else { - try { - Bitmap bmp = MediaStore.Images.Media.getBitmap( - imageView.getContext().getContentResolver(), Uri.parse(photo.getPath())); - imageView.setImage(ImageSource.bitmap(bmp), imageViewState); - } catch (IOException e) { - e.printStackTrace(); - } - } + imageView.setMinimumDpi(80); + + imageView.setImage(ImageSource.uri(photo.getPath()), imageViewState); + if (placeholderView != null) { imageView.setOnImageEventListener( new SubsamplingScaleImageView.DefaultOnImageEventListener() { diff --git a/app/src/main/java/us/koller/cameraroll/util/SortUtil.java b/app/src/main/java/us/koller/cameraroll/util/SortUtil.java index 70cd50d6..781399a8 100644 --- a/app/src/main/java/us/koller/cameraroll/util/SortUtil.java +++ b/app/src/main/java/us/koller/cameraroll/util/SortUtil.java @@ -7,6 +7,7 @@ import java.util.Comparator; import us.koller.cameraroll.data.Album; +import us.koller.cameraroll.data.File_POJO; public class SortUtil { @@ -36,6 +37,12 @@ public static ArrayList sortAlbums(Activity context, ArrayLi return albums; } + public static File_POJO sortFiles(Activity context, File_POJO files) { + sort(context, files.getChildren(), BY_NAME); + + return files; + } + private static ArrayList sort(Activity context, ArrayList sortables, int by) { switch (by) { case BY_DATE: diff --git a/app/src/main/res/layout/activity_file_explorer.xml b/app/src/main/res/layout/activity_file_explorer.xml new file mode 100644 index 00000000..ab063576 --- /dev/null +++ b/app/src/main/res/layout/activity_file_explorer.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/file_cover.xml b/app/src/main/res/layout/file_cover.xml new file mode 100644 index 00000000..b9487e3f --- /dev/null +++ b/app/src/main/res/layout/file_cover.xml @@ -0,0 +1,26 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_view_layout.xml b/app/src/main/res/layout/recycler_view_layout.xml index eaf5cfc2..03033e49 100644 --- a/app/src/main/res/layout/recycler_view_layout.xml +++ b/app/src/main/res/layout/recycler_view_layout.xml @@ -3,7 +3,8 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/root_view" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:background="@color/dark_bg"> + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml index 62fa4d5f..69ff6abf 100644 --- a/app/src/main/res/menu/main.xml +++ b/app/src/main/res/menu/main.xml @@ -18,6 +18,11 @@ android:checkable="true" app:showAsAction="never"/> + + Refresh Sort by… Hidden Folders + File Explorer About Set as… @@ -63,6 +64,12 @@ Photos deleted "successfully deleted " + Move + Copy + Paste + successfully moved + successfully copied + Pick Photo Pick Photos @@ -83,6 +90,8 @@ Photo + no Files + Image Indicator diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 8c698c2b..d941d3ed 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -26,6 +26,11 @@ + +