From 33d8c38bd636cee165775ae8502a94234df4e6aa Mon Sep 17 00:00:00 2001 From: Jonas Kalderstam Date: Thu, 23 Apr 2015 00:02:03 +0200 Subject: [PATCH] Update to Material Design and newer API using RecyclerView. Signed-off-by: Jonas Kalderstam --- build.gradle | 2 +- gradle.properties | 6 +- gradle/wrapper/gradle-wrapper.properties | 4 +- library/build.gradle | 7 +- .../AbstractFilePickerActivity.java | 68 +- .../AbstractFilePickerFragment.java | 607 +++++++++--------- .../filepicker/BindableArrayAdapter.java | 207 ------ .../filepicker/FileItemAdapter.java | 76 +++ .../filepicker/FilePickerFragment.java | 105 +-- .../filepicker/LogicHandler.java | 125 ++++ .../filepicker/NewFolderFragment.java | 17 +- .../filepicker/NewItemFragment.java | 108 ++-- .../ab_solid_filepickertheme.9.png | Bin 200 -> 0 bytes .../ic_collections_collection_light.png | Bin 862 -> 0 bytes .../main/res/drawable-hdpi/ic_file_folder.png | Bin 0 -> 457 bytes .../res/drawable-hdpi/ic_navigation_back.png | Bin 694 -> 0 bytes .../ab_solid_filepickertheme.9.png | Bin 166 -> 0 bytes .../ic_collections_collection_light.png | Bin 591 -> 0 bytes .../main/res/drawable-mdpi/ic_file_folder.png | Bin 0 -> 284 bytes .../res/drawable-mdpi/ic_navigation_back.png | Bin 431 -> 0 bytes .../ab_solid_filepickertheme.9.png | Bin 258 -> 0 bytes .../ic_collections_collection_light.png | Bin 1133 -> 0 bytes .../res/drawable-xhdpi/ic_file_folder.png | Bin 0 -> 567 bytes .../res/drawable-xhdpi/ic_navigation_back.png | Bin 839 -> 0 bytes .../ab_solid_filepickertheme.9.png | Bin 351 -> 0 bytes .../ic_collections_collection_light.png | Bin 1928 -> 0 bytes .../res/drawable-xxhdpi/ic_file_folder.png | Bin 0 -> 955 bytes .../drawable-xxhdpi/ic_navigation_back.png | Bin 1649 -> 0 bytes .../main/res/drawable/selectable_action.xml | 26 - .../main/res/layout/dialog_folder_name.xml | 38 ++ .../src/main/res/layout/dialog_new_item.xml | 78 --- .../layout/filepicker_listitem_checkable.xml | 24 +- .../res/layout/filepicker_listitem_dir.xml | 15 +- .../main/res/layout/fragment_filepicker.xml | 121 ++-- library/src/main/res/menu/picker_actions.xml | 10 + .../src/main/res/values-sw600dp/dimens.xml | 22 - .../src/main/res/values-sw600dp/styles.xml | 30 - library/src/main/res/values/attrs.xml | 25 - library/src/main/res/values/colors.xml | 23 - library/src/main/res/values/strings.xml | 19 - library/src/main/res/values/styles.xml | 50 -- sample/build.gradle | 4 +- sample/src/main/AndroidManifest.xml | 29 +- .../sample/FilePickerActivity2.java | 28 + .../sample/NoNonsenseFilePicker.java | 35 +- .../dropbox/DropboxFilePickerActivity.java | 2 +- .../dropbox/DropboxFilePickerActivity2.java | 25 + .../dropbox/DropboxFilePickerFragment.java | 125 ++-- .../sample/dropbox/DropboxSyncHelper.java | 4 +- .../activity_no_nonsense_file_picker.xml | 234 +++---- sample/src/main/res/values/colors.xml | 11 + sample/src/main/res/values/styles.xml | 38 +- 52 files changed, 1088 insertions(+), 1260 deletions(-) delete mode 100644 library/src/main/java/com/nononsenseapps/filepicker/BindableArrayAdapter.java create mode 100644 library/src/main/java/com/nononsenseapps/filepicker/FileItemAdapter.java create mode 100644 library/src/main/java/com/nononsenseapps/filepicker/LogicHandler.java delete mode 100644 library/src/main/res/drawable-hdpi/ab_solid_filepickertheme.9.png delete mode 100644 library/src/main/res/drawable-hdpi/ic_collections_collection_light.png create mode 100644 library/src/main/res/drawable-hdpi/ic_file_folder.png delete mode 100644 library/src/main/res/drawable-hdpi/ic_navigation_back.png delete mode 100644 library/src/main/res/drawable-mdpi/ab_solid_filepickertheme.9.png delete mode 100644 library/src/main/res/drawable-mdpi/ic_collections_collection_light.png create mode 100644 library/src/main/res/drawable-mdpi/ic_file_folder.png delete mode 100644 library/src/main/res/drawable-mdpi/ic_navigation_back.png delete mode 100644 library/src/main/res/drawable-xhdpi/ab_solid_filepickertheme.9.png delete mode 100644 library/src/main/res/drawable-xhdpi/ic_collections_collection_light.png create mode 100644 library/src/main/res/drawable-xhdpi/ic_file_folder.png delete mode 100644 library/src/main/res/drawable-xhdpi/ic_navigation_back.png delete mode 100644 library/src/main/res/drawable-xxhdpi/ab_solid_filepickertheme.9.png delete mode 100644 library/src/main/res/drawable-xxhdpi/ic_collections_collection_light.png create mode 100644 library/src/main/res/drawable-xxhdpi/ic_file_folder.png delete mode 100644 library/src/main/res/drawable-xxhdpi/ic_navigation_back.png delete mode 100644 library/src/main/res/drawable/selectable_action.xml create mode 100644 library/src/main/res/layout/dialog_folder_name.xml delete mode 100644 library/src/main/res/layout/dialog_new_item.xml create mode 100644 library/src/main/res/menu/picker_actions.xml delete mode 100644 library/src/main/res/values-sw600dp/dimens.xml delete mode 100644 library/src/main/res/values-sw600dp/styles.xml delete mode 100644 library/src/main/res/values/attrs.xml delete mode 100644 library/src/main/res/values/colors.xml delete mode 100644 library/src/main/res/values/styles.xml create mode 100644 sample/src/main/java/com/nononsenseapps/filepicker/sample/FilePickerActivity2.java create mode 100644 sample/src/main/java/com/nononsenseapps/filepicker/sample/dropbox/DropboxFilePickerActivity2.java create mode 100644 sample/src/main/res/values/colors.xml diff --git a/build.gradle b/build.gradle index b488131..8e50b18 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:0.13.0' + classpath 'com.android.tools.build:gradle:1.1.0' classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.5.1" } } diff --git a/gradle.properties b/gradle.properties index 50eeac7..60bb1d9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,6 +32,6 @@ POM_LICENCE_DIST=repo POM_DEVELOPER_ID=neckbeard POM_DEVELOPER_NAME=Jonas Kalderstam -ANDROID_BUILD_TARGET_SDK_VERSION=19 -ANDROID_BUILD_TOOLS_VERSION=19.1.0 -ANDROID_BUILD_SDK_VERSION=19 +ANDROID_BUILD_TARGET_SDK_VERSION=21 +ANDROID_BUILD_TOOLS_VERSION=21.1.1 +ANDROID_BUILD_SDK_VERSION=21 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f82ff71..4d98bfb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Sep 25 18:29:09 CEST 2014 +#Thu Apr 23 00:07:46 CEST 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/library/build.gradle b/library/build.gradle index 7b8e6a7..38e0cd6 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -17,14 +17,17 @@ android { } buildTypes { release { - runProguard false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + //minify false + //proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:22.1+' + compile 'com.android.support:support-v4:22.1+' + compile 'com.android.support:recyclerview-v7:+' } // apply from: 'https://raw.github.com/chrisbanes/gradle-mvn-push/master/gradle-mvn-push.gradle' diff --git a/library/src/main/java/com/nononsenseapps/filepicker/AbstractFilePickerActivity.java b/library/src/main/java/com/nononsenseapps/filepicker/AbstractFilePickerActivity.java index 1b8ee88..c6ca9ed 100644 --- a/library/src/main/java/com/nononsenseapps/filepicker/AbstractFilePickerActivity.java +++ b/library/src/main/java/com/nononsenseapps/filepicker/AbstractFilePickerActivity.java @@ -20,16 +20,13 @@ package com.nononsenseapps.filepicker; import android.annotation.TargetApi; import android.app.Activity; -import android.app.FragmentManager; import android.content.ClipData; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.Window; -import android.view.WindowManager; +import android.support.v4.app.FragmentManager; +import android.support.v7.app.AppCompatActivity; import java.util.ArrayList; import java.util.List; @@ -62,7 +59,7 @@ import java.util.List; * * @param */ -public abstract class AbstractFilePickerActivity extends Activity +public abstract class AbstractFilePickerActivity extends AppCompatActivity implements AbstractFilePickerFragment.OnFilePickedListener { public static final String EXTRA_START_PATH = "nononsense.intent" + ".START_PATH"; @@ -85,9 +82,6 @@ public abstract class AbstractFilePickerActivity extends Activity @Override protected void onCreate(Bundle savedInstanceState) { - requestWindowFeature(Window.FEATURE_ACTION_BAR); - setupFauxDialog(); - setupActionBar(); super.onCreate(savedInstanceState); setContentView(R.layout.activity_filepicker); @@ -102,7 +96,7 @@ public abstract class AbstractFilePickerActivity extends Activity intent.getBooleanExtra(EXTRA_ALLOW_MULTIPLE, allowMultiple); } - FragmentManager fm = getFragmentManager(); + FragmentManager fm = getSupportFragmentManager(); AbstractFilePickerFragment fragment = (AbstractFilePickerFragment) fm.findFragmentByTag(TAG); @@ -120,64 +114,10 @@ public abstract class AbstractFilePickerActivity extends Activity setResult(Activity.RESULT_CANCELED); } - protected void setupFauxDialog() { - // Check if this should be a dialog - TypedValue tv = new TypedValue(); - if (!getTheme().resolveAttribute(R.attr.isDialog, tv, true) || - tv.data == 0) { - return; - } - - // Should be a dialog; set up the window parameters. - DisplayMetrics dm = getResources().getDisplayMetrics(); - - WindowManager.LayoutParams params = getWindow().getAttributes(); - params.width = getResources() - .getDimensionPixelSize(R.dimen.configure_dialog_width); - params.height = Math.min(getResources() - .getDimensionPixelSize(R.dimen.configure_dialog_max_height), - dm.heightPixels * 3 / 4); - params.alpha = 1.0f; - params.dimAmount = 0.5f; - getWindow().setAttributes(params); - } - - protected void setupActionBar() { - getActionBar().setTitle(getWindowTitle()); - } - protected abstract AbstractFilePickerFragment getFragment( final String startPath, final int mode, final boolean allowMultiple, final boolean allowCreateDir); - /** - * @return the title to apply to the window - */ - protected String getWindowTitle() { - final int res; - switch (mode) { - case AbstractFilePickerFragment.MODE_DIR: - res = R.plurals.select_dir; - break; - case AbstractFilePickerFragment.MODE_FILE_AND_DIR: - res = R.plurals.select_dir_or_file; - break; - case AbstractFilePickerFragment.MODE_FILE: - default: - res = R.plurals.select_file; - break; - } - - final int count; - if (allowMultiple) { - count = 99; - } else { - count = 1; - } - - return getResources().getQuantityString(res, count); - } - @Override public void onSaveInstanceState(Bundle b) { super.onSaveInstanceState(b); diff --git a/library/src/main/java/com/nononsenseapps/filepicker/AbstractFilePickerFragment.java b/library/src/main/java/com/nononsenseapps/filepicker/AbstractFilePickerFragment.java index d5c1436..b3805cc 100644 --- a/library/src/main/java/com/nononsenseapps/filepicker/AbstractFilePickerFragment.java +++ b/library/src/main/java/com/nononsenseapps/filepicker/AbstractFilePickerFragment.java @@ -18,23 +18,28 @@ package com.nononsenseapps.filepicker; import android.app.Activity; -import android.app.ListFragment; -import android.app.LoaderManager; -import android.content.Loader; import android.net.Uri; import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.Loader; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.util.SortedList; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.CheckedTextView; -import android.widget.ListView; +import android.widget.CheckBox; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; +import java.util.HashSet; import java.util.List; /** @@ -45,10 +50,9 @@ import java.util.List; * OnFilePickedListener} * interface. */ -public abstract class AbstractFilePickerFragment extends ListFragment - implements LoaderManager.LoaderCallbacks>, - NewItemFragment.OnNewFolderListener, - AdapterView.OnItemLongClickListener { +public abstract class AbstractFilePickerFragment extends Fragment + implements LoaderManager.LoaderCallbacks>, + NewItemFragment.OnNewFolderListener, LogicHandler { // The different preset modes of operation. This impacts the behaviour // and possible actions in the UI. @@ -66,21 +70,34 @@ public abstract class AbstractFilePickerFragment extends ListFragment public static final String KEY_ALLOW_MULTIPLE = "KEY_ALLOW_MULTIPLE"; // Used for saving state. protected static final String KEY_CURRENT_PATH = "KEY_CURRENT PATH"; - protected final DefaultHashMap checkedItems; + protected final HashSet checkedItems; + protected final HashSet checkedVisibleViewHolders; protected T currentPath = null; protected boolean allowCreateDir = false; protected boolean allowMultiple = false; - protected Comparator comparator = null; private OnFilePickedListener listener; - private BindableArrayAdapter adapter; + private FileItemAdapter mAdapter = null; private TextView currentDirView; + private Toolbar mToolbar; + private RecyclerView mRecyclerView; + private LinearLayoutManager mLayoutManager; + private SortedList mFiles = null; + + protected FileItemAdapter getAdapter() { + return mAdapter; + } + + protected FileItemAdapter getDummyAdapter() { + return new FileItemAdapter<>(this); + } /** * Mandatory empty constructor for the fragment manager to instantiate the * fragment (e.g. upon screen orientation changes). */ public AbstractFilePickerFragment() { - checkedItems = new DefaultHashMap(false); + checkedItems = new HashSet<>(); + checkedVisibleViewHolders = new HashSet<>(); } /** @@ -92,7 +109,7 @@ public abstract class AbstractFilePickerFragment extends ListFragment * @param allowDirCreate */ public void setArgs(final String startPath, final int mode, - final boolean allowMultiple, final boolean allowDirCreate) { + final boolean allowMultiple, final boolean allowDirCreate) { Bundle b = new Bundle(); if (startPath != null) { b.putString(KEY_START_PATH, startPath); @@ -105,12 +122,26 @@ public abstract class AbstractFilePickerFragment extends ListFragment @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_filepicker, null); - ListView lv = (ListView) view.findViewById(android.R.id.list); + mToolbar = (Toolbar) view.findViewById(R.id.picker_toolbar); + ((AppCompatActivity) getActivity()).setSupportActionBar(mToolbar); - lv.setOnItemLongClickListener(this); + + mRecyclerView = (RecyclerView) view.findViewById(android.R.id.list); + // improve performance if you know that changes in content + // do not change the size of the RecyclerView + mRecyclerView.setHasFixedSize(true); + // I want some dividers + //mRecyclerView.addItemDecoration(new DividerColor + // (getActivity(), DividerColor.VERTICAL_LIST, 0, 1)); + // use a linear layout manager + mLayoutManager = new LinearLayoutManager(getActivity()); + mRecyclerView.setLayoutManager(mLayoutManager); + // Set adapter + mAdapter = new FileItemAdapter<>(this); + mRecyclerView.setAdapter(mAdapter); view.findViewById(R.id.button_cancel) .setOnClickListener(new View.OnClickListener() { @@ -131,13 +162,7 @@ public abstract class AbstractFilePickerFragment extends ListFragment } // Some invalid cases first - if (allowMultiple && checkedItems.isEmpty()) { - Toast.makeText(getActivity(), - R.string.select_something_first, - Toast.LENGTH_SHORT).show(); - return; - } - if (mode == MODE_FILE && isDir(currentPath)) { + if ((allowMultiple || mode == MODE_FILE) && checkedItems.isEmpty()) { Toast.makeText(getActivity(), R.string.select_something_first, Toast.LENGTH_SHORT).show(); @@ -145,37 +170,22 @@ public abstract class AbstractFilePickerFragment extends ListFragment } if (allowMultiple) { - listener.onFilesPicked(toUri(getCheckedItems())); - } else { + listener.onFilesPicked(toUri(checkedItems)); + } else if (mode == MODE_FILE) { + listener.onFilePicked(toUri(getFirstCheckedItem())); + } else if (mode == MODE_DIR) { listener.onFilePicked(toUri(currentPath)); + } else { + // single FILE OR DIR + if (checkedItems.isEmpty()) { + listener.onFilePicked(toUri(currentPath)); + } else { + listener.onFilePicked(toUri(getFirstCheckedItem())); + } } } }); - view.findViewById(R.id.button_go_parent) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(final View v) { - // Go to parent - currentPath = getParent(currentPath); - refresh(); - } - }); - - final View createDirView = view.findViewById(R.id.button_create_dir); - // Only show the create dir button if configured to - createDirView.setVisibility((allowCreateDir && (mode == MODE_DIR)) ? - View.VISIBLE : - View.INVISIBLE); - createDirView.setEnabled((allowCreateDir && (mode == MODE_DIR))); - createDirView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(final View v) { - NewFolderFragment.showDialog(getFragmentManager(), - AbstractFilePickerFragment.this); - } - }); - currentDirView = (TextView) view.findViewById(R.id.current_dir); // Restore state if (currentPath != null) { @@ -185,14 +195,14 @@ public abstract class AbstractFilePickerFragment extends ListFragment return view; } - /** - * Return true if the path is a directory and not a file. - * - * @param path - */ - protected abstract boolean isDir(final T path); + public T getFirstCheckedItem() { + for (T file : checkedItems) { + return file; + } + return null; + } - protected List toUri(List files) { + protected List toUri(Iterable files) { ArrayList uris = new ArrayList(); for (T file : files) { uris.add(toUri(file)); @@ -200,61 +210,11 @@ public abstract class AbstractFilePickerFragment extends ListFragment return uris; } - /** - * @return the selected files. Can be empty. - */ - protected List getCheckedItems() { - final BindableArrayAdapter adapter = - (BindableArrayAdapter) getListAdapter(); - final ArrayList files = new ArrayList(); - for (int pos : checkedItems.keySet()) { - if (checkedItems.get(pos)) { - files.add(adapter.getItem(pos)); - } - } - return files; - } - - /** - * Convert the path to a URI for the return intent - * - * @param path - * @return - */ - protected abstract Uri toUri(final T path); - - /** - * Return the path to the parent directory. Should return the root if - * from is root. - * - * @param from - */ - protected abstract T getParent(final T from); - - /** - * @param path - * @return the full path to the file - */ - protected abstract String getFullPath(final T path); - - @Override - public void onListItemClick(ListView l, View v, int position, long id) { - super.onListItemClick(l, v, position, id); - currentPath = (T) getListAdapter().getItem(position); - if (isDir(currentPath)) { - refresh(); - } else if (isCheckable(currentPath)) { - toggleItemCheck( - (CheckedTextView) v.findViewById(android.R.id.text1), - position, currentPath); - } - } - - protected boolean isCheckable(final T data) { + public boolean isCheckable(final T data) { final boolean checkable; if (isDir(data)) { checkable = ((mode == MODE_DIR && allowMultiple) || - (mode == MODE_FILE_AND_DIR && allowMultiple)); + (mode == MODE_FILE_AND_DIR && allowMultiple)); } else { // File checkable = (mode != MODE_DIR); @@ -262,22 +222,6 @@ public abstract class AbstractFilePickerFragment extends ListFragment return checkable; } - protected void toggleItemCheck(final CheckedTextView view, - final int position, final T data) { - if (!isCheckable(data)) { - return; - } - - final boolean oldVal = checkedItems.get(position); - - if (!allowMultiple) { - checkedItems.clear(); - } - checkedItems.put(position, !oldVal); - // Redraw the items - getListView().invalidateViews(); - } - @Override public void onAttach(Activity activity) { super.onAttach(activity); @@ -285,7 +229,7 @@ public abstract class AbstractFilePickerFragment extends ListFragment listener = (OnFilePickedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + - " must implement OnFilePickedListener"); + " must implement OnFilePickedListener"); } } @@ -328,6 +272,29 @@ public abstract class AbstractFilePickerFragment extends ListFragment setHasOptionsMenu(true); } + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.picker_actions, menu); + + MenuItem item = menu.findItem(R.id.action_createdir); + item.setVisible(allowCreateDir && (mode == MODE_DIR)); + } + + @Override + public boolean onOptionsItemSelected(MenuItem menuItem) { + if (R.id.action_createdir == menuItem.getItemId()) { + Activity activity = getActivity(); + if (activity instanceof AppCompatActivity) { + NewFolderFragment.showDialog(((AppCompatActivity) activity).getSupportFragmentManager(), + AbstractFilePickerFragment.this); + } + return true; + } else { + return false; + } + } + + @Override public void onSaveInstanceState(Bundle b) { super.onSaveInstanceState(b); @@ -343,18 +310,6 @@ public abstract class AbstractFilePickerFragment extends ListFragment listener = null; } - /** - * Convert the path to the type used. - * - * @param path - */ - protected abstract T getPath(final String path); - - /** - * Get the root path (lowest allowed). - */ - protected abstract T getRoot(); - /** * Refreshes the list. Call this when current path changes. */ @@ -363,36 +318,6 @@ public abstract class AbstractFilePickerFragment extends ListFragment .restartLoader(0, null, AbstractFilePickerFragment.this); } - /** - * Callback method to be invoked when an item in this view has been - * clicked and held. - *

