From 24c6be9d0f8dfddc84898cd02c2b98c34f444712 Mon Sep 17 00:00:00 2001 From: Eder Bastos Date: Sat, 9 May 2015 12:36:17 -0400 Subject: Add File Browser screen to new UI. --- .../activities/AddDirectoryActivity.java | 96 ++++++++++++ .../dolphinemu/activities/GameGridActivity.java | 118 +++++++++------ .../dolphinemu/adapters/FileAdapter.java | 161 +++++++++++++++++++++ .../dolphinemu/adapters/GameAdapter.java | 7 +- .../dolphinemu/dolphinemu/model/FileListItem.java | 81 +++++++++++ .../dolphinemu/viewholders/FileViewHolder.java | 27 ++++ 6 files changed, 448 insertions(+), 42 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AddDirectoryActivity.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/FileAdapter.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/FileListItem.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/FileViewHolder.java (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AddDirectoryActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AddDirectoryActivity.java new file mode 100644 index 0000000000..3a282f6433 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AddDirectoryActivity.java @@ -0,0 +1,96 @@ +package org.dolphinemu.dolphinemu.activities; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Environment; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.widget.Toolbar; + +import org.dolphinemu.dolphinemu.BuildConfig; +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.adapters.FileAdapter; + +public class AddDirectoryActivity extends Activity implements FileAdapter.FileClickListener +{ + public static final String KEY_CURRENT_PATH = BuildConfig.APPLICATION_ID + ".path"; + + private FileAdapter mAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_add_directory); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_folder_list); + setActionBar(toolbar); + + 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.setPath(mAdapter.getPath() + "/.."); + 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()); + } + + @Override + public void finishSuccessfully() + { + Intent resultData = new Intent(); + + resultData.putExtra(KEY_CURRENT_PATH, mAdapter.getPath()); + setResult(RESULT_OK, resultData); + + finish(); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/GameGridActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/GameGridActivity.java index be0fbbed1a..14633d1277 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/GameGridActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/GameGridActivity.java @@ -2,13 +2,21 @@ package org.dolphinemu.dolphinemu.activities; import android.app.Activity; import android.content.Intent; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.net.Uri; import android.os.Bundle; import android.os.Environment; +import android.preference.PreferenceManager; +import android.provider.DocumentsContract; +import android.provider.OpenableColumns; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; +import android.view.View; +import android.widget.ImageButton; import android.widget.Toolbar; import org.dolphinemu.dolphinemu.AssetCopyService; @@ -24,11 +32,11 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Set; -public class GameGridActivity extends Activity +public final class GameGridActivity extends Activity { - private RecyclerView mRecyclerView; - private RecyclerView.Adapter mAdapter; - private RecyclerView.LayoutManager mLayoutManager; + private static final int REQUEST_ADD_DIRECTORY = 1; + + private GameAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) @@ -39,21 +47,32 @@ public class GameGridActivity extends Activity Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_game_list); setActionBar(toolbar); - mRecyclerView = (RecyclerView) findViewById(R.id.grid_games); + ImageButton buttonAddDirectory = (ImageButton) findViewById(R.id.button_add_directory); + RecyclerView recyclerView = (RecyclerView) findViewById(R.id.grid_games); // use this setting to improve performance if you know that changes // in content do not change the layout size of the RecyclerView //mRecyclerView.setHasFixedSize(true); // Specifying the LayoutManager determines how the RecyclerView arranges views. - mLayoutManager = new GridLayoutManager(this, 4); - mRecyclerView.setLayoutManager(mLayoutManager); + RecyclerView.LayoutManager layoutManager = new GridLayoutManager(this, 4); + recyclerView.setLayoutManager(layoutManager); - mRecyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8)); + recyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8)); // Create an adapter that will relate the dataset to the views on-screen. mAdapter = new GameAdapter(getGameList()); - mRecyclerView.setAdapter(mAdapter); + recyclerView.setAdapter(mAdapter); + + buttonAddDirectory.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View view) + { + Intent fileChooser = new Intent(GameGridActivity.this, AddDirectoryActivity.class); + startActivityForResult(fileChooser, REQUEST_ADD_DIRECTORY); + } + }); // Stuff in this block only happens when this activity is newly created (i.e. not a rotation) if (savedInstanceState == null) @@ -64,11 +83,33 @@ public class GameGridActivity extends Activity } } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent result) + { + if (resultCode == RESULT_OK) + { + if (requestCode == REQUEST_ADD_DIRECTORY) + { + String path = result.getStringExtra(AddDirectoryActivity.KEY_CURRENT_PATH); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + SharedPreferences.Editor editor = prefs.edit(); + + editor.putString(AddDirectoryActivity.KEY_CURRENT_PATH, path); + + // Using commit in order to block so the next method has the correct data to load. + editor.commit(); + + mAdapter.setGameList(getGameList()); + } + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.gamelist_menu, menu); + inflater.inflate(R.menu.menu_game_grid, menu); return true; } @@ -82,48 +123,43 @@ public class GameGridActivity extends Activity NativeLibrary.SetUserDirectory(DefaultDir); - String Directories = NativeLibrary.GetConfig("Dolphin.ini", "General", "ISOPaths", "0"); - Log.v("DolphinEmu", "Directories: " + Directories); - int intDirectories = Integer.parseInt(Directories); - // Extensions to filter by. Set exts = new HashSet(Arrays.asList(".dff", ".dol", ".elf", ".gcm", ".gcz", ".iso", ".wad", ".wbfs")); - for (int a = 0; a < intDirectories; ++a) - { - String BrowseDir = NativeLibrary.GetConfig("Dolphin.ini", "General", "ISOPath" + a, ""); - Log.v("DolphinEmu", "Directory " + a + ": " + BrowseDir); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + + String path = prefs.getString(AddDirectoryActivity.KEY_CURRENT_PATH, "/"); - File currentDir = new File(BrowseDir); - File[] dirs = currentDir.listFiles(); - try + File currentDir = new File(path); + File[] dirs = currentDir.listFiles(); + try + { + for (File entry : dirs) { - for (File entry : dirs) + if (!entry.isHidden() && !entry.isDirectory()) { - if (!entry.isHidden() && !entry.isDirectory()) - { - String entryName = entry.getName(); - - // Check that the file has an appropriate extension before trying to read out of it. - if (exts.contains(entryName.toLowerCase().substring(entryName.lastIndexOf('.')))) - { - GcGame game = new GcGame(NativeLibrary.GetTitle(entry.getAbsolutePath()), - NativeLibrary.GetDescription(entry.getAbsolutePath()).replace("\n", " "), - // TODO Some games might actually not be from this region, believe it or not. - "United States", - entry.getAbsolutePath(), - NativeLibrary.GetGameId(entry.getAbsolutePath()), - NativeLibrary.GetDate(entry.getAbsolutePath())); - - gameList.add(game); - } + String entryName = entry.getName(); + // Check that the file has an appropriate extension before trying to read out of it. + if (exts.contains(entryName.toLowerCase().substring(entryName.lastIndexOf('.')))) + { + GcGame game = new GcGame(NativeLibrary.GetTitle(entry.getAbsolutePath()), + NativeLibrary.GetDescription(entry.getAbsolutePath()).replace("\n", " "), + // TODO Some games might actually not be from this region, believe it or not. + "United States", + entry.getAbsolutePath(), + NativeLibrary.GetGameId(entry.getAbsolutePath()), + NativeLibrary.GetDate(entry.getAbsolutePath())); + + gameList.add(game); } } - } catch (Exception ignored) - { + } + } catch (Exception ignored) + { + } return gameList; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/FileAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/FileAdapter.java new file mode 100644 index 0000000000..7f4acf6abe --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/FileAdapter.java @@ -0,0 +1,161 @@ +package org.dolphinemu.dolphinemu.adapters; + +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +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 class FileAdapter extends RecyclerView.Adapter implements View.OnClickListener +{ + private ArrayList 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 gameList + */ + public FileAdapter(String path, FileClickListener listener) + { + mFileList = generateFileList(new File(path)); + mListener = listener; + } + + /** + * 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(); + } + + @Override + public void onClick(View view) + { + String path = (String) view.getTag(); + + File clickedFile = new File(path); + + if (clickedFile.isDirectory()) + { + mFileList = generateFileList(clickedFile); + notifyDataSetChanged(); + } else + { + // Pass the activity the path of the parent directory of the clicked file. + mListener.finishSuccessfully(); + } + } + + private ArrayList generateFileList(File directory) + { + File[] children = directory.listFiles(); + ArrayList fileList = new ArrayList(children.length); + + for (File child : children) + { + if (!child.isHidden()) + { + FileListItem item = new FileListItem(child); + fileList.add(item); + } + } + + mPath = directory.getAbsolutePath(); + + Collections.sort(fileList); + return fileList; + } + + public String getPath() + { + return mPath; + } + + public void setPath(String path) + { + mPath = path; + File parentDirectory = new File(path); + + mFileList = generateFileList(parentDirectory); + notifyDataSetChanged(); + } + + public static interface FileClickListener + { + public void finishSuccessfully(); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java index d868ed46de..bf7bf435b7 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java @@ -79,7 +79,6 @@ public class GameAdapter extends RecyclerView.Adapter holder.path = game.getPath(); holder.screenshotPath = game.getScreenPath(); holder.game = game; - } /** @@ -112,4 +111,10 @@ public class GameAdapter extends RecyclerView.Adapter } } + + public void setGameList(ArrayList gameList) + { + mGameList = gameList; + notifyDataSetChanged(); + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/FileListItem.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/FileListItem.java new file mode 100644 index 0000000000..e15516dbaf --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/FileListItem.java @@ -0,0 +1,81 @@ +package org.dolphinemu.dolphinemu.model; + + +import org.dolphinemu.dolphinemu.NativeLibrary; + +import java.io.File; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class FileListItem implements Comparable +{ + public static final int TYPE_FOLDER = 0; + public static final int TYPE_GC = 1; + public static final int TYPE_WII = 2; + public static final int TYPE_OTHER = 3; + + private int mType; + private String mFilename; + private String mPath; + + public FileListItem(File file) + { + mPath = file.getAbsolutePath(); + + if (file.isDirectory()) + { + mType = TYPE_FOLDER; + } else + { + String fileExtension = mPath.substring(mPath.lastIndexOf('.')); + + // Extensions to filter by. + Set allowedExtensions = new HashSet(Arrays.asList(".dff", ".dol", ".elf", ".gcm", ".gcz", ".iso", ".wad", ".wbfs")); + + // Check that the file has an appropriate extension before trying to read out of it. + if (allowedExtensions.contains(fileExtension)) + { + mType = NativeLibrary.IsWiiTitle(mPath) ? TYPE_WII : TYPE_GC; + } else + { + mType = TYPE_OTHER; + } + } + + mFilename = file.getName(); + } + + public int getType() + { + return mType; + } + + public String getFilename() + { + return mFilename; + } + + public String getPath() + { + return mPath; + } + + @Override + public int compareTo(FileListItem theOther) + { + if (theOther.getType() == getType()) + { + return getFilename().toLowerCase().compareTo(theOther.getFilename().toLowerCase()); + } else + { + if (getType() > theOther.getType()) + { + return 1; + } else + { + return -1; + } + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/FileViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/FileViewHolder.java new file mode 100644 index 0000000000..79acc8400b --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/FileViewHolder.java @@ -0,0 +1,27 @@ +package org.dolphinemu.dolphinemu.viewholders; + +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import org.dolphinemu.dolphinemu.R; + + +public class FileViewHolder extends RecyclerView.ViewHolder +{ + public View itemView; + + public TextView textFileName; + public ImageView imageType; + + public FileViewHolder(View itemView) + { + super(itemView); + + this.itemView = itemView; + + textFileName = (TextView) itemView.findViewById(R.id.text_file_name); + imageType = (ImageView) itemView.findViewById(R.id.image_type); + } +} -- cgit v1.2.3