diff --git a/Source/Android/app/build.gradle b/Source/Android/app/build.gradle index ae34d82377..c47c00a1a4 100644 --- a/Source/Android/app/build.gradle +++ b/Source/Android/app/build.gradle @@ -29,6 +29,7 @@ android { } } + // Define build types, which are orthogonal to product flavors. buildTypes { // Signed by release key, allowing for upload to Play Store. release { @@ -43,6 +44,35 @@ android { jniDebuggable true } } + + // Define product flavors, which can be split into categories. Common examples + // of product flavors are paid vs. free, ARM vs. x86, etc. + productFlavors { + arm { + // This flavor is mutually exclusive against any flavor in the same dimension. + flavorDimension "abi" + + // When building this flavor, only include native libs from the specified folder. + ndk { + abiFilter "armeabi-v7a" + } + } + + arm_64 { + flavorDimension "abi" + ndk { + abiFilter "arm64-v8a" + } + } + + // TODO Uncomment this when we successfully build for x86_64. + /*x86_64 { + flavorDimension "abi" + ndk { + abiFilter "x86_64" + } + }*/ + } } dependencies { diff --git a/Source/Android/app/src/arm/res/values/arrays.xml b/Source/Android/app/src/arm/res/values/arrays.xml new file mode 100644 index 0000000000..a98f723b24 --- /dev/null +++ b/Source/Android/app/src/arm/res/values/arrays.xml @@ -0,0 +1,16 @@ + + + + + + + + @string/interpreter + @string/jit_arm_recompiler + + + 0 + 3 + + + \ No newline at end of file diff --git a/Source/Android/app/src/arm/res/values/strings.xml b/Source/Android/app/src/arm/res/values/strings.xml new file mode 100644 index 0000000000..ec82295355 --- /dev/null +++ b/Source/Android/app/src/arm/res/values/strings.xml @@ -0,0 +1,5 @@ + + + + Dolphin ARM32 + \ No newline at end of file diff --git a/Source/Android/app/src/arm_64/res/values/arrays.xml b/Source/Android/app/src/arm_64/res/values/arrays.xml new file mode 100644 index 0000000000..12ed1695da --- /dev/null +++ b/Source/Android/app/src/arm_64/res/values/arrays.xml @@ -0,0 +1,16 @@ + + + + + + + + @string/interpreter + @string/jit_arm64_recompiler + + + 0 + 4 + + + \ No newline at end of file diff --git a/Source/Android/app/src/arm_64/res/values/strings.xml b/Source/Android/app/src/arm_64/res/values/strings.xml new file mode 100644 index 0000000000..c98efb06b6 --- /dev/null +++ b/Source/Android/app/src/arm_64/res/values/strings.xml @@ -0,0 +1,5 @@ + + + + Dolphin ARM64 + \ No newline at end of file diff --git a/Source/Android/app/src/main/AndroidManifest.xml b/Source/Android/app/src/main/AndroidManifest.xml index 9042094c43..7976a1cc8d 100644 --- a/Source/Android/app/src/main/AndroidManifest.xml +++ b/Source/Android/app/src/main/AndroidManifest.xml @@ -14,7 +14,7 @@ @@ -27,9 +27,14 @@ + + - + + + diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/GameGridActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/GameGridActivity.java index f4f4519b94..c0ba984023 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/GameGridActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/GameGridActivity.java @@ -10,16 +10,17 @@ import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.Menu; import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.widget.ImageButton; import android.widget.Toolbar; -import org.dolphinemu.dolphinemu.AssetCopyService; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.adapters.GameAdapter; import org.dolphinemu.dolphinemu.model.Game; import org.dolphinemu.dolphinemu.model.GcGame; +import org.dolphinemu.dolphinemu.services.AssetCopyService; import java.io.File; import java.util.ArrayList; @@ -89,8 +90,8 @@ public final class GameGridActivity extends Activity * Callback from AddDirectoryActivity. Applies any changes necessary to the GameGridActivity. * * @param requestCode An int describing whether the Activity that is returning did so successfully. - * @param resultCode An int describing what Activity is giving us this callback. - * @param result The information the returning Activity is providing us. + * @param resultCode An int describing what Activity is giving us this callback. + * @param result The information the returning Activity is providing us. */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent result) @@ -126,7 +127,27 @@ public final class GameGridActivity extends Activity MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_game_grid, menu); return true; + } + /** + * Called by the framework whenever any actionbar/toolbar icon is clicked. + * + * @param item The icon that was clicked on. + * @return True if the event was handled, false to bubble it up to the OS. + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) + { + switch (item.getItemId()) + { + case R.id.menu_settings: + // Launch the Settings Actvity. + Intent settings = new Intent(this, SettingsActivity.class); + startActivity(settings); + return true; + } + + return false; } // TODO Replace all of this with a SQLite database @@ -172,7 +193,8 @@ public final class GameGridActivity extends Activity } } - } catch (Exception ignored) + } + catch (Exception ignored) { } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/SettingsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/SettingsActivity.java new file mode 100644 index 0000000000..fe1ddd14d4 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/SettingsActivity.java @@ -0,0 +1,41 @@ +package org.dolphinemu.dolphinemu.activities; + + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +import org.dolphinemu.dolphinemu.fragments.SettingsFragment; +import org.dolphinemu.dolphinemu.services.SettingsSaveService; + +public final class SettingsActivity extends Activity +{ + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + // Display the fragment as the main content. + getFragmentManager().beginTransaction() + .replace(android.R.id.content, new SettingsFragment(), "settings_fragment") + .commit(); + } + + /** + * If this is called, the user has left the settings screen (potentially through the + * home button) and will expect their changes to be persisted. So we kick off an + * IntentService which will do so on a background thread. + */ + @Override + protected void onStop() + { + super.onStop(); + + Log.d("DolphinEmulator", "Settings activity stopping. Saving settings to INI..."); + + // Copy assets into appropriate locations. + Intent settingsSaver = new Intent(this, SettingsSaveService.class); + startService(settingsSaver); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/SettingsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/SettingsFragment.java new file mode 100644 index 0000000000..8f77e4a72d --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/SettingsFragment.java @@ -0,0 +1,197 @@ +package org.dolphinemu.dolphinemu.fragments; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.Environment; +import android.preference.ListPreference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; + +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.utils.EGLHelper; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import javax.microedition.khronos.opengles.GL10; + +public final class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener +{ + private SharedPreferences mPreferences; + private ListPreference mVideoBackendPreference; + + private final EGLHelper mEglHelper = new EGLHelper(EGLHelper.EGL_OPENGL_ES2_BIT); + private final String mVendor = mEglHelper.getGL().glGetString(GL10.GL_VENDOR); + + private final String mVersion = mEglHelper.getGL().glGetString(GL10.GL_VERSION); + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + // Load the preferences from an XML resource + addPreferencesFromResource(R.xml.preferences); + + // TODO Below here is effectively ported from the old VideoSettingsFragment. There is + // TODO probably a simpler way to do this, but potentially could require UI discussion/feedback. + + // Setting valid video backends. + mVideoBackendPreference = (ListPreference) findPreference("gpuPref"); + final boolean deviceSupportsGL = mEglHelper.supportsOpenGL(); + final boolean deviceSupportsGLES3 = mEglHelper.supportsGLES3(); + + if (deviceSupportsGL) + { + mVideoBackendPreference.setEntries(R.array.videoBackendEntriesGL); + mVideoBackendPreference.setEntryValues(R.array.videoBackendValuesGL); + } + else if (deviceSupportsGLES3) + { + mVideoBackendPreference.setEntries(R.array.videoBackendEntriesGLES3); + mVideoBackendPreference.setEntryValues(R.array.videoBackendValuesGLES3); + } + else + { + mVideoBackendPreference.setEntries(R.array.videoBackendEntriesNoGLES3); + mVideoBackendPreference.setEntryValues(R.array.videoBackendValuesNoGLES3); + } + + // + // Set available post processing shaders + // + + List shader_names = new ArrayList(); + List shader_values = new ArrayList(); + + // Disabled option + shader_names.add("Disabled"); + shader_values.add(""); + + // TODO Since shaders are included with the APK, we know what they are at build-time. We should + // TODO be able to run this logic somehow at build-time and not rely on the device doing it. + + File shaders_folder = new File(Environment.getExternalStorageDirectory() + File.separator + "dolphin-emu" + File.separator + "Shaders"); + if (shaders_folder.exists()) + { + File[] shaders = shaders_folder.listFiles(); + for (File file : shaders) + { + if (file.isFile()) + { + String filename = file.getName(); + if (filename.endsWith(".glsl")) + { + // Strip the extension and put it in to the list + shader_names.add(filename.substring(0, filename.lastIndexOf('.'))); + shader_values.add(filename.substring(0, filename.lastIndexOf('.'))); + } + } + } + } + + final ListPreference shader_preference = (ListPreference) findPreference("postProcessingShader"); + shader_preference.setEntries(shader_names.toArray(new CharSequence[shader_names.size()])); + shader_preference.setEntryValues(shader_values.toArray(new CharSequence[shader_values.size()])); + + // + // Disable all options if Software Rendering is used. + // + // Note that the numeric value in 'getPreference()' + // denotes the placement on the UI. So if more elements are + // added to the video settings, these may need to change. + // + mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); + + if (mVideoBackendPreference.getValue().equals("Software Renderer")) + { + findPreference("enhancements").setEnabled(false); + findPreference("hacks").setEnabled(false); + findPreference("showFPS").setEnabled(false); + } + else if (mVideoBackendPreference.getValue().equals("OGL")) + { + findPreference("enhancements").setEnabled(true); + findPreference("hacks").setEnabled(true); + findPreference("showFPS").setEnabled(true); + + // Check if we support stereo + // If we support desktop GL then we must support at least OpenGL 3.2 + // If we only support OpenGLES then we need both OpenGLES 3.1 and AEP + if ((mEglHelper.supportsOpenGL() && mEglHelper.GetVersion() >= 320) || + (mEglHelper.supportsGLES3() && mEglHelper.GetVersion() >= 310 && mEglHelper.SupportsExtension("GL_ANDROID_extension_pack_es31a"))) + findPreference("StereoscopyScreen").setEnabled(true); + else + findPreference("StereoscopyScreen").setEnabled(false); + } + + // Also set a listener, so that if someone changes the video backend, it will disable + // the video settings, upon the user choosing "Software Rendering". + mPreferences.registerOnSharedPreferenceChangeListener(this); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences preferences, String key) + { + if (key.equals("gpuPref")) + { + if (preferences.getString(key, "Software Renderer").equals("Software Renderer")) + { + findPreference("enhancements").setEnabled(false); + findPreference("hacks").setEnabled(false); + findPreference("showFPS").setEnabled(false); + } + else if (preferences.getString(key, "Software Renderer").equals("OGL")) + { + findPreference("enhancements").setEnabled(true); + findPreference("hacks").setEnabled(true); + findPreference("showFPS").setEnabled(true); + + // Create an alert telling them that their phone sucks + if (mEglHelper.supportsGLES3() + && mVendor.equals("Qualcomm") + && getQualcommVersion() == 14.0f) + { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.device_compat_warning); + builder.setMessage(R.string.device_gles3compat_warning_msg); + builder.setPositiveButton(R.string.yes, null); + builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int which) + { + // Get an editor. + SharedPreferences.Editor editor = mPreferences.edit(); + editor.putString("gpuPref", "Software Renderer"); + editor.apply(); + mVideoBackendPreference.setValue("Software Renderer"); + } + }); + builder.show(); + } + } + } + } + + private float getQualcommVersion() + { + final int start = mVersion.indexOf("V@") + 2; + final StringBuilder versionBuilder = new StringBuilder(); + + for (int i = start; i < mVersion.length(); i++) + { + char c = mVersion.charAt(i); + + // End of numeric portion of version string. + if (c == ' ') + break; + + versionBuilder.append(c); + } + + return Float.parseFloat(versionBuilder.toString()); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/gamelist/GameListActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/gamelist/GameListActivity.java index 3accec11de..06b236da82 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/gamelist/GameListActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/gamelist/GameListActivity.java @@ -20,7 +20,6 @@ import android.os.Environment; import android.preference.PreferenceManager; import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.widget.DrawerLayout; -import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -28,12 +27,12 @@ import android.view.View; import android.widget.AdapterView; import android.widget.ListView; -import org.dolphinemu.dolphinemu.AssetCopyService; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.about.AboutActivity; import org.dolphinemu.dolphinemu.emulation.EmulationActivity; import org.dolphinemu.dolphinemu.folderbrowser.FolderBrowser; +import org.dolphinemu.dolphinemu.services.AssetCopyService; import org.dolphinemu.dolphinemu.settings.PrefsActivity; import org.dolphinemu.dolphinemu.sidemenu.SideMenuAdapter; import org.dolphinemu.dolphinemu.sidemenu.SideMenuItem; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/AssetCopyService.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/AssetCopyService.java similarity index 97% rename from Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/AssetCopyService.java rename to Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/AssetCopyService.java index d39bbec59f..f538e8e251 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/AssetCopyService.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/AssetCopyService.java @@ -4,13 +4,13 @@ * Refer to the license.txt file included. */ -package org.dolphinemu.dolphinemu; +package org.dolphinemu.dolphinemu.services; import android.app.IntentService; import android.content.Intent; -import android.os.Environment; import android.util.Log; +import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.settings.UserPreferences; import java.io.File; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/SettingsSaveService.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/SettingsSaveService.java new file mode 100644 index 0000000000..3af7aef1d6 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/SettingsSaveService.java @@ -0,0 +1,30 @@ +package org.dolphinemu.dolphinemu.services; + +import android.app.IntentService; +import android.content.Intent; +import android.util.Log; + +import org.dolphinemu.dolphinemu.settings.UserPreferences; + +/** + * IntentServices, unlike regular services, inherently run on a background thread. + * This IntentService saves all the options the user set in the Java-based UI into + * INI files the native code can read. + */ +public final class SettingsSaveService extends IntentService +{ + private static final String TAG = "DolphinEmulator"; + + public SettingsSaveService() + { + super("SettingsSaveService"); + } + + @Override + protected void onHandleIntent(Intent intent) + { + Log.v(TAG, "Saving settings to INI files..."); + UserPreferences.SavePrefsToIni(this); + Log.v(TAG, "Save successful."); + } +} diff --git a/Source/Android/app/src/main/res/layout/activity_settings.xml b/Source/Android/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000000..d72e5a0142 --- /dev/null +++ b/Source/Android/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/Source/Android/app/src/main/res/menu/menu_game_grid.xml b/Source/Android/app/src/main/res/menu/menu_game_grid.xml index 0d689474ad..7dfcfdc21c 100644 --- a/Source/Android/app/src/main/res/menu/menu_game_grid.xml +++ b/Source/Android/app/src/main/res/menu/menu_game_grid.xml @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/Source/Android/app/src/main/res/values-w1050dp/dimens.xml b/Source/Android/app/src/main/res/values-w1050dp/dimens.xml new file mode 100644 index 0000000000..92fcb2b668 --- /dev/null +++ b/Source/Android/app/src/main/res/values-w1050dp/dimens.xml @@ -0,0 +1,6 @@ + + + + 96dp + diff --git a/Source/Android/app/src/main/res/values-w820dp/dimens.xml b/Source/Android/app/src/main/res/values-w820dp/dimens.xml index 63fc816444..d27181e85e 100644 --- a/Source/Android/app/src/main/res/values-w820dp/dimens.xml +++ b/Source/Android/app/src/main/res/values-w820dp/dimens.xml @@ -1,6 +1,5 @@ + (such as screen margins) for screens with more than 820dp of available width. --> 64dp diff --git a/Source/Android/app/src/main/res/values/arrays.xml b/Source/Android/app/src/main/res/values/arrays.xml index 0ea284c71e..4ada90cb1f 100644 --- a/Source/Android/app/src/main/res/values/arrays.xml +++ b/Source/Android/app/src/main/res/values/arrays.xml @@ -2,7 +2,7 @@ - + @string/interpreter @@ -42,7 +42,14 @@ 0 - + + + + @string/interpreter + + + 0 + diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 99defca7bf..07e5ab3690 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -220,8 +220,20 @@ Disabled Other + + Dolphin New UI + + + Settings + + Add Folder to Library Up one level That folder is empty. + + + CPU Settings + Input Settings + Video Settings diff --git a/Source/Android/app/src/main/res/values/styles.xml b/Source/Android/app/src/main/res/values/styles.xml index b185e6cf10..efc39de62c 100644 --- a/Source/Android/app/src/main/res/values/styles.xml +++ b/Source/Android/app/src/main/res/values/styles.xml @@ -9,6 +9,12 @@ @color/dolphin_blue_dark + + + + + + + + + + \ No newline at end of file diff --git a/Source/Android/app/src/main/res/xml/preferences.xml b/Source/Android/app/src/main/res/xml/preferences.xml new file mode 100644 index 0000000000..b2af687783 --- /dev/null +++ b/Source/Android/app/src/main/res/xml/preferences.xml @@ -0,0 +1,690 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Android/app/src/x86_64/res/values/arrays.xml b/Source/Android/app/src/x86_64/res/values/arrays.xml new file mode 100644 index 0000000000..a6eeff8c60 --- /dev/null +++ b/Source/Android/app/src/x86_64/res/values/arrays.xml @@ -0,0 +1,18 @@ + + + + + + + + @string/interpreter + @string/jit64_recompiler + @string/jitil_recompiler + + + 0 + 1 + 2 + + + \ No newline at end of file diff --git a/Source/Android/app/src/x86_64/res/values/strings.xml b/Source/Android/app/src/x86_64/res/values/strings.xml new file mode 100644 index 0000000000..ec82295355 --- /dev/null +++ b/Source/Android/app/src/x86_64/res/values/strings.xml @@ -0,0 +1,5 @@ + + + + Dolphin ARM32 + \ No newline at end of file