mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-02-10 14:39:01 +01:00
Merge pull request #6282 from mahdihijazi/replace_file_browser
[Android] Replace current file browser
This commit is contained in:
commit
a1467f0e5a
@ -96,6 +96,8 @@ dependencies {
|
|||||||
|
|
||||||
// Allows FRP-style asynchronous operations in Android.
|
// Allows FRP-style asynchronous operations in Android.
|
||||||
api 'io.reactivex:rxandroid:1.2.1'
|
api 'io.reactivex:rxandroid:1.2.1'
|
||||||
|
|
||||||
|
compile 'com.nononsenseapps:filepicker:4.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
def getVersion() {
|
def getVersion() {
|
||||||
|
@ -50,11 +50,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".activities.AddDirectoryActivity"
|
|
||||||
android:theme="@style/DolphinGamecube"
|
|
||||||
android:label="@string/add_directory_title"/>
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.settings.SettingsActivity"
|
android:name=".ui.settings.SettingsActivity"
|
||||||
android:theme="@style/DolphinSettingsGamecube"
|
android:theme="@style/DolphinSettingsGamecube"
|
||||||
@ -64,6 +59,15 @@
|
|||||||
android:name=".activities.EmulationActivity"
|
android:name=".activities.EmulationActivity"
|
||||||
android:theme="@style/DolphinEmulationGamecube"/>
|
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"/>
|
<service android:name=".services.DirectoryInitializationService"/>
|
||||||
|
|
||||||
@ -74,6 +78,16 @@
|
|||||||
android:exported="false">
|
android:exported="false">
|
||||||
</provider>
|
</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>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,9 +18,9 @@ import org.dolphinemu.dolphinemu.utils.PicassoUtils;
|
|||||||
import org.dolphinemu.dolphinemu.viewholders.GameViewHolder;
|
import org.dolphinemu.dolphinemu.viewholders.GameViewHolder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This adapter, unlike {@link FileAdapter} which is backed by an ArrayList, gets its
|
* This adapter gets its information from a database Cursor. This fact, paired with the usage of
|
||||||
* information from a database Cursor. This fact, paired with the usage of ContentProviders
|
* ContentProviders and Loaders, allows for efficient display of a limited view into a (possibly)
|
||||||
* and Loaders, allows for efficient display of a limited view into a (possibly) large dataset.
|
* large dataset.
|
||||||
*/
|
*/
|
||||||
public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> implements
|
public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> implements
|
||||||
View.OnClickListener,
|
View.OnClickListener,
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -17,13 +17,14 @@ import android.view.View;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.activities.AddDirectoryActivity;
|
|
||||||
import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter;
|
import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter;
|
||||||
import org.dolphinemu.dolphinemu.model.GameProvider;
|
import org.dolphinemu.dolphinemu.model.GameProvider;
|
||||||
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
|
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView;
|
import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView;
|
||||||
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
|
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.PermissionsHandler;
|
||||||
import org.dolphinemu.dolphinemu.utils.StartupHandler;
|
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.
|
// TODO: Replace with a ButterKnife injection.
|
||||||
private void findViews()
|
private void findViews()
|
||||||
{
|
{
|
||||||
@ -127,7 +135,7 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
|||||||
@Override
|
@Override
|
||||||
public void launchFileListActivity()
|
public void launchFileListActivity()
|
||||||
{
|
{
|
||||||
AddDirectoryActivity.launch(this);
|
FileBrowserHelper.openDirectoryPicker(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 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 resultCode An int describing what Activity is giving us this callback.
|
||||||
* @param result The information the returning Activity is providing us.
|
* @param result The information the returning Activity is providing us.
|
||||||
@ -146,7 +152,20 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
|||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent result)
|
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
|
@Override
|
||||||
@ -198,7 +217,7 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
|||||||
@Nullable
|
@Nullable
|
||||||
private PlatformGamesView getPlatformGamesView(Platform platform)
|
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);
|
return (PlatformGamesView) getSupportFragmentManager().findFragmentByTag(fragmentTag);
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
package org.dolphinemu.dolphinemu.ui.main;
|
package org.dolphinemu.dolphinemu.ui.main;
|
||||||
|
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.BuildConfig;
|
import org.dolphinemu.dolphinemu.BuildConfig;
|
||||||
import org.dolphinemu.dolphinemu.DolphinApplication;
|
import org.dolphinemu.dolphinemu.DolphinApplication;
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.model.GameDatabase;
|
import org.dolphinemu.dolphinemu.model.GameDatabase;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||||
|
import org.dolphinemu.dolphinemu.utils.AddDirectoryHelper;
|
||||||
import org.dolphinemu.dolphinemu.utils.SettingsFile;
|
import org.dolphinemu.dolphinemu.utils.SettingsFile;
|
||||||
|
|
||||||
import rx.android.schedulers.AndroidSchedulers;
|
import rx.android.schedulers.AndroidSchedulers;
|
||||||
import rx.functions.Action1;
|
|
||||||
import rx.schedulers.Schedulers;
|
import rx.schedulers.Schedulers;
|
||||||
|
|
||||||
public final class MainPresenter
|
public final class MainPresenter
|
||||||
@ -20,6 +18,7 @@ public final class MainPresenter
|
|||||||
public static final int REQUEST_EMULATE_GAME = 2;
|
public static final int REQUEST_EMULATE_GAME = 2;
|
||||||
|
|
||||||
private final MainView mView;
|
private final MainView mView;
|
||||||
|
private String mDirToAdd;
|
||||||
|
|
||||||
public MainPresenter(MainView view)
|
public MainPresenter(MainView view)
|
||||||
{
|
{
|
||||||
@ -71,24 +70,27 @@ public final class MainPresenter
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleActivityResult(int requestCode, int resultCode)
|
public void addDirIfNeeded(AddDirectoryHelper helper)
|
||||||
{
|
{
|
||||||
switch (requestCode)
|
if (mDirToAdd != null)
|
||||||
{
|
{
|
||||||
case REQUEST_ADD_DIRECTORY:
|
helper.addDirectory(mDirToAdd, mView::refresh);
|
||||||
// If the user picked a file, as opposed to just backing out.
|
|
||||||
if (resultCode == MainActivity.RESULT_OK)
|
|
||||||
{
|
|
||||||
mView.refresh();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQUEST_EMULATE_GAME:
|
mDirToAdd = null;
|
||||||
mView.refreshFragmentScreenshot(resultCode);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onDirectorySelected(String dir)
|
||||||
|
{
|
||||||
|
mDirToAdd = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshFragmentScreenshot(int resultCode)
|
||||||
|
{
|
||||||
|
mView.refreshFragmentScreenshot(resultCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void loadGames(final Platform platform)
|
public void loadGames(final Platform platform)
|
||||||
{
|
{
|
||||||
GameDatabase databaseHelper = DolphinApplication.databaseHelper;
|
GameDatabase databaseHelper = DolphinApplication.databaseHelper;
|
||||||
|
@ -12,17 +12,12 @@ import android.support.v17.leanback.widget.CursorObjectAdapter;
|
|||||||
import android.support.v17.leanback.widget.HeaderItem;
|
import android.support.v17.leanback.widget.HeaderItem;
|
||||||
import android.support.v17.leanback.widget.ListRow;
|
import android.support.v17.leanback.widget.ListRow;
|
||||||
import android.support.v17.leanback.widget.ListRowPresenter;
|
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.FragmentActivity;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.activities.AddDirectoryActivity;
|
|
||||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
||||||
import org.dolphinemu.dolphinemu.adapters.GameRowPresenter;
|
import org.dolphinemu.dolphinemu.adapters.GameRowPresenter;
|
||||||
import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter;
|
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.services.DirectoryInitializationService;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||||
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
|
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.PermissionsHandler;
|
||||||
import org.dolphinemu.dolphinemu.utils.StartupHandler;
|
import org.dolphinemu.dolphinemu.utils.StartupHandler;
|
||||||
import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder;
|
import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder;
|
||||||
@ -58,6 +55,13 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
|||||||
StartupHandler.HandleInit(this);
|
StartupHandler.HandleInit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume()
|
||||||
|
{
|
||||||
|
super.onResume();
|
||||||
|
mPresenter.addDirIfNeeded(new AddDirectoryHelper(this));
|
||||||
|
}
|
||||||
|
|
||||||
void setupUI() {
|
void setupUI() {
|
||||||
final FragmentManager fragmentManager = getSupportFragmentManager();
|
final FragmentManager fragmentManager = getSupportFragmentManager();
|
||||||
mBrowseFragment = new BrowseSupportFragment();
|
mBrowseFragment = new BrowseSupportFragment();
|
||||||
@ -125,7 +129,7 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
|||||||
@Override
|
@Override
|
||||||
public void launchFileListActivity()
|
public void launchFileListActivity()
|
||||||
{
|
{
|
||||||
AddDirectoryActivity.launch(this);
|
FileBrowserHelper.openDirectoryPicker(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -150,7 +154,20 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
|||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent result)
|
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
|
@Override
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -145,4 +145,27 @@
|
|||||||
<item name="android:textColor">@color/button_text_color</item>
|
<item name="android:textColor">@color/button_text_color</item>
|
||||||
</style>
|
</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>
|
||||||
|
|
||||||
|
<!--<!– Setting a divider is entirely optional –>-->
|
||||||
|
<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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user