mirror of
https://github.com/libretro/RetroArch.git
synced 2024-12-02 13:28:35 +00:00
Merge pull request #1285 from lioncash/android
Android: Very early initial core downloader implementation
This commit is contained in:
commit
aa9b4a9aa3
@ -5,5 +5,6 @@
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="lib" path="libs/jsoup-1.8.1.jar"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
||||
|
BIN
android/phoenix/libs/jsoup-1.8.1.jar
Normal file
BIN
android/phoenix/libs/jsoup-1.8.1.jar
Normal file
Binary file not shown.
@ -40,6 +40,11 @@
|
||||
<string name="core_info_manufacturer">Manufacturer</string>
|
||||
<string name="core_info_permissions">Permissions</string>
|
||||
|
||||
<!-- Core Downloader Fragment -->
|
||||
<string name="download_core_confirm_title">Confirm</string>
|
||||
<string name="download_core_confirm_msg">Are you sure you want to download %1$s?</string>
|
||||
<string name="downloading_msg">Downloading %1$s…</string>
|
||||
|
||||
<!-- Display Refresh Rate Test Class -->
|
||||
<string name="refresh_rate_calibration">Refresh rate calibration</string>
|
||||
<string name="touch_screen_with_fingers">Touch the screen with your fingers for more accurate measurements.</string>
|
||||
|
@ -4,6 +4,7 @@ import java.util.List;
|
||||
|
||||
import com.retroarch.R;
|
||||
import com.retroarch.browser.coremanager.fragments.DownloadableCoresFragment;
|
||||
import com.retroarch.browser.coremanager.fragments.InstalledCoresFragment;
|
||||
import com.retroarch.browser.coremanager.fragments.InstalledCoresManagerFragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
@ -17,7 +18,7 @@ import android.support.v7.app.ActionBarActivity;
|
||||
* Activity which provides the base for viewing installed cores,
|
||||
* as well as the ability to download other cores.
|
||||
*/
|
||||
public final class CoreManagerActivity extends ActionBarActivity
|
||||
public final class CoreManagerActivity extends ActionBarActivity implements DownloadableCoresFragment.OnCoreDownloadedListener
|
||||
{
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
@ -76,6 +77,19 @@ public final class CoreManagerActivity extends ActionBarActivity
|
||||
return false;
|
||||
}
|
||||
|
||||
// Callback function used to update the installed cores list
|
||||
@Override
|
||||
public void onCoreDownloaded()
|
||||
{
|
||||
InstalledCoresManagerFragment icmf = (InstalledCoresManagerFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.coreviewer_viewPager + ":" + 0);
|
||||
if (icmf != null)
|
||||
{
|
||||
InstalledCoresFragment icf = (InstalledCoresFragment) icmf.getChildFragmentManager().findFragmentByTag("InstalledCoresList");
|
||||
if (icf != null)
|
||||
icf.updateInstalledCoresList();
|
||||
}
|
||||
}
|
||||
|
||||
// Adapter for the core manager ViewPager.
|
||||
private final class ViewPagerAdapter extends FragmentPagerAdapter
|
||||
{
|
||||
|
@ -0,0 +1,57 @@
|
||||
package com.retroarch.browser.coremanager.fragments;
|
||||
|
||||
/**
|
||||
* Represents a core that can be downloaded.
|
||||
*/
|
||||
final class DownloadableCore
|
||||
{
|
||||
private final String coreName;
|
||||
private final String coreURL;
|
||||
private final String shortURL;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param coreName Name of the core.
|
||||
* @param coreURL URL to this core.
|
||||
*/
|
||||
public DownloadableCore(String coreName, String coreURL)
|
||||
{
|
||||
this.coreName = coreName;
|
||||
this.coreURL = coreURL;
|
||||
this.shortURL = coreURL.substring(coreURL.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this core.
|
||||
*
|
||||
* @return The name of this core.
|
||||
*/
|
||||
public String getCoreName()
|
||||
{
|
||||
return coreName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL to download this core.
|
||||
*
|
||||
* @return The URL to download this core.
|
||||
*/
|
||||
public String getCoreURL()
|
||||
{
|
||||
return coreURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the short URL name of this core.
|
||||
* <p>
|
||||
* e.g. Consider the url: www.somesite/somecore.zip.
|
||||
* This would return "somecore.zip"
|
||||
*
|
||||
* @return the short URL name of this core.
|
||||
*/
|
||||
public String getShortURLName()
|
||||
{
|
||||
return shortURL;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.retroarch.browser.coremanager.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* {@link ArrayAdapter} that handles the display of downloadable cores.
|
||||
*/
|
||||
final class DownloadableCoresAdapter extends ArrayAdapter<DownloadableCore>
|
||||
{
|
||||
private final int layoutId;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param context The current {@link Context}.
|
||||
* @param layoutID The resource ID for a layout file containing a layout to use when instantiating views
|
||||
*/
|
||||
public DownloadableCoresAdapter(Context context, int layoutId)
|
||||
{
|
||||
super(context, layoutId);
|
||||
|
||||
this.layoutId = layoutId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent)
|
||||
{
|
||||
if (convertView == null)
|
||||
{
|
||||
final LayoutInflater li = LayoutInflater.from(getContext());
|
||||
convertView = li.inflate(layoutId, parent, false);
|
||||
}
|
||||
|
||||
final DownloadableCore core = getItem(position);
|
||||
if (core != null)
|
||||
{
|
||||
TextView title = (TextView) convertView.findViewById(android.R.id.text1);
|
||||
TextView subtitle = (TextView) convertView.findViewById(android.R.id.text2);
|
||||
|
||||
if (title != null)
|
||||
title.setText(core.getCoreName());
|
||||
|
||||
if (subtitle != null)
|
||||
subtitle.setText(core.getCoreURL());
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
@ -1,27 +1,378 @@
|
||||
package com.retroarch.browser.coremanager.fragments;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import org.jsoup.Connection;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
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.TextView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import com.retroarch.R;
|
||||
|
||||
/**
|
||||
* {@link ListFragment} that is responsible for showing
|
||||
* cores that are able to be downloaded or are not installed.
|
||||
*/
|
||||
public final class DownloadableCoresFragment extends Fragment
|
||||
public final class DownloadableCoresFragment extends ListFragment
|
||||
{
|
||||
// TODO: Implement complete functionality.
|
||||
// List of TODOs.
|
||||
// - Eventually make the core downloader capable of directory-based browsing from the base URL.
|
||||
// - Allow for 'repository'-like core downloading.
|
||||
// - Clean this up a little better. It can likely be way more organized.
|
||||
// - Don't re-download the info files on orientation changes.
|
||||
// - Use a loading wheel when core retrieval is being done. User may think something went wrong otherwise.
|
||||
// - Check the info directory for an info file before downloading it. Can save bandwidth this way (and list load times would be faster).
|
||||
// - Should probably display a dialog or a toast message when the Internet connection process fails.
|
||||
|
||||
/**
|
||||
* Dictates what actions will occur when a core download completes.
|
||||
* <p>
|
||||
* Acts like a callback so that communication between fragments is possible.
|
||||
*/
|
||||
public interface OnCoreDownloadedListener
|
||||
{
|
||||
/** The action that will occur when a core is successfully downloaded. */
|
||||
void onCoreDownloaded();
|
||||
}
|
||||
|
||||
private static final String BUILDBOT_BASE_URL = "http://buildbot.libretro.com";
|
||||
private static final String BUILDBOT_CORE_URL = BUILDBOT_BASE_URL + "/stable/android/armv7/1.0.0.2/cores/";
|
||||
private static final String BUILDBOT_INFO_URL = BUILDBOT_BASE_URL + "/stable/android/armv7/1.0.0.2/info/";
|
||||
|
||||
private OnCoreDownloadedListener coreDownloadedListener = null;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
{
|
||||
TextView tv = new TextView(getActivity());
|
||||
tv.setText("In development, just a little longer!");
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
final ListView coreList = (ListView) inflater.inflate(R.layout.coremanager_listview, container, false);
|
||||
|
||||
return tv;
|
||||
final DownloadableCoresAdapter adapter = new DownloadableCoresAdapter(getActivity(), android.R.layout.simple_list_item_2);
|
||||
adapter.setNotifyOnChange(true);
|
||||
coreList.setAdapter(adapter);
|
||||
|
||||
coreDownloadedListener = (OnCoreDownloadedListener) getActivity();
|
||||
|
||||
new PopulateCoresListOperation(adapter).execute();
|
||||
|
||||
return coreList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(final ListView lv, final View v, final int position, final long id)
|
||||
{
|
||||
super.onListItemClick(lv, v, position, id);
|
||||
final DownloadableCore core = (DownloadableCore) lv.getItemAtPosition(position);
|
||||
|
||||
// Prompt the user for confirmation on downloading the core.
|
||||
AlertDialog.Builder notification = new AlertDialog.Builder(getActivity());
|
||||
notification.setMessage(String.format(getString(R.string.download_core_confirm_msg), core.getCoreName()));
|
||||
notification.setTitle(R.string.download_core_confirm_title);
|
||||
notification.setNegativeButton(R.string.no, null);
|
||||
notification.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
// Begin downloading the core.
|
||||
new DownloadCoreOperation(getActivity(), core.getCoreName()).execute(core.getCoreURL(), core.getShortURLName());
|
||||
}
|
||||
});
|
||||
notification.show();
|
||||
}
|
||||
|
||||
// Async event responsible for populating the Downloadable Cores list.
|
||||
private static final class PopulateCoresListOperation extends AsyncTask<Void, Void, ArrayList<DownloadableCore>>
|
||||
{
|
||||
// Acts as an object reference to an adapter in a list view.
|
||||
private DownloadableCoresAdapter adapter;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param adapter The adapter to asynchronously update.
|
||||
*/
|
||||
public PopulateCoresListOperation(DownloadableCoresAdapter adapter)
|
||||
{
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ArrayList<DownloadableCore> doInBackground(Void... params)
|
||||
{
|
||||
try
|
||||
{
|
||||
final Connection core_connection = Jsoup.connect(BUILDBOT_CORE_URL);
|
||||
final Elements coreElements = core_connection.get().body().getElementsByClass("fb-n").select("a");
|
||||
|
||||
final ArrayList<DownloadableCore> downloadableCores = new ArrayList<DownloadableCore>();
|
||||
|
||||
// NOTE: Start from 1 to skip the ".." (parent directory element)
|
||||
// Set this to zero if directory-based browsing becomes a thing.
|
||||
for (int i = 1; i < coreElements.size(); i++)
|
||||
{
|
||||
Element coreElement = coreElements.get(i);
|
||||
|
||||
final String coreURL = BUILDBOT_BASE_URL + coreElement.attr("href");
|
||||
final String coreName = coreURL.substring(coreURL.lastIndexOf("/") + 1);
|
||||
final String infoURL = BUILDBOT_INFO_URL + coreName.replace("_android.so.zip", ".info");
|
||||
|
||||
downloadableCores.add(new DownloadableCore(getCoreName(infoURL), coreURL));
|
||||
}
|
||||
|
||||
return downloadableCores;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Log.e("PopulateCoresListOperation", e.getMessage());
|
||||
|
||||
// Make a dummy entry to notify an error.
|
||||
final ArrayList<DownloadableCore> errorList = new ArrayList<DownloadableCore>();
|
||||
errorList.add(new DownloadableCore("Error", e.getMessage()));
|
||||
return errorList;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(ArrayList<DownloadableCore> result)
|
||||
{
|
||||
super.onPostExecute(result);
|
||||
adapter.addAll(result);
|
||||
}
|
||||
|
||||
// Literally downloads the info file, writes it, and parses it for the corename key/value pair.
|
||||
// AKA an argument for having a manifest file on the server.
|
||||
//
|
||||
// This makes list loading take way longer than it should.
|
||||
//
|
||||
// One way this can be improved is by checking the info directory for
|
||||
// existing info files that match the core. Eliminating the download retrieval.
|
||||
private String getCoreName(String urlPath) throws IOException
|
||||
{
|
||||
final URL url = new URL(urlPath);
|
||||
final BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
String str = "";
|
||||
while ((str = br.readLine()) != null)
|
||||
sb.append(str + "\n");
|
||||
br.close();
|
||||
|
||||
// Write the info file.
|
||||
File outputPath = new File(adapter.getContext().getApplicationInfo().dataDir + "/info/", urlPath.substring(urlPath.lastIndexOf('/') + 1));
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter(outputPath));
|
||||
bw.append(sb);
|
||||
bw.close();
|
||||
|
||||
// Now read the core name
|
||||
String[] lines = sb.toString().split("\n");
|
||||
String name = "";
|
||||
for (int i = 0; i < lines.length; i++)
|
||||
{
|
||||
if (lines[i].contains("corename"))
|
||||
{
|
||||
// Gross
|
||||
name = lines[i].split("=")[1].trim().replace("\"", "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
// Executed when the user confirms a core download.
|
||||
private final class DownloadCoreOperation extends AsyncTask<String, Integer, Void>
|
||||
{
|
||||
private final ProgressDialog dlg;
|
||||
private final Context ctx;
|
||||
private final String coreName;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param ctx The current {@link Context}.
|
||||
* @param coreName The name of the core being downloaded.
|
||||
*/
|
||||
public DownloadCoreOperation(Context ctx, String coreName)
|
||||
{
|
||||
this.dlg = new ProgressDialog(ctx);
|
||||
this.ctx = ctx;
|
||||
this.coreName = coreName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute()
|
||||
{
|
||||
super.onPreExecute();
|
||||
|
||||
dlg.setMessage(String.format(ctx.getString(R.string.downloading_msg), coreName));
|
||||
dlg.setCancelable(false);
|
||||
dlg.setCanceledOnTouchOutside(false);
|
||||
dlg.setIndeterminate(false);
|
||||
dlg.setMax(100);
|
||||
dlg.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
dlg.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(String... params)
|
||||
{
|
||||
InputStream input = null;
|
||||
OutputStream output = null;
|
||||
HttpURLConnection connection = null;
|
||||
try
|
||||
{
|
||||
URL url = new URL(params[0]);
|
||||
connection = (HttpURLConnection) url.openConnection();
|
||||
connection.connect();
|
||||
|
||||
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
|
||||
{
|
||||
Log.i("DownloadCoreOperation", "HTTP response code not OK. Response code: " + connection.getResponseCode());
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set up the streams
|
||||
final int fileLen = connection.getContentLength();
|
||||
final File zipPath = new File(ctx.getApplicationInfo().dataDir + "/cores/", params[1]);
|
||||
input = new BufferedInputStream(connection.getInputStream(), 8192);
|
||||
output = new FileOutputStream(zipPath);
|
||||
|
||||
// Download and write to storage.
|
||||
long totalDownloaded = 0;
|
||||
byte[] buffer = new byte[4096];
|
||||
int countBytes = 0;
|
||||
while ((countBytes = input.read(buffer)) != -1)
|
||||
{
|
||||
totalDownloaded += countBytes;
|
||||
if (fileLen > 0)
|
||||
publishProgress((int) (totalDownloaded * 100 / fileLen));
|
||||
|
||||
output.write(buffer, 0, countBytes);
|
||||
}
|
||||
|
||||
unzipCore(zipPath);
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
// Can't really do anything to recover.
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (output != null)
|
||||
output.close();
|
||||
|
||||
if (input != null)
|
||||
input.close();
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
}
|
||||
|
||||
if (connection != null)
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... progress)
|
||||
{
|
||||
super.onProgressUpdate(progress);
|
||||
|
||||
dlg.setProgress(progress[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result)
|
||||
{
|
||||
super.onPostExecute(result);
|
||||
|
||||
if (dlg.isShowing())
|
||||
dlg.dismiss();
|
||||
|
||||
// Invoke callback to update the installed cores list.
|
||||
coreDownloadedListener.onCoreDownloaded();
|
||||
}
|
||||
}
|
||||
|
||||
// Java 6 ladies and gentlemen.
|
||||
private static void unzipCore(File zipFile)
|
||||
{
|
||||
ZipInputStream zis = null;
|
||||
|
||||
try
|
||||
{
|
||||
zis = new ZipInputStream(new FileInputStream(zipFile));
|
||||
ZipEntry entry = zis.getNextEntry();
|
||||
|
||||
while (entry != null)
|
||||
{
|
||||
File file = new File(zipFile.getParent(), entry.getName());
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
int len = 0;
|
||||
byte[] buffer = new byte[4096];
|
||||
while ((len = zis.read(buffer)) != -1)
|
||||
{
|
||||
fos.write(buffer, 0, len);
|
||||
}
|
||||
fos.close();
|
||||
|
||||
entry = zis.getNextEntry();
|
||||
}
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
// Can't do anything.
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (zis != null)
|
||||
{
|
||||
zis.closeEntry();
|
||||
zis.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
// Can't do anything
|
||||
}
|
||||
|
||||
zipFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,70 +56,23 @@ public final class InstalledCoresFragment extends ListFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
public void onActivityCreated(Bundle savedInstanceState)
|
||||
{
|
||||
// Inflate the layout for this ListFragment.
|
||||
ListView parentView = (ListView) inflater.inflate(R.layout.coremanager_listview, container, false);
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
// Set the long click listener.
|
||||
parentView.setOnItemLongClickListener(itemLongClickListener);
|
||||
adapter = new InstalledCoresAdapter(getActivity(), android.R.layout.simple_list_item_2, getInstalledCoresList());
|
||||
setListAdapter(adapter);
|
||||
|
||||
// Get the callback. (implemented within InstalledCoresManagerFragment).
|
||||
callback = (OnCoreItemClickedListener) getParentFragment();
|
||||
}
|
||||
|
||||
// The list of items that will be added to the adapter backing this ListFragment.
|
||||
final List<ModuleWrapper> items = new ArrayList<ModuleWrapper>();
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState)
|
||||
{
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
// 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();
|
||||
if (libs != null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Add it to the list.
|
||||
items.add(new ModuleWrapper(getActivity(), lib));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the list alphabetically
|
||||
Collections.sort(items);
|
||||
|
||||
// Initialize and set the backing adapter for this ListFragment.
|
||||
adapter = new InstalledCoresAdapter(getActivity(), android.R.layout.simple_list_item_2, items);
|
||||
parentView.setAdapter(adapter);
|
||||
|
||||
return parentView;
|
||||
getListView().setOnItemLongClickListener(itemLongClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -131,6 +84,68 @@ public final class InstalledCoresFragment extends ListFragment
|
||||
getListView().setItemChecked(position, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the list of installed cores.
|
||||
*/
|
||||
public void updateInstalledCoresList()
|
||||
{
|
||||
adapter.clear();
|
||||
adapter.addAll(getInstalledCoresList());
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private List<ModuleWrapper> getInstalledCoresList()
|
||||
{
|
||||
// The list of items that will be added to the adapter backing this ListFragment.
|
||||
final List<ModuleWrapper> items = new ArrayList<ModuleWrapper>();
|
||||
|
||||
// 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();
|
||||
if (libs != null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Add it to the list.
|
||||
items.add(new ModuleWrapper(getActivity(), lib));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the list alphabetically
|
||||
Collections.sort(items);
|
||||
return items;
|
||||
}
|
||||
|
||||
// This will be the handler for long clicks on individual list items in this ListFragment.
|
||||
private final OnItemLongClickListener itemLongClickListener = new OnItemLongClickListener()
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ public class InstalledCoresManagerFragment extends Fragment implements Installed
|
||||
|
||||
final Fragment installedCores = new InstalledCoresFragment();
|
||||
final FragmentTransaction ft = getChildFragmentManager().beginTransaction();
|
||||
ft.replace(R.id.installed_cores_fragment_container1, installedCores);
|
||||
ft.replace(R.id.installed_cores_fragment_container1, installedCores, "InstalledCoresList");
|
||||
ft.commit();
|
||||
|
||||
return v;
|
||||
|
@ -1,9 +1,12 @@
|
||||
package com.retroarch.browser.mainmenu;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.retroarch.R;
|
||||
import com.retroarch.browser.preferences.util.UserPreferences;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceActivity;
|
||||
@ -21,6 +24,15 @@ public final class MainMenuActivity extends FragmentActivity
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Ensure resource directories are created.
|
||||
final ApplicationInfo info = getApplicationInfo();
|
||||
final File coresDir = new File(info.dataDir, "cores");
|
||||
final File infoDir = new File(info.dataDir, "info");
|
||||
if (!coresDir.exists())
|
||||
coresDir.mkdir();
|
||||
if (!infoDir.exists())
|
||||
infoDir.mkdir();
|
||||
|
||||
// Load the main menu layout
|
||||
setContentView(R.layout.mainmenu_activity_layout);
|
||||
if (savedInstanceState == null)
|
||||
|
Loading…
Reference in New Issue
Block a user