diff --git a/android/phoenix/res/layout/coremanager_listview_layout.xml b/android/phoenix/res/layout/coremanager_listview.xml similarity index 69% rename from android/phoenix/res/layout/coremanager_listview_layout.xml rename to android/phoenix/res/layout/coremanager_listview.xml index 5583543704..7e1f57d214 100644 --- a/android/phoenix/res/layout/coremanager_listview_layout.xml +++ b/android/phoenix/res/layout/coremanager_listview.xml @@ -2,7 +2,5 @@ - - - + android:layout_height="match_parent" + android:orientation="vertical" /> diff --git a/android/phoenix/res/values/strings.xml b/android/phoenix/res/values/strings.xml index 790c2f15bb..b909b60509 100644 --- a/android/phoenix/res/values/strings.xml +++ b/android/phoenix/res/values/strings.xml @@ -26,6 +26,10 @@ Installed Cores + Uninstall Core + Would you like to uninstall %1$s? + Failed to uninstall core: %1$s. + Successfully uninstalled core: %1$s. Downloadable Cores @@ -214,8 +218,10 @@ OK Close - General Enable + General + No + Yes diff --git a/android/phoenix/src/com/retroarch/browser/CoreSelection.java b/android/phoenix/src/com/retroarch/browser/CoreSelection.java index 548f56e0da..2b5c1f4a77 100644 --- a/android/phoenix/src/com/retroarch/browser/CoreSelection.java +++ b/android/phoenix/src/com/retroarch/browser/CoreSelection.java @@ -44,7 +44,7 @@ public final class CoreSelection extends ListActivity { setTitle(R.string.select_libretro_core); // Populate the list - final File[] libs = new File(getApplicationInfo().dataDir, "cores").listFiles(); + final File[] libs = new File(getApplicationInfo().dataDir, "/cores").listFiles(); for (final File lib : libs) { String libName = lib.getName(); diff --git a/android/phoenix/src/com/retroarch/browser/coremanager/CoreManagerListItem.java b/android/phoenix/src/com/retroarch/browser/coremanager/CoreManagerListItem.java index 6145002b50..7b60afd688 100644 --- a/android/phoenix/src/com/retroarch/browser/coremanager/CoreManagerListItem.java +++ b/android/phoenix/src/com/retroarch/browser/coremanager/CoreManagerListItem.java @@ -5,7 +5,7 @@ import java.io.File; /** * Represents a list item within the CoreManager fragments. */ -public final class CoreManagerListItem +public final class CoreManagerListItem implements Comparable { private final String name; private final String subtitle; @@ -66,4 +66,13 @@ public final class CoreManagerListItem { return underlyingFile; } + + @Override + public int compareTo(CoreManagerListItem other) + { + if(name != null) + return name.toLowerCase().compareTo(other.getName().toLowerCase()); + else + throw new NullPointerException("The name of this CoreManagerListItem is null"); + } } diff --git a/android/phoenix/src/com/retroarch/browser/coremanager/fragments/InstalledCoresFragment.java b/android/phoenix/src/com/retroarch/browser/coremanager/fragments/InstalledCoresFragment.java index a8114ad626..29eca73171 100644 --- a/android/phoenix/src/com/retroarch/browser/coremanager/fragments/InstalledCoresFragment.java +++ b/android/phoenix/src/com/retroarch/browser/coremanager/fragments/InstalledCoresFragment.java @@ -1,39 +1,123 @@ package com.retroarch.browser.coremanager.fragments; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import com.retroarch.R; import com.retroarch.browser.coremanager.CoreManagerListItem; +import com.retroarch.browser.preferences.util.ConfigFile; +import com.retroarch.browser.preferences.util.UserPreferences; +import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; import android.os.Bundle; import android.support.v4.app.ListFragment; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemLongClickListener; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; +import android.widget.Toast; /** * {@link ListFragment} that displays all of the currently installed cores */ public final class InstalledCoresFragment extends ListFragment { + // Adapter backing this ListFragment. + private InstalledCoresAdapter adapter; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // List which will contain all of the items for the list. - List items = new ArrayList(); + // The list of items that will be added to the adapter backing this ListFragment. + final List items = new ArrayList(); - // TODO: Populate list adapter. + // Initialize the core config for retrieving core names. + ConfigFile coreConfig = new ConfigFile(); + try + { + coreConfig.append(getActivity().getAssets().open("libretro_cores.cfg")); + } + catch (IOException ioe) + { + Log.e("InstalledCoresFragment", "Failed to load libretro_cores.cfg from assets."); + } - // Set the list adapter. - final InstalledCoresAdapter adapter = new InstalledCoresAdapter(getActivity(), R.layout.coremanager_list_item, items); + // Check if the device supports NEON. + final String cpuInfo = UserPreferences.readCPUInfo(); + final boolean supportsNeon = cpuInfo.contains("neon"); + + // Populate the list + final File[] libs = new File(getActivity().getApplicationInfo().dataDir, "/cores").listFiles(); + for (File lib : libs) + { + String libName = lib.getName(); + + // Never append a NEON lib if we don't have NEON. + if (libName.contains("neon") && !supportsNeon) + continue; + + // If we have a NEON version with NEON capable CPU, + // never append a non-NEON version. + if (supportsNeon && !libName.contains("neon")) + { + boolean hasNeonVersion = false; + for (File lib_ : libs) + { + String otherName = lib_.getName(); + String baseName = libName.replace(".so", ""); + + if (otherName.contains("neon") && otherName.startsWith(baseName)) + { + hasNeonVersion = true; + break; + } + } + + if (hasNeonVersion) + continue; + } + + // Attempt to get the core name. + String coreName; + String strippedName = libName.replace(".so", ""); + if (coreConfig.keyExists(strippedName)) + coreName = coreConfig.getString(strippedName); + else + coreName = strippedName; + + // Attempt to get the core subtitle. + String subtitle = strippedName + "_system"; + if (coreConfig.keyExists(subtitle)) + subtitle = coreConfig.getString(subtitle); + else + subtitle = ""; + + Log.d("InstalledCoresFragment", "Core Name: " + coreName); + Log.d("InstalledCoresFragment", "Core Subtitle: " + subtitle); + + // Add it to the list. + items.add(new CoreManagerListItem(coreName, subtitle, lib.getPath())); + } + + // Sort the list alphabetically + Collections.sort(items); + + // Initialize and set the backing adapter for this ListFragment. + adapter = new InstalledCoresAdapter(getActivity(), R.layout.coremanager_list_item, items); setListAdapter(adapter); } @@ -41,11 +125,51 @@ public final class InstalledCoresFragment extends ListFragment public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this ListFragment. - View parentView = inflater.inflate(R.layout.coremanager_listview_layout, container, false); + View parentView = inflater.inflate(R.layout.coremanager_listview, container, false); + + // Set the long click listener. + ListView mainList = (ListView) parentView.findViewById(android.R.id.list); + mainList.setOnItemLongClickListener(itemLongClickListener); - return parentView.findViewById(android.R.id.list); + return mainList; } + // This will be the handler for long clicks on individual list items in this ListFragment. + private final OnItemLongClickListener itemLongClickListener = new OnItemLongClickListener() + { + @Override + public boolean onItemLongClick(AdapterView parent, View view, int position, long id) + { + // Begin building the AlertDialog + final CoreManagerListItem item = adapter.getItem(position); + final AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); + alert.setTitle(R.string.uninstall_core); + alert.setMessage(String.format(getString(R.string.uninstall_core_message), item.getName())); + alert.setNegativeButton(R.string.no, null); + alert.setPositiveButton(R.string.yes, new OnClickListener() + { + @Override + public void onClick(DialogInterface dialog, int which) + { + // Attempt to uninstall the core item. + if (item.getUnderlyingFile().delete()) + { + Toast.makeText(getActivity(), String.format(getString(R.string.uninstall_success), item.getName()), Toast.LENGTH_LONG).show(); + adapter.remove(item); + adapter.notifyDataSetChanged(); + } + else // Failed to uninstall. + { + Toast.makeText(getActivity(), String.format(getString(R.string.uninstall_failure), item.getName()), Toast.LENGTH_LONG).show(); + } + } + }); + alert.show(); + + return true; + } + }; + /** * The {@link ArrayAdapter} that backs this InstalledCoresFragment. */ @@ -60,7 +184,7 @@ public final class InstalledCoresFragment extends ListFragment * * @param context The current {@link Context}. * @param resourceId The resource ID for a layout file containing a layout to use when instantiating views. - * @param objects The items to represent in the {@link ListView}. + * @param items The list of items to represent in this adapter. */ public InstalledCoresAdapter(Context context, int resourceId, List items) {