Merge pull request #388 from lioncash/master

[Android] UI revamp/decoupling.
This commit is contained in:
Squarepusher 2013-11-17 04:29:39 -08:00
commit edb4ec6509
31 changed files with 1728 additions and 1216 deletions

View File

@ -6,7 +6,7 @@
<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="18" />
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
@ -15,10 +15,8 @@
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:hasCode="true">
<activity android:name="com.retroarch.browser.CoreSelection"/>
<activity android:name="com.retroarch.browser.HistorySelection"/>
<activity android:name="com.retroarch.browser.DisplayRefreshRateTest"/>
<activity android:name="com.retroarch.browser.MainMenuActivity" android:exported="true">
<activity android:name="com.retroarch.browser.mainmenu.MainMenuActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@ -28,15 +26,7 @@
<activity android:name="com.retroarch.browser.FileWrapper"/>
<activity android:name="com.retroarch.browser.RetroTVMode"/>
<activity android:name="com.retroarch.browser.diractivities.DirectoryActivity"/>
<activity android:name="com.retroarch.browser.diractivities.ROMActivity"/>
<activity android:name="com.retroarch.browser.diractivities.ROMDirActivity"/>
<activity android:name="com.retroarch.browser.diractivities.ShaderActivity"/>
<activity android:name="com.retroarch.browser.diractivities.OverlayActivity"/>
<activity android:name="com.retroarch.browser.diractivities.SRMDirActivity"/>
<activity android:name="com.retroarch.browser.diractivities.StateDirActivity"/>
<activity android:name="com.retroarch.browser.diractivities.SystemDirActivity"/>
<activity android:name="com.retroarch.browser.preferences.fragments.PreferenceActivity" android:theme="@style/Theme.AppCompat" />
<activity android:name="com.retroarch.browser.preferences.PreferenceActivity" android:theme="@style/Theme.AppCompat" />
<activity android:name="com.retroarch.browser.coremanager.CoreManagerActivity" android:theme="@style/Theme.AppCompat"/>
<activity android:name="com.retroarch.browser.RetroActivity" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:background="@android:color/transparent">
<ListView android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="0px"
android:layout_weight="1"
android:paddingTop="0dip"
android:paddingBottom="@dimen/preference_fragment_padding_bottom"
android:paddingLeft="@dimen/preference_fragment_padding_side"
android:paddingRight="@dimen/preference_fragment_padding_side"
android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
android:clipToPadding="false"
android:drawSelectorOnTop="false"
android:cacheColorHint="@android:color/transparent"
android:scrollbarAlwaysDrawVerticalTrack="true" />
<TextView android:id="@android:id/empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="@dimen/preference_fragment_padding_side"
android:gravity="center"
android:visibility="gone" />
<RelativeLayout android:id="@+id/button_bar"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="0"
android:visibility="gone">
<Button android:id="@+id/back_button"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentLeft="true"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true">
<Button android:id="@+id/skip_button"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:visibility="gone"/>
<Button android:id="@+id/next_button"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"/>
</LinearLayout>
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/assets/res/any/dimens.xml
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
<!-- Preference fragment padding, bottom -->
<dimen name="preference_fragment_padding_bottom">0dp</dimen>
<!-- Preference fragment padding, sides -->
<dimen name="preference_fragment_padding_side">16dp</dimen>
<integer name="preference_fragment_scrollbarStyle">0x02000000</integer> <!-- outsideOverlay -->
</resources>

View File

@ -22,7 +22,7 @@
<string name="about">About</string>
<!-- Core Selection Class -->
<string name="select_libretro_core">Select Libretro core</string>
<string name="select_libretro_core">Select a libretro core</string>
<!-- Core Manager -->
<string name="installed_cores">Installed Cores</string>
@ -69,6 +69,13 @@
<string name="no_optimal_settings">Device either not detected in list or doesn\'t have any optimal settings in our database.</string>
<string name="load_a_core_first">Go to \'Load Core\' and select a core first.</string>
<string name="loading_data">Loading [%1$s]…</string>
<!-- Asset extraction dialog -->
<string name="asset_extraction">Asset Extraction</string>
<!-- GPL cores AlertDialog -->
<string name="keep_cores">Keep cores</string>
<string name="remove_cores">Remove non-GPL cores</string>
<!-- Refresh Rate Set OS Class -->
<string name="using_os_reported_refresh_rate">Using OS-reported refresh rate of: %1$s Hz.</string>
@ -118,6 +125,7 @@
<string name="touchscreen_overlay_desc">Enable touchscreen overlays (WARNING: Lower-resolution overlays might be more suitable for less powerful devices).</string>
<string name="input_overlay">Input overlay</string>
<string name="input_overlay_desc">Sets touchscreen overlay config.</string>
<string name="input_overlay_select">Select an input overlay</string>
<string name="overlay_opacity">Overlay opacity</string>
<string name="overlay_opacity_desc">Set the opacity of the touch overlay.</string>
<string name="custom_binds">Custom Binds</string>
@ -151,19 +159,23 @@
<string name="rom_paths">ROM paths</string>
<string name="rom_directory">ROM directory</string>
<string name="rom_directory_desc">Sets directory where ROM browser will first browse for ROM files.</string>
<string name="rom_directory_select">Select a ROM directory</string>
<string name="save_files">Save files</string>
<string name="enable_custom_dir">Enable custom directory</string>
<string name="savefiles_custom_dir">Enables use of custom save file folder. (.srm) save files will be saved and loaded to configured directory. If not enabled, save files will reside in ROM folder.</string>
<string name="savefile_directory">Savefile directory</string>
<string name="savefile_directory_desc">Sets directory where to save and load game save files.</string>
<string name="savefile_directory_select">Select a savefile directory</string>
<string name="save_states">Save states</string>
<string name="savestates_custom_dir">Enables use of custom save state folder. (.state) save states will be saved and loaded to configured directory. If not enabled, save states will reside in ROM folder.</string>
<string name="save_states_custom_dir">Enables use of custom save state folder. (.state) save states will be saved and loaded to configured directory. If not enabled, save states will reside in ROM folder.</string>
<string name="save_state_directory">Save state directory</string>
<string name="save_state_directory_desc">Sets directory where to save and load game save states.</string>
<string name="save_state_directory_select">Select a savestate directory</string>
<string name="system">System</string>
<string name="system_custom_dir">Enables use of custom system folder. Cores will look for system specific files, like BIOSes, in this folder. If not enabled, it will look in the ROM folder.</string>
<string name="system_directory">System directory</string>
<string name="system_directory_desc">Sets directory where system files are loaded from.</string>
<string name="system_directory_select">Select a system directory</string>
<!-- General Settings -->
<string name="general_options">General</string>
@ -210,6 +222,7 @@
<string name="first_pass_shader_desc">Enable first pass shader (WARNING: performance varies per device and per shader).</string>
<string name="glsl_shader">GLSL shader</string>
<string name="glsl_shader_desc">Sets GLES2 style shader.</string>
<string name="glsl_shader_select">Select a GLSL shader</string>
<string name="fonts">Fonts</string>
<string name="onscreen_fonts">On-screen fonts</string>
<string name="onscreen_fonts_desc">Enable rendering of on-screen fonts for messages.</string>

View File

@ -72,13 +72,11 @@
android:title="@string/enable" />
<Preference
android:key="inputOverlayDirPref"
android:summary="@string/input_overlay_desc"
android:title="@string/input_overlay"
android:dependency="input_overlay_enable" >
<intent
android:targetClass="com.retroarch.browser.diractivities.OverlayActivity"
android:targetPackage="com.retroarch" />
</Preference>
android:dependency="input_overlay_enable"/>
<com.retroarch.browser.preferences.util.SeekbarPreference
android:summary="@string/overlay_opacity_desc"
android:title="@string/overlay_opacity"

View File

@ -3,39 +3,33 @@
android:title="@string/mainmenu_title" >
<!-- TV Mode -->
<PreferenceScreen android:title="@string/tv_mode">
<Preference android:title="@string/tv_mode">
<intent
android:targetClass="com.retroarch.browser.RetroTVMode"
android:targetPackage="com.retroarch" />
</PreferenceScreen>
</Preference>
<!-- Load Core -->
<PreferenceScreen android:title="@string/load_core">
<intent
android:targetClass="com.retroarch.browser.CoreSelection"
android:targetPackage="com.retroarch" />
</PreferenceScreen>
<Preference
android:key="loadCorePref"
android:title="@string/load_core"/>
<!-- Load Game -->
<PreferenceScreen android:title="@string/load_game">
<intent
android:targetClass="com.retroarch.browser.diractivities.ROMActivity"
android:targetPackage="com.retroarch" />
</PreferenceScreen>
<Preference
android:key="loadRomPref"
android:title="@string/load_game"/>
<!-- Load Game (History) -->
<PreferenceScreen android:title="@string/load_game_history">
<intent
android:targetClass="com.retroarch.browser.HistorySelection"
android:targetPackage="com.retroarch" />
</PreferenceScreen>
<Preference
android:key="loadRomHistoryPref"
android:title="@string/load_game_history"/>
<!-- Settings -->
<PreferenceScreen android:title="@string/settings">
<Preference android:title="@string/settings">
<intent
android:targetClass="com.retroarch.browser.preferences.fragments.PreferenceActivity"
android:targetClass="com.retroarch.browser.preferences.PreferenceActivity"
android:targetPackage="com.retroarch" />
</PreferenceScreen>
</Preference>
<!-- About -->
<PreferenceScreen android:title="@string/about">

View File

@ -3,12 +3,10 @@
<!-- ROM Paths -->
<PreferenceCategory android:title="@string/rom_paths">
<Preference android:title="@string/rom_directory"
android:summary="@string/rom_directory_desc">
<intent
android:targetClass="com.retroarch.browser.diractivities.ROMDirActivity"
android:targetPackage="com.retroarch" />
</Preference>
<Preference
android:key="romDirPref"
android:title="@string/rom_directory"
android:summary="@string/rom_directory_desc"/>
</PreferenceCategory>
<!-- Save Files -->
@ -17,28 +15,24 @@
android:summary="@string/savefiles_custom_dir"
android:key="savefile_directory_enable"
android:defaultValue="false"/>
<Preference android:title="@string/savefile_directory"
<Preference
android:key="srmDirPref"
android:title="@string/savefile_directory"
android:summary="@string/savefile_directory_desc"
android:dependency="savefile_directory_enable">
<intent
android:targetClass="com.retroarch.browser.diractivities.SRMDirActivity"
android:targetPackage="com.retroarch" />
</Preference>
android:dependency="savefile_directory_enable"/>
</PreferenceCategory>
<!-- Save States -->
<PreferenceCategory android:title="@string/save_states">
<CheckBoxPreference android:title="@string/enable_custom_dir"
android:summary="@string/savestates_custom_dir"
android:summary="@string/save_states_custom_dir"
android:key="savestate_directory_enable"
android:defaultValue="false"/>
<Preference android:title="@string/save_state_directory"
<Preference
android:key="saveStateDirPref"
android:title="@string/save_state_directory"
android:summary="@string/save_state_directory_desc"
android:dependency="savestate_directory_enable">
<intent
android:targetClass="com.retroarch.browser.diractivities.StateDirActivity"
android:targetPackage="com.retroarch" />
</Preference>
android:dependency="savestate_directory_enable"/>
</PreferenceCategory>
<!-- System -->
@ -47,12 +41,10 @@
android:summary="@string/system_custom_dir"
android:key="system_directory_enable"
android:defaultValue="false"/>
<Preference android:title="@string/system_directory"
<Preference
android:key="systemDirPref"
android:title="@string/system_directory"
android:summary="@string/system_directory_desc"
android:dependency="system_directory_enable">
<intent
android:targetClass="com.retroarch.browser.diractivities.SystemDirActivity"
android:targetPackage="com.retroarch" />
</Preference>
android:dependency="system_directory_enable"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@ -64,15 +64,11 @@
android:defaultValue="false"
android:key="video_shader_enable"
android:title="@string/first_pass_shader" />
<Preference
android:key="glsl_shader_pref"
android:summary="@string/glsl_shader_desc"
android:title="@string/glsl_shader"
android:dependency="video_shader_enable" >
<intent
android:targetClass="com.retroarch.browser.diractivities.ShaderActivity"
android:targetPackage="com.retroarch" />
</Preference>
android:dependency="video_shader_enable" />
</PreferenceCategory>
<!-- Fonts -->