- * Implementers can call getItemAtPosition(position) if they need to access - * the data associated with the selected item. - * - * @param parent The AbsListView where the click happened - * @param view The view within the AbsListView that was clicked - * @param position The position of the view in the list - * @param id The row id of the item that was clicked - * @return true if the callback consumed the long click, false otherwise - */ - @Override - public boolean onItemLongClick(final AdapterView parent, final View view, - final int position, final long id) { - final T data = (T) getListAdapter().getItem(position); - if (!isCheckable(data)) { - return false; - } - // Special case for single choice to handle directories - if (!allowMultiple) { - return false; - } - - toggleItemCheck((CheckedTextView) view.findViewById(android.R.id.text1), - position, data); - return true; - } - /** * Instantiate and return a new Loader for the given ID. * @@ -401,16 +326,10 @@ public abstract class AbstractFilePickerFragment extends ListFragment * @return Return a new Loader instance that is ready to start loading. */ @Override - public Loader> onCreateLoader(final int id, final Bundle args) { + public Loader> onCreateLoader(final int id, final Bundle args) { return getLoader(); } - /** - * Get a loader that lists the files in the current path, - * and monitors changes. - */ - protected abstract Loader> getLoader(); - /** * Called when a previously created loader has finished its load. * @@ -418,125 +337,15 @@ public abstract class AbstractFilePickerFragment extends ListFragment * @param data The data generated by the Loader. */ @Override - public void onLoadFinished(final Loader> loader, - final List data) { - if (adapter == null) { - // View type not really used, overridden in ViewBinder - adapter = new BindableArrayAdapter(getActivity(), - R.layout.filepicker_listitem_checkable); - adapter.setViewBinder(getViewBinder()); - } else { - adapter.clear(); - } - if (comparator == null) { - comparator = getComparator(); - } + public void onLoadFinished(final Loader> loader, + final SortedList data) { checkedItems.clear(); - adapter.addAll(data); - adapter.sort(comparator); - setListAdapter(adapter); - adapter.notifyDataSetChanged(); + checkedVisibleViewHolders.clear(); + mFiles = data; + mAdapter.setList(data); currentDirView.setText(getFullPath(currentPath)); } - /** - * @return a ViewBinder to handle list items, or null. - */ - protected BindableArrayAdapter.ViewBinder getViewBinder() { - class ViewHolder { - protected View icon; - protected TextView text; - protected CheckedTextView checkbox; - } - - return new BindableArrayAdapter.ViewBinder() { - - /** - * Called if convertView is null. If this returns null, - * the specified resource is used. Use this to return multiple views - * depending on type. - * - * @param position - * @param defResource - * @param inflater - * @param parent - * @return - */ - @Override - public View inflateView(final int position, final int defResource, - final LayoutInflater inflater, final ViewGroup parent) { - final boolean checkable = - isCheckable((T) getListAdapter().getItem(position)); - final View view = inflater.inflate(checkable ? - R.layout.filepicker_listitem_checkable : - R.layout.filepicker_listitem_dir, - parent, false); - - ViewHolder viewHolder = new ViewHolder(); - viewHolder.icon = view.findViewById(R.id.item_icon); - viewHolder.text = - (TextView) view.findViewById(android.R.id.text1); - if (checkable) { - viewHolder.checkbox = (CheckedTextView) view - .findViewById(android.R.id.text1); - } else { - viewHolder.checkbox = null; - } - view.setTag(viewHolder); - - return view; - } - - /** - * Used to determine the view's type. Returning false will use same - * type for all rows. - * - * @param position - * @param data - * @return - */ - @Override - public boolean isDir(final int position, final T data) { - return AbstractFilePickerFragment.this.isDir(data); - } - - /** - * Fill the content in the row - * @param view - * @param position - * @param data - */ - @Override - public void setViewValue(final View view, final int position, - final T data) { - if (view.getTag() == null) { - return; - } - - ((ViewHolder) view.getTag()).text.setText(getName(data)); - - ((ViewHolder) view.getTag()).icon.setVisibility( - isDir(position, data) ? View.VISIBLE : View.GONE); - - if (((ViewHolder) view.getTag()).checkbox != null) { - ((ViewHolder) view.getTag()).checkbox - .setChecked(checkedItems.get(position)); - } - } - }; - } - - /** - * @return a comparator that can sort the items alphabetically - */ - protected abstract Comparator getComparator(); - - /** - * @param path - * @return the name of this file/folder - */ - protected abstract String getName(final T path); - /** * Called when a previously created loader is being reset, and thus * making its data unavailable. The application should at this point @@ -545,9 +354,9 @@ public abstract class AbstractFilePickerFragment extends ListFragment * @param loader The Loader that is being reset. */ @Override - public void onLoaderReset(final Loader> loader) { - setListAdapter(null); - adapter = null; + public void onLoaderReset(final Loader> loader) { + mAdapter.setList(null); + mFiles = null; } /** @@ -569,16 +378,202 @@ public abstract class AbstractFilePickerFragment extends ListFragment public void onCancelled(); } - public class DefaultHashMap extends HashMap { - protected final V defaultValue; - - public DefaultHashMap(final V defaultValue) { - this.defaultValue = defaultValue; - } - - @Override - public V get(Object k) { - return containsKey(k) ? super.get(k) : defaultValue; + /** + * @param position 0 - n, where the header has been subtracted + * @param data + * @return an integer greater than 0 + */ + @Override + public int getItemViewType(int position, T data) { + if (isCheckable(data)) { + return LogicHandler.VIEWTYPE_CHECKABLE; + } else { + return LogicHandler.VIEWTYPE_DIR; } } + + @Override + public void onBindHeaderViewHolder(HeaderViewHolder viewHolder) { + viewHolder.text.setText(".."); + } + + /** + * @param parent + * @param viewType + * @return a view holder for a file or directory + */ + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View v; + switch (viewType) { + case LogicHandler.VIEWTYPE_HEADER: + v = LayoutInflater.from(getActivity()).inflate(R.layout.filepicker_listitem_dir, + parent, false); + return new HeaderViewHolder(v); + case LogicHandler.VIEWTYPE_CHECKABLE: + v = LayoutInflater.from(getActivity()).inflate(R.layout.filepicker_listitem_checkable, + parent, false); + return new CheckableViewHolder(v); + case LogicHandler.VIEWTYPE_DIR: + default: + v = LayoutInflater.from(getActivity()).inflate(R.layout.filepicker_listitem_dir, + parent, false); + return new DirViewHolder(v); + } + } + + /** + * @param vh to bind data from either a file or directory + * @param position 0 - n, where the header has been subtracted + * @param data + */ + @Override + public void onBindViewHolder(DirViewHolder vh, int position, T data) { + vh.file = data; + vh.icon.setVisibility(isDir(data) ? View.VISIBLE : View.GONE); + vh.text.setText(getName(data)); + + if (isCheckable(data)) { + if (checkedItems.contains(data)) { + checkedVisibleViewHolders.add((CheckableViewHolder) vh); + ((CheckableViewHolder) vh).checkbox.setChecked(true); + } else { + checkedVisibleViewHolders.remove(vh); + ((CheckableViewHolder) vh).checkbox.setChecked(false); + } + } + } + + public class HeaderViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + final TextView text; + + public HeaderViewHolder(View v) { + super(v); + v.setOnClickListener(this); + text = (TextView) v.findViewById(android.R.id.text1); + } + + /** + * Called when a view has been clicked. + * + * @param v The view that was clicked. + */ + @Override + public void onClick(View v) { + currentPath = getParent(currentPath); + checkedItems.clear(); + checkedVisibleViewHolders.clear(); + refresh(); + } + } + + public class DirViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { + + public View icon; + public TextView text; + public T file; + + public DirViewHolder(View v) { + super(v); + v.setOnClickListener(this); + v.setOnLongClickListener(this); + icon = v.findViewById(R.id.item_icon); + text = (TextView) v.findViewById(android.R.id.text1); + } + + /** + * Called when a view has been clicked. + * + * @param v The view that was clicked. + */ + @Override + public void onClick(View v) { + if (isDir(file)) { + currentPath = file; + checkedItems.clear(); + checkedVisibleViewHolders.clear(); + refresh(); + } + } + + /** + * Called when a view has been clicked and held. + * + * @param v The view that was clicked and held. + * @return true if the callback consumed the long click, false otherwise. + */ + @Override + public boolean onLongClick(View v) { + return false; + } + } + + public class CheckableViewHolder extends DirViewHolder { + + public CheckBox checkbox; + + public CheckableViewHolder(View v) { + super(v); + checkbox = (CheckBox) v.findViewById(R.id.checkbox); + checkbox.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onLongClick(v); + } + }); + } + + /** + * Called when a view has been clicked. + * + * @param v The view that was clicked. + */ + @Override + public void onClick(View v) { + if (isDir(file)) { + currentPath = file; + checkedItems.clear(); + checkedVisibleViewHolders.clear(); + refresh(); + } else { + onLongClick(v); + } + } + + /** + * Called when a view has been clicked and held. + * + * @param v The view that was clicked and held. + * @return true if the callback consumed the long click, false otherwise. + */ + @Override + public boolean onLongClick(View v) { + if (checkedItems.contains(file)) { + checkbox.setChecked(false); + checkedItems.remove(file); + checkedVisibleViewHolders.remove(this); + } else { + if (!allowMultiple) { + clearSelections(); + } + checkbox.setChecked(true); + checkedItems.add(file); + checkedVisibleViewHolders.add(this); + } + return true; + } + } + + /** + * Animate de-selection of visible views and clear + * selected set. + */ + public void clearSelections() { + for (CheckableViewHolder vh : checkedVisibleViewHolders) { + vh.checkbox.setChecked(false); + } + checkedVisibleViewHolders.clear(); + checkedItems.clear(); + } + } diff --git a/library/src/main/java/com/nononsenseapps/filepicker/BindableArrayAdapter.java b/library/src/main/java/com/nononsenseapps/filepicker/BindableArrayAdapter.java deleted file mode 100644 index f270f0e..0000000 --- a/library/src/main/java/com/nononsenseapps/filepicker/BindableArrayAdapter.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2014 Jonas Kalderstam - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.nononsenseapps.filepicker; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; - -import java.util.List; - -public class BindableArrayAdapter extends ArrayAdapter { - - private final LayoutInflater mInflater; - private final int mResource; - private ViewBinder viewBinder = null; - - /** - * Constructor - * - * @param context The current context. - * @param resource The resource ID for a layout file containing a TextView to use when - */ - public BindableArrayAdapter(final Context context, final int resource) { - super(context, resource); - mResource = resource; - mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - } - - /** - * Constructor - * - * @param context The current context. - * @param resource The resource ID for a layout file containing a layout to use when - * instantiating views. - * @param textViewResourceId The id of the TextView within the layout resource to be populated - */ - public BindableArrayAdapter(final Context context, final int resource, final int textViewResourceId) { - super(context, resource, textViewResourceId); - mResource = resource; - mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - } - - /** - * Constructor - * - * @param context The current context. - * @param resource The resource ID for a layout file containing a TextView to use when - * instantiating views. - * @param objects The objects to represent in the ListView. - */ - public BindableArrayAdapter(final Context context, final int resource, final T[] objects) { - super(context, resource, objects); - mResource = resource; - mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - } - - /** - * Constructor - * - * @param context The current context. - * @param resource The resource ID for a layout file containing a layout to use when - * instantiating views. - * @param textViewResourceId The id of the TextView within the layout resource to be populated - * @param objects The objects to represent in the ListView. - */ - public BindableArrayAdapter(final Context context, final int resource, final int textViewResourceId, final T[] objects) { - super(context, resource, textViewResourceId, objects); - mResource = resource; - mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - } - - /** - * Constructor - * - * @param context The current context. - * @param resource The resource ID for a layout file containing a TextView to use when - * instantiating views. - * @param objects The objects to represent in the ListView. - */ - public BindableArrayAdapter(final Context context, final int resource, final List objects) { - super(context, resource, objects); - mResource = resource; - mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - } - - /** - * Constructor - * - * @param context The current context. - * @param resource The resource ID for a layout file containing a layout to use when - * instantiating views. - * @param textViewResourceId The id of the TextView within the layout resource to be populated - * @param objects The objects to represent in the ListView. - */ - public BindableArrayAdapter(final Context context, final int resource, final int textViewResourceId, final List objects) { - super(context, resource, textViewResourceId, objects); - mResource = resource; - mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - } - - @Override - public int getItemViewType(int position) { - if (viewBinder != null) { - if (viewBinder.isDir(position, getItem(position))) { - return 1; - } - } - return 0; - } - - @Override - public int getViewTypeCount() { - // Files and dirs, so 2 - return 2; - } - - /** - * Set the view binder to use to bind data to the list item views. - * @param binder - */ - public void setViewBinder(ViewBinder binder) { - this.viewBinder = binder; - } - - /** - * Handle more complex views than standard implementation. - * - * @param position - * @param convertView - * @param parent - * @return - */ - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (viewBinder == null) { - return super.getView(position, convertView, parent); - } - else { - View view; - - if (convertView == null && viewBinder != null) { - view = viewBinder.inflateView(position, mResource, mInflater, - parent); - } - else if (convertView == null) { - view = mInflater.inflate(mResource, parent, false); - } else { - view = convertView; - } - - viewBinder.setViewValue(view, position, getItem(position)); - return view; - } - } - - public interface ViewBinder { - /** - * Called if convertView is null. If this returns null, - * the specified resource is used. Use this to return multiple views - * depending on type. - * @param position - * @param resource - * @param inflater - * @param parent - * @return - */ - public View inflateView(final int position, - final int resource, - final LayoutInflater inflater, - final ViewGroup parent); - - /** - * Used to determine the view's type. Returning false will use same - * type for all rows. - * @param position - * @param data - * @return - */ - public boolean isDir(final int position, final T data); - - /** - * Fill the content in the row - * @param view - * @param position - * @param data - */ - public void setViewValue(final View view, final int position, final T - data); - } -} diff --git a/library/src/main/java/com/nononsenseapps/filepicker/FileItemAdapter.java b/library/src/main/java/com/nononsenseapps/filepicker/FileItemAdapter.java new file mode 100644 index 0000000..0cfa309 --- /dev/null +++ b/library/src/main/java/com/nononsenseapps/filepicker/FileItemAdapter.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015 Jonas Kalderstam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nononsenseapps.filepicker; + +import android.support.v7.util.SortedList; +import android.support.v7.widget.RecyclerView; +import android.view.ViewGroup; + +/** + * A simple adapter which also inserts a header item ".." to handle going up to the parent folder. + * @param the type which is used, for example a normal java File object. + */ +public class FileItemAdapter extends RecyclerView.Adapter { + + private final LogicHandler mLogic; + private SortedList mList = null; + + public FileItemAdapter(LogicHandler logic) { + this.mLogic = logic; + } + + public void setList(SortedList list) { + mList = list; + notifyDataSetChanged(); + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return mLogic.onCreateViewHolder(parent, viewType); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int headerPosition) { + if (headerPosition == 0) { + mLogic.onBindHeaderViewHolder((AbstractFilePickerFragment.HeaderViewHolder) viewHolder); + } else { + int pos = headerPosition - 1; + mLogic.onBindViewHolder((AbstractFilePickerFragment.DirViewHolder) viewHolder, pos, mList.get(pos)); + } + } + + @Override + public int getItemViewType(int headerPosition) { + if (0 == headerPosition) { + return LogicHandler.VIEWTYPE_HEADER; + } else { + int pos = headerPosition - 1; + return mLogic.getItemViewType(pos, mList.get(pos)); + } + } + + @Override + public int getItemCount() { + if (mList == null) { + return 0; + } + + // header + count + return 1 + mList.size(); + } +} diff --git a/library/src/main/java/com/nononsenseapps/filepicker/FilePickerFragment.java b/library/src/main/java/com/nononsenseapps/filepicker/FilePickerFragment.java index b154dfe..7f6ff74 100644 --- a/library/src/main/java/com/nononsenseapps/filepicker/FilePickerFragment.java +++ b/library/src/main/java/com/nononsenseapps/filepicker/FilePickerFragment.java @@ -17,21 +17,21 @@ package com.nononsenseapps.filepicker; -import android.content.AsyncTaskLoader; -import android.content.Loader; import android.net.Uri; import android.os.Environment; import android.os.FileObserver; +import android.support.v4.content.AsyncTaskLoader; +import android.support.v4.content.Loader; +import android.support.v7.util.SortedList; +import android.support.v7.widget.util.SortedListAdapterCallback; import android.widget.Toast; import java.io.File; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; public class FilePickerFragment extends AbstractFilePickerFragment { - public FilePickerFragment() {} + public FilePickerFragment() { + } /** * Return true if the path is a directory and not a file. @@ -39,10 +39,19 @@ public class FilePickerFragment extends AbstractFilePickerFragment { * @param path */ @Override - protected boolean isDir(final File path) { + public boolean isDir(final File path) { return path.isDirectory(); } + /** + * @param path + * @return filename of path + */ + @Override + public String getName(File path) { + return path.getName(); + } + /** * Return the path to the parent directory. Should return the root if * from is root. @@ -50,7 +59,7 @@ public class FilePickerFragment extends AbstractFilePickerFragment { * @param from */ @Override - protected File getParent(final File from) { + public File getParent(final File from) { if (from.getParentFile() != null) { if (from.isFile()) { return getParent(from.getParentFile()); @@ -68,7 +77,7 @@ public class FilePickerFragment extends AbstractFilePickerFragment { * @param path */ @Override - protected File getPath(final String path) { + public File getPath(final String path) { return new File(path); } @@ -77,72 +86,71 @@ public class FilePickerFragment extends AbstractFilePickerFragment { * @return the full path to the file */ @Override - protected String getFullPath(final File path) { + public String getFullPath(final File path) { return path.getPath(); } - /** - * @param path - * @return the name of this file/folder - */ - @Override - protected String getName(final File path) { - return path.getName(); - } - /** * Get the root path (lowest allowed). */ @Override - protected File getRoot() { + public File getRoot() { return Environment.getExternalStorageDirectory(); } /** * Convert the path to a URI for the return intent + * * @param file * @return */ @Override - protected Uri toUri(final File file) { + public Uri toUri(final File file) { return Uri.fromFile(file); } - /** - * @return a comparator that can sort the items alphabetically - */ - @Override - protected Comparator getComparator() { - return new Comparator() { - @Override - public int compare(final File lhs, final File rhs) { - if (lhs.isDirectory() && !rhs.isDirectory()) { - return -1; - } else if (rhs.isDirectory() && !lhs.isDirectory()) { - return 1; - } else { - return lhs.getName().toLowerCase().compareTo(rhs.getName() - .toLowerCase()); - } - } - }; - } - /** * Get a loader that lists the Files in the current path, * and monitors changes. */ @Override - protected Loader> getLoader() { - return new AsyncTaskLoader>(getActivity()) { + public Loader> getLoader() { + return new AsyncTaskLoader>(getActivity()) { FileObserver fileObserver; @Override - public List loadInBackground() { - ArrayList files = new ArrayList(); + public SortedList loadInBackground() { File[] listFiles = currentPath.listFiles(); - if(listFiles != null) { + final int initCap = listFiles == null ? 0 : listFiles.length; + + SortedList files = new SortedList<>(File.class, new SortedListAdapterCallback(getDummyAdapter()) { + @Override + public int compare(File lhs, File rhs) { + if (lhs.isDirectory() && !rhs.isDirectory()) { + return -1; + } else if (rhs.isDirectory() && !lhs.isDirectory()) { + return 1; + } else { + return lhs.getName().toLowerCase().compareTo(rhs.getName() + .toLowerCase()); + } + } + + @Override + public boolean areContentsTheSame(File file, File file2) { + return file.getAbsolutePath().equals(file2.getAbsolutePath()) && (file.isFile() == file2.isFile()); + } + + @Override + public boolean areItemsTheSame(File file, File file2) { + return areContentsTheSame(file, file2); + } + }, initCap); + + + files.beginBatchedUpdates(); + if (listFiles != null) { for (java.io.File f : listFiles) { if ((mode == MODE_FILE || mode == MODE_FILE_AND_DIR) || f.isDirectory()) { @@ -150,6 +158,9 @@ public class FilePickerFragment extends AbstractFilePickerFragment { } } } + + files.endBatchedUpdates(); + return files; } @@ -217,4 +228,6 @@ public class FilePickerFragment extends AbstractFilePickerFragment { Toast.LENGTH_SHORT).show(); } } + + } diff --git a/library/src/main/java/com/nononsenseapps/filepicker/LogicHandler.java b/library/src/main/java/com/nononsenseapps/filepicker/LogicHandler.java new file mode 100644 index 0000000..c06eda9 --- /dev/null +++ b/library/src/main/java/com/nononsenseapps/filepicker/LogicHandler.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2015 Jonas Kalderstam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nononsenseapps.filepicker; + +import android.net.Uri; +import android.support.v4.content.Loader; +import android.support.v7.util.SortedList; +import android.support.v7.widget.RecyclerView; +import android.view.ViewGroup; + +/** + * An interface for the methods required to handle backend-specific stuff. + */ +public interface LogicHandler { + + /** + * Return true if the path is a directory and not a file. + * + * @param path + */ + public boolean isDir(final T path); + + /** + * + * @param path + * @return filename of path + */ + public String getName(final T path); + + /** + * Convert the path to a URI for the return intent + * + * @param path + * @return + */ + public Uri toUri(final T path); + + /** + * Return the path to the parent directory. Should return the root if + * from is root. + * + * @param from + */ + public T getParent(final T from); + + /** + * @param path + * @return the full path to the file + */ + public String getFullPath(final T path); + + /** + * Convert the path to the type used. + * + * @param path + */ + public T getPath(final String path); + + /** + * Get the root path (lowest allowed). + */ + public T getRoot(); + + /** + * Get a loader that lists the files in the current path, + * and monitors changes. + */ + public Loader> getLoader(); + + /** + * @return a comparator that can sort the items alphabetically + */ + //public Comparator getComparator(); + + + public final static int VIEWTYPE_HEADER = 0; + public final static int VIEWTYPE_DIR = 1; + public final static int VIEWTYPE_CHECKABLE = 2; + + /** + * Bind the header ".." which goes to parent folder. + * @param viewHolder + */ + public void onBindHeaderViewHolder(AbstractFilePickerFragment.HeaderViewHolder viewHolder); + + /** + * Header is subtracted from the position + * + * @param parent + * @param viewType + * @return a view holder for a file or directory + */ + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType); + + /** + * + * @param viewHolder to bind data from either a file or directory + * @param position 0 - n, where the header has been subtracted + * @param data + */ + public void onBindViewHolder(AbstractFilePickerFragment.DirViewHolder viewHolder, int position, T data); + + /** + * + * @param position 0 - n, where the header has been subtracted + * @param data + * @return an integer greater than 0 + */ + public int getItemViewType(int position, T data); +} diff --git a/library/src/main/java/com/nononsenseapps/filepicker/NewFolderFragment.java b/library/src/main/java/com/nononsenseapps/filepicker/NewFolderFragment.java index 5673aa9..3bc73ad 100644 --- a/library/src/main/java/com/nononsenseapps/filepicker/NewFolderFragment.java +++ b/library/src/main/java/com/nononsenseapps/filepicker/NewFolderFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Jonas Kalderstam + * Copyright (c) 2015 Jonas Kalderstam * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,11 +18,13 @@ package com.nononsenseapps.filepicker; -import android.app.FragmentManager; +import android.support.v4.app.FragmentManager; +import android.text.TextUtils; public class NewFolderFragment extends NewItemFragment { private static final String TAG = "new_folder_fragment"; + public static void showDialog(final FragmentManager fm, final OnNewFolderListener listener) { NewItemFragment d = new NewFolderFragment(); d.setListener(listener); @@ -31,12 +33,9 @@ public class NewFolderFragment extends NewItemFragment { @Override protected boolean validateName(final String itemName) { - return itemName != null && !itemName.isEmpty() - && !itemName.contains("/"); - } - - @Override - protected int getDialogTitle() { - return R.string.new_folder; + return !TextUtils.isEmpty(itemName) + && !itemName.contains("/") + && !itemName.equals(".") + && !itemName.equals(".."); } } diff --git a/library/src/main/java/com/nononsenseapps/filepicker/NewItemFragment.java b/library/src/main/java/com/nononsenseapps/filepicker/NewItemFragment.java index 39d6b16..acb9b13 100644 --- a/library/src/main/java/com/nononsenseapps/filepicker/NewItemFragment.java +++ b/library/src/main/java/com/nononsenseapps/filepicker/NewItemFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Jonas Kalderstam + * Copyright (c) 2015 Jonas Kalderstam * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,21 +18,20 @@ package com.nononsenseapps.filepicker; -import android.annotation.SuppressLint; import android.app.Activity; -import android.app.DialogFragment; +import android.app.Dialog; +import android.content.DialogInterface; import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AlertDialog; import android.text.Editable; import android.text.TextWatcher; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; +import android.widget.Button; import android.widget.EditText; public abstract class NewItemFragment extends DialogFragment { - private String itemName = null; - private View okButton = null; private OnNewFolderListener listener = null; public NewItemFragment() { @@ -50,67 +49,70 @@ public abstract class NewItemFragment extends DialogFragment { } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - getDialog().setTitle(getDialogTitle()); + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setView(R.layout.dialog_folder_name) + .setTitle(R.string.new_folder) + .setNegativeButton(android.R.string.cancel, + null) + .setPositiveButton(android.R.string.ok, + null); - @SuppressLint("InflateParams") final View view = - inflater.inflate(R.layout.dialog_new_item, null); + final AlertDialog dialog = builder.create(); - okButton = view.findViewById(R.id.button_ok); - okButton.setOnClickListener(new View.OnClickListener() { + dialog.setOnShowListener(new DialogInterface.OnShowListener() { @Override - public void onClick(final View v) { - if (listener != null) { - listener.onNewFolder(itemName); - } - dismiss(); - } - }); + public void onShow(DialogInterface dialog1) { + final AlertDialog dialog = (AlertDialog) dialog1; + final EditText editText = (EditText) dialog.findViewById(R.id.edit_text); + + Button cancel = dialog.getButton(AlertDialog.BUTTON_NEGATIVE); + cancel.setOnClickListener(new View.OnClickListener() { - view.findViewById(R.id.button_cancel) - .setOnClickListener(new View.OnClickListener() { @Override - public void onClick(final View v) { - dismiss(); + public void onClick(View view) { + dialog.cancel(); } }); - final EditText editText = (EditText) view.findViewById(R.id.edit_text); - if (itemName == null) { - okButton.setEnabled(false); - } else { - editText.setText(itemName); - validateItemName(); - } + final Button ok = dialog.getButton(AlertDialog.BUTTON_POSITIVE); + // Start disabled + ok.setEnabled(false); + ok.setOnClickListener(new View.OnClickListener() { - editText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(final CharSequence s, final int start, - final int count, final int after) { - } + @Override + public void onClick(View view) { + String itemName = editText.getText().toString(); + if (validateName(itemName)) { + if (listener != null) { + listener.onNewFolder(itemName); + } + dialog.dismiss(); + } + } + }); - @Override - public void onTextChanged(final CharSequence s, final int start, - final int before, final int count) { - } + editText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(final CharSequence s, final int start, + final int count, final int after) { + } - @Override - public void afterTextChanged(final Editable s) { - itemName = s.toString(); - validateItemName(); + @Override + public void onTextChanged(final CharSequence s, final int start, + final int before, final int count) { + } + + @Override + public void afterTextChanged(final Editable s) { + ok.setEnabled(validateName(s.toString())); + } + }); } }); - return view; - } - protected abstract int getDialogTitle(); - - private void validateItemName() { - if (okButton != null) { - okButton.setEnabled(validateName(itemName)); - } + return dialog; } protected abstract boolean validateName(final String itemName); diff --git a/library/src/main/res/drawable-hdpi/ab_solid_filepickertheme.9.png b/library/src/main/res/drawable-hdpi/ab_solid_filepickertheme.9.png deleted file mode 100644 index d431708b7d7a352332d087e02c0417992206dea0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^S`aNA7Ln7SY&T!;9puod2`#p!< zf&cQK7!JO_vBl|p>;{2T6>*2s<^;dF@jI<&v#j!a#kE_r6J|Ysm%TeGdsWij@-EL= z4nBuO6FVGzR2MueHqED5y{bMcf1jA%z$^ih>9(ifvGk4uT*wLkM|E(8WN( zK`6yRq#`ITMNtrh;NYK0&;%{|;`fSzL)zrsb8fCYz6XUu-Z}TXzx$nY?+x+B_-shP zBcF{45YPaJ5D)>$ng|_42hl+?0(6j!AA1lTL_K!89V8<_2g&%c2hl-v zkc@yW2j%nm&FOS{QxtWnRO(BmQt3vCas94w4=NUmcOgC>Wdfe(-Sd6_bd(s^?~W7@ zCL1IYiOE{6w(NT1!;U{{K%uUsdcA&pSb?tJa{*`!rFIgfcHi~nhZ)}qAP8IObUNd$ zR_oU=B3-`Y3Q#!bxn8gLT66NrWO5n(?VV;$mw+_qv6c(K&omm1re_}TQP9YQ<&$VX?`t0Yu5*q*KG(nZz{%8+!biMVVYY75LW3jR%s4^ z-ayE1Q!zH;(4hA>o6Qx|+$w+|Vgg%y4 z%}B&($3o9d0JJs_E3^n3efTS91#h=3Vz93B=S3uu0 znanngi)&l~Ey6t~r0xmhV+jcT=N$UaJOT9~060Ifz3;-wKD;!GpEx?`3CioRn9QF9 zaRf92K-URq9Ret^1L!~Dsb1xYn}gosUbv@JDg`QZ;5(Gp*vSOu!=pZ^sMt}~I@na97gXka`0Xj&=k3EPEqJv}v o=pY$C_8>Zl4w4a|gJk@E0|`wNSO+YF;s5{u07*qoM6N<$f)MY1OaK4? diff --git a/library/src/main/res/drawable-hdpi/ic_file_folder.png b/library/src/main/res/drawable-hdpi/ic_file_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..eeeee8ccd7f296329e77315c27c0d05070709b7f GIT binary patch literal 457 zcmV;)0XF`LP)mqQfmzl;ITi9pD7~G|(c^ra%fOfRY~H!X*`yn2G{7k&qA%nIa8^ z* zo+aR_^xoaW>iXt*3dkW4MxEIRE~`Ad=$u>PfXncH`Fs_EoT~IiHtjMzDeG4RY^e;l z!x2sU4Byuu09Mq7O$*Wh;ii%K`BT38Zo0*H){blPuZ9;wXH}7%SY;<@SemNX9uLSc zP!*XOlzFjDI8vo)SW|Bj)}+NsYtnMe@}Z>&&wGYHy?lp%$IyL^~fZ)2yvt#QmU1xZw zd|Vr0V~QXL%O@=jB-}QbpGEtoWlcHeo{Mly;nv@#VQX@!?2&LPq^vqg*qU4_dnB9+ zDXUHrwkDU#9to#H%Bquut;wabN5ZL)vg-T-y#RYXI;Zw200000NkvXXu0mjfV9m~F literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-hdpi/ic_navigation_back.png b/library/src/main/res/drawable-hdpi/ic_navigation_back.png deleted file mode 100644 index 265c4997cf0433626bedbaafe46c598b90902493..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 694 zcmeAS@N?(olHy`uVBq!ia0vp^At21b1|(&&1r9PWFx7gxIEGZjy}j+|bvQtz^z z2UaD*R-%RMdIv&xFugorx`V+{K$e@Yl>L#%!WTa7U)iKM4w`+xXkYn#sXIr1+Yk%DR}=PcP7^X_Jqe(vtIt6p)&{Z=SUl+K#*b?4dK z-5cIs-j?iKE9t_>!^^oV>)BPV&5?P%RZ@FbK6JRAV7B7@x3i54>l&9jEp}=wJGN){ zh4|kGcP8Fjz*G<+abaHCwHfJcmF(-~`?(ok9pJft=GnZHd{(W7`PXfM!Myo|;dv&jr-t&fK%qs)YeImkJ|^2qy;#@tQ)7Yc z*)``M-rMH>C&@~;Aa6bYpCxxwKHqDO(X=!;X__;6YyN`nT&7*yk2+Q#n`2Y1Tfz$VAu6q{0j5PjdQ*zl+)>rr6uDXzbAYr# n;G`mydncfS4>5;+H)z4*}Q$iB}A)YcS diff --git a/library/src/main/res/drawable-mdpi/ab_solid_filepickertheme.9.png b/library/src/main/res/drawable-mdpi/ab_solid_filepickertheme.9.png deleted file mode 100644 index a5826a233cb58d908b5c82e393231c27306b20c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%xt=bLAs*hzDGL05*t3#VT(u)M zE?uhXp?vnh$H&JN*Il}Lb?Hji>H6_fso+vJ5?GoKvO;?BeuDsBG{ z{OZWLF0{DN#85!kE8^+}`xjfbY{|G~v9s*0)RICc^X`VSi&xwi98+WXsF1|m|IQWU O5(ZCKKbLh*2~7b0ML!Mz diff --git a/library/src/main/res/drawable-mdpi/ic_collections_collection_light.png b/library/src/main/res/drawable-mdpi/ic_collections_collection_light.png deleted file mode 100644 index d309856cc695fb54fe197111220ff74347d2b031..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 591 zcmV-V0Y5X~hah`nhfa+QUR7Ghy%k^i7pK`m?ycG`%+B881bx)?7Xjdo%m#9EQ)KTr@W z!7tE4FXuZ@kbv2lom;%XE^Kvg-preso!xU11HPOAk3V4r{C5Ry1#AU`8L%xROrY4@ zRzRFWA%U%cFo9xoQUwZy!cZ!e8t&&Um&?7BN~KS6L`(vzfOYWh_2YINXSZIjZ!rCf z;gJbIB=6~Tdc0Pv-HD@O5JV{;bn7)5jb(;^F*IrcK3Zu$2!aQV$gUv%iE9_N0IvVv z_&EqT;}Wn1Aj2Iv+2=Y^;lX?2dEUJW*ZZ9<00f)Zr*OE!b;iN_zJJhawa!&GIs#@u zx`e%|A{TrQFww>+6OAk2(RJPI4HkkM6$GdtZm) zwwul74#l1;U=8G15N}b)bur0gata6fnqtoqfP*!GH_;)8Cn@Bbm|L`uOSR<)D1&qs zI~=1XR|Nu!R0kXZ;bZO%yYH)%>i91l+9!|pX;XQ)0;s&YxaUkJGg7TqKd1z86L5s~ zS)c~b9-|y$9V|xlBK|4DAwgboszBH_S?-H8HUe4y3b(BQB}*nTwgM)zOX0Q^ dpk&D;<_rConlVq4MN|L)002ovPDHLkV1j;o0IdK3 diff --git a/library/src/main/res/drawable-mdpi/ic_file_folder.png b/library/src/main/res/drawable-mdpi/ic_file_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..22fe6fcee2532df845e31d95184c5c139db4a7d1 GIT binary patch literal 284 zcmV+%0ptFOP)Pwe3;7l zKtOGUXpjcA2DEwhAPs5_X!GJ8Y|nAs8yo@5M>jRthn3xFfj;wMz{bZbfaN4W0mu}< zJAhdRL!AM(7iUkBwSECOaCO1iMa_T+&WhxSCJiTTe9Tqir#J#i`e$m+UmEPS)?V`^ z3=(jxLMfja8q^xReSOKK-=6%xy4J@{4;(T;mU%3sOUjcFUy?lArLRK3bZ74g8l*w3 i0d1Z=NP}7f+WZ4r4@xOz(tWl70000 literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-mdpi/ic_navigation_back.png b/library/src/main/res/drawable-mdpi/ic_navigation_back.png deleted file mode 100644 index c36e021d184126402a61c99259320e657cb7346f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 431 zcmV;g0Z{&lP)W(yLCu*6SEOv7zSp^n9%3|^XB2gVw%c7 zrgFYU0mp7w4XQ!m70@LrT#z?CHRvso@BlR^T#z?CHRvr7BA{*Ci81Dh=t2(7W)R@E zknIxM93b!H%mW9=0@%TAx}1(VA=v>3aF+1~Z3-68ff7hztbh)<8Ni)LStB~L0ZM?g zbzlLM0>=U<2`)LHG`OUI65)~oN`*@TC>f3n_yf+4xrcT|Y*nG5E7(8Zp}~Dz^fuvu z!_JS1w+HnXrUS`f^?7iS`Lq{g4?0Wb{L3xTK_-Oa|!qPuh3C ztglDI!Y2ik2$>vE8YBy#BuF+uDUhsye<16)baL#I2l##gr;4hA^Zg2ou=f&?A zWW87S diff --git a/library/src/main/res/drawable-xhdpi/ab_solid_filepickertheme.9.png b/library/src/main/res/drawable-xhdpi/ab_solid_filepickertheme.9.png deleted file mode 100644 index fb4071713f32fac6016dd89adc01a9f2513ef3b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 258 zcmV+d0sa1oP)GCETb zBPOtNEkeme|8hAIknpv z+2`g%N@?l4D7Dt-E=r7X>5a73*vOpE(Z?yTk@5c0M{BGU@`G@aSW-5dpq0TBP3AbsH~=V zqqL%k*BqsQ2~Cq6I#-0c%wD8$fy2FJTCVdX3C_cRa}GJoRlFkb%XxK1AWN&;g4v6n zDyX{6+&M!cdCp6l@WejF_jTX*Y&165@v-*#?&sy_Yv13S*%|uMwN$|^bb7^9h6(D8 z3>*wM88___Qea47Kk><{fnkEI!f9Ho%Jl_O1?K?w6(SE-58NmKEqJ7`{?`R z^p3CH+A|!A4V(JLf1U=i3P_ames8LtSZiv zdYq@sx?SmQ!ii^#_!Qbd$}+Vao$$El&C@`30g3NhLbT3Slzd3Fnp^kQd%Y0Dk55Ul z>>GYFCR~`lf+44%YB|%7o*MMeQ~y<$J2_2XdfjGk*v#-@&FQ4?2A0jw+D;#IRdNh0vj`U`@&ZyA3d_JSi-;iJJTHQaE9ka9X5s7Wy&Qf4!+Ovvo0bdijkx0?Bl%+-yOS;*~O*h9)C(&zrA>pq>@ z@af&T#Bc2F`KpW?N*2_-Rb+Uvd0H6LDeF6o440V_-X3`NwR!QEU%-$z&8}2U5N_Z2 z^Hjp;tGT�r3oG>=#%Wgqa>3ZrpF$A?0u+SfL*pk>EBePA)d;OXk; Jvd$@?2>`2P0R8{~ diff --git a/library/src/main/res/drawable-xhdpi/ic_file_folder.png b/library/src/main/res/drawable-xhdpi/ic_file_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..ec597610a618155a1024acb690cf106d6645ae22 GIT binary patch literal 567 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdoh8VEpIl;uum9_x9FCzuO5SZ4W=s z?sILrS$tL0R5l^a$#GGT{xWIq;P|_WX~gw{G+aCG8 z?yvu1W`^&v*xM+5f8G7HAy-{p*6goXVJxo*ZCnQ0pH<9oiVTL>H}IdE~w&fX4F zo*TRp43Cr;>Rpb^7CF$Pn0v>)b92zq>8I!Z(n@}ES@OUAgU8#pFYQ{Jb0n4NjYj|S z$G?KpxTkY(=>7dNZ?Beuz>L1vS1v8#d;LwycUE0(=bu(xg^YM{(EtYxhR0^eWC15b z^p3|)kDW6XG%R6S?5VtkqIwuI;>DnV0z&B zx%S;>gFm_HvkfM+d$|j4G{5ck|Iey__SgQ@8ojo8Tj4(MW9-VL&ENU2?XJD}ahuJC zc28&PTNC%z-+#XJ|CHCi58eE(t{{TNF~|2vL}@gKp4XX=a%471b|?ChjD7F04ld%U@k+T6O6yfHdr$Im`ZnYHZx zGrPMVPczy*sLfyhC;7L1c8C2gO$qq`t`D5|ma|X(e^u}_!@7gfZ$mSW2sKE5IHi1x z`P_kM#kg(1AMX74Ht+3=YYog5;k@sQP4_%~y^)pW{lAheEG+LoMkTRx-1{uuD5_Ag z{{17fh6iiwxziXq?%jSr|1xva_MZ>cO%*Ess$bvI@ZjsmOLpu{=KT7q3KgF}USe!A zZ=22~u%~d3NyCHO_UTLld)|Kh#>a6lucDe&=D_Z}=DOQ|m!8tPvxUF^|H@r&pE}kt z_)m3Cna|JrAwT7y@75;`M?X%Byv}OFV8akPeO~#S;Bw~a3uO<8U1PIhIP$BZxnuvW zkMYw_|L&W<_r#_8Q}%9aE=lc}GwpBEyLIV(e+(ZzJ)rQ;ZAbZmgQqoT|2SmLs?c#e zKT*m-V7s#qE5{>?Br6Aj;{w4m*o%o>n&G*#ebrI-go0i9V(ntDupMj`2Un(IHla5%aM>X g?^D*GVOFqfFBfkp&-^wAm_ZmkUHx3vIVCg!0DTU2761SM diff --git a/library/src/main/res/drawable-xxhdpi/ab_solid_filepickertheme.9.png b/library/src/main/res/drawable-xxhdpi/ab_solid_filepickertheme.9.png deleted file mode 100644 index 354813dcaad886914920a2c5f7d7e328fe7906d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^ULefD1|(%J94cgBU{v#TaSW-5dwa)_uPK0m)zN}i z^t(NW=<>iT5l!B#oymLK=06m)I`(kh{^RF19$Ve_d*26}^VRITU&U<69M0&_ zU9q2&?Y8{QNA~|o{}wB-RDu(ux;Jz=iMm_`j)e5*;Oq7 kJN!!IinZUbS-VNkpWCD)RL84P2Mjm{Pgg&ebxsLQ0GdF6p8x;= diff --git a/library/src/main/res/drawable-xxhdpi/ic_collections_collection_light.png b/library/src/main/res/drawable-xxhdpi/ic_collections_collection_light.png deleted file mode 100644 index e6bbbcc207dae569c14eda2668a2ceec6b8c6410..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1928 zcmdUw>p$Cv0>&f#32|AX%xyX`wdzWgI&J3~1eJ7TF0CkKHy6^9P*s;(4Ao&+G5^1}-?zP|s2i1Ogcb zokE9xSmr;`g?#8g0_OWbAf1pPv`=`}`L$bkVswQ0$c6pSPHlwWTPbg~Z z06u!XEDrcl%UBC=d1-4-Pkn?2(NDk84M14^FWw&YqkjJOQcwdflF;E6km^-mzoJ(E zg$xL*G1on5*4Eg#0Fi2n8(#G_Xr$}2!^6WV^AHg?4g4QGr%1TDzwJ(%1NyPuy=iT? zliuD#IYi=8c^`^+)`I>s5*G{}aHJ#K4fO9W?2p_6LmxjhmHYuSFjy_p&(C}fmE@tm zgl-7{!Ex4MFUu&Q4bB)`f%-w5mxHyk@46AfsRZOwk?3AWZ|_don=B%}6y*ptx%iYU zYgz42!@b#g10-P(-~2i=Jv}`s7H?9h=q?XadX%xf{O(ZLgY9vSzYCBQjQFNj(z4o^ zu-+{DWp;M953OLIFG$GY`qNo9RcTJmelS_n_rh6EaJ5m3SPT3fcDC@sg4hBdIPe)9 zTpjL*Xhcmof3oIGOSH^4(2>bxYNb-CaoFmGuUHbtS67pN866$XSOSp-265V!((F66 zBwA|c`X0lCIDEM9q3H|T-I;bgXoW8n2n0x3?EqYR(z&ajUr<=+T>|mj_`bo~eLF^- zp6bHXP^5jn$98;{gqmA!FSAP#R)d+X0|PY1UeAOb`{paTL$)d{hLQ?~KH`Hf zjd&wm_YJhlNYRWZQC@5J&$owJrwF+2C+V&;KS&L>(h~o~a}`4(B}5kIQmJN6d1EhI z&!%vdAAQAH+FdL^C`tfs^ZA=(T4!CFp&jY%0}nlsdCEu z>{5BRREK?YksA99xgL>z3VdMlZM`@Hk$ffbJs)4swWY)lg8q$AL!eu2JU51sy#Rxo zc^zR?#N~1q2n0fA5a)PooD1uO+nJtFFm3hDi#UMg@JaW9+%{KDbelr)THDSqd&F)$ zyF)f(SU#+KP`3&yJlCwf6~CJuTL78Fw%2r@NSwMV!fu34;(*$%<*hK~PT)hMC&Ba1v?cOZ3>gky-YBUMP~$!FZ0KHW)EycF>N#XS*RnmX zKd3bD;j}L=FB=PmLc(Pgsl7)@0rE+?r56J-Cc7eziOvI07c<$J{*7WYlJ@U*3Y_ma z5BBRB$2y)~!x)^vSuz*ytI4lRPD)ylDz|N}LHi4sDmBc9^HcoCF)YNCLb9wgpWF}CyAF)q^}Q! zFx;OrP}0)2nZEaOT8dPq87GS?#|u!47TGCA8w)D<=z~ue^+m7i&s~1k%A;a;9O0v$ zo0wx`%De}JnA9D=W;#-C%hZ3`V-=gIJ~FLYfR7&UYY!y7-@N|kpz15nuPdPNHcTq_ z$F9BpNM(IoN+%j|_Ve(n3%y)PvUqzw_l#NHRTFUY_%!QfY?+3i!9L>vZuUQ3eU&!_ z-YrQGU|P)pOd)Ciz+TfKc2{X>>8Gb|^SgFak+}Pj(V_MxmCf4)rq~atd!^-hWO+uU zC`+gN>iE(sj8L4XSra*0T7WO~|AhYk%}jfcxAW$_AzGr<2g8Db{DaY4-?+TL0omec ADgXcg diff --git a/library/src/main/res/drawable-xxhdpi/ic_file_folder.png b/library/src/main/res/drawable-xxhdpi/ic_file_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..05f6a9ce55ad6999cb504c8a5524e83dc0d43357 GIT binary patch literal 955 zcmeAS@N?(olHy`uVBq!ia0vp^6(G#P1|%(0%q}r7FgJO+IEGZjy`5v57aSoDxvz+hFiz)`26i?4An6BmnNkW!E%>kP4-3M`tRoW;Bj z#wVV9|NFz9$G3K>7x%gQb(MR6@XB4VWM1FHrUNJKSygE$_Y|LgHILWIb-_J{9>Ah^{zTB6!57oV|6L@!p(mD4dmvTCuzFTm6pYiO?&%TSi zW?OIj_d=eo%xm`}S+-oAIovhxPTqTT>C>sx9TBV5-(6gJwl9Z!&%OU8+N(ZoxVOXQ z8|ypXc$Xugz7IH;&Yb;*=Z~sVSV`k-$(xcts$F!ln7uRDRUhSwKK;0&ruIwS=Qk;S zx4RE~U3K45rhm8fb$|ZXNB{gTVY;4QmmczM{SLF42fnh;`_s7H^u;!LkAVG-96B4~ zl(uc(J=dG*iaZBX_8NJfU2}z{uB;bf>R!XYOSU_kyW~;HUh^}*@-M~P8?lJxPU3b> zm|iJ3{eaR%J)XwcXRa|!-7dQgSjuLK7jo(dy*(swBQ@@b>IRQ3*cJNn#OzG5-oa89 zA#TK}Gv%#=;ElxC6Dk`5wlTXVw3jMQKHzkdPoz=yTB{EecdB&&euW{z%=NSHt}W|J z2{xSl{l^Qp{2#lzZp`qyJZIm|iTn-mHUBDCPkD83j@j&2*VnBo`h9lmx4$dvGTidF z*~bP0l+XkKesr+9 literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xxhdpi/ic_navigation_back.png b/library/src/main/res/drawable-xxhdpi/ic_navigation_back.png deleted file mode 100644 index 62b086e4d438a6d715a62fd3377745787aee8386..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1649 zcmeHI{a2D_7={HRc1p}Pwd2>qW>{uRJZf6@`hlS-#(}1&)v_af=rl`b>88HsmgRDq z=@5l?n(Yn69Yabmr1deIl9Gudi)tMcg8?}q5@zh}$NdXC`{91>>$%Q#-S@fgbDs0m zrllsXa9QJmLZMbrcgE9^48IR&C*(Yl+|`dlIYm(80Y>RwgQo9%W`gH8fAq`^xNdSf zjajwU#Xo^emVE5edIsErWp1iEAH-ldAA&nj`9BaN8~dgPuW7lDo@t{uNbczr*72c* z+4A|IYX!cGvC!r!?yWLW7{=GF3Dboq^qc3^-auqpD+);VtH1BowCuk@M){Q5-x$h* zUu2X;%O#SN553rnXf#-|{=rPSJlN5@Z78q4#V~(~YLgNt=wPn5`nSdWG`xP@Xce{N~fEry+JxG;GP~RxNR|>a$aop*wu9SOx=_ zR`zR|fe!MVU-MWo%^gPCjDF=bT6c#f&%ae*;QEOx>W6fm@>g9($m;Fjh==%Z*!J7Q zDP#Qb{B*4=O&NvSo32lOu~y4QxUJ*tg;D&e_^L6MW3oMT;mdQ-^+JZ;OR|cD*#^N= z?yQ$voTbBbq4rMFI=2&vbm31;BeR75d4@jYO5fa(;h z>(i6m)eK>M!{K<{EQYBklGn6INPx-~(Lh;OKuHsCPx`S>!X+S*i-!){bXY`U@#4#l zAd=E53N2j-9r>z%>+!!nBf%^_alDSy?amO6D|4ULG4X;aZEfh$i6cl|Q4=SM{UIo@ zNG23*B^Cs5qzZnq3IU6^6ueR~0v-vX6*sz9_jgWtj8uq+{amXDCnsss+~kcWAEr&O z!x&)81a2P1wL^HDewkw1(HSOwdbgepJi2!mlj3`VEeyPVwRt0WITe|G=ev+^AyN+} zr6FLB(3lLLt?w+xr4TIvq@*TjciZ6RV!zHLrRPXBG$!5mlsEWy2%OmWcy#H;lCRl{ zKQ5(YsZ4;iV&c4X-;DVqP4=`Rvs?@sc@e>Ua`~S`ys2X@Zsly`RM;y4)=*wz$~hpN zN49Q-Pd54 zdtwhgtvKcZ^h@WS4Y1TNZ|u@r=5m2CFoRsEKsIh&X5bKHc*KOc;Pjok4l#WXROf`R zd#`<1`Sw0uV8o@9Hy2$%8dOngI>V7Fu3Yl^FCHQUum_tH9)b|lWdE~J6zmgnXCY<* zcV=-@BUDx>BNRY~Jt*#GRMxQGC?l9+@g^2UnK=jB(U-G5sY>GW$)y@fO^cg(C8XP) z_ZWK&Bg3^ys6@%?E8beX+8FYv7cMpDy+08C1BJXR$D>S$PYEpxNVLsLVoSp@rc4p z#(me!$YTiSkmyfVc8Zfpp|6K~WP@<9lQGgZ}_;nfp2b diff --git a/library/src/main/res/drawable/selectable_action.xml b/library/src/main/res/drawable/selectable_action.xml deleted file mode 100644 index cd12f00..0000000 --- a/library/src/main/res/drawable/selectable_action.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/library/src/main/res/layout/dialog_folder_name.xml b/library/src/main/res/layout/dialog_folder_name.xml new file mode 100644 index 0000000..e06a59c --- /dev/null +++ b/library/src/main/res/layout/dialog_folder_name.xml @@ -0,0 +1,38 @@ + + + + + + \ No newline at end of file diff --git a/library/src/main/res/layout/dialog_new_item.xml b/library/src/main/res/layout/dialog_new_item.xml deleted file mode 100644 index cc26f4a..0000000 --- a/library/src/main/res/layout/dialog_new_item.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - -