Merge pull request #6282 from mahdihijazi/replace_file_browser

[Android] Replace current file browser
This commit is contained in:
Markus Wick 2018-01-10 09:50:16 +01:00 committed by GitHub
commit a1467f0e5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 251 additions and 395 deletions

View File

@ -96,6 +96,8 @@ dependencies {
// Allows FRP-style asynchronous operations in Android.
api 'io.reactivex:rxandroid:1.2.1'
compile 'com.nononsenseapps:filepicker:4.1.0'
}
def getVersion() {

View File

@ -50,11 +50,6 @@
</intent-filter>
</activity>
<activity
android:name=".activities.AddDirectoryActivity"
android:theme="@style/DolphinGamecube"
android:label="@string/add_directory_title"/>
<activity
android:name=".ui.settings.SettingsActivity"
android:theme="@style/DolphinSettingsGamecube"
@ -64,6 +59,15 @@
android:name=".activities.EmulationActivity"
android:theme="@style/DolphinEmulationGamecube"/>
<activity
android:name=".activities.CustomFilePickerActivity"
android:label="@string/app_name"
android:theme="@style/FilePickerTheme">
<intent-filter>
<action android:name="android.intent.action.GET_CONTENT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service android:name=".services.DirectoryInitializationService"/>
@ -74,6 +78,16 @@
android:exported="false">
</provider>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.filesprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/nnf_provider_paths" />
</provider>
</application>
</manifest>

View File

@ -1,141 +0,0 @@
package org.dolphinemu.dolphinemu.activities;
import android.content.AsyncQueryHandler;
import android.content.ContentValues;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.FragmentActivity;
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.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.adapters.FileAdapter;
import org.dolphinemu.dolphinemu.model.GameDatabase;
import org.dolphinemu.dolphinemu.model.GameProvider;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
/**
* An Activity that shows a list of files and folders, allowing the user to tell the app which folder(s)
* contains the user's games.
*/
public class AddDirectoryActivity extends AppCompatActivity implements FileAdapter.FileClickListener
{
private static final String KEY_CURRENT_PATH = "path";
private FileAdapter mAdapter;
private Toolbar mToolbar;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_directory);
mToolbar = (Toolbar) findViewById(R.id.toolbar_folder_list);
setSupportActionBar(mToolbar);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list_files);
// Specifying the LayoutManager determines how the RecyclerView arranges views.
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(layoutManager);
String path;
// Stuff in this block only happens when this activity is newly created (i.e. not a rotation)
if (savedInstanceState == null)
{
path = Environment.getExternalStorageDirectory().getPath();
}
else
{
// Get the path we were looking at before we rotated.
path = savedInstanceState.getString(KEY_CURRENT_PATH);
}
mAdapter = new FileAdapter(path, this);
recyclerView.setAdapter(mAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_add_directory, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case R.id.menu_up_one_level:
mAdapter.upOneLevel();
break;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
// Save the path we're looking at so when rotation is done, we start from same folder.
outState.putString(KEY_CURRENT_PATH, mAdapter.getPath());
}
/**
* Add a directory to the library, and if successful, end the activity.
*/
@Override
public void addDirectory()
{
// Set up a callback for when the addition is complete
// TODO This has a nasty warning on it; find a cleaner way to do this Insert asynchronously
AsyncQueryHandler handler = new AsyncQueryHandler(getContentResolver())
{
@Override
protected void onInsertComplete(int token, Object cookie, Uri uri)
{
Intent resultData = new Intent();
resultData.putExtra(KEY_CURRENT_PATH, mAdapter.getPath());
setResult(RESULT_OK, resultData);
finish();
}
};
ContentValues file = new ContentValues();
file.put(GameDatabase.KEY_FOLDER_PATH, mAdapter.getPath());
handler.startInsert(0, // We don't need to identify this call to the handler
null, // We don't need to pass additional data to the handler
GameProvider.URI_FOLDER, // Tell the GameProvider we are adding a folder
file); // Tell the GameProvider what folder we are adding
}
@Override
public void updateSubtitle(String path)
{
mToolbar.setSubtitle(path);
}
public static void launch(FragmentActivity activity)
{
Intent fileChooser = new Intent(activity, AddDirectoryActivity.class);
activity.startActivityForResult(fileChooser, MainPresenter.REQUEST_ADD_DIRECTORY);
}
}

View File

@ -0,0 +1,28 @@
package org.dolphinemu.dolphinemu.activities;
import android.os.Environment;
import android.support.annotation.Nullable;
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
import com.nononsenseapps.filepicker.FilePickerActivity;
import org.dolphinemu.dolphinemu.fragments.CustomFilePickerFragment;
import java.io.File;
public class CustomFilePickerActivity extends FilePickerActivity
{
@Override
protected AbstractFilePickerFragment<File> getFragment(
@Nullable final String startPath, final int mode, final boolean allowMultiple,
final boolean allowCreateDir, final boolean allowExistingFile,
final boolean singleClick)
{
AbstractFilePickerFragment<File> fragment = new CustomFilePickerFragment();
// startPath is allowed to be null. In that case, default folder should be SD-card and not "/"
fragment.setArgs(startPath != null ? startPath : Environment.getExternalStorageDirectory().getPath(),
mode, allowMultiple, allowCreateDir, allowExistingFile, singleClick);
return fragment;
}
}

View File

@ -1,217 +0,0 @@
package org.dolphinemu.dolphinemu.adapters;
import android.os.Environment;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.FileListItem;
import org.dolphinemu.dolphinemu.viewholders.FileViewHolder;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
public final class FileAdapter extends RecyclerView.Adapter<FileViewHolder> implements View.OnClickListener
{
private ArrayList<FileListItem> mFileList;
private String mPath;
private FileClickListener mListener;
/**
* Initializes the dataset to be displayed, and associates the Adapter with the
* Activity as an event listener.
*
* @param path A String containing the path to the directory to be shown by this Adapter.
* @param listener An Activity that can respond to callbacks from this Adapter.
*/
public FileAdapter(String path, FileClickListener listener)
{
mFileList = generateFileList(new File(path));
mListener = listener;
mListener.updateSubtitle(path);
}
/**
* Called by the LayoutManager when it is necessary to create a new view.
*
* @param parent The RecyclerView (I think?) the created view will be thrown into.
* @param viewType Not used here, but useful when more than one type of child will be used in the RecyclerView.
* @return The created ViewHolder with references to all the child view's members.
*/
@Override
public FileViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
// Create a new view.
View listItem = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_file, parent, false);
listItem.setOnClickListener(this);
// Use that view to create a ViewHolder.
return new FileViewHolder(listItem);
}
/**
* Called by the LayoutManager when a new view is not necessary because we can recycle
* an existing one (for example, if a view just scrolled onto the screen from the bottom, we
* can use the view that just scrolled off the top instead of inflating a new one.)
*
* @param holder A ViewHolder representing the view we're recycling.
* @param position The position of the 'new' view in the dataset.
*/
@Override
public void onBindViewHolder(FileViewHolder holder, int position)
{
// Get a reference to the item from the dataset; we'll use this to fill in the view contents.
final FileListItem file = mFileList.get(position);
// Fill in the view contents.
switch (file.getType())
{
case FileListItem.TYPE_FOLDER:
holder.imageType.setImageResource(R.drawable.ic_folder);
break;
case FileListItem.TYPE_GC:
holder.imageType.setImageResource(R.drawable.ic_gamecube);
break;
case FileListItem.TYPE_WII:
holder.imageType.setImageResource(R.drawable.ic_wii);
break;
case FileListItem.TYPE_OTHER:
holder.imageType.setImageResource(android.R.color.transparent);
break;
}
holder.textFileName.setText(file.getFilename());
holder.itemView.setTag(file.getPath());
}
/**
* Called by the LayoutManager to find out how much data we have.
*
* @return Size of the dataset.
*/
@Override
public int getItemCount()
{
return mFileList.size();
}
/**
* When a file is clicked, determine if it is a directory; if it is, show that new directory's
* contents. If it is not, end the activity successfully.
*
* @param view The View representing the file the user clicked on.
*/
@Override
public void onClick(final View view)
{
final String path = (String) view.getTag();
File clickedFile = new File(path);
if (clickedFile.isDirectory())
{
final ArrayList<FileListItem> fileList = generateFileList(clickedFile);
if (fileList.isEmpty())
{
Toast.makeText(view.getContext(), R.string.add_directory_empty_folder, Toast.LENGTH_SHORT).show();
}
else
{
// Delay the loading of the new directory to give a little bit of time for UI feedback
// to happen. Hacky, but good enough for now; this is necessary because we're modifying
// the RecyclerView's contents, rather than constructing a new one.
view.getHandler().postDelayed(() ->
{
mFileList = fileList;
notifyDataSetChanged();
mListener.updateSubtitle(path);
}, 200);
}
}
else
{
// Pass the activity the path of the parent directory of the clicked file.
mListener.addDirectory();
}
}
/**
* For a given directory, return a list of Files it contains.
*
* @param directory A File representing the directory that should have its contents displayed.
* @return The list of files contained in the directory.
*/
private ArrayList<FileListItem> generateFileList(File directory)
{
File[] children = directory.listFiles();
mPath = directory.getAbsolutePath();
ArrayList<FileListItem> fileList = new ArrayList<FileListItem>(0);
if (children != null)
{
fileList = new ArrayList<FileListItem>(children.length);
for (File child : children)
{
if (!child.isHidden())
{
FileListItem item = new FileListItem(child);
fileList.add(item);
}
}
Collections.sort(fileList);
}
return fileList;
}
public String getPath()
{
return mPath;
}
public void setPath(String path)
{
File directory = new File(path);
mFileList = generateFileList(directory);
notifyDataSetChanged();
mListener.updateSubtitle(path);
}
public void upOneLevel()
{
if (!mPath.equals("/"))
{
File currentDirectory = new File(mPath);
File parentDirectory = currentDirectory.getParentFile();
mFileList = generateFileList(parentDirectory);
notifyDataSetChanged();
mListener.updateSubtitle(mPath);
}
}
/**
* Callback to the containing Activity.
*/
public interface FileClickListener
{
void addDirectory();
void updateSubtitle(String path);
}
}