View File

@ -1,42 +1,48 @@
package com.retroarch.browser;
import com.retroarch.R;
import com.retroarch.browser.preferences.util.UserPreferences;
import java.io.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.app.*;
import android.media.AudioManager;
import android.os.*;
import android.widget.*;
import android.view.*;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.ListFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import com.retroarch.R;
import com.retroarch.browser.mainmenu.MainMenuActivity;
import com.retroarch.browser.preferences.util.UserPreferences;
/**
* {@link ListActivity} subclass that displays the list
* {@link ListFragment} subclass that displays the list
* of selectable cores for emulating games.
*/
public final class CoreSelection extends ListActivity {
public final class CoreSelection extends DialogFragment
{
private IconAdapter<ModuleWrapper> adapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Inflate the ListView we're using.
final ListView coreList = (ListView) inflater.inflate(R.layout.line_list, container, false);
coreList.setOnItemClickListener(onClickListener);
// Set the title of the dialog
getDialog().setTitle(R.string.select_libretro_core);
final String cpuInfo = UserPreferences.readCPUInfo();
final boolean cpuIsNeon = cpuInfo.contains("neon");
// Setup the layout
setContentView(R.layout.line_list);
// Set the activity title.
setTitle(R.string.select_libretro_core);
// Populate the list
final List<ModuleWrapper> cores = new ArrayList<ModuleWrapper>();
final File[] libs = new File(getApplicationInfo().dataDir, "/cores").listFiles();
final File[] libs = new File(getActivity().getApplicationInfo().dataDir, "/cores").listFiles();
for (final File lib : libs) {
String libName = lib.getName();
@ -46,13 +52,15 @@ public final class CoreSelection extends ListActivity {
// If we have a NEON version with NEON capable CPU,
// never append a non-NEON version.
if (cpuIsNeon && !libName.contains("neon")) {
if (cpuIsNeon && !libName.contains("neon"))
{
boolean hasNeonVersion = false;
for (final File lib_ : libs) {
for (final File lib_ : libs)
{
String otherName = lib_.getName();
String baseName = libName.replace(".so", "");
if (otherName.contains("neon")
&& otherName.startsWith(baseName)) {
if (otherName.contains("neon") && otherName.startsWith(baseName))
{
hasNeonVersion = true;
break;
}
@ -62,24 +70,28 @@ public final class CoreSelection extends ListActivity {
continue;
}
cores.add(new ModuleWrapper(this, lib));
cores.add(new ModuleWrapper(getActivity(), lib));
}
// Sort the list of cores alphabetically
Collections.sort(cores);
// Initialize the IconAdapter with the list of cores.
adapter = new IconAdapter<ModuleWrapper>(this, R.layout.line_list_item, cores);
setListAdapter(adapter);
adapter = new IconAdapter<ModuleWrapper>(getActivity(), R.layout.line_list_item, cores);
coreList.setAdapter(adapter);
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
return coreList;
}
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
final ModuleWrapper item = adapter.getItem(position);
MainMenuActivity.getInstance().setModule(item.getUnderlyingFile().getAbsolutePath(), item.getText());
UserPreferences.updateConfigFile(this);
finish();
}
private final OnItemClickListener onClickListener = new OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> listView, View view, int position, long id)
{
final ModuleWrapper item = adapter.getItem(position);
((MainMenuActivity)getActivity()).setModule(item.getUnderlyingFile().getAbsolutePath(), item.getText());
UserPreferences.updateConfigFile(getActivity());
dismiss();
}
};
}

View File

@ -7,45 +7,62 @@ import java.io.IOException;
import java.io.InputStreamReader;
import com.retroarch.R;
import com.retroarch.browser.mainmenu.MainMenuActivity;
import com.retroarch.browser.preferences.util.UserPreferences;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.ListFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
/**
* Represents the {@link ListActivity} responsible
* Represents the {@link ListFragment} responsible
* for displaying the list of previously played games.
*/
public final class HistorySelection extends ListActivity {
public final class HistorySelection extends DialogFragment
{
private FragmentActivity ctx;
private IconAdapter<HistoryWrapper> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Setup the layout.
setContentView(R.layout.line_list);
// Cache the context
this.ctx = getActivity();
}
// Setup the list
adapter = new IconAdapter<HistoryWrapper>(this, R.layout.line_list_item);
setListAdapter(adapter);
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ListView rootView = (ListView) inflater.inflate(R.layout.line_list, container, false);
rootView.setOnItemClickListener(onItemClickListener);
// Set activity title.
setTitle(R.string.recently_played_games);
// Set the title for this dialog.
getDialog().setTitle(R.string.load_game_history);
File history = new File(getApplicationInfo().dataDir, "retroarch-history.txt");
// Setup the list adapter
adapter = new IconAdapter<HistoryWrapper>(ctx, R.layout.line_list_item);
try {
// Populate the adapter
File history = new File(ctx.getApplicationInfo().dataDir, "retroarch-history.txt");
try
{
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream(history)));
for (;;) {
for (;;)
{
String game = br.readLine();
String core = br.readLine();
String name = br.readLine();
@ -55,30 +72,42 @@ public final class HistorySelection extends ListActivity {
adapter.add(new HistoryWrapper(game, core, name));
}
br.close();
} catch (IOException ex) {
}
catch (IOException ignored)
{
}
// Set the adapter
rootView.setAdapter(adapter);
return rootView;
}
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
final HistoryWrapper item = adapter.getItem(position);
final String gamePath = item.getGamePath();
final String corePath = item.getCorePath();
MainMenuActivity.getInstance().setModule(corePath, item.getCoreName());
String current_ime = Settings.Secure.getString(getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD);
UserPreferences.updateConfigFile(this);
Toast.makeText(this, String.format(getString(R.string.loading_gamepath), gamePath), Toast.LENGTH_SHORT).show();
Intent myIntent = new Intent(this, RetroActivity.class);
myIntent.putExtra("ROM", gamePath);
myIntent.putExtra("LIBRETRO", corePath);
myIntent.putExtra("CONFIGFILE", UserPreferences.getDefaultConfigPath(this));
myIntent.putExtra("IME", current_ime);
startActivity(myIntent);
finish();
}
private final OnItemClickListener onItemClickListener = new OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> listView, View view, int position, long id)
{
final HistoryWrapper item = adapter.getItem(position);
final String gamePath = item.getGamePath();
final String corePath = item.getCorePath();
// Set the core the selected game uses.
((MainMenuActivity)getActivity()).setModule(corePath, item.getCoreName());
// Update the config accordingly.
UserPreferences.updateConfigFile(ctx);
// Launch the game.
String current_ime = Settings.Secure.getString(ctx.getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD);
Toast.makeText(ctx, String.format(getString(R.string.loading_gamepath), gamePath), Toast.LENGTH_SHORT).show();
Intent myIntent = new Intent(ctx, RetroActivity.class);
myIntent.putExtra("ROM", gamePath);
myIntent.putExtra("LIBRETRO", corePath);
myIntent.putExtra("CONFIGFILE", UserPreferences.getDefaultConfigPath(ctx));
myIntent.putExtra("IME", current_ime);
startActivity(myIntent);
dismiss();
}
};
}

View File

