diff --git a/android/phoenix/res/layout/coremanager_list_item.xml b/android/phoenix/res/layout/coremanager_list_item.xml deleted file mode 100644 index ab4c4bbb0e..0000000000 --- a/android/phoenix/res/layout/coremanager_list_item.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/android/phoenix/res/values/strings.xml b/android/phoenix/res/values/strings.xml index 16c780ca65..30a6676b82 100644 --- a/android/phoenix/res/values/strings.xml +++ b/android/phoenix/res/values/strings.xml @@ -5,19 +5,18 @@ File type icon Select the button to use Unbind - Report Refresh Rate Detect Optimal device settings Extracting assets, please wait … RetroArch - Main Menu - Resume Content + Resume Content Load Core - Load Content - Load Content (History) + Load Content + Load Content (Detect Core) + Load Content (History) Settings - Help About Quit RetroArch @@ -47,15 +46,16 @@ Refresh rate measured to: %1$s Hz. - Recently run content Loading [%1$s]… Current: %1$s Press key to use + + Multiple cores detected + - No core Welcome to RetroArch This is your first time starting up RetroArch. RetroArch will now be preconfigured for the best possible user experience. GPL waiver diff --git a/android/phoenix/res/xml/main_menu.xml b/android/phoenix/res/xml/main_menu.xml index f0efdf339b..e3c8daeb66 100644 --- a/android/phoenix/res/xml/main_menu.xml +++ b/android/phoenix/res/xml/main_menu.xml @@ -4,23 +4,28 @@ + android:key="resumeContentPref" + android:title="@string/resume_content"/> - + + android:key="loadContentPref" + android:title="@string/load_content"/> - + + android:key="loadContentAutoPref" + android:title="@string/load_content_auto"/> + + + diff --git a/android/phoenix/src/com/retroarch/browser/CoreSelection.java b/android/phoenix/src/com/retroarch/browser/CoreSelection.java index f2097a6c57..ff837232b3 100644 --- a/android/phoenix/src/com/retroarch/browser/CoreSelection.java +++ b/android/phoenix/src/com/retroarch/browser/CoreSelection.java @@ -52,7 +52,7 @@ public final class CoreSelection extends DialogFragment // Populate the list final List cores = new ArrayList(); - final File[] libs = new File(getActivity().getApplicationInfo().dataDir, "/cores").listFiles(); + final File[] libs = new File(getActivity().getApplicationInfo().dataDir, "cores").listFiles(); for (final File lib : libs) { String libName = lib.getName(); diff --git a/android/phoenix/src/com/retroarch/browser/HistorySelection.java b/android/phoenix/src/com/retroarch/browser/HistorySelection.java index 068c0730dc..7726bb82c7 100644 --- a/android/phoenix/src/com/retroarch/browser/HistorySelection.java +++ b/android/phoenix/src/com/retroarch/browser/HistorySelection.java @@ -71,7 +71,7 @@ public final class HistorySelection extends DialogFragment rootView.setOnItemClickListener(onItemClickListener); // Set the title for this dialog. - getDialog().setTitle(R.string.load_game_history); + getDialog().setTitle(R.string.load_content_history); // Setup the list adapter adapter = new IconAdapter(ctx, R.layout.line_list_item); diff --git a/android/phoenix/src/com/retroarch/browser/ModuleWrapper.java b/android/phoenix/src/com/retroarch/browser/ModuleWrapper.java index 19c05e2ee6..663bd49353 100644 --- a/android/phoenix/src/com/retroarch/browser/ModuleWrapper.java +++ b/android/phoenix/src/com/retroarch/browser/ModuleWrapper.java @@ -115,6 +115,17 @@ public final class ModuleWrapper implements IconAdapterItem, Comparable + * Basically, how it works is the user selects a file. + * Then, we iterate over all the cores and check what their supported extensions are. + * Then, if any cores contain the supported extension, they are added to a list and + * displayed to the user to choose from. + *

+ * The only exception is if only one core matches the extension of the chosen file. + * In this case, we just attempt to launch the core with that file directly. + */ +// TODO: This is ugly as hell. Clean this up sometime. +// For example, maybe breaking this out into two fragments +// to handle the behavior would be better. One for browsing, +// one for handling the list of selectable cores. +public final class DetectCoreDirectoryFragment extends DirectoryFragment +{ + private ListView backingListView = null; + private boolean inFileBrowser = true; + private ArrayList supportedCorePaths = new ArrayList(); + + /** + * Retrieves a new instance of a DetectCoreDirectoryFragment + * with a title specified by the given resource ID. + * + * @param titleResId String resource ID for the title + * of this DetectCoreDirectoryFragment. + * + * @return A new instance of a DetectCoreDirectoryFragment. + */ + public static DetectCoreDirectoryFragment newInstance(int titleResId) + { + final DetectCoreDirectoryFragment dFrag = new DetectCoreDirectoryFragment(); + final Bundle bundle = new Bundle(); + bundle.putInt("titleResId", titleResId); + dFrag.setArguments(bundle); + + return dFrag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) + { + backingListView = (ListView) inflater.inflate(R.layout.line_list, container, false); + backingListView.setOnItemClickListener(onItemClickListener); + + // Get whether or not we were in the file browser prior to recreation. + if (savedInstanceState != null) + { + inFileBrowser = savedInstanceState.getBoolean("inFileBrowser"); + + if (inFileBrowser) + backStack = savedInstanceState.getParcelableArrayList("BACKSTACK"); + } + + // Set the dialog title. + if (inFileBrowser) + getDialog().setTitle(getArguments().getInt("titleResId")); + else + getDialog().setTitle(R.string.multiple_cores_detected); + + // If we're in the file browser, reinitialize the file list adapter. + if (savedInstanceState == null || inFileBrowser) + { + // Setup the list + adapter = new IconAdapter(getActivity(), R.layout.line_list_item); + backingListView.setAdapter(adapter); + } + + if (inFileBrowser) + { + if (backStack == null || backStack.isEmpty()) + { + backStack = new ArrayList(); + String startPath = (startDirectory == null || startDirectory.isEmpty()) ? Environment + .getExternalStorageDirectory().getPath() : startDirectory; + backStack.add(new BackStackItem(startPath, false)); + } + + wrapFiles(); + } + else // Rebuild the core adapter. + { + supportedCorePaths = savedInstanceState.getStringArrayList("coreFilePaths"); + CoreSelectionAdapter adapter = new CoreSelectionAdapter(getActivity(), android.R.layout.simple_list_item_2); + + for (String path : supportedCorePaths) + { + ModuleWrapper mw = new ModuleWrapper(getActivity(), path); + adapter.add(new CoreItem(mw.getInternalName(), mw.getEmulatedSystemName())); + } + + backingListView.setAdapter(adapter); + } + + return backingListView; + } + + @Override + public void onSaveInstanceState(Bundle outState) + { + // Save whether or not we're in core selection or the file browser. + outState.putBoolean("inFileBrowser", inFileBrowser); + + if (!inFileBrowser) + outState.putStringArrayList("coreFilePaths", supportedCorePaths); + } + + private File chosenFile = null; + private final OnItemClickListener onItemClickListener = new OnItemClickListener() + { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) + { + final FileWrapper item = adapter.getItem(position); + + if (inFileBrowser && item.isParentItem() && backStack.get(backStack.size() - 1).parentIsBack) + { + backStack.remove(backStack.size() - 1); + wrapFiles(); + return; + } + + final File selected = item.isParentItem() ? listedDirectory.getParentFile() : item.getFile(); + if (inFileBrowser && selected.isDirectory()) + { + Log.d("DirectoryFrag", "Is Directory."); + backStack.add(new BackStackItem(selected.getAbsolutePath(), !item.isParentItem())); + wrapFiles(); + return; + } + else if (inFileBrowser && selected.isFile()) + { + String filePath = selected.getAbsolutePath(); + String fileExt = ""; + chosenFile = selected; + + // Attempt to get the file extension. + int i = filePath.lastIndexOf('.'); + if (i >= 0) + fileExt = filePath.substring(i+1); + + // Enumerate the cores and check for the extension + File coreDir = new File(getActivity().getApplicationInfo().dataDir + File.separator + "cores"); + File[] coreFiles = coreDir.listFiles(); + ListsupportedCores = new ArrayList(); + + for (File core : coreFiles) + { + ModuleWrapper mw = new ModuleWrapper(getActivity(), core); + + if (mw.getSupportedExtensions().contains(fileExt)) + { + supportedCores.add(mw); + supportedCorePaths.add(mw.getUnderlyingFile().getAbsolutePath()); + } + } + + // If only one core is supported, + if (supportedCores.size() == 1) + { + launchCore(selected.getPath(), supportedCores.get(0).getUnderlyingFile().getPath()); + } + // Otherwise build the list for the user to choose from. + else if (supportedCores.size() > 1) + { + // Modify the title to notify of multiple cores. + getDialog().setTitle(R.string.multiple_cores_detected); + + // Add all the cores to the adapter and swap it with the one in the ListView. + final CoreSelectionAdapter csa = new CoreSelectionAdapter(getActivity(), android.R.layout.simple_list_item_2); + + for (ModuleWrapper core : supportedCores) + { + csa.add(new CoreItem(core.getInternalName(), core.getEmulatedSystemName())); + } + + backingListView.setAdapter(csa); + } + } + else // Selection made + { + launchCore(chosenFile.getPath(), DetectCoreDirectoryFragment.this.supportedCorePaths.get(position)); + } + + // Not in the file browser any more. + inFileBrowser = false; + } + }; + + private void launchCore(String contentPath, String corePath) + { + Intent retro; + if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)) + retro = new Intent(getActivity(), RetroActivityFuture.class); + else + retro = new Intent(getActivity(), RetroActivityPast.class); + + UserPreferences.updateConfigFile(getActivity()); + String current_ime = Settings.Secure.getString(getActivity().getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); + retro.putExtra("ROM", contentPath); + retro.putExtra("LIBRETRO", corePath); + retro.putExtra("CONFIGFILE", UserPreferences.getDefaultConfigPath(getActivity())); + retro.putExtra("IME", current_ime); + startActivity(retro); + dismiss(); + } + + // Used to represent data in the ListView after the user chooses an item. + private static final class CoreItem + { + public final String Title; + public final String Subtitle; + + public CoreItem(String title, String subtitle) + { + this.Title = title; + this.Subtitle = subtitle; + } + } + + // Adapter that the ListView is flipped to after choosing a file to launch. + private static final class CoreSelectionAdapter extends ArrayAdapter + { + private final int resourceId; + + /** + * Constructor + * + * @param context The current {@link Context}. + * @param resourceId The resource ID for a layout file. + */ + public CoreSelectionAdapter(Context context, int resourceId) + { + super(context, resourceId); + + this.resourceId = resourceId; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) + { + if (convertView == null) + { + LayoutInflater inflater = LayoutInflater.from(getContext()); + convertView = inflater.inflate(resourceId, parent, false); + } + + final CoreItem core = getItem(position); + if (core != null) + { + final TextView title = (TextView) convertView.findViewById(android.R.id.text1); + final TextView subtitle = (TextView) convertView.findViewById(android.R.id.text2); + + if (title != null) + title.setText(core.Title); + + if (subtitle != null) + subtitle.setText(core.Subtitle); + } + + return convertView; + } + } +} diff --git a/android/phoenix/src/com/retroarch/browser/dirfragment/DirectoryFragment.java b/android/phoenix/src/com/retroarch/browser/dirfragment/DirectoryFragment.java index 6007e81d2f..2317eb8136 100644 --- a/android/phoenix/src/com/retroarch/browser/dirfragment/DirectoryFragment.java +++ b/android/phoenix/src/com/retroarch/browser/dirfragment/DirectoryFragment.java @@ -6,7 +6,6 @@ import android.os.Environment; import android.os.Parcel; import android.os.Parcelable; import android.support.v4.app.DialogFragment; -import android.support.v4.app.ListFragment; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -25,7 +24,7 @@ import java.io.*; /** - * {@link ListFragment} subclass that provides a file-browser + * {@link DialogFragment} subclass that provides a file-browser * like UI for browsing for specific files. *

* This file browser also allows for custom filtering @@ -38,15 +37,15 @@ import java.io.*; * To instantiate a new instance of this class * you must use the {@code newInstance} method. */ -public final class DirectoryFragment extends DialogFragment +public class DirectoryFragment extends DialogFragment { - private IconAdapter adapter; - private File listedDirectory; + protected IconAdapter adapter; + protected File listedDirectory; public static final class BackStackItem implements Parcelable { - private final String path; - private final boolean parentIsBack; + protected final String path; + protected boolean parentIsBack; public BackStackItem(String path, boolean parentIsBack) { @@ -101,12 +100,11 @@ public final class DirectoryFragment extends DialogFragment } - private ArrayList backStack; - - private String startDirectory; - private String pathSettingKey; - private boolean isDirectoryTarget; - private OnDirectoryFragmentClosedListener onClosedListener; + protected ArrayList backStack; + protected String startDirectory; + protected String pathSettingKey; + protected boolean isDirectoryTarget; + protected OnDirectoryFragmentClosedListener onClosedListener; /** * Sets the starting directory for this DirectoryFragment @@ -349,7 +347,7 @@ public final class DirectoryFragment extends DialogFragment disallowedExt.addAll(Arrays.asList(exts)); } - private void wrapFiles() + protected void wrapFiles() { listedDirectory = new File(backStack.get(backStack.size() - 1).path); diff --git a/android/phoenix/src/com/retroarch/browser/mainmenu/MainMenuFragment.java b/android/phoenix/src/com/retroarch/browser/mainmenu/MainMenuFragment.java index 5c783f4acd..6d12e39c9a 100644 --- a/android/phoenix/src/com/retroarch/browser/mainmenu/MainMenuFragment.java +++ b/android/phoenix/src/com/retroarch/browser/mainmenu/MainMenuFragment.java @@ -28,6 +28,7 @@ import com.retroarch.R; import com.retroarch.browser.CoreSelection; import com.retroarch.browser.HistorySelection; import com.retroarch.browser.NativeInterface; +import com.retroarch.browser.dirfragment.DetectCoreDirectoryFragment; import com.retroarch.browser.dirfragment.DirectoryFragment; import com.retroarch.browser.dirfragment.DirectoryFragment.OnDirectoryFragmentClosedListener; import com.retroarch.browser.mainmenu.gplwaiver.GPLWaiverDialogFragment; @@ -65,10 +66,11 @@ public final class MainMenuFragment extends PreferenceListFragment implements On addPreferencesFromResource(R.xml.main_menu); // Set the listeners for the menu items - findPreference("retroTVMode").setOnPreferenceClickListener(this); + findPreference("resumeContentPref").setOnPreferenceClickListener(this); findPreference("loadCorePref").setOnPreferenceClickListener(this); - findPreference("loadRomPref").setOnPreferenceClickListener(this); - findPreference("loadRomHistoryPref").setOnPreferenceClickListener(this); + findPreference("loadContentAutoPref").setOnPreferenceClickListener(this); + findPreference("loadContentPref").setOnPreferenceClickListener(this); + findPreference("loadContentHistoryPref").setOnPreferenceClickListener(this); findPreference("quitRetroArch").setOnPreferenceClickListener(this); // Extract assets. @@ -340,8 +342,8 @@ public final class MainMenuFragment extends PreferenceListFragment implements On { final String prefKey = preference.getKey(); - // TV Mode - if (prefKey.equals("retroTVMode")) + // Resume Content + if (prefKey.equals("resumeContentPref")) { UserPreferences.updateConfigFile(ctx); @@ -360,30 +362,43 @@ public final class MainMenuFragment extends PreferenceListFragment implements On CoreSelection.newInstance().show(getFragmentManager(), "core_selection"); } // Load ROM Preference - else if (prefKey.equals("loadRomPref")) + else if (prefKey.equals("loadContentPref")) { final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); final String libretro_path = prefs.getString("libretro_path", ctx.getApplicationInfo().dataDir + "/cores"); - + if (!new File(libretro_path).isDirectory()) { - final DirectoryFragment romBrowser = DirectoryFragment.newInstance(R.string.load_game); - romBrowser.addDisallowedExts(".state", ".srm", ".state.auto", ".rtc"); - romBrowser.setOnDirectoryFragmentClosedListener(this); + final DirectoryFragment contentBrowser = DirectoryFragment.newInstance(R.string.load_content); + contentBrowser.addDisallowedExts(".state", ".srm", ".state.auto", ".rtc"); + contentBrowser.setOnDirectoryFragmentClosedListener(this); final String startPath = prefs.getString("rgui_browser_directory", ""); if (!startPath.isEmpty() && new File(startPath).exists()) - romBrowser.setStartDirectory(startPath); + contentBrowser.setStartDirectory(startPath); - romBrowser.show(getFragmentManager(), "romBrowser"); + contentBrowser.show(getFragmentManager(), "contentBrowser"); } else { Toast.makeText(ctx, R.string.load_a_core_first, Toast.LENGTH_SHORT).show(); } } - // Load ROM (History) Preference - else if (prefKey.equals("loadRomHistoryPref")) + else if (prefKey.equals("loadContentAutoPref")) + { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); + final DetectCoreDirectoryFragment contentBrowser = DetectCoreDirectoryFragment.newInstance(R.string.load_content_auto); + contentBrowser.addDisallowedExts(".state", ".srm", ".state.auto", ".rtc"); + contentBrowser.setOnDirectoryFragmentClosedListener(this); + + final String startPath = prefs.getString("rgui_browser_directory", ""); + if (!startPath.isEmpty() && new File(startPath).exists()) + contentBrowser.setStartDirectory(startPath); + + contentBrowser.show(getFragmentManager(), "contentBrowser"); + } + // Load Content (History) Preference + else if (prefKey.equals("loadContentHistoryPref")) { HistorySelection.newInstance().show(getFragmentManager(), "history_selection"); }