View File

@ -18,9 +18,9 @@ import org.dolphinemu.dolphinemu.utils.PicassoUtils;
import org.dolphinemu.dolphinemu.viewholders.GameViewHolder;
/**
* This adapter, unlike {@link FileAdapter} which is backed by an ArrayList, gets its
* information from a database Cursor. This fact, paired with the usage of ContentProviders
* and Loaders, allows for efficient display of a limited view into a (possibly) large dataset.
* This adapter gets its information from a database Cursor. This fact, paired with the usage of
* ContentProviders and Loaders, allows for efficient display of a limited view into a (possibly)
* large dataset.
*/
public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> implements
View.OnClickListener,

View File

@ -0,0 +1,22 @@
package org.dolphinemu.dolphinemu.fragments;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.v4.content.FileProvider;
import com.nononsenseapps.filepicker.FilePickerFragment;
import java.io.File;
public class CustomFilePickerFragment extends FilePickerFragment
{
@NonNull
@Override
public Uri toUri(@NonNull final File file)
{
return FileProvider
.getUriForFile(getContext(),
getContext().getApplicationContext().getPackageName() + ".filesprovider",
file);
}
}

View File

@ -17,13 +17,14 @@ import android.view.View;
import android.widget.Toast;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.AddDirectoryActivity;
import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter;
import org.dolphinemu.dolphinemu.model.GameProvider;
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
import org.dolphinemu.dolphinemu.ui.platform.Platform;
import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView;
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
import org.dolphinemu.dolphinemu.utils.AddDirectoryHelper;
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
import org.dolphinemu.dolphinemu.utils.StartupHandler;
@ -71,6 +72,13 @@ public final class MainActivity extends AppCompatActivity implements MainView
}
}
@Override
protected void onResume()
{
super.onResume();
mPresenter.addDirIfNeeded(new AddDirectoryHelper(this));
}
// TODO: Replace with a ButterKnife injection.
private void findViews()
{
@ -127,7 +135,7 @@ public final class MainActivity extends AppCompatActivity implements MainView
@Override
public void launchFileListActivity()
{
AddDirectoryActivity.launch(this);
FileBrowserHelper.openDirectoryPicker(this);
}
@Override
@ -137,8 +145,6 @@ public final class MainActivity extends AppCompatActivity implements MainView
}
/**
* Callback from AddDirectoryActivity. Applies any changes necessary to the GameGridActivity.
*
* @param requestCode An int describing whether the Activity that is returning did so successfully.
* @param resultCode An int describing what Activity is giving us this callback.
* @param result The information the returning Activity is providing us.
@ -146,7 +152,20 @@ public final class MainActivity extends AppCompatActivity implements MainView
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent result)
{
mPresenter.handleActivityResult(requestCode, resultCode);
switch (requestCode)
{
case MainPresenter.REQUEST_ADD_DIRECTORY:
// If the user picked a file, as opposed to just backing out.
if (resultCode == MainActivity.RESULT_OK)
{
mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result));
}
break;
case MainPresenter.REQUEST_EMULATE_GAME:
mPresenter.refreshFragmentScreenshot(resultCode);
break;
}
}
@Override
@ -198,7 +217,7 @@ public final class MainActivity extends AppCompatActivity implements MainView
@Nullable
private PlatformGamesView getPlatformGamesView(Platform platform)
{
String fragmentTag = "android:switcher:" + mViewPager.getId() + ":" + platform;
String fragmentTag = "android:switcher:" + mViewPager.getId() + ":" + platform.toInt();
return (PlatformGamesView) getSupportFragmentManager().findFragmentByTag(fragmentTag);
}

View File

@ -1,17 +1,15 @@
package org.dolphinemu.dolphinemu.ui.main;
import android.database.Cursor;
import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.GameDatabase;
import org.dolphinemu.dolphinemu.ui.platform.Platform;
import org.dolphinemu.dolphinemu.utils.AddDirectoryHelper;
import org.dolphinemu.dolphinemu.utils.SettingsFile;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
public final class MainPresenter
@ -20,6 +18,7 @@ public final class MainPresenter
public static final int REQUEST_EMULATE_GAME = 2;
private final MainView mView;
private String mDirToAdd;
public MainPresenter(MainView view)
{
@ -71,24 +70,27 @@ public final class MainPresenter
return false;
}
public void handleActivityResult(int requestCode, int resultCode)
public void addDirIfNeeded(AddDirectoryHelper helper)
{
switch (requestCode)
if (mDirToAdd != null)
{
case REQUEST_ADD_DIRECTORY:
// If the user picked a file, as opposed to just backing out.
if (resultCode == MainActivity.RESULT_OK)
{
mView.refresh();
}
break;
helper.addDirectory(mDirToAdd, mView::refresh);
case REQUEST_EMULATE_GAME:
mView.refreshFragmentScreenshot(resultCode);
break;
mDirToAdd = null;
}
}
public void onDirectorySelected(String dir)
{
mDirToAdd = dir;
}
public void refreshFragmentScreenshot(int resultCode)
{
mView.refreshFragmentScreenshot(resultCode);
}
public void loadGames(final Platform platform)
{
GameDatabase databaseHelper = DolphinApplication.databaseHelper;

View File

@ -12,17 +12,12 @@ import android.support.v17.leanback.widget.CursorObjectAdapter;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.OnItemViewClickedListener;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
import android.widget.Toast;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.AddDirectoryActivity;
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.adapters.GameRowPresenter;
import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter;
@ -31,6 +26,8 @@ import org.dolphinemu.dolphinemu.model.TvSettingsItem;
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
import org.dolphinemu.dolphinemu.ui.platform.Platform;
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
import org.dolphinemu.dolphinemu.utils.AddDirectoryHelper;
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
import org.dolphinemu.dolphinemu.utils.StartupHandler;
import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder;
@ -58,6 +55,13 @@ public final class TvMainActivity extends FragmentActivity implements MainView
StartupHandler.HandleInit(this);
}
@Override
protected void onResume()
{
super.onResume();
mPresenter.addDirIfNeeded(new AddDirectoryHelper(this));
}
void setupUI() {
final FragmentManager fragmentManager = getSupportFragmentManager();
mBrowseFragment = new BrowseSupportFragment();
@ -125,7 +129,7 @@ public final class TvMainActivity extends FragmentActivity implements MainView
@Override
public void launchFileListActivity()
{
AddDirectoryActivity.launch(this);
FileBrowserHelper.openDirectoryPicker(this);
}
@Override
@ -150,7 +154,20 @@ public final class TvMainActivity extends FragmentActivity implements MainView
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent result)
{
mPresenter.handleActivityResult(requestCode, resultCode);
switch (requestCode)
{
case MainPresenter.REQUEST_ADD_DIRECTORY:
// If the user picked a file, as opposed to just backing out.
if (resultCode == MainActivity.RESULT_OK)
{
mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result));
}
break;
case MainPresenter.REQUEST_EMULATE_GAME:
mPresenter.refreshFragmentScreenshot(resultCode);
break;
}
}
@Override

View File

@ -0,0 +1,44 @@
package org.dolphinemu.dolphinemu.utils;
import android.content.AsyncQueryHandler;
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import org.dolphinemu.dolphinemu.model.GameDatabase;
import org.dolphinemu.dolphinemu.model.GameProvider;
public class AddDirectoryHelper
{
private Context mContext;
public interface AddDirectoryListener
{
void onDirectoryAdded();
}
public AddDirectoryHelper(Context context)
{
this.mContext = context;
}
public void addDirectory(String dir, AddDirectoryListener addDirectoryListener)
{
AsyncQueryHandler handler = new AsyncQueryHandler(mContext.getContentResolver())
{
@Override
protected void onInsertComplete(int token, Object cookie, Uri uri)
{
addDirectoryListener.onDirectoryAdded();
}
};
ContentValues file = new ContentValues();
file.put(GameDatabase.KEY_FOLDER_PATH, dir);
handler.startInsert(0, // We don't need to identify this call to the handler
null, // We don't need to pass additional data to the handler
GameProvider.URI_FOLDER, // Tell the GameProvider we are adding a folder
file);
}
}

View File

@ -0,0 +1,43 @@
package org.dolphinemu.dolphinemu.utils;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import com.nononsenseapps.filepicker.FilePickerActivity;
import com.nononsenseapps.filepicker.Utils;
import org.dolphinemu.dolphinemu.activities.CustomFilePickerActivity;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import java.io.File;
import java.util.List;
public final class FileBrowserHelper
{
public static void openDirectoryPicker(FragmentActivity activity) {
Intent i = new Intent(activity, CustomFilePickerActivity.class);
i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false);
i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, false);
i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR);
i.putExtra(FilePickerActivity.EXTRA_START_PATH, Environment.getExternalStorageDirectory().getPath());
activity.startActivityForResult(i, MainPresenter.REQUEST_ADD_DIRECTORY);
}
@Nullable
public static String getSelectedDirectory(Intent result) {
// Use the provided utility method to parse the result
List<Uri> files = Utils.getSelectedFilesFromResult(result);
if(!files.isEmpty()) {
File file = Utils.getFileForUri(files.get(0));
return file.getAbsolutePath();
}
return null;
}
}

View File

@ -145,4 +145,27 @@
<item name="android:textColor">@color/button_text_color</item>
</style>
</resources>
<!-- You can also inherit from NNF_BaseTheme.Light -->
<style name="FilePickerTheme" parent="NNF_BaseTheme.Light">
<item name="colorPrimary">@color/dolphin_blue</item>
<item name="colorPrimaryDark">@color/dolphin_blue_dark</item>
<item name="colorAccent">@color/dolphin_accent_gamecube</item>
<!--&lt;!&ndash; Setting a divider is entirely optional &ndash;&gt;-->
<item name="nnf_list_item_divider">?android:attr/listDivider</item>
<!-- Need to set this also to style create folder dialog -->
<item name="alertDialogTheme">@style/FilePickerAlertDialogTheme</item>
<!-- If you want to set a specific toolbar theme, do it here -->
<item name="nnf_toolbarTheme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item>
</style>
<style name="FilePickerAlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert">
<item name="colorPrimary">@color/dolphin_blue</item>
<item name="colorPrimaryDark">@color/dolphin_blue_dark</item>
<item name="colorAccent">@color/dolphin_accent_gamecube</item>
</style>
</resources>