@ -1,409 +0,0 @@
package com.retroarch.browser;
import java.io.*;
import com.retroarch.R;
import com.retroarch.browser.preferences.util.UserPreferences;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager.NameNotFoundException;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceActivity;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
/**
* {@link PreferenceActivity} subclass that provides all of the
* functionality of the main menu screen.
*/
public final class MainMenuActivity extends PreferenceActivity {
private static MainMenuActivity instance = null;
private static final int ACTIVITY_LOAD_ROM = 0;
private static final int ACTIVITY_RETROARCH = 1;
private static final String TAG = "MainMenu";
private static String libretro_path;
private static String libretro_name;
private void showGPLWaiver() {
AlertDialog.Builder alert = new AlertDialog.Builder(this)
.setTitle(R.string.gpl_waiver)
.setMessage(R.string.gpl_waiver_desc)
.setPositiveButton("Keep", null)
.setNegativeButton("Remove non-GPL cores",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final File[] libs = new File(getApplicationInfo().dataDir, "/cores").listFiles();
for (final File lib : libs) {
ModuleWrapper module = new ModuleWrapper(getApplicationContext(), lib);
boolean gplv3 = module.getCoreLicense().equals("GPLv3");
boolean gplv2 = module.getCoreLicense().equals("GPLv2");
if (!gplv3 && !gplv2) {
String libName = lib.getName();
Log.i("GPL WAIVER", "Deleting non-GPL core" + libName + "...");
lib.delete();
}
}
}
});
alert.show();
}
@Override
@SuppressWarnings("deprecation")
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the main menu XML.
addPreferencesFromResource(R.xml.main_menu);
// Cache an instance of this class (TODO: Bad practice, kill this somehow).
instance = this;
// Get libretro path and name.
SharedPreferences prefs = UserPreferences.getPreferences(this);
libretro_path = prefs.getString("libretro_path", getApplicationInfo().dataDir + "/cores");
libretro_name = prefs.getString("libretro_name", getString(R.string.no_core));
// Bind audio stream to hardware controls.
setVolumeControlStream(AudioManager.STREAM_MUSIC);
// Extract assets.
extractAssets();
if (!prefs.getBoolean("first_time_refreshrate_calculate", false)) {
prefs.edit().putBoolean("first_time_refreshrate_calculate", true).commit();
if (!detectDevice(false)) {
AlertDialog.Builder alert = new AlertDialog.Builder(this)
.setTitle(R.string.welcome_to_retroarch)
.setMessage(R.string.welcome_to_retroarch_desc)
.setPositiveButton("OK", null);
alert.show();
}
showGPLWaiver();
}
Intent startedByIntent = getIntent();
if (startedByIntent.getStringExtra("ROM") != null && startedByIntent.getStringExtra("LIBRETRO") != null) {
if (savedInstanceState == null || !savedInstanceState.getBoolean("romexec"))
loadRomExternal(startedByIntent.getStringExtra("ROM"),
startedByIntent.getStringExtra("LIBRETRO"));
else
finish();
}
}
public static MainMenuActivity getInstance() {
return instance;
}
private int getVersionCode() {
int version = 0;
try {
version = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
} catch (NameNotFoundException e) {
}
return version;
}
private boolean areAssetsExtracted() {
int version = getVersionCode();
try {
String dataDir = getApplicationInfo().dataDir;
File cacheVersion = new File(dataDir, ".cacheversion");
if (cacheVersion.isFile() && cacheVersion.canRead() && cacheVersion.canWrite()) {
DataInputStream cacheStream = new DataInputStream(
new FileInputStream(cacheVersion));
int currentCacheVersion = 0;
try {
currentCacheVersion = cacheStream.readInt();
} catch (IOException e) {
}
cacheStream.close();
if (currentCacheVersion == version) {
Log.i("ASSETS", "Assets already extracted, skipping...");
return true;
}
}
} catch (IOException e) {
Log.e(TAG, "Failed to extract assets to cache.");
return false;
}
return false;
}
// Extract assets from native code. Doing it from Java side is apparently unbearably slow ...
private void extractAssetsThread() {
try {
String dataDir = getApplicationInfo().dataDir;
String apk = getApplicationInfo().sourceDir;
Log.i(TAG, "Extracting RetroArch assets from: " + apk + " ...");
boolean success = NativeInterface.extractArchiveTo(apk, "assets", dataDir);
if (!success) {
throw new IOException("Failed to extract assets ...");
}
Log.i(TAG, "Extracted assets ...");
File cacheVersion = new File(dataDir, ".cacheversion");
DataOutputStream outputCacheVersion = new DataOutputStream(
new FileOutputStream(cacheVersion, false));
outputCacheVersion.writeInt(getVersionCode());
outputCacheVersion.close();
} catch (IOException e) {
Log.e(TAG, "Failed to extract assets to cache.");
}
}
private void extractAssets() {
if (areAssetsExtracted())
return;
final Dialog dialog = new Dialog(this);
final Handler handler = new Handler();
dialog.setContentView(R.layout.assets);
dialog.setCancelable(false);
dialog.setTitle("Asset extraction");
// Java is fun :)
Thread assetsThread = new Thread(new Runnable() {
public void run() {
extractAssetsThread();
handler.post(new Runnable() {
public void run() {
dialog.dismiss();
}
});
}
});
assetsThread.start();
dialog.show();
}
public void setModule(String core_path, String core_name) {
UserPreferences.updateConfigFile(this);
libretro_path = core_path;
libretro_name = core_name;
SharedPreferences prefs = UserPreferences.getPreferences(this);
SharedPreferences.Editor edit = prefs.edit();
edit.putString("libretro_path", libretro_path);
edit.putString("libretro_name", libretro_name);
edit.commit();
// Set the title section to contain the name of the selected core.
setCoreTitle(libretro_name);
}
public void setCoreTitle(String core_name) {
setTitle("RetroArch : " + core_name);
}
private boolean detectDevice(boolean show_dialog) {
boolean retval = false;
final Context ctx = this;
final boolean mentionPlayStore = !Build.MODEL.equals("OUYA Console");
final String message = (mentionPlayStore ? getString(R.string.detect_device_msg_general) : getString(R.string.detect_device_msg_ouya));
Log.i("Device MODEL", Build.MODEL);
if (Build.MODEL.equals("SHIELD")) {
AlertDialog.Builder alert = new AlertDialog.Builder(this)
.setTitle(R.string.nvidia_shield_detected)
.setMessage(message)
.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SharedPreferences prefs = UserPreferences.getPreferences(MainMenuActivity.this);
SharedPreferences.Editor edit = prefs.edit();
edit.putString("video_refresh_rate", Double.toString(60.00d));
edit.putBoolean("input_overlay_enable", false);
edit.putBoolean("input_autodetect_enable", true);
edit.putString("audio_latency", "64");
edit.putBoolean("audio_latency_auto", true);
edit.commit();
UserPreferences.updateConfigFile(ctx);
}
});
alert.show();
retval = true;
} else if (Build.MODEL.equals("GAMEMID_BT")) {
AlertDialog.Builder alert = new AlertDialog.Builder(this)
.setTitle(R.string.game_mid_detected)
.setMessage(message)
.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SharedPreferences prefs = UserPreferences.getPreferences(MainMenuActivity.this);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean("input_overlay_enable", false);
edit.putBoolean("input_autodetect_enable", true);
edit.putString("audio_latency", "160");
edit.putBoolean("audio_latency_auto", false);
edit.commit();
UserPreferences.updateConfigFile(ctx);
}
});
alert.show();
retval = true;
} else if (Build.MODEL.equals("OUYA Console")) {
AlertDialog.Builder alert = new AlertDialog.Builder(this)
.setTitle(R.string.ouya_detected)
.setMessage(message)
.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SharedPreferences prefs = UserPreferences.getPreferences(MainMenuActivity.this);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean("input_overlay_enable", false);
edit.putBoolean("input_autodetect_enable", true);
edit.putString("audio_latency", "64");
edit.putBoolean("audio_latency_auto", true);
edit.commit();
UserPreferences.updateConfigFile(ctx);
}
});
alert.show();
retval = true;
} else if (Build.MODEL.equals("R800x")) {
AlertDialog.Builder alert = new AlertDialog.Builder(this)
.setTitle(R.string.xperia_play_detected)
.setMessage(message)
.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SharedPreferences prefs = UserPreferences.getPreferences(MainMenuActivity.this);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean("video_threaded", false);
edit.putBoolean("input_overlay_enable", false);
edit.putBoolean("input_autodetect_enable", true);
edit.putString("video_refresh_rate", Double.toString(59.19132938771038));
edit.putString("audio_latency", "128");
edit.putBoolean("audio_latency_auto", false);
edit.commit();
UserPreferences.updateConfigFile(ctx);
}
});
alert.show();
retval = true;
} else if (Build.ID.equals("JSS15J")) {
AlertDialog.Builder alert = new AlertDialog.Builder(this)
.setTitle(R.string.nexus_7_2013_detected)
.setMessage(message)
.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SharedPreferences prefs = UserPreferences.getPreferences(MainMenuActivity.this);
SharedPreferences.Editor edit = prefs.edit();
edit.putString("video_refresh_rate", Double.toString(59.65));
edit.putString("audio_latency", "64");
edit.putBoolean("audio_latency_auto", false);
edit.commit();
UserPreferences.updateConfigFile(ctx);
}
});
alert.show();
retval = true;
}
if (show_dialog) {
Toast.makeText(this, R.string.no_optimal_settings, Toast.LENGTH_SHORT).show();
}
return retval;
}
@Override
public void startActivity(Intent intent) {
if (intent.getComponent().getClassName()
.equals("com.retroarch.browser.diractivities.ROMActivity")) {
if (!new File(libretro_path).isDirectory()) {
super.startActivityForResult(intent, ACTIVITY_LOAD_ROM);
} else {
Toast.makeText(this, R.string.load_a_core_first, Toast.LENGTH_SHORT).show();
}
} else {
super.startActivity(intent);
}
}
@Override
protected void onActivityResult(int reqCode, int resCode, Intent data) {
switch (reqCode) {
case ACTIVITY_LOAD_ROM: {
if (data.getStringExtra("PATH") != null) {
UserPreferences.updateConfigFile(this);
String current_ime = Settings.Secure.getString(getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
Toast.makeText(this,String.format(getString(R.string.loading_data), data.getStringExtra("PATH")), Toast.LENGTH_SHORT).show();
Intent myIntent = new Intent(this, RetroActivity.class);
myIntent.putExtra("ROM", data.getStringExtra("PATH"));
myIntent.putExtra("LIBRETRO", libretro_path);
myIntent.putExtra("CONFIGFILE", UserPreferences.getDefaultConfigPath(this));
myIntent.putExtra("IME", current_ime);
startActivityForResult(myIntent, ACTIVITY_RETROARCH);
}
break;
}
case ACTIVITY_RETROARCH: {
Log.i(TAG, "RetroArch finished running.");
UserPreferences.readbackConfigFile(this);
break;
}
}
}
@Override
protected void onSaveInstanceState(Bundle data) {
super.onSaveInstanceState(data);
data.putCharSequence("title", getTitle());
data.putBoolean("romexec", true);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
if (state != null) {
setTitle(state.getCharSequence("title"));
}
}
private void loadRomExternal(String rom, String core) {
UserPreferences.updateConfigFile(this);
String current_ime = Settings.Secure.getString(getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
Toast.makeText(this, String.format(getString(R.string.loading_data), rom), Toast.LENGTH_SHORT).show();
Intent myIntent = new Intent(this, RetroActivity.class);
myIntent.putExtra("ROM", rom);
myIntent.putExtra("LIBRETRO", core);
myIntent.putExtra("CONFIGFILE", UserPreferences.getDefaultConfigPath(this));
myIntent.putExtra("IME", current_ime);
startActivity(myIntent);
}
}

View File

@ -1,20 +1,24 @@
package com.retroarch.browser;
import com.retroarch.browser.preferences.util.UserPreferences;
import android.app.NativeActivity;
import android.os.Bundle;
public final class RetroActivity extends NativeActivity {
public final class RetroActivity extends NativeActivity
{
@Override
public void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
public void onDestroy()
{
UserPreferences.readbackConfigFile(this);
}
@Override
public void onLowMemory() {
public void onLowMemory()
{
}
@Override
public void onTrimMemory(int level) {
public void onTrimMemory(int level)
{
}
}

View File

@ -6,51 +6,37 @@ import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
/**
* The {@link Activity} derivative responsible for displaying the TV Mode feature.
*/
public final class RetroTVMode extends Activity {
private static final String TAG = "RetroTVMode";
private static final int ACTIVITY_RETROARCH = 1;
public final class RetroTVMode extends Activity
{
// Need to do this wonky logic as we have to keep this activity alive until
// RetroArch is done running.
// Have to readback config right after RetroArch has run to avoid potential
// broken config state.
@Override
public void onCreate(Bundle savedInstanceState) {
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState == null
|| !savedInstanceState.getBoolean("started", false)) {
if (savedInstanceState == null || !savedInstanceState.getBoolean("started", false))
{
UserPreferences.updateConfigFile(this);
Intent myIntent = new Intent(this, RetroActivity.class);
String current_ime = Settings.Secure.getString(
getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
myIntent.putExtra("CONFIGFILE",
UserPreferences.getDefaultConfigPath(this));
String current_ime = Settings.Secure.getString(getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
myIntent.putExtra("CONFIGFILE", UserPreferences.getDefaultConfigPath(this));
myIntent.putExtra("IME", current_ime);
startActivityForResult(myIntent, ACTIVITY_RETROARCH);
}
}
@Override
protected void onActivityResult(int reqCode, int resCode, Intent data) {
switch (reqCode) {
case ACTIVITY_RETROARCH: {
Log.i(TAG, "RetroArch finished running.");
UserPreferences.readbackConfigFile(this);
startActivity(myIntent);
finish();
break;
}
}
}
@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
protected void onSaveInstanceState(Bundle savedInstanceState)
{
savedInstanceState.putBoolean("started", true);
}
}

View File

@ -1,271 +0,0 @@
package com.retroarch.browser.diractivities;
import com.retroarch.R;
import com.retroarch.browser.FileWrapper;
import com.retroarch.browser.IconAdapter;
import com.retroarch.browser.preferences.util.UserPreferences;
import java.util.*;
import java.io.*;
import android.content.*;
import android.app.*;
import android.media.AudioManager;
import android.os.*;
import android.widget.*;
import android.view.*;
/**
* {@link ListActivity} subclass that provides a file-browser
* like UI for browsing for specific files.
* <p>
* This file browser also allows for custom filtering
* depending on the type of class that inherits it.
* <p>
* This file browser also uses an implementation of a
* backstack for remembering previously browsed folders
* within this DirectoryActivity.
*/
public class DirectoryActivity extends ListActivity {
private IconAdapter<FileWrapper> adapter;
private File listedDirectory;
public static class BackStackItem implements Parcelable {
private final String path;
private final boolean parentIsBack;
public BackStackItem(String path, boolean parentIsBack) {
this.path = path;
this.parentIsBack = parentIsBack;
}
private BackStackItem(Parcel in) {
this.path = in.readString();
this.parentIsBack = in.readInt() != 0;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeString(path);
out.writeInt(parentIsBack ? 1 : 0);
}
public static final Parcelable.Creator<BackStackItem> CREATOR = new Parcelable.Creator<BackStackItem>() {
public BackStackItem createFromParcel(Parcel in) {
return new BackStackItem(in);
}
public BackStackItem[] newArray(int size) {
return new BackStackItem[size];
}
};
}
private ArrayList<BackStackItem> backStack;
protected String startDirectory;
protected String pathSettingKey;
protected void setStartDirectory(String path) {
startDirectory = path;
}
protected void setPathSettingKey(String key) {
pathSettingKey = key;
}
private boolean isDirectoryTarget;
protected void setIsDirectoryTarget(boolean enable) {
isDirectoryTarget = enable;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.line_list);
// Setup the list
adapter = new IconAdapter<FileWrapper>(this, R.layout.line_list_item);
setListAdapter(adapter);
// Load Directory
if (savedInstanceState != null) {
backStack = savedInstanceState.getParcelableArrayList("BACKSTACK");
}
if (backStack == null || backStack.isEmpty()) {
backStack = new ArrayList<BackStackItem>();
String startPath = (startDirectory == null || startDirectory.isEmpty()) ? Environment
.getExternalStorageDirectory().getPath() : startDirectory;
backStack.add(new BackStackItem(startPath, false));
}
wrapFiles();
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
@Override
protected void onSaveInstanceState(Bundle aState) {
super.onSaveInstanceState(aState);
aState.putParcelableArrayList("BACKSTACK", backStack);
}
private void finishWithPath(String path) {
if (pathSettingKey != null && !pathSettingKey.isEmpty()) {
SharedPreferences settings = UserPreferences.getPreferences(this);
SharedPreferences.Editor editor = settings.edit();
editor.putString(pathSettingKey, path);
editor.commit();
}
Intent intent = new Intent();
intent.putExtra("PATH", path);
setResult(RESULT_OK, intent);
finish();
}
@Override
public void onListItemClick(ListView listView, View aView, int position, long id) {
final FileWrapper item = adapter.getItem(position);
if (item.isParentItem() && backStack.get(backStack.size() - 1).parentIsBack) {
backStack.remove(backStack.size() - 1);
wrapFiles();
return;
} else if (item.isDirSelectItem()) {
finishWithPath(listedDirectory.getAbsolutePath());
return;
}
final File selected = item.isParentItem() ? listedDirectory.getParentFile() : item.getFile();
if (selected.isDirectory()) {
backStack.add(new BackStackItem(selected.getAbsolutePath(),
!item.isParentItem()));
wrapFiles();
} else {
String filePath = selected.getAbsolutePath();
finishWithPath(filePath);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (backStack.size() > 1) {
backStack.remove(backStack.size() - 1);
wrapFiles();
} else {
Intent intent = new Intent();
setResult(RESULT_CANCELED, intent);
finish();
}
return true;
}
return super.onKeyDown(keyCode, event);
}
private ArrayList<String> allowedExt;
private ArrayList<String> disallowedExt;
private boolean filterPath(String path) {
if (disallowedExt != null) {
for (String ext : disallowedExt)
if (path.endsWith(ext))
return false;
}
if (allowedExt != null) {
for (String ext : allowedExt)
if (path.endsWith(ext))
return true;
return false;
}
return true;
}
/**
* Allows specifying an allowed file extension.
* <p>
* Any files that contain this file extension will be shown
* within the DirectoryActivity file browser. Those that don't
* contain this extension will not be shows.
* <p>
* It is possible to specify more than one allowed extension by
* simply calling this method with a different file extension specified.
*
* @param ext The file extension to allow being shown in this DirectoryActivity.
*/
protected void addAllowedExt(String ext) {
if (allowedExt == null)
allowedExt = new ArrayList<String>();
allowedExt.add(ext);
}
/**
* Allows specifying a disallowed file extension.
* <p>
* Any files that contain this file extension will not be shown
* within the DirectoryActivity file browser.
* <p>
* It is possible to specify more than one disallowed extension by
* simply calling this method with a different file extension specified.
*
* @param ext The file extension to hide from being shown in this DirectoryActivity.
*/
protected void addDisallowedExt(String ext) {
if (disallowedExt == null)
disallowedExt = new ArrayList<String>();
disallowedExt.add(ext);
}
private void wrapFiles() {
listedDirectory = new File(backStack.get(backStack.size() - 1).path);
if (!listedDirectory.isDirectory()) {
throw new IllegalArgumentException("Directory is not valid.");
}
adapter.clear();
setTitle(listedDirectory.getAbsolutePath());
if (isDirectoryTarget)
adapter.add(new FileWrapper(null, FileWrapper.DIRSELECT, true));
if (listedDirectory.getParentFile() != null)
adapter.add(new FileWrapper(null, FileWrapper.PARENT, true));
// Copy new items
final File[] files = listedDirectory.listFiles();
if (files != null) {
for (File file : files) {
String path = file.getName();
boolean allowFile = file.isDirectory() || (filterPath(path) && !isDirectoryTarget);
if (allowFile)
adapter.add(new FileWrapper(file, FileWrapper.FILE, true));
}
}
// Sort items
adapter.sort(new Comparator<FileWrapper>() {
@Override
public int compare(FileWrapper left, FileWrapper right) {
return left.compareTo(right);
};
});
// Update
adapter.notifyDataSetChanged();
}
}

View File

@ -1,24 +0,0 @@
package com.retroarch.browser.diractivities;
import java.io.File;
import android.os.Bundle;
/**
* {@link DirectoryActivity} subclass used for the sole
* purpose of navigating the Android filesystem for input overlays.
* @author Lioncash-yay
*
*/
public final class OverlayActivity extends DirectoryActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
File overlayDir = new File(getApplicationInfo().dataDir, "overlays");
if (overlayDir.exists())
super.setStartDirectory(overlayDir.getAbsolutePath());
super.addAllowedExt(".cfg");
super.setPathSettingKey("input_overlay");
super.onCreate(savedInstanceState);
}
}

View File

@ -1,30 +0,0 @@
package com.retroarch.browser.diractivities;
import java.io.File;
import com.retroarch.browser.preferences.util.UserPreferences;
import android.content.SharedPreferences;
import android.os.Bundle;
/**
* {@link DirectoryActivity} subclass used for the sole
* purpose of navigating the Android filesystem for selecting
* a ROM file to execute during emulation.
*/
public final class ROMActivity extends DirectoryActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
SharedPreferences prefs = UserPreferences.getPreferences(this);
String startPath = prefs.getString("rgui_browser_directory", "");
if (!startPath.isEmpty() && new File(startPath).exists())
super.setStartDirectory(startPath);
super.addDisallowedExt(".state");
super.addDisallowedExt(".srm");
super.addDisallowedExt(".state.auto");
super.addDisallowedExt(".rtc");
super.onCreate(savedInstanceState);
}
}

View File

@ -1,17 +0,0 @@
package com.retroarch.browser.diractivities;
import android.os.Bundle;
/**
* {@link DirectoryActivity} subclass used for the sole
* purpose of navigating the Android filesystem to select
* a custom ROM directory.
*/
public final class ROMDirActivity extends DirectoryActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.setPathSettingKey("rgui_browser_directory");
super.setIsDirectoryTarget(true);
super.onCreate(savedInstanceState);
}
}

View File

@ -1,17 +0,0 @@
package com.retroarch.browser.diractivities;
import android.os.Bundle;
/**
* {@link DirectoryActivity} subclass used for the sole
* purpose of navigating the Android filesystem for selecting
* a custom save file directory.
*/
public final class SRMDirActivity extends DirectoryActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.setPathSettingKey("savefile_directory");
super.setIsDirectoryTarget(true);
super.onCreate(savedInstanceState);
}
}

View File

@ -1,23 +0,0 @@
package com.retroarch.browser.diractivities;
import java.io.File;
import android.os.Bundle;
/**
* {@link DirectoryActivity} subclass used for the sole
* purpose of navigating the Android filesystem for selecting
* a shader to use during emulation.
*/
public final class ShaderActivity extends DirectoryActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
File shaderDir = new File(getApplicationInfo().dataDir, "shaders_glsl");
if (shaderDir.exists())
super.setStartDirectory(shaderDir.getAbsolutePath());
super.addAllowedExt(".glsl");
super.setPathSettingKey("video_shader");
super.onCreate(savedInstanceState);
}
}

View File

@ -1,17 +0,0 @@
package com.retroarch.browser.diractivities;
import android.os.Bundle;
/**
* {@link DirectoryActivity} subclass used for the sole
* purpose of navigating the Android filesystem to select
* a custom save state directory.
*/
public final class StateDirActivity extends DirectoryActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.setPathSettingKey("savestate_directory");
super.setIsDirectoryTarget(true);
super.onCreate(savedInstanceState);
}
}

View File

@ -1,19 +0,0 @@
package com.retroarch.browser.diractivities;
import android.os.Bundle;
/**
* {@link DirectoryActivity} subclass used for the sole
* purpose of navigating the Android filesystem for selecting
* a custom 'System' directory that the cores will look in for
* required files, such as BIOS files and other miscellaneous
* system-required files.
*/
public final class SystemDirActivity extends DirectoryActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.setPathSettingKey("system_directory");
super.setIsDirectoryTarget(true);
super.onCreate(savedInstanceState);
}
}

View File

@ -0,0 +1,371 @@
package com.retroarch.browser.dirfragment;
import android.content.SharedPreferences;
import android.os.Bundle;
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;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import com.retroarch.R;
import com.retroarch.browser.FileWrapper;
import com.retroarch.browser.IconAdapter;
import com.retroarch.browser.preferences.util.UserPreferences;
import java.util.*;
import java.io.*;
/**
* {@link ListFragment} subclass that provides a file-browser
* like UI for browsing for specific files.
* <p>
* This file browser also allows for custom filtering
* depending on the type of class that inherits it.
* <p>
* This file browser also uses an implementation of a
* backstack for remembering previously browsed folders
* within this DirectoryFragment.
* <p>
* To instantiate a new instance of this class
* you must use the {@code newInstance} method.
*/
public final class DirectoryFragment extends DialogFragment
{
private IconAdapter<FileWrapper> adapter;
private File listedDirectory;
public static final class BackStackItem implements Parcelable
{
private final String path;
private final boolean parentIsBack;
public BackStackItem(String path, boolean parentIsBack)
{
this.path = path;
this.parentIsBack = parentIsBack;
}
private BackStackItem(Parcel in)
{
this.path = in.readString();
this.parentIsBack = in.readInt() != 0;
}
public int describeContents()
{
return 0;
}
public void writeToParcel(Parcel out, int flags)
{
out.writeString(path);
out.writeInt(parentIsBack ? 1 : 0);
}
public static final Parcelable.Creator<BackStackItem> CREATOR = new Parcelable.Creator<BackStackItem>()
{
public BackStackItem createFromParcel(Parcel in)
{
return new BackStackItem(in);
}
public BackStackItem[] newArray(int size)
{
return new BackStackItem[size];
}
};
}
/**
* Listener interface for executing ROMs or performing
* other things upon the DirectoryFragment instance closing.
*/
public interface OnDirectoryFragmentClosedListener
{
/**
* Performs some arbitrary action after the
* {@link DirectoryFragment} closes.
*
* @param path The path to the file chosen within the {@link DirectoryFragment}
*/
void onDirectoryFragmentClosed(String path);
}
private ArrayList<BackStackItem> backStack;
private String startDirectory;
private String pathSettingKey;
public void setStartDirectory(String path)
{
startDirectory = path;
}
public void setPathSettingKey(String key)
{
pathSettingKey = key;
}
private boolean isDirectoryTarget;
public void setIsDirectoryTarget(boolean enable)
{
isDirectoryTarget = enable;
}
private OnDirectoryFragmentClosedListener onClosedListener;
public void setOnDirectoryFragmentClosedListener(OnDirectoryFragmentClosedListener onClosedListener)
{
this.onClosedListener = onClosedListener;
}
/**
* Retrieves a new instance of a DirectoryFragment
* with a title specified by the given resource ID.
*
* @param titleResId String resource ID for the title
* of this DirectoryFragment.
*
* @return A new instance of a DirectoryFragment.
*/
public static DirectoryFragment newInstance(int titleResId)
{
final DirectoryFragment dFrag = new DirectoryFragment();
final Bundle bundle = new Bundle();
bundle.putInt("titleResId", titleResId);
dFrag.setArguments(bundle);
return dFrag;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ListView rootView = (ListView) inflater.inflate(R.layout.line_list, container, false);
rootView.setOnItemClickListener(onItemClickListener);
// Set the dialog title.
getDialog().setTitle(getArguments().getInt("titleResId"));
// Setup the list
adapter = new IconAdapter<FileWrapper>(getActivity(), R.layout.line_list_item);
rootView.setAdapter(adapter);
// Load Directory
if (savedInstanceState != null)
{
backStack = savedInstanceState.getParcelableArrayList("BACKSTACK");
}
if (backStack == null || backStack.isEmpty())
{
backStack = new ArrayList<BackStackItem>();
String startPath = (startDirectory == null || startDirectory.isEmpty()) ? Environment
.getExternalStorageDirectory().getPath() : startDirectory;
backStack.add(new BackStackItem(startPath, false));
}
wrapFiles();
return rootView;
}
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 (item.isParentItem() && backStack.get(backStack.size() - 1).parentIsBack)
{
backStack.remove(backStack.size() - 1);
wrapFiles();
return;
}
else if (item.isDirSelectItem())
{
finishWithPath(listedDirectory.getAbsolutePath());
return;
}
final File selected = item.isParentItem() ? listedDirectory.getParentFile() : item.getFile();
if (selected.isDirectory())
{
backStack.add(new BackStackItem(selected.getAbsolutePath(), !item.isParentItem()));
wrapFiles();
}
else
{
String filePath = selected.getAbsolutePath();
finishWithPath(filePath);
}
}
};
@Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putParcelableArrayList("BACKSTACK", backStack);
}
private void finishWithPath(String path)
{
if (pathSettingKey != null && !pathSettingKey.isEmpty())
{
SharedPreferences settings = UserPreferences.getPreferences(getActivity());
SharedPreferences.Editor editor = settings.edit();
editor.putString(pathSettingKey, path);
editor.commit();
}
if (onClosedListener != null)
{
onClosedListener.onDirectoryFragmentClosed(path);
}
dismiss();
}
// TODO: Hook this up to a callable interface (if backstack is desirable).
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_BACK)
{
if (backStack.size() > 1)
{
backStack.remove(backStack.size() - 1);
wrapFiles();
}
return true;
}
return false;
}
private ArrayList<String> allowedExt;
private ArrayList<String> disallowedExt;
private boolean filterPath(String path)
{
if (disallowedExt != null)
{
for (String ext : disallowedExt)
{
if (path.endsWith(ext))
return false;
}
}
if (allowedExt != null)
{
for (String ext : allowedExt)
{
if (path.endsWith(ext))
return true;
}
return false;
}
return true;
}
/**
* Allows specifying an allowed file extension.
* <p>
* Any files that contain this file extension will be shown
* within the DirectoryFragment file browser. Those that don't
* contain this extension will not be shows.
* <p>
* It is possible to specify more than one allowed extension by
* simply calling this method with a different file extension specified.
*
* @param ext The file extension(s) to allow being shown in this DirectoryFragment.
*/
public void addAllowedExts(String... exts)
{
if (allowedExt == null)
allowedExt = new ArrayList<String>();
allowedExt.addAll(Arrays.asList(exts));
}
/**
* Allows specifying a disallowed file extension.
* <p>
* Any files that contain this file extension will not be shown
* within the DirectoryFragment file browser.
* <p>
* It is possible to specify more than one disallowed extension by
* simply calling this method with a different file extension specified.
*
* @param exts The file extension(s) to hide from being shown in this DirectoryFragment.
*/
public void addDisallowedExts(String... exts)
{
if (disallowedExt == null)
disallowedExt = new ArrayList<String>();
disallowedExt.addAll(Arrays.asList(exts));
}
private void wrapFiles()
{
listedDirectory = new File(backStack.get(backStack.size() - 1).path);
if (!listedDirectory.isDirectory())
{
throw new IllegalArgumentException("Directory is not valid.");
}
adapter.clear();
if (isDirectoryTarget)
adapter.add(new FileWrapper(null, FileWrapper.DIRSELECT, true));
if (listedDirectory.getParentFile() != null)
adapter.add(new FileWrapper(null, FileWrapper.PARENT, true));
// Copy new items
final File[] files = listedDirectory.listFiles();
if (files != null)
{
for (File file : files)
{
String path = file.getName();
boolean allowFile = file.isDirectory() || (filterPath(path) && !isDirectoryTarget);
if (allowFile)
{
adapter.add(new FileWrapper(file, FileWrapper.FILE, true));
}
}
}
// Sort items
adapter.sort(new Comparator<FileWrapper>()
{
@Override
public int compare(FileWrapper left, FileWrapper right)
{
return left.compareTo(right);
};
});
// Update
adapter.notifyDataSetChanged();
}
}

View File

@ -0,0 +1,74 @@
package com.retroarch.browser.mainmenu;
import com.retroarch.R;
import com.retroarch.browser.preferences.util.UserPreferences;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
/**
* {@link PreferenceActivity} subclass that provides all of the
* functionality of the main menu screen.
*/
public final class MainMenuActivity extends FragmentActivity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Load the main menu layout
setContentView(R.layout.mainmenu_activity_layout);
if (savedInstanceState == null)
{
final MainMenuFragment mmf = new MainMenuFragment();
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
// Add the base main menu fragment to the content view.
ft.replace(R.id.content_frame, mmf);
ft.commit();
}
// Bind audio stream to hardware controls.
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
public void setModule(String core_path, String core_name)
{
UserPreferences.updateConfigFile(this);
SharedPreferences prefs = UserPreferences.getPreferences(this);
SharedPreferences.Editor edit = prefs.edit();
edit.putString("libretro_path", core_path);
edit.putString("libretro_name", core_name);
edit.commit();
// Set the title section to contain the name of the selected core.
setCoreTitle(core_name);
}
public void setCoreTitle(String core_name)
{
setTitle("RetroArch : " + core_name);
}
@Override
protected void onSaveInstanceState(Bundle data)
{
super.onSaveInstanceState(data);
data.putCharSequence("title", getTitle());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
setTitle(savedInstanceState.getCharSequence("title"));
}
}

View File

@ -0,0 +1,416 @@
package com.retroarch.browser.mainmenu;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
import com.retroarch.R;
import com.retroarch.browser.CoreSelection;
import com.retroarch.browser.HistorySelection;
import com.retroarch.browser.ModuleWrapper;
import com.retroarch.browser.NativeInterface;
import com.retroarch.browser.RetroActivity;
import com.retroarch.browser.dirfragment.DirectoryFragment;
import com.retroarch.browser.dirfragment.DirectoryFragment.OnDirectoryFragmentClosedListener;
import com.retroarch.browser.preferences.fragments.util.PreferenceListFragment;
import com.retroarch.browser.preferences.util.UserPreferences;
/**
* Represents the fragment that handles the layout of the main menu.
*/
public final class MainMenuFragment extends PreferenceListFragment implements OnPreferenceClickListener, OnDirectoryFragmentClosedListener
{
private static final String TAG = "MainMenuFragment";
private Context ctx;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Cache the context
this.ctx = getActivity();
// Add the layout through the XML.
addPreferencesFromResource(R.xml.main_menu);
// Set the listeners for the menu items
findPreference("loadCorePref").setOnPreferenceClickListener(this);
findPreference("loadRomPref").setOnPreferenceClickListener(this);
findPreference("loadRomHistoryPref").setOnPreferenceClickListener(this);
// Extract assets.
extractAssets();
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
if (!prefs.getBoolean("first_time_refreshrate_calculate", false))
{
prefs.edit().putBoolean("first_time_refreshrate_calculate", true).commit();
if (!detectDevice(false))
{
AlertDialog.Builder alert = new AlertDialog.Builder(ctx)
.setTitle(R.string.welcome_to_retroarch)
.setMessage(R.string.welcome_to_retroarch_desc)
.setPositiveButton(R.string.ok, null);
alert.show();
}
showGPLWaiver();
}
}
private void showGPLWaiver()
{
AlertDialog.Builder alert = new AlertDialog.Builder(getActivity())
.setTitle(R.string.gpl_waiver)
.setMessage(R.string.gpl_waiver_desc)
.setPositiveButton(R.string.keep_cores, null)
.setNegativeButton(R.string.remove_cores, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
final File[] libs = new File(getActivity().getApplicationInfo().dataDir, "/cores").listFiles();
for (final File lib : libs)
{
ModuleWrapper module = new ModuleWrapper(getActivity().getApplicationContext(), lib);
boolean gplv3 = module.getCoreLicense().equals("GPLv3");
boolean gplv2 = module.getCoreLicense().equals("GPLv2");
if (!gplv3 && !gplv2)
{
String libName = lib.getName();
Log.i("GPL WAIVER", "Deleting non-GPL core" + libName + "...");
lib.delete();
}
}
}
});
alert.show();
}
private void extractAssets()
{
if (areAssetsExtracted())
return;
final Dialog dialog = new Dialog(ctx);
final Handler handler = new Handler();
dialog.setContentView(R.layout.assets);
dialog.setCancelable(false);
dialog.setTitle(R.string.asset_extraction);
// Java is fun :)
Thread assetsThread = new Thread(new Runnable()
{
public void run()
{
extractAssetsThread();
handler.post(new Runnable()
{
public void run()
{
dialog.dismiss();
}
});
}
});
assetsThread.start();
dialog.show();
}
// Extract assets from native code. Doing it from Java side is apparently unbearably slow ...
private void extractAssetsThread()
{
try
{
final String dataDir = ctx.getApplicationInfo().dataDir;
final String apk = ctx.getApplicationInfo().sourceDir;
Log.i(TAG, "Extracting RetroArch assets from: " + apk + " ...");
boolean success = NativeInterface.extractArchiveTo(apk, "assets", dataDir);
if (!success) {
throw new IOException("Failed to extract assets ...");
}
Log.i(TAG, "Extracted assets ...");
File cacheVersion = new File(dataDir, ".cacheversion");
DataOutputStream outputCacheVersion = new DataOutputStream(new FileOutputStream(cacheVersion, false));
outputCacheVersion.writeInt(getVersionCode());
outputCacheVersion.close();
}
catch (IOException e)
{
Log.e(TAG, "Failed to extract assets to cache.");
}
}
private boolean areAssetsExtracted()
{
int version = getVersionCode();
try
{
String dataDir = ctx.getApplicationInfo().dataDir;
File cacheVersion = new File(dataDir, ".cacheversion");
if (cacheVersion.isFile() && cacheVersion.canRead() && cacheVersion.canWrite())
{
DataInputStream cacheStream = new DataInputStream(new FileInputStream(cacheVersion));
int currentCacheVersion = 0;
try
{
currentCacheVersion = cacheStream.readInt();
cacheStream.close();
}
catch (IOException ignored)
{
}
if (currentCacheVersion == version)
{
Log.i("ASSETS", "Assets already extracted, skipping...");
return true;
}
}
}
catch (IOException e)
{
Log.e(TAG, "Failed to extract assets to cache.");
return false;
}
return false;
}
private int getVersionCode()
{
int version = 0;
try
{
version = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0).versionCode;
}
catch (NameNotFoundException ignored)
{
}
return version;
}
private boolean detectDevice(boolean show_dialog)
{
boolean retval = false;
final boolean mentionPlayStore = !Build.MODEL.equals("OUYA Console");
final int messageId = (mentionPlayStore ? R.string.detect_device_msg_general : R.string.detect_device_msg_ouya);
Log.i("Device MODEL", Build.MODEL);
if (Build.MODEL.equals("SHIELD"))
{
AlertDialog.Builder alert = new AlertDialog.Builder(ctx);
alert.setTitle(R.string.nvidia_shield_detected);
alert.setMessage(messageId);
alert.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
SharedPreferences prefs = UserPreferences.getPreferences(ctx);
SharedPreferences.Editor edit = prefs.edit();
edit.putString("video_refresh_rate", Double.toString(60.00d));
edit.putBoolean("input_overlay_enable", false);
edit.putBoolean("input_autodetect_enable", true);
edit.putString("audio_latency", "64");
edit.putBoolean("audio_latency_auto", true);
edit.commit();
UserPreferences.updateConfigFile(ctx);
}
});
alert.show();
retval = true;
}
else if (Build.MODEL.equals("GAMEMID_BT"))
{
AlertDialog.Builder alert = new AlertDialog.Builder(ctx);
alert.setTitle(R.string.game_mid_detected);
alert.setMessage(messageId);
alert.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
SharedPreferences prefs = UserPreferences.getPreferences(ctx);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean("input_overlay_enable", false);
edit.putBoolean("input_autodetect_enable", true);
edit.putString("audio_latency", "160");
edit.putBoolean("audio_latency_auto", false);
edit.commit();
UserPreferences.updateConfigFile(ctx);
}
});
alert.show();
retval = true;
}
else if (Build.MODEL.equals("OUYA Console"))
{
AlertDialog.Builder alert = new AlertDialog.Builder(ctx);
alert.setTitle(R.string.ouya_detected);
alert.setMessage(messageId);
alert.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
SharedPreferences prefs = UserPreferences.getPreferences(ctx);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean("input_overlay_enable", false);
edit.putBoolean("input_autodetect_enable", true);
edit.putString("audio_latency", "64");
edit.putBoolean("audio_latency_auto", true);
edit.commit();
UserPreferences.updateConfigFile(ctx);
}
});
alert.show();
retval = true;
}
else if (Build.MODEL.equals("R800x"))
{
AlertDialog.Builder alert = new AlertDialog.Builder(ctx);
alert.setTitle(R.string.xperia_play_detected);
alert.setMessage(messageId);
alert.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
SharedPreferences prefs = UserPreferences.getPreferences(ctx);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean("video_threaded", false);
edit.putBoolean("input_overlay_enable", false);
edit.putBoolean("input_autodetect_enable", true);
edit.putString("video_refresh_rate", Double.toString(59.19132938771038));
edit.putString("audio_latency", "128");
edit.putBoolean("audio_latency_auto", false);
edit.commit();
UserPreferences.updateConfigFile(ctx);
}
});
alert.show();
retval = true;
}
else if (Build.ID.equals("JSS15J"))
{
AlertDialog.Builder alert = new AlertDialog.Builder(ctx);
alert.setTitle(R.string.nexus_7_2013_detected);
alert.setMessage(messageId);
alert.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
SharedPreferences prefs = UserPreferences.getPreferences(ctx);
SharedPreferences.Editor edit = prefs.edit();
edit.putString("video_refresh_rate", Double.toString(59.65));
edit.putString("audio_latency", "64");
edit.putBoolean("audio_latency_auto", false);
edit.commit();
UserPreferences.updateConfigFile(ctx);
}
});
alert.show();
retval = true;
}
if (show_dialog)
{
Toast.makeText(ctx, R.string.no_optimal_settings, Toast.LENGTH_SHORT).show();
}
return retval;
}
@Override
public boolean onPreferenceClick(Preference preference)
{
final String prefKey = preference.getKey();
// Load Core Preference
if (prefKey.equals("loadCorePref"))
{
final CoreSelection coreSelection = new CoreSelection();
coreSelection.show(getFragmentManager(), "core_selection");
}
// Load ROM Preference
else if (prefKey.equals("loadRomPref"))
{
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 String startPath = prefs.getString("rgui_browser_directory", "");
if (!startPath.isEmpty() && new File(startPath).exists())
romBrowser.setStartDirectory(startPath);
romBrowser.show(getFragmentManager(), "romBrowser");
}
else
{
Toast.makeText(ctx, R.string.load_a_core_first, Toast.LENGTH_SHORT).show();
}
}
// Load ROM (History) Preference
else if (prefKey.equals("loadRomHistoryPref"))
{
final HistorySelection historySelection = new HistorySelection();
historySelection.show(getFragmentManager(), "history_selection");
}
return true;
}
@Override
public void onDirectoryFragmentClosed(String path)
{
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
final String libretro_path = prefs.getString("libretro_path", "");
UserPreferences.updateConfigFile(ctx);
String current_ime = Settings.Secure.getString(ctx.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
Toast.makeText(ctx, String.format(getString(R.string.loading_data), path), Toast.LENGTH_SHORT).show();
Intent myIntent = new Intent(ctx, RetroActivity.class);
myIntent.putExtra("ROM", path);
myIntent.putExtra("LIBRETRO", libretro_path);
myIntent.putExtra("CONFIGFILE", UserPreferences.getDefaultConfigPath(ctx));
myIntent.putExtra("IME", current_ime);
startActivity(myIntent);
}
}

View File

@ -1,4 +1,4 @@
package com.retroarch.browser.preferences.fragments;
package com.retroarch.browser.preferences;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
@ -15,6 +15,11 @@ import android.support.v7.app.ActionBar.TabListener;
import android.support.v7.app.ActionBarActivity;
import com.retroarch.R;
import com.retroarch.browser.preferences.fragments.AudioPreferenceFragment;
import com.retroarch.browser.preferences.fragments.GeneralPreferenceFragment;
import com.retroarch.browser.preferences.fragments.InputPreferenceFragment;
import com.retroarch.browser.preferences.fragments.PathPreferenceFragment;
import com.retroarch.browser.preferences.fragments.VideoPreferenceFragment;
import com.retroarch.browser.preferences.fragments.util.PreferenceListFragment;
import com.retroarch.browser.preferences.util.UserPreferences;

View File

@ -1,6 +1,9 @@
package com.retroarch.browser.preferences.fragments;
import java.io.File;
import com.retroarch.R;
import com.retroarch.browser.dirfragment.DirectoryFragment;
import com.retroarch.browser.preferences.fragments.util.PreferenceListFragment;
import android.app.AlertDialog;
@ -14,47 +17,59 @@ import android.view.inputmethod.InputMethodManager;
/**
* A {@link PreferenceListFragment} responsible for handling the input preferences.
*/
public final class InputPreferenceFragment extends PreferenceListFragment
public final class InputPreferenceFragment extends PreferenceListFragment implements OnPreferenceClickListener
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Add input preferences from the XML.
addPreferencesFromResource(R.xml.input_preferences);
// Set Input Method preference
final Preference setImePref = findPreference("set_ime_pref");
setImePref.setOnPreferenceClickListener(new OnPreferenceClickListener()
{
@Override
public boolean onPreferenceClick(Preference preference)
{
// Show an IME picker so the user can change their set IME.
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showInputMethodPicker();
return true;
}
});
// Set preference listeners
findPreference("set_ime_pref").setOnPreferenceClickListener(this);
findPreference("report_ime_pref").setOnPreferenceClickListener(this);
findPreference("inputOverlayDirPref").setOnPreferenceClickListener(this);
}
// Report IME preference
final Preference reportImePref = findPreference("report_ime_pref");
reportImePref.setOnPreferenceClickListener(new OnPreferenceClickListener()
@Override
public boolean onPreferenceClick(Preference preference)
{
final String prefKey = preference.getKey();
// Set Input Method preference
if (prefKey.equals("set_ime_pref"))
{
@Override
public boolean onPreferenceClick(Preference preference)
{
final String currentIme = Settings.Secure.getString(getActivity().getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD);
AlertDialog.Builder reportImeDialog = new AlertDialog.Builder(getActivity());
reportImeDialog.setTitle(R.string.current_ime);
reportImeDialog.setMessage(currentIme);
reportImeDialog.setNegativeButton(R.string.close, null);
reportImeDialog.show();
return true;
}
});
// Show an IME picker so the user can change their set IME.
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showInputMethodPicker();
}
// Report IME preference
else if (prefKey.equals("report_ime_pref"))
{
final String currentIme = Settings.Secure.getString(getActivity().getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD);
AlertDialog.Builder reportImeDialog = new AlertDialog.Builder(getActivity());
reportImeDialog.setTitle(R.string.current_ime);
reportImeDialog.setMessage(currentIme);
reportImeDialog.setNegativeButton(R.string.close, null);
reportImeDialog.show();
}
// Input Overlay selection
else if (prefKey.equals("inputOverlayDirPref"))
{
final DirectoryFragment overlayBrowser = DirectoryFragment.newInstance(R.string.input_overlay_select);
File overlayDir = new File(getActivity().getApplicationInfo().dataDir, "overlays");
if (overlayDir.exists())
overlayBrowser.setStartDirectory(overlayDir.getAbsolutePath());
overlayBrowser.addAllowedExts(".cfg");
overlayBrowser.setPathSettingKey("input_overlay");
overlayBrowser.show(getFragmentManager(), "overlayBrowser");
}
return true;
}
}

View File

@ -1,14 +1,17 @@
package com.retroarch.browser.preferences.fragments;
import com.retroarch.R;
import com.retroarch.browser.dirfragment.DirectoryFragment;
import com.retroarch.browser.preferences.fragments.util.PreferenceListFragment;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
/**
* A {@link PreferenceListFragment} that handles the path preferences.
*/
public final class PathPreferenceFragment extends PreferenceListFragment
public final class PathPreferenceFragment extends PreferenceListFragment implements OnPreferenceClickListener
{
@Override
public void onCreate(Bundle savedInstanceState)
@ -17,5 +20,52 @@ public final class PathPreferenceFragment extends PreferenceListFragment
// Add path preferences from the XML.
addPreferencesFromResource(R.xml.path_preferences);
// Set preference click listeners
findPreference("romDirPref").setOnPreferenceClickListener(this);
findPreference("srmDirPref").setOnPreferenceClickListener(this);
findPreference("saveStateDirPref").setOnPreferenceClickListener(this);
findPreference("systemDirPref").setOnPreferenceClickListener(this);
}
@Override
public boolean onPreferenceClick(Preference preference)
{
final String prefKey = preference.getKey();
// Custom ROM directory
if (prefKey.equals("romDirPref"))
{
final DirectoryFragment romDirBrowser = DirectoryFragment.newInstance(R.string.rom_directory_select);
romDirBrowser.setPathSettingKey("rgui_browser_directory");
romDirBrowser.setIsDirectoryTarget(true);
romDirBrowser.show(getFragmentManager(), "romDirBrowser");
}
// Custom savefile directory
else if (prefKey.equals("srmDirPref"))
{
final DirectoryFragment srmDirBrowser = DirectoryFragment.newInstance(R.string.savefile_directory_select);
srmDirBrowser.setPathSettingKey("savefile_directory");
srmDirBrowser.setIsDirectoryTarget(true);
srmDirBrowser.show(getFragmentManager(), "srmDirBrowser");
}
// Custom save state directory
else if (prefKey.equals("saveStateDirPref"))
{
final DirectoryFragment saveStateDirBrowser = DirectoryFragment.newInstance(R.string.save_state_directory_select);
saveStateDirBrowser.setPathSettingKey("savestate_directory");
saveStateDirBrowser.setIsDirectoryTarget(true);
saveStateDirBrowser.show(getFragmentManager(), "saveStateDirBrowser");
}
// Custom system directory
else if (prefKey.equals("systemDirPref"))
{
final DirectoryFragment systemDirBrowser = DirectoryFragment.newInstance(R.string.system_directory_select);
systemDirBrowser.setPathSettingKey("system_directory");
systemDirBrowser.setIsDirectoryTarget(true);
systemDirBrowser.show(getFragmentManager(), "systemDirBrowser");
}
return true;
}
}

View File

@ -1,6 +1,9 @@
package com.retroarch.browser.preferences.fragments;
import java.io.File;
import com.retroarch.R;
import com.retroarch.browser.dirfragment.DirectoryFragment;
import com.retroarch.browser.preferences.fragments.util.PreferenceListFragment;
import android.content.SharedPreferences;
@ -15,7 +18,7 @@ import android.widget.Toast;
/**
* A {@link PreferenceListFragment} responsible for handling the video preferences.
*/
public final class VideoPreferenceFragment extends PreferenceListFragment
public final class VideoPreferenceFragment extends PreferenceListFragment implements OnPreferenceClickListener
{
@Override
public void onCreate(Bundle savedInstanceState)
@ -25,25 +28,44 @@ public final class VideoPreferenceFragment extends PreferenceListFragment
// Add preferences from the resources
addPreferencesFromResource(R.xml.video_preferences);
// Set preference click listeners
findPreference("set_os_reported_ref_rate_pref").setOnPreferenceClickListener(this);
findPreference("glsl_shader_pref").setOnPreferenceClickListener(this);
}
@Override
public boolean onPreferenceClick(Preference preference)
{
final String prefKey = preference.getKey();
// Set OS-reported refresh rate preference.
final Preference osReportedRatePref = findPreference("set_os_reported_ref_rate_pref");
osReportedRatePref.setOnPreferenceClickListener(new OnPreferenceClickListener()
if (prefKey.equals("set_os_reported_ref_rate_pref"))
{
@Override
public boolean onPreferenceClick(Preference preference)
{
final WindowManager wm = getActivity().getWindowManager();
final Display display = wm.getDefaultDisplay();
final double rate = display.getRefreshRate();
final WindowManager wm = getActivity().getWindowManager();
final Display display = wm.getDefaultDisplay();
final double rate = display.getRefreshRate();
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
final SharedPreferences.Editor edit = prefs.edit();
edit.putString("video_refresh_rate", Double.toString(rate));
edit.commit();
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
final SharedPreferences.Editor edit = prefs.edit();
edit.putString("video_refresh_rate", Double.toString(rate));
edit.commit();
Toast.makeText(getActivity(), String.format(getString(R.string.using_os_reported_refresh_rate), rate), Toast.LENGTH_LONG).show();
return true;
}
});
Toast.makeText(getActivity(), String.format(getString(R.string.using_os_reported_refresh_rate), rate), Toast.LENGTH_LONG).show();
}
// GLSL shader selection
else if (prefKey.equals("glsl_shader_pref"))
{
final DirectoryFragment shaderBrowser = DirectoryFragment.newInstance(R.string.glsl_shader_select);
File shaderDir = new File(getActivity().getApplicationInfo().dataDir, "shaders_glsl");
if (shaderDir.exists())
shaderBrowser.setStartDirectory(shaderDir.getAbsolutePath());
shaderBrowser.addAllowedExts(".glsl");
shaderBrowser.setPathSettingKey("video_shader");
shaderBrowser.show(getFragmentManager(), "shaderBrowser");
}
return true;
}
}

View File

@ -1,28 +1,43 @@
package com.retroarch.browser.preferences.fragments.util;
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
package com.retroarch.browser.preferences.fragments.util;
import com.retroarch.R;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.support.v4.app.ListFragment;
import android.support.v4.app.Fragment;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.widget.ListView;
public class PreferenceListFragment extends ListFragment
public abstract class PreferenceListFragment extends Fragment implements PreferenceManagerCompat.OnPreferenceTreeClickListener
{
private static final String PREFERENCES_TAG = "android:preferences";
private static final String PREFERENCES_TAG = "android:preferences";
private PreferenceManager mPreferenceManager;
private ListView mList;
private boolean mHavePrefs;
@ -34,7 +49,7 @@ public class PreferenceListFragment extends ListFragment
private static final int FIRST_REQUEST_CODE = 100;
private static final int MSG_BIND_PREFERENCES = 1;
private final Handler mHandler = new Handler()
private Handler mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
@ -48,35 +63,42 @@ public class PreferenceListFragment extends ListFragment
}
};
private final Runnable mRequestFocus = new Runnable()
final private Runnable mRequestFocus = new Runnable()
{
@Override
public void run()
{
mList.focusableViewAvailable(mList);
}
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle b)
/**
* Interface that PreferenceFragment's containing activity should
* implement to be able to process preference items that wish to
* switch to a new fragment.
*/
public interface OnPreferenceStartFragmentCallback
{
View view = inflater.inflate(R.layout.preference_list_content, container, false);
view.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
return view;
/**
* Called when the user has clicked on a Preference that has
* a fragment class name associated with it. The implementation
* to should instantiate and switch to an instance of the given
* fragment.
*/
boolean onPreferenceStartFragment(PreferenceListFragment caller, Preference pref);
}
@Override
public void onDestroyView()
public void onCreate(Bundle paramBundle)
{
super.onDestroyView();
super.onCreate(paramBundle);
mPreferenceManager = PreferenceManagerCompat.newInstance(getActivity(), FIRST_REQUEST_CODE);
PreferenceManagerCompat.setFragment(mPreferenceManager, this);
}
// Kill the list
mList = null;
// Remove callbacks and messages.
mHandler.removeCallbacks(mRequestFocus);
mHandler.removeMessages(MSG_BIND_PREFERENCES);
@Override
public View onCreateView(LayoutInflater paramLayoutInflater, ViewGroup paramViewGroup, Bundle paramBundle)
{
return paramLayoutInflater.inflate(R.layout.preference_list_fragment, paramViewGroup, false);
}
@Override
@ -84,12 +106,10 @@ public class PreferenceListFragment extends ListFragment
{
super.onActivityCreated(savedInstanceState);
if (mHavePrefs)
{
bindPreferences();
if (mHavePrefs) {
bindPreferences();
}
// Done initializing.
mInitDone = true;
if (savedInstanceState != null)
@ -107,47 +127,34 @@ public class PreferenceListFragment extends ListFragment
}
@Override
public void onCreate(Bundle savedInstanceState)
public void onStart()
{
super.onCreate(savedInstanceState);
mPreferenceManager = onCreatePreferenceManager();
postBindPreferences();
super.onStart();
PreferenceManagerCompat.setOnPreferenceTreeClickListener(mPreferenceManager, this);
}
@Override
public void onStop()
{
super.onStop();
PreferenceManagerCompat.dispatchActivityStop(mPreferenceManager);
PreferenceManagerCompat.setOnPreferenceTreeClickListener(mPreferenceManager, null);
}
try
{
Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityStop");
m.setAccessible(true);
m.invoke(mPreferenceManager);
}
catch(Exception e)
{
e.printStackTrace();
}
@Override
public void onDestroyView()
{
mList = null;
mHandler.removeCallbacks(mRequestFocus);
mHandler.removeMessages(MSG_BIND_PREFERENCES);
super.onDestroyView();
}
@Override
public void onDestroy()
{
super.onDestroy();
try
{
Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityDestroy");
m.setAccessible(true);
m.invoke(mPreferenceManager);
}
catch(Exception e)
{
e.printStackTrace();
}
PreferenceManagerCompat.dispatchActivityDestroy(mPreferenceManager);
}
@Override
@ -169,69 +176,12 @@ public class PreferenceListFragment extends ListFragment
{
super.onActivityResult(requestCode, resultCode, data);
try
{
Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityResult", int.class, int.class, Intent.class);
m.setAccessible(true);
m.invoke(mPreferenceManager, requestCode, resultCode, data);
}
catch(Exception e)
{
e.printStackTrace();
}
PreferenceManagerCompat.dispatchActivityResult(mPreferenceManager, requestCode, resultCode, data);
}
/**
* Posts a message to bind the preferences to the list view.
* <p>
* Binding late is preferred as any custom preference types created in
* {@link #onCreate(Bundle)} are able to have their views recycled.
*/
private void postBindPreferences()
{
if (mHandler.hasMessages(MSG_BIND_PREFERENCES))
{
return;
}
mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
}
private void bindPreferences()
{
final PreferenceScreen preferenceScreen = getPreferenceScreen();
if (preferenceScreen != null)
{
preferenceScreen.bind(getListView());
}
}
/**
* Creates the {@link PreferenceManager}.
*
* @return The {@link PreferenceManager} used by this fragment.
*/
private PreferenceManager onCreatePreferenceManager()
{
try
{
Constructor<PreferenceManager> c = PreferenceManager.class.getDeclaredConstructor(Activity.class, int.class);
c.setAccessible(true);
PreferenceManager preferenceManager = c.newInstance(getActivity(), FIRST_REQUEST_CODE);
return preferenceManager;
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
/**
* Gets the {@link PreferenceManager} used by this fragment.
*
* @return The {@link PreferenceManager} used by this fragment.
* Returns the {@link PreferenceManager} used by this fragment.
* @return The {@link PreferenceManager}.
*/
public PreferenceManager getPreferenceManager()
{
@ -240,90 +190,75 @@ public class PreferenceListFragment extends ListFragment
/**
* Sets the root of the preference hierarchy that this fragment is showing.
*
*
* @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
*/
public void setPreferenceScreen(PreferenceScreen preferenceScreen)
{
try
if (PreferenceManagerCompat.setPreferences(mPreferenceManager, preferenceScreen) && preferenceScreen != null)
{
Method m = PreferenceManager.class.getDeclaredMethod("setPreferences", PreferenceScreen.class);
m.setAccessible(true);
boolean result = (Boolean) m.invoke(mPreferenceManager, preferenceScreen);
if (result && preferenceScreen != null)
mHavePrefs = true;
if (mInitDone)
{
mHavePrefs = true;
if (mInitDone)
{
postBindPreferences();
}
postBindPreferences();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
/**
* Gets the root of the preference hierarchy that this fragment is showing.
*
*
* @return The {@link PreferenceScreen} that is the root of the preference
* hierarchy.
*/
public PreferenceScreen getPreferenceScreen()
{
try
{
Method m = PreferenceManager.class.getDeclaredMethod("getPreferenceScreen");
m.setAccessible(true);
return (PreferenceScreen) m.invoke(mPreferenceManager);
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
return PreferenceManagerCompat.getPreferenceScreen(mPreferenceManager);
}
/**
* Adds preferences from activities that match the given {@link Intent}.
*
*
* @param intent The {@link Intent} to query activities.
*/
public void addPreferencesFromIntent(Intent intent)
{
throw new UnsupportedOperationException("addPreferencesFromIntent not implemented yet.");
requirePreferenceManager();
setPreferenceScreen(PreferenceManagerCompat.inflateFromIntent(mPreferenceManager, intent, getPreferenceScreen()));
}
/**
* Inflates the given XML resource and adds the preference hierarchy to the current
* preference hierarchy.
*
*
* @param preferencesResId The XML resource ID to inflate.
*/
public void addPreferencesFromResource(int preferencesResId)
{
try
requirePreferenceManager();
setPreferenceScreen(PreferenceManagerCompat.inflateFromResource(mPreferenceManager, getActivity(),
preferencesResId, getPreferenceScreen()));
}
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)
{
//if (preference.getFragment() != null &&
if (getActivity() instanceof OnPreferenceStartFragmentCallback)
{
Method m = PreferenceManager.class.getDeclaredMethod("inflateFromResource", Context.class, int.class, PreferenceScreen.class);
m.setAccessible(true);
PreferenceScreen prefScreen = (PreferenceScreen) m.invoke(mPreferenceManager, getActivity(), preferencesResId, getPreferenceScreen());
setPreferenceScreen(prefScreen);
}
catch(Exception e)
{
e.printStackTrace();
return ((OnPreferenceStartFragmentCallback)getActivity()).onPreferenceStartFragment(
this, preference);
}
return false;
}
/**
* Finds a {@link Preference} based on its key.
*
* @param key The key of the preference to retrieve.
*
* @return The {@link Preference} with the key, or null.
*
* @see PreferenceGroup#findPreference(CharSequence)
*/
public Preference findPreference(CharSequence key)
@ -336,6 +271,29 @@ public class PreferenceListFragment extends ListFragment
return mPreferenceManager.findPreference(key);
}
private void requirePreferenceManager()
{
if (mPreferenceManager == null)
{
throw new RuntimeException("This should be called after super.onCreate.");
}
}
private void postBindPreferences()
{
if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
}
private void bindPreferences()
{
final PreferenceScreen preferenceScreen = getPreferenceScreen();
if (preferenceScreen != null)
{
preferenceScreen.bind(getListView());
}
}
public ListView getListView()
{
ensureList();
@ -349,24 +307,45 @@ public class PreferenceListFragment extends ListFragment
return;
}
final View root = getView();
View root = getView();
if (root == null)
{
throw new IllegalStateException("Content view not yet created");
}
final View rawListView = root.findViewById(android.R.id.list);
View rawListView = root.findViewById(android.R.id.list);
if (!(rawListView instanceof ListView))
{
throw new RuntimeException("Content has view with id attribute 'android.R.id.list' that is not a ListView class");
throw new RuntimeException(
"Content has view with id attribute 'android.R.id.list' that is not a ListView class");
}
mList = (ListView)rawListView;
if (mList == null)
{
throw new RuntimeException("Your content must have a ListView whose id attribute is 'android.R.id.list'");
throw new RuntimeException(
"Your content must have a ListView whose id attribute is 'android.R.id.list'");
}
mList.setOnKeyListener(mListOnKeyListener);
mHandler.post(mRequestFocus);
}
private OnKeyListener mListOnKeyListener = new OnKeyListener()
{
@Override
public boolean onKey(View v, int keyCode, KeyEvent event)
{
Object selectedItem = mList.getSelectedItem();
if (selectedItem instanceof Preference)
{
@SuppressWarnings("unused")
View selectedView = mList.getSelectedView();
//return ((Preference)selectedItem).onKey(
// selectedView, keyCode, event);
return false;
}
return false;
}
};
}

View File

@ -0,0 +1,275 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.retroarch.browser.preferences.fragments.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.preference.Preference;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.util.Log;
class PreferenceManagerCompat
{
private static final String TAG = PreferenceManagerCompat.class.getSimpleName();
/**
* Interface definition for a callback to be invoked when a
* {@link Preference} in the hierarchy rooted at this {@link PreferenceScreen} is
* clicked.
*/
interface OnPreferenceTreeClickListener
{
/**
* Called when a preference in the tree rooted at this
* {@link PreferenceScreen} has been clicked.
*
* @param preferenceScreen The {@link PreferenceScreen} that the
* preference is located in.
* @param preference The preference that was clicked.
* @return Whether the click was handled.
*/
boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference);
}
static PreferenceManager newInstance(Activity activity, int firstRequestCode)
{
try
{
Constructor<PreferenceManager> c = PreferenceManager.class.getDeclaredConstructor(Activity.class, int.class);
c.setAccessible(true);
return c.newInstance(activity, firstRequestCode);
}
catch (Exception e)
{
Log.w(TAG, "Couldn't call constructor PreferenceManager by reflection", e);
}
return null;
}
/**
* Sets the owning preference fragment
*/
static void setFragment(PreferenceManager manager, PreferenceListFragment fragment)
{
// stub
}
/**
* Sets the callback to be invoked when a {@link Preference} in the
* hierarchy rooted at this {@link PreferenceManager} is clicked.
*
* @param listener The callback to be invoked.
*/
static void setOnPreferenceTreeClickListener(PreferenceManager manager, final OnPreferenceTreeClickListener listener)
{
try
{
Field onPreferenceTreeClickListener = PreferenceManager.class.getDeclaredField("mOnPreferenceTreeClickListener");
onPreferenceTreeClickListener.setAccessible(true);
if (listener != null)
{
Object proxy = Proxy.newProxyInstance(onPreferenceTreeClickListener.getType().getClassLoader(), new Class[] { onPreferenceTreeClickListener.getType() }, new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
{
if (method.getName().equals("onPreferenceTreeClick"))
{
return Boolean.valueOf(listener.onPreferenceTreeClick((PreferenceScreen) args[0], (Preference) args[1]));
}
else
{
return null;
}
}
});
onPreferenceTreeClickListener.set(manager, proxy);
}
else
{
onPreferenceTreeClickListener.set(manager, null);
}
}
catch (Exception e)
{
Log.w(TAG, "Couldn't set PreferenceManager.mOnPreferenceTreeClickListener by reflection", e);
}
}
/**
* Inflates a preference hierarchy from the preference hierarchies of
* {@link Activity Activities} that match the given {@link Intent}. An
* {@link Activity} defines its preference hierarchy with meta-data using
* the {@link #METADATA_KEY_PREFERENCES} key.
* <p>
* If a preference hierarchy is given, the new preference hierarchies will
* be merged in.
*
* @param queryIntent The intent to match activities.
* @param rootPreferences Optional existing hierarchy to merge the new
* hierarchies into.
* @return The root hierarchy (if one was not provided, the new hierarchy's
* root).
*/
static PreferenceScreen inflateFromIntent(PreferenceManager manager, Intent intent, PreferenceScreen screen)
{
try
{
Method m = PreferenceManager.class.getDeclaredMethod("inflateFromIntent", Intent.class, PreferenceScreen.class);
m.setAccessible(true);
PreferenceScreen prefScreen = (PreferenceScreen) m.invoke(manager, intent, screen);
return prefScreen;
}
catch (Exception e)
{
Log.w(TAG, "Couldn't call PreferenceManager.inflateFromIntent by reflection", e);
}
return null;
}
/**
* Inflates a preference hierarchy from XML. If a preference hierarchy is
* given, the new preference hierarchies will be merged in.
*
* @param context The context of the resource.
* @param resId The resource ID of the XML to inflate.
* @param rootPreferences Optional existing hierarchy to merge the new
* hierarchies into.
* @return The root hierarchy (if one was not provided, the new hierarchy's
* root).
* @hide
*/
static PreferenceScreen inflateFromResource(PreferenceManager manager, Activity activity, int resId, PreferenceScreen screen)
{
try
{
Method m = PreferenceManager.class.getDeclaredMethod("inflateFromResource", Context.class, int.class, PreferenceScreen.class);
m.setAccessible(true);
PreferenceScreen prefScreen = (PreferenceScreen) m.invoke(manager, activity, resId, screen);
return prefScreen;
}
catch (Exception e)
{
Log.w(TAG, "Couldn't call PreferenceManager.inflateFromResource by reflection", e);
}
return null;
}
/**
* Returns the root of the preference hierarchy managed by this class.
*
* @return The {@link PreferenceScreen} object that is at the root of the hierarchy.
*/
static PreferenceScreen getPreferenceScreen(PreferenceManager manager)
{
try
{
Method m = PreferenceManager.class.getDeclaredMethod("getPreferenceScreen");
m.setAccessible(true);
return (PreferenceScreen) m.invoke(manager);
}
catch (Exception e)
{
Log.w(TAG, "Couldn't call PreferenceManager.getPreferenceScreen by reflection", e);
}
return null;
}
/**
* Called by the {@link PreferenceManager} to dispatch a subactivity result.
*/
static void dispatchActivityResult(PreferenceManager manager, int requestCode, int resultCode, Intent data)
{
try
{
Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityResult", int.class, int.class, Intent.class);
m.setAccessible(true);
m.invoke(manager, requestCode, resultCode, data);
}
catch (Exception e)
{
Log.w(TAG, "Couldn't call PreferenceManager.dispatchActivityResult by reflection", e);
}
}
/**
* Called by the {@link PreferenceManager} to dispatch the activity stop
* event.
*/
static void dispatchActivityStop(PreferenceManager manager)
{
try
{
Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityStop");
m.setAccessible(true);
m.invoke(manager);
}
catch (Exception e)
{
Log.w(TAG, "Couldn't call PreferenceManager.dispatchActivityStop by reflection", e);
}
}
/**
* Called by the {@link PreferenceManager} to dispatch the activity destroy
* event.
*/
static void dispatchActivityDestroy(PreferenceManager manager)
{
try
{
Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityDestroy");
m.setAccessible(true);
m.invoke(manager);
}
catch (Exception e)
{
Log.w(TAG, "Couldn't call PreferenceManager.dispatchActivityDestroy by reflection", e);
}
}
/**
* Sets the root of the preference hierarchy.
*
* @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
* @return Whether the {@link PreferenceScreen} given is different than the previous.
*/
static boolean setPreferences(PreferenceManager manager, PreferenceScreen screen)
{
try
{
Method m = PreferenceManager.class.getDeclaredMethod("setPreferences", PreferenceScreen.class);
m.setAccessible(true);
return ((Boolean) m.invoke(manager, screen));
}
catch (Exception e)
{
Log.w(TAG, "Couldn't call PreferenceManager.setPreferences by reflection", e);
}
return false;
}
}