From 9407d9ee0b4deca4ded47ee7528ecaccee2c63e0 Mon Sep 17 00:00:00 2001 From: Mike Harris Date: Thu, 28 Sep 2017 19:06:41 -0700 Subject: [PATCH 01/13] Bump compile SDK and buildtools version to 26 (Oreo). Bump the support lib version to 26. This allows for using property animators (R.animator) in FragmentTransaction.setCustomAnimations. Add the google maven repo, as from support lib 26 onwards, they're only publishing it in there. Bump the gradle version while we're at it, keep Android Studio quiet. --- Source/Android/app/build.gradle | 6 +++--- Source/Android/build.gradle | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/Android/app/build.gradle b/Source/Android/app/build.gradle index bdb0df444e..ae502037b1 100644 --- a/Source/Android/app/build.gradle +++ b/Source/Android/app/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion '25.0.2' + compileSdkVersion 26 + buildToolsVersion '26.0.2' lintOptions { // This is important as it will run lint but not abort on error @@ -71,7 +71,7 @@ android { } ext { - androidSupportVersion = '25.3.0' + androidSupportVersion = '26.1.0' } dependencies { diff --git a/Source/Android/build.gradle b/Source/Android/build.gradle index 3f806cd79c..aac9f5bae5 100644 --- a/Source/Android/build.gradle +++ b/Source/Android/build.gradle @@ -3,12 +3,15 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.0' + classpath 'com.android.tools.build:gradle:2.3.3' } } allprojects { repositories { jcenter() + maven { + url "https://maven.google.com" + } } } From 9fb0d9a664066adf494a40812ad98a954fe94ffc Mon Sep 17 00:00:00 2001 From: Mike Harris Date: Tue, 3 Oct 2017 23:25:38 -0700 Subject: [PATCH 02/13] Move all Activities and Fragments to the support library versions. In the support lib, the code comes from the SDK, not the device like the framework version. This means we're shipping a more recent and less buggy version. It's also a good idea to keep the entire project on one version. We have a bit of a mix now. I think some of the Fragment animation issues were because of this mixing. For the leanback activities, AppCompatActivity requires AppCompat themes, which they don't ship for Theme.Leanback. So use FragmentActivity instead (that's the parent of AppCompatActivity, but still in the support library). For passed around Activities, use FragmentActivity to work with both. --- .../activities/AddDirectoryActivity.java | 4 +-- .../activities/EmulationActivity.java | 30 ++++++++++--------- .../dolphinemu/adapters/GameAdapter.java | 8 ++--- .../adapters/PlatformPagerAdapter.java | 6 ++-- .../dolphinemu/dialogs/GameDetailsDialog.java | 2 +- .../fragments/EmulationFragment.java | 2 +- .../dolphinemu/fragments/MenuFragment.java | 2 +- .../fragments/SaveLoadStateFragment.java | 2 +- .../dolphinemu/ui/main/MainActivity.java | 8 +++-- .../dolphinemu/ui/main/TvMainActivity.java | 13 ++++---- .../ui/platform/PlatformGamesFragment.java | 2 +- .../ui/settings/SettingsActivity.java | 8 ++--- .../ui/settings/SettingsFragment.java | 2 +- .../ui/settings/SettingsFragmentView.java | 4 +-- .../dolphinemu/utils/PermissionsHandler.java | 8 ++--- .../dolphinemu/utils/StartupHandler.java | 6 ++-- 16 files changed, 56 insertions(+), 51 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AddDirectoryActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AddDirectoryActivity.java index 45e1f87ca9..84080854d6 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AddDirectoryActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AddDirectoryActivity.java @@ -1,12 +1,12 @@ package org.dolphinemu.dolphinemu.activities; -import android.app.Activity; import android.content.AsyncQueryHandler; import android.content.ContentValues; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Environment; +import android.support.v4.app.FragmentActivity; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -133,7 +133,7 @@ public class AddDirectoryActivity extends AppCompatActivity implements FileAdapt mToolbar.setSubtitle(path); } - public static void launch(Activity activity) + public static void launch(FragmentActivity activity) { Intent fileChooser = new Intent(activity, AddDirectoryActivity.class); activity.startActivityForResult(fileChooser, MainPresenter.REQUEST_ADD_DIRECTORY); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index 3162b650d3..06370c0028 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -1,9 +1,7 @@ package org.dolphinemu.dolphinemu.activities; -import android.app.Activity; import android.app.ActivityOptions; import android.app.AlertDialog; -import android.app.Fragment; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -14,6 +12,8 @@ import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; import android.support.annotation.IntDef; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; import android.support.v7.app.AppCompatActivity; import android.util.SparseIntArray; import android.view.InputDevice; @@ -157,7 +157,7 @@ public final class EmulationActivity extends AppCompatActivity buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT); } - public static void launch(Activity activity, String path, String title, String screenshotPath, int position, View sharedView) + public static void launch(FragmentActivity activity, String path, String title, String screenshotPath, int position, View sharedView) { Intent launcher = new Intent(activity, EmulationActivity.class); @@ -171,6 +171,8 @@ public final class EmulationActivity extends AppCompatActivity sharedView, "image_game_screenshot"); + // I believe this warning is a bug. Activities are FragmentActivity from the support lib + //noinspection RestrictedApi activity.startActivityForResult(launcher, MainPresenter.REQUEST_EMULATE_GAME, options.toBundle()); } @@ -286,7 +288,7 @@ public final class EmulationActivity extends AppCompatActivity EmulationFragment emulationFragment = EmulationFragment.newInstance(path); // Add fragment to the activity - this triggers all its lifecycle callbacks. - getFragmentManager().beginTransaction() + getSupportFragmentManager().beginTransaction() .add(R.id.frame_emulation_fragment, emulationFragment, EmulationFragment.FRAGMENT_TAG) .commit(); } @@ -302,7 +304,7 @@ public final class EmulationActivity extends AppCompatActivity } else { - MenuFragment menuFragment = (MenuFragment) getFragmentManager() + MenuFragment menuFragment = (MenuFragment) getSupportFragmentManager() .findFragmentById(R.id.fragment_menu); if (menuFragment != null) @@ -413,7 +415,7 @@ public final class EmulationActivity extends AppCompatActivity private void stopEmulation() { - EmulationFragment fragment = (EmulationFragment) getFragmentManager() + EmulationFragment fragment = (EmulationFragment) getSupportFragmentManager() .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); fragment.notifyEmulationStopped(); @@ -612,7 +614,7 @@ public final class EmulationActivity extends AppCompatActivity private void editControlsPlacement() { - EmulationFragment emulationFragment = (EmulationFragment) getFragmentManager() + EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() .findFragmentById(R.id.frame_emulation_fragment); if (emulationFragment.isConfiguringControls()) { emulationFragment.stopConfiguringControls(); @@ -707,7 +709,7 @@ public final class EmulationActivity extends AppCompatActivity builder.setNeutralButton(getString(R.string.emulation_toggle_all), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { - EmulationFragment emulationFragment = (EmulationFragment) getFragmentManager() + EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); emulationFragment.toggleInputOverlayVisibility(); } @@ -717,7 +719,7 @@ public final class EmulationActivity extends AppCompatActivity public void onClick(DialogInterface dialogInterface, int i) { editor.apply(); - EmulationFragment emulationFragment = (EmulationFragment) getFragmentManager() + EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); emulationFragment.refreshInputOverlay(); } @@ -764,7 +766,7 @@ public final class EmulationActivity extends AppCompatActivity editor.putInt("controlScale", seekbar.getProgress()); editor.apply(); - EmulationFragment emulationFragment = (EmulationFragment) getFragmentManager() + EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); emulationFragment.refreshInputOverlay(); } @@ -793,7 +795,7 @@ public final class EmulationActivity extends AppCompatActivity public void onClick(DialogInterface dialogInterface, int i) { editor.apply(); - EmulationFragment emulationFragment = (EmulationFragment) getFragmentManager() + EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); emulationFragment.refreshInputOverlay(); @@ -885,7 +887,7 @@ public final class EmulationActivity extends AppCompatActivity private void showMenu(SaveLoadStateFragment.SaveOrLoad saveOrLoad) { Fragment fragment = SaveLoadStateFragment.newInstance(saveOrLoad); - getFragmentManager().beginTransaction() + getSupportFragmentManager().beginTransaction() .setCustomAnimations(R.animator.menu_slide_in, R.animator.menu_slide_out) .replace(R.id.frame_submenu, fragment, FRAGMENT_SUBMENU_TAG) .commit(); @@ -894,7 +896,7 @@ public final class EmulationActivity extends AppCompatActivity private void removeSubMenu() { - final Fragment fragment = getFragmentManager().findFragmentByTag(FRAGMENT_SUBMENU_TAG); + final Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_SUBMENU_TAG); if (fragment != null) { @@ -908,7 +910,7 @@ public final class EmulationActivity extends AppCompatActivity { if (mMenuVisible) { - getFragmentManager().beginTransaction() + getSupportFragmentManager().beginTransaction() .remove(fragment) .commit(); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java index 153470df6a..7c5a6ea415 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java @@ -1,9 +1,9 @@ package org.dolphinemu.dolphinemu.adapters; -import android.app.Activity; import android.database.Cursor; import android.database.DataSetObserver; import android.graphics.Rect; +import android.support.v4.app.FragmentActivity; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; @@ -202,7 +202,7 @@ public final class GameAdapter extends RecyclerView.Adapter impl { GameViewHolder holder = (GameViewHolder) view.getTag(); - EmulationActivity.launch((Activity) view.getContext(), + EmulationActivity.launch((FragmentActivity) view.getContext(), holder.path, holder.title, holder.screenshotPath, @@ -225,13 +225,13 @@ public final class GameAdapter extends RecyclerView.Adapter impl // TODO This should be all we need to pass in, eventually. // String gameId = (String) holder.gameId; - Activity activity = (Activity) view.getContext(); + FragmentActivity activity = (FragmentActivity) view.getContext(); GameDetailsDialog.newInstance(holder.title, holder.description, holder.country, holder.company, holder.path, - holder.screenshotPath).show(activity.getFragmentManager(), "game_details"); + holder.screenshotPath).show(activity.getSupportFragmentManager(), "game_details"); return true; } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/PlatformPagerAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/PlatformPagerAdapter.java index afdf67f34e..c5ab5522bb 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/PlatformPagerAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/PlatformPagerAdapter.java @@ -1,10 +1,10 @@ package org.dolphinemu.dolphinemu.adapters; -import android.app.Fragment; -import android.app.FragmentManager; import android.content.Context; import android.graphics.drawable.Drawable; -import android.support.v13.app.FragmentPagerAdapter; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ImageSpan; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.java index f67ac0c442..10c04778d0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.java @@ -2,9 +2,9 @@ package org.dolphinemu.dolphinemu.dialogs; import android.app.AlertDialog; import android.app.Dialog; -import android.app.DialogFragment; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; +import android.support.v4.app.DialogFragment; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java index 94d411a78d..f80487b938 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java @@ -1,9 +1,9 @@ package org.dolphinemu.dolphinemu.fragments; -import android.app.Fragment; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; +import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.Surface; import android.view.SurfaceHolder; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/MenuFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/MenuFragment.java index 2ab3e0ba6a..2b718ac58d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/MenuFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/MenuFragment.java @@ -1,8 +1,8 @@ package org.dolphinemu.dolphinemu.fragments; -import android.app.Fragment; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; import android.util.SparseIntArray; import android.view.LayoutInflater; import android.view.View; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/SaveLoadStateFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/SaveLoadStateFragment.java index 2752a8d4b3..a59e435da0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/SaveLoadStateFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/SaveLoadStateFragment.java @@ -1,8 +1,8 @@ package org.dolphinemu.dolphinemu.fragments; -import android.app.Fragment; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; import android.util.SparseIntArray; import android.view.LayoutInflater; import android.view.View; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java index 10d76b4e42..7c9fa225b1 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java @@ -70,7 +70,8 @@ public final class MainActivity extends AppCompatActivity implements MainView if (PermissionsHandler.hasWriteAccess(this)) { - PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(getFragmentManager(), this); + PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter( + getSupportFragmentManager(), this); mViewPager.setAdapter(platformPagerAdapter); } else { mViewPager.setVisibility(View.INVISIBLE); @@ -162,7 +163,8 @@ public final class MainActivity extends AppCompatActivity implements MainView if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { StartupHandler.copyAssetsIfNeeded(this); - PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(getFragmentManager(), this); + PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter( + getSupportFragmentManager(), this); mViewPager.setAdapter(platformPagerAdapter); mTabLayout.setupWithViewPager(mViewPager); mViewPager.setVisibility(View.VISIBLE); @@ -205,6 +207,6 @@ public final class MainActivity extends AppCompatActivity implements MainView { String fragmentTag = "android:switcher:" + mViewPager.getId() + ":" + platform; - return (PlatformGamesView) getFragmentManager().findFragmentByTag(fragmentTag); + return (PlatformGamesView) getSupportFragmentManager().findFragmentByTag(fragmentTag); } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java index 567e2ca492..383b30aeba 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java @@ -1,12 +1,11 @@ package org.dolphinemu.dolphinemu.ui.main; -import android.app.Activity; -import android.app.FragmentManager; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.os.Bundle; import android.support.v17.leanback.app.BrowseFragment; +import android.support.v17.leanback.app.BrowseSupportFragment; import android.support.v17.leanback.database.CursorMapper; import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.CursorObjectAdapter; @@ -17,6 +16,8 @@ import android.support.v17.leanback.widget.OnItemViewClickedListener; import android.support.v17.leanback.widget.Presenter; import android.support.v17.leanback.widget.Row; import android.support.v17.leanback.widget.RowPresenter; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; import android.widget.Toast; @@ -33,11 +34,11 @@ import org.dolphinemu.dolphinemu.utils.PermissionsHandler; import org.dolphinemu.dolphinemu.utils.StartupHandler; import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder; -public final class TvMainActivity extends Activity implements MainView +public final class TvMainActivity extends FragmentActivity implements MainView { private MainPresenter mPresenter = new MainPresenter(this); - private BrowseFragment mBrowseFragment; + private BrowseSupportFragment mBrowseFragment; private ArrayObjectAdapter mRowsAdapter; @@ -57,8 +58,8 @@ public final class TvMainActivity extends Activity implements MainView } void setupUI() { - final FragmentManager fragmentManager = getFragmentManager(); - mBrowseFragment = new BrowseFragment(); + final FragmentManager fragmentManager = getSupportFragmentManager(); + mBrowseFragment = new BrowseSupportFragment(); fragmentManager .beginTransaction() .add(R.id.content, mBrowseFragment, "BrowseFragment") diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/platform/PlatformGamesFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/platform/PlatformGamesFragment.java index 45bfee591b..3325a1ca23 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/platform/PlatformGamesFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/platform/PlatformGamesFragment.java @@ -1,9 +1,9 @@ package org.dolphinemu.dolphinemu.ui.platform; -import android.app.Fragment; import android.database.Cursor; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivity.java index 7c30cd54f1..43715cecba 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivity.java @@ -1,9 +1,9 @@ package org.dolphinemu.dolphinemu.ui.settings; -import android.app.FragmentTransaction; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuInflater; @@ -88,7 +88,7 @@ public final class SettingsActivity extends AppCompatActivity implements Setting @Override public void showSettingsFragment(String menuTag, boolean addToStack) { - FragmentTransaction transaction = getFragmentManager().beginTransaction(); + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); if (addToStack) { @@ -149,7 +149,7 @@ public final class SettingsActivity extends AppCompatActivity implements Setting @Override public void popBackStack() { - getFragmentManager().popBackStackImmediate(); + getSupportFragmentManager().popBackStackImmediate(); } @Override @@ -178,6 +178,6 @@ public final class SettingsActivity extends AppCompatActivity implements Setting private SettingsFragment getFragment() { - return (SettingsFragment) getFragmentManager().findFragmentByTag(FRAGMENT_TAG); + return (SettingsFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG); } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsFragment.java index dd752c6664..276a26ee9a 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsFragment.java @@ -1,10 +1,10 @@ package org.dolphinemu.dolphinemu.ui.settings; import android.app.Activity; -import android.app.Fragment; import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsFragmentView.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsFragmentView.java index 43453ffb2a..3029dc2a9d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsFragmentView.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsFragmentView.java @@ -1,6 +1,6 @@ package org.dolphinemu.dolphinemu.ui.settings; -import android.app.Activity; +import android.support.v4.app.FragmentActivity; import org.dolphinemu.dolphinemu.model.settings.Setting; import org.dolphinemu.dolphinemu.model.settings.SettingSection; @@ -48,7 +48,7 @@ public interface SettingsFragmentView /** * @return The Fragment's containing activity. */ - Activity getActivity(); + FragmentActivity getActivity(); /** * Tell the Fragment to tell the containing Activity to show a new diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/PermissionsHandler.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/PermissionsHandler.java index 84ea110884..ceca9e4195 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/PermissionsHandler.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/PermissionsHandler.java @@ -1,11 +1,11 @@ package org.dolphinemu.dolphinemu.utils; import android.annotation.TargetApi; -import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.os.Build; +import android.support.v4.app.FragmentActivity; import android.support.v4.content.ContextCompat; import android.widget.Toast; @@ -17,7 +17,7 @@ public class PermissionsHandler { public static final int REQUEST_CODE_WRITE_PERMISSION = 500; @TargetApi(Build.VERSION_CODES.M) - public static boolean checkWritePermission(final Activity activity) { + public static boolean checkWritePermission(final FragmentActivity activity) { if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { return true; } @@ -45,7 +45,7 @@ public class PermissionsHandler { return true; } - public static boolean hasWriteAccess(Activity activity) { + public static boolean hasWriteAccess(FragmentActivity activity) { if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { int hasWritePermission = ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE); return hasWritePermission == PackageManager.PERMISSION_GRANTED; @@ -54,7 +54,7 @@ public class PermissionsHandler { return true; } - private static void showMessageOKCancel(final Activity activity, String message, DialogInterface.OnClickListener okListener) { + private static void showMessageOKCancel(final FragmentActivity activity, String message, DialogInterface.OnClickListener okListener) { new AlertDialog.Builder(activity) .setMessage(message) .setPositiveButton(android.R.string.ok, okListener) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/StartupHandler.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/StartupHandler.java index 2e31f405d0..34cde9e59c 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/StartupHandler.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/StartupHandler.java @@ -1,10 +1,10 @@ package org.dolphinemu.dolphinemu.utils; -import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; +import android.support.v4.app.FragmentActivity; import android.text.TextUtils; import org.dolphinemu.dolphinemu.NativeLibrary; @@ -13,7 +13,7 @@ import org.dolphinemu.dolphinemu.services.AssetCopyService; public final class StartupHandler { - public static boolean HandleInit(Activity parent) + public static boolean HandleInit(FragmentActivity parent) { NativeLibrary.SetUserDirectory(""); // Auto-Detect @@ -46,7 +46,7 @@ public final class StartupHandler return false; } - public static void copyAssetsIfNeeded(Activity parent) { + public static void copyAssetsIfNeeded(FragmentActivity parent) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(parent); boolean assetsCopied = preferences.getBoolean("assetsCopied", false); From bb3f61296e8a436cc2543a9211e96081e07943ab Mon Sep 17 00:00:00 2001 From: Mike Harris Date: Wed, 4 Oct 2017 22:48:27 -0700 Subject: [PATCH 03/13] Add a clearEmulation method. This makes it clear that the Activity is being cleared and removes null as a valid param. This improves readability (and logging slightly). Fix spacing between [Tag] and message. This matches the rest of the log messages. --- .../java/org/dolphinemu/dolphinemu/NativeLibrary.java | 10 ++++++++-- .../dolphinemu/activities/EmulationActivity.java | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index 614a1076f6..6df525e4bc 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -391,13 +391,19 @@ public final class NativeLibrary public static void endEmulationActivity() { - Log.verbose("[NativeLibrary]Ending EmulationActivity."); + Log.verbose("[NativeLibrary] Ending EmulationActivity."); sEmulationActivity.exitWithAnimation(); } public static void setEmulationActivity(EmulationActivity emulationActivity) { - Log.verbose("[NativeLibrary]Registering EmulationActivity."); + Log.verbose("[NativeLibrary] Registering EmulationActivity."); sEmulationActivity = emulationActivity; } + + public static void clearEmulationActivity() + { + Log.verbose("[NativeLibrary] Unregistering EmulationActivity."); + sEmulationActivity = null; + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index 06370c0028..773e0a7880 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -332,7 +332,7 @@ public final class EmulationActivity extends AppCompatActivity super.onStop(); Log.debug("[EmulationActivity] EmulationActivity stopping."); - NativeLibrary.setEmulationActivity(null); + NativeLibrary.clearEmulationActivity(); } @Override From 80d51c97abd6c379ca6c587d0e24079ac649626b Mon Sep 17 00:00:00 2001 From: Mike Harris Date: Wed, 4 Oct 2017 23:04:26 -0700 Subject: [PATCH 04/13] Add dummy View that works around a bug with the nVidia Shield. Without this View, the emulation SurfaceView acts like it has the highest Z-value, blocking any other View. This includes the menu fragments and the screenshot ImageView. --- .../ui/NVidiaShieldWorkaroundView.java | 25 +++++++++++++++++++ .../layout-television/activity_emulation.xml | 6 ++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/NVidiaShieldWorkaroundView.java diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/NVidiaShieldWorkaroundView.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/NVidiaShieldWorkaroundView.java new file mode 100644 index 0000000000..5d2e4a8bbe --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/NVidiaShieldWorkaroundView.java @@ -0,0 +1,25 @@ +/** + * Copyright 2013 Dolphin Emulator Project + * Licensed under GPLv2+ + * Refer to the license.txt file included. + */ + +package org.dolphinemu.dolphinemu.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; + +/** + * Work around a bug with the nVidia Shield. + */ +public final class NVidiaShieldWorkaroundView extends View +{ + public NVidiaShieldWorkaroundView(Context context, AttributeSet attrs) + { + super(context, attrs); + + // Setting this seems to workaround the bug + setWillNotDraw(false); + } +} diff --git a/Source/Android/app/src/main/res/layout-television/activity_emulation.xml b/Source/Android/app/src/main/res/layout-television/activity_emulation.xml index a8cba9adb5..59e1ba02ec 100644 --- a/Source/Android/app/src/main/res/layout-television/activity_emulation.xml +++ b/Source/Android/app/src/main/res/layout-television/activity_emulation.xml @@ -11,6 +11,10 @@ android:layout_height="match_parent" android:visibility="invisible"/> + + - \ No newline at end of file + From cde003c5cc1de6b2caba21baa9e4050a2f30d112 Mon Sep 17 00:00:00 2001 From: Mike Harris Date: Wed, 4 Oct 2017 23:10:49 -0700 Subject: [PATCH 05/13] Remove EmulationActivity.MenuType. This should have been removed when SaveLoadStateFragment was refactored. --- .../dolphinemu/dolphinemu/activities/EmulationActivity.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index 773e0a7880..8102b30e8c 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -72,11 +72,6 @@ public final class EmulationActivity extends AppCompatActivity private static boolean mIsGameCubeGame; - private enum MenuType - { - SAVE, LOAD - } - /** * Handlers are a way to pass a message to an Activity telling it to do something * on the UI thread. This Handler responds to any message, even blank ones, by From 80e1cc56b344a69aca0db07ebb2d9483d2b3d523 Mon Sep 17 00:00:00 2001 From: Mike Harris Date: Thu, 5 Oct 2017 10:11:08 -0700 Subject: [PATCH 06/13] Use weak references for the static Activity in NativeLibrary. Add in null checks as well. --- .../dolphinemu/dolphinemu/NativeLibrary.java | 39 ++++++++++---- .../dolphinemu/utils/Java_GCAdapter.java | 52 +++++++++++++------ .../dolphinemu/utils/Java_WiimoteAdapter.java | 31 +++++++---- 3 files changed, 84 insertions(+), 38 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index 6df525e4bc..7d55a89e2d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -12,13 +12,15 @@ import android.widget.Toast; import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.utils.Log; +import java.lang.ref.WeakReference; + /** * Class which contains methods that interact * with the native side of the Dolphin code. */ public final class NativeLibrary { - public static EmulationActivity sEmulationActivity; + public static WeakReference sEmulationActivity = new WeakReference<>(null); /** * Button type for use in onTouchEvent @@ -379,31 +381,48 @@ public final class NativeLibrary public static void displayAlertMsg(final String alert) { Log.error("[NativeLibrary] Alert: " + alert); - sEmulationActivity.runOnUiThread(new Runnable() + final EmulationActivity emulationActivity = sEmulationActivity.get(); + if (emulationActivity != null) { - @Override - public void run() + emulationActivity.runOnUiThread(new Runnable() { - Toast.makeText(sEmulationActivity, "Panic Alert: " + alert, Toast.LENGTH_LONG).show(); - } - }); + @Override + public void run() + { + Toast.makeText(emulationActivity, "Panic Alert: " + alert, Toast.LENGTH_LONG).show(); + } + }); + } + else + { + Log.warning("[NativeLibrary] EmulationActivity is null, can't do panic toast."); + } } public static void endEmulationActivity() { Log.verbose("[NativeLibrary] Ending EmulationActivity."); - sEmulationActivity.exitWithAnimation(); + EmulationActivity emulationActivity = sEmulationActivity.get(); + if (emulationActivity != null) + { + emulationActivity.exitWithAnimation(); + } + else + { + Log.warning("[NativeLibrary] EmulationActivity is null, can't end."); + } } public static void setEmulationActivity(EmulationActivity emulationActivity) { Log.verbose("[NativeLibrary] Registering EmulationActivity."); - sEmulationActivity = emulationActivity; + sEmulationActivity = new WeakReference<>(emulationActivity); } public static void clearEmulationActivity() { Log.verbose("[NativeLibrary] Unregistering EmulationActivity."); - sEmulationActivity = null; + + sEmulationActivity.clear(); } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java index d7731b6040..91e2a9ca46 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java @@ -1,6 +1,8 @@ package org.dolphinemu.dolphinemu.utils; +import android.app.Activity; import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.hardware.usb.UsbConfiguration; import android.hardware.usb.UsbConstants; @@ -15,7 +17,6 @@ import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.services.USBPermService; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; public class Java_GCAdapter { @@ -29,22 +30,31 @@ public class Java_GCAdapter { private static void RequestPermission() { - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) + Context context = NativeLibrary.sEmulationActivity.get(); + if (context != null) { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) + HashMap devices = manager.getDeviceList(); + for (Map.Entry pair : devices.entrySet()) { - if (!manager.hasPermission(dev)) + UsbDevice dev = pair.getValue(); + if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) { - Intent intent = new Intent(); - PendingIntent pend_intent; - intent.setClass(NativeLibrary.sEmulationActivity, USBPermService.class); - pend_intent = PendingIntent.getService(NativeLibrary.sEmulationActivity, 0, intent, 0); - manager.requestPermission(dev, pend_intent); + if (!manager.hasPermission(dev)) + { + Intent intent = new Intent(); + PendingIntent pend_intent; + intent.setClass(context, USBPermService.class); + pend_intent = PendingIntent.getService(context, 0, intent, 0); + manager.requestPermission(dev, pend_intent); + } } } } + else + { + Log.warning("Cannot request GameCube Adapter permission as EmulationActivity is null."); + } + } public static void Shutdown() @@ -124,14 +134,22 @@ public class Java_GCAdapter { } } - NativeLibrary.sEmulationActivity.runOnUiThread(new Runnable() + final Activity emulationActivity = NativeLibrary.sEmulationActivity.get(); + if (emulationActivity != null) { - @Override - public void run() + emulationActivity.runOnUiThread(new Runnable() { - Toast.makeText(NativeLibrary.sEmulationActivity, "GameCube Adapter couldn't be opened. Please re-plug the device.", Toast.LENGTH_LONG).show(); - } - }); + @Override + public void run() + { + Toast.makeText(emulationActivity, "GameCube Adapter couldn't be opened. Please re-plug the device.", Toast.LENGTH_LONG).show(); + } + }); + } + else + { + Log.warning("Cannot show toast for GameCube Adapter failure."); + } usb_con.close(); } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java index 5fdc49b021..a38f7ecd4b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java @@ -1,6 +1,7 @@ package org.dolphinemu.dolphinemu.utils; import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.hardware.usb.UsbConfiguration; import android.hardware.usb.UsbDevice; @@ -33,23 +34,31 @@ public class Java_WiimoteAdapter private static void RequestPermission() { - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) + Context context = NativeLibrary.sEmulationActivity.get(); + if (context != null) { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && dev.getVendorId() == NINTENDO_VENDOR_ID) + HashMap devices = manager.getDeviceList(); + for (Map.Entry pair : devices.entrySet()) { - if (!manager.hasPermission(dev)) + UsbDevice dev = pair.getValue(); + if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && dev.getVendorId() == NINTENDO_VENDOR_ID) { - Log.warning("Requesting permission for Wii Remote adapter"); - Intent intent = new Intent(); - PendingIntent pend_intent; - intent.setClass(NativeLibrary.sEmulationActivity, USBPermService.class); - pend_intent = PendingIntent.getService(NativeLibrary.sEmulationActivity, 0, intent, 0); - manager.requestPermission(dev, pend_intent); + if (!manager.hasPermission(dev)) + { + Log.warning("Requesting permission for Wii Remote adapter"); + Intent intent = new Intent(); + PendingIntent pend_intent; + intent.setClass(context, USBPermService.class); + pend_intent = PendingIntent.getService(context, 0, intent, 0); + manager.requestPermission(dev, pend_intent); + } } } } + else + { + Log.warning("Cannot request Wiimote adapter permission as EmulationActivity is null."); + } } public static boolean QueryAdapter() From 25a08fc5ccebb8abac086416871ce27e39f10165 Mon Sep 17 00:00:00 2001 From: Mike Harris Date: Sat, 7 Oct 2017 22:14:37 -0700 Subject: [PATCH 07/13] Only postpone transistions on Activity creation. This is causing bugs (no UI is rendered) when the Activity is rotated. --- .../dolphinemu/activities/EmulationActivity.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index 8102b30e8c..d28e78a8f7 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -220,11 +220,6 @@ public final class EmulationActivity extends AppCompatActivity Java_GCAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE); Java_WiimoteAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE); - - // Picasso will take a while to load these big-ass screenshots. So don't run - // the animation until we say so. - postponeEnterTransition(); - setContentView(R.layout.activity_emulation); mImageView = (ImageView) findViewById(R.id.image_screenshot); @@ -240,6 +235,10 @@ public final class EmulationActivity extends AppCompatActivity if (savedInstanceState == null) { + // Picasso will take a while to load these big-ass screenshots. So don't run + // the animation until we say so. + postponeEnterTransition(); + Picasso.with(this) .load(mScreenPath) .noFade() From d48c64457ad8b08ccd51d60608aa68e5c12a2766 Mon Sep 17 00:00:00 2001 From: Mike Harris Date: Sat, 7 Oct 2017 22:17:46 -0700 Subject: [PATCH 08/13] Start the postponed activity transition as soon as Picasso loads the image. Doing it on the preDraw for the View is too complicated. This works just as well. --- .../activities/EmulationActivity.java | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index d28e78a8f7..0748a34004 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -23,7 +23,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; @@ -248,14 +247,14 @@ public final class EmulationActivity extends AppCompatActivity @Override public void onSuccess() { - scheduleStartPostponedTransition(mImageView); + startPostponedEnterTransition(); } @Override public void onError() { // Still have to do this, or else the app will crash. - scheduleStartPostponedTransition(mImageView); + startPostponedEnterTransition(); } }); @@ -862,22 +861,6 @@ public final class EmulationActivity extends AppCompatActivity hideSystemUiAfterDelay(); } - - private void scheduleStartPostponedTransition(final View sharedElement) - { - sharedElement.getViewTreeObserver().addOnPreDrawListener( - new ViewTreeObserver.OnPreDrawListener() - { - @Override - public boolean onPreDraw() - { - sharedElement.getViewTreeObserver().removeOnPreDrawListener(this); - startPostponedEnterTransition(); - return true; - } - }); - } - private void showMenu(SaveLoadStateFragment.SaveOrLoad saveOrLoad) { Fragment fragment = SaveLoadStateFragment.newInstance(saveOrLoad); From 4cab718065a126b204f38f1238fa1aae4c0591e3 Mon Sep 17 00:00:00 2001 From: Mike Harris Date: Thu, 5 Oct 2017 00:21:37 -0700 Subject: [PATCH 09/13] Move emulation lifecycle handling into EmulationFragment. The Activity is responsible for just its views and menus and such. It signals the Fragment via setGamePath, StartEmulation and StopEmulation. The Fragment manages the actual emulation lifecycle. It is solely responsible for calling the NativeLibrary lifecycle methods. With this lifecycle simplification, the NativeLibrary no longer needs to kill the Activity. It happens normally now. This simplifies a lot of things, live handling rotation. --- .../dolphinemu/dolphinemu/NativeLibrary.java | 14 -- .../activities/EmulationActivity.java | 100 +++------ .../fragments/EmulationFragment.java | 205 +++++++++++------- .../layout-television/activity_emulation.xml | 8 +- .../main/res/layout/activity_emulation.xml | 8 +- Source/Android/jni/MainAndroid.cpp | 5 - 6 files changed, 162 insertions(+), 178 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index 7d55a89e2d..3cbbf3299a 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -399,20 +399,6 @@ public final class NativeLibrary } } - public static void endEmulationActivity() - { - Log.verbose("[NativeLibrary] Ending EmulationActivity."); - EmulationActivity emulationActivity = sEmulationActivity.get(); - if (emulationActivity != null) - { - emulationActivity.exitWithAnimation(); - } - else - { - Log.warning("[NativeLibrary] EmulationActivity is null, can't end."); - } - } - public static void setEmulationActivity(EmulationActivity emulationActivity) { Log.verbose("[NativeLibrary] Registering EmulationActivity."); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index 0748a34004..14d0e2c9be 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -23,7 +23,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.SeekBar; @@ -55,8 +54,8 @@ public final class EmulationActivity extends AppCompatActivity private static final String FRAGMENT_SUBMENU_TAG = "submenu"; private View mDecorView; private ImageView mImageView; + private EmulationFragment mEmulationFragment; - private FrameLayout mFrameEmulation; private LinearLayout mMenuLayout; private SharedPreferences mPreferences; @@ -85,7 +84,6 @@ public final class EmulationActivity extends AppCompatActivity } }; private String mScreenPath; - private FrameLayout mFrameContent; private String mSelectedTitle; @Retention(SOURCE) @@ -219,12 +217,13 @@ public final class EmulationActivity extends AppCompatActivity Java_GCAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE); Java_WiimoteAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE); + setContentView(R.layout.activity_emulation); mImageView = (ImageView) findViewById(R.id.image_screenshot); - mFrameContent = (FrameLayout) findViewById(R.id.frame_content); - mFrameEmulation = (FrameLayout) findViewById(R.id.frame_emulation_fragment); mMenuLayout = (LinearLayout) findViewById(R.id.layout_ingame_menu); + mEmulationFragment = (EmulationFragment) getSupportFragmentManager() + .findFragmentById(R.id.fragment_emulation); Intent gameToEmulate = getIntent(); String path = gameToEmulate.getStringExtra("SelectedGame"); @@ -260,14 +259,6 @@ public final class EmulationActivity extends AppCompatActivity Animations.fadeViewOut(mImageView) .setStartDelay(2000) - .withStartAction(new Runnable() - { - @Override - public void run() - { - mFrameEmulation.setVisibility(View.VISIBLE); - } - }) .withEndAction(new Runnable() { @Override @@ -277,18 +268,12 @@ public final class EmulationActivity extends AppCompatActivity } }); - // Instantiate an EmulationFragment. - EmulationFragment emulationFragment = EmulationFragment.newInstance(path); - - // Add fragment to the activity - this triggers all its lifecycle callbacks. - getSupportFragmentManager().beginTransaction() - .add(R.id.frame_emulation_fragment, emulationFragment, EmulationFragment.FRAGMENT_TAG) - .commit(); + mEmulationFragment.setGamePath(path); + mEmulationFragment.startEmulation(); } else { mImageView.setVisibility(View.GONE); - mFrameEmulation.setVisibility(View.VISIBLE); } if (mDeviceHasTouchScreen) @@ -311,23 +296,6 @@ public final class EmulationActivity extends AppCompatActivity mIsGameCubeGame = Platform.fromNativeInt(NativeLibrary.GetPlatform(path)) == Platform.GAMECUBE; } - @Override - protected void onStart() - { - super.onStart(); - Log.debug("[EmulationActivity] EmulationActivity starting."); - NativeLibrary.setEmulationActivity(this); - } - - @Override - protected void onStop() - { - super.onStop(); - Log.debug("[EmulationActivity] EmulationActivity stopping."); - - NativeLibrary.clearEmulationActivity(); - } - @Override protected void onPostCreate(Bundle savedInstanceState) { @@ -376,7 +344,8 @@ public final class EmulationActivity extends AppCompatActivity } else { - stopEmulation(); + mEmulationFragment.stopEmulation(); + exitWithAnimation(); } } @@ -406,15 +375,6 @@ public final class EmulationActivity extends AppCompatActivity } } - private void stopEmulation() - { - EmulationFragment fragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); - fragment.notifyEmulationStopped(); - - NativeLibrary.StopEmulation(); - } - public void exitWithAnimation() { runOnUiThread(new Runnable() @@ -458,7 +418,6 @@ public final class EmulationActivity extends AppCompatActivity @Override public void run() { - mFrameContent.removeView(mFrameEmulation); setResult(mPosition); finishAfterTransition(); } @@ -599,20 +558,23 @@ public final class EmulationActivity extends AppCompatActivity return; case MENU_ACTION_EXIT: - toggleMenu(); - stopEmulation(); + toggleMenu(); // Hide the menu (it will be showing since we just clicked it) + mEmulationFragment.stopEmulation(); + exitWithAnimation(); return; } } - private void editControlsPlacement() { - EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentById(R.id.frame_emulation_fragment); - if (emulationFragment.isConfiguringControls()) { - emulationFragment.stopConfiguringControls(); - } else { - emulationFragment.startConfiguringControls(); + private void editControlsPlacement() + { + if (mEmulationFragment.isConfiguringControls()) + { + mEmulationFragment.stopConfiguringControls(); + } + else + { + mEmulationFragment.startConfiguringControls(); } } @@ -701,20 +663,18 @@ public final class EmulationActivity extends AppCompatActivity } builder.setNeutralButton(getString(R.string.emulation_toggle_all), new DialogInterface.OnClickListener() { @Override - public void onClick(DialogInterface dialogInterface, int i) { - EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); - emulationFragment.toggleInputOverlayVisibility(); + public void onClick(DialogInterface dialogInterface, int i) + { + mEmulationFragment.toggleInputOverlayVisibility(); } }); builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { @Override - public void onClick(DialogInterface dialogInterface, int i) { + public void onClick(DialogInterface dialogInterface, int i) + { editor.apply(); - EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); - emulationFragment.refreshInputOverlay(); + mEmulationFragment.refreshInputOverlay(); } }); @@ -759,9 +719,7 @@ public final class EmulationActivity extends AppCompatActivity editor.putInt("controlScale", seekbar.getProgress()); editor.apply(); - EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); - emulationFragment.refreshInputOverlay(); + mEmulationFragment.refreshInputOverlay(); } }); @@ -788,9 +746,7 @@ public final class EmulationActivity extends AppCompatActivity public void onClick(DialogInterface dialogInterface, int i) { editor.apply(); - EmulationFragment emulationFragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentByTag(EmulationFragment.FRAGMENT_TAG); - emulationFragment.refreshInputOverlay(); + mEmulationFragment.refreshInputOverlay(); Toast.makeText(getApplication(), R.string.emulation_controller_changed, Toast.LENGTH_SHORT).show(); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java index f80487b938..4ba035f950 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java @@ -1,5 +1,6 @@ package org.dolphinemu.dolphinemu.fragments; +import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; @@ -14,15 +15,12 @@ import android.widget.Button; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.overlay.InputOverlay; import org.dolphinemu.dolphinemu.utils.Log; public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback { - public static final String FRAGMENT_TAG = "emulation_fragment"; - - private static final String ARG_GAME_PATH = "game_path"; - private SharedPreferences mPreferences; private Surface mSurface; @@ -31,18 +29,22 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C private Thread mEmulationThread; - private boolean mEmulationStarted; - private boolean mEmulationRunning; + private String mGamePath; + private final EmulationState mEmulationState = new EmulationState(); - public static EmulationFragment newInstance(String path) + @Override + public void onAttach(Context context) { - EmulationFragment fragment = new EmulationFragment(); + super.onAttach(context); - Bundle arguments = new Bundle(); - arguments.putString(ARG_GAME_PATH, path); - fragment.setArguments(arguments); - - return fragment; + if (context instanceof EmulationActivity) + { + NativeLibrary.setEmulationActivity((EmulationActivity) context); + } + else + { + throw new IllegalStateException("EmulationFragment must have EmulationActivity parent"); + } } /** @@ -67,38 +69,20 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C { View contents = inflater.inflate(R.layout.fragment_emulation, container, false); - SurfaceView surfaceView = (SurfaceView) contents.findViewById(R.id.surface_emulation); - mInputOverlay = (InputOverlay) contents.findViewById(R.id.surface_input_overlay); - + SurfaceView surfaceView = contents.findViewById(R.id.surface_emulation); surfaceView.getHolder().addCallback(this); - // If the input overlay was previously disabled, then don't show it. + mInputOverlay = contents.findViewById(R.id.surface_input_overlay); if (mInputOverlay != null) { + // If the input overlay was previously disabled, then don't show it. if (!mPreferences.getBoolean("showInputOverlay", true)) { mInputOverlay.setVisibility(View.GONE); } } - if (savedInstanceState == null) - { - mEmulationThread = new Thread(mEmulationRunner); - } - else - { - // Likely a rotation occurred. - // TODO Pass native code the Surface, which will have been recreated, from surfaceChanged() - // TODO Also, write the native code that will get the video backend to accept the new Surface as one of its own. - } - - return contents; - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) - { - Button doneButton = (Button) view.findViewById(R.id.done_control_config); + Button doneButton = contents.findViewById(R.id.done_control_config); if (doneButton != null) { doneButton.setOnClickListener(new View.OnClickListener() @@ -110,29 +94,29 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C } }); } - } - @Override - public void onStart() - { - super.onStart(); - startEmulation(); + // The new Surface created here will get passed to the native code via onSurfaceChanged. + + return contents; } @Override public void onStop() { + pauseEmulation(); super.onStop(); } @Override - public void onDestroyView() + public void onDetach() { - super.onDestroyView(); - if (getActivity().isFinishing() && mEmulationStarted) - { - NativeLibrary.StopEmulation(); - } + NativeLibrary.clearEmulationActivity(); + super.onDetach(); + } + + public void setGamePath(String setPath) + { + this.mGamePath = setPath; } public void toggleInputOverlayVisibility() @@ -171,6 +155,12 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height); + + if (mEmulationState.isPaused()) + { + NativeLibrary.UnPauseEmulation(); + } + mSurface = holder.getSurface(); NativeLibrary.SurfaceChanged(mSurface); } @@ -181,45 +171,57 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C Log.debug("[EmulationFragment] Surface destroyed."); NativeLibrary.SurfaceDestroyed(); - if (mEmulationRunning) + if (mEmulationState.isRunning()) { pauseEmulation(); } } - private void startEmulation() + public void startEmulation() { - if (!mEmulationStarted) + synchronized (mEmulationState) { - Log.debug("[EmulationFragment] Starting emulation thread."); + if (mEmulationState.isStopped()) + { + Log.debug("[EmulationFragment] Starting emulation thread."); - mEmulationThread.start(); + mEmulationThread = new Thread(mEmulationRunner, "NativeEmulation"); + mEmulationThread.start(); + // The thread will call mEmulationState.run() + } + else if (mEmulationState.isPaused()) + { + Log.debug("[EmulationFragment] Resuming emulation."); + NativeLibrary.UnPauseEmulation(); + mEmulationState.run(); + } + else + { + Log.debug("[EmulationFragment] Bug, startEmulation called while running."); + } } - else + } + + public void stopEmulation() { + synchronized (mEmulationState) { - Log.debug("[EmulationFragment] Resuming emulation."); - NativeLibrary.UnPauseEmulation(); + if (!mEmulationState.isStopped()) + { + NativeLibrary.StopEmulation(); + mEmulationState.stop(); + } } - - mEmulationRunning = true; } private void pauseEmulation() { - Log.debug("[EmulationFragment] Pausing emulation."); + synchronized (mEmulationState) + { + Log.debug("[EmulationFragment] Pausing emulation."); - NativeLibrary.PauseEmulation(); - mEmulationRunning = false; - } - - /** - * Called by containing activity to tell the Fragment emulation is already stopping, - * so it doesn't try to stop emulation on its way to the garbage collector. - */ - public void notifyEmulationStopped() - { - mEmulationStarted = false; - mEmulationRunning = false; + NativeLibrary.PauseEmulation(); + mEmulationState.pause(); + } } private Runnable mEmulationRunner = new Runnable() @@ -227,18 +229,17 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C @Override public void run() { - mEmulationRunning = true; - mEmulationStarted = true; + // Busy-wait for surface to be set + while (mSurface == null) {} - while (mSurface == null) - if (!mEmulationRunning) - return; - - Log.info("[EmulationFragment] Starting emulation: " + mSurface); + synchronized (mEmulationState) + { + Log.info("[EmulationFragment] Starting emulation: " + mSurface); + mEmulationState.run(); + } // Start emulation using the provided Surface. - String path = getArguments().getString(ARG_GAME_PATH); - NativeLibrary.Run(path); + NativeLibrary.Run(mGamePath); } }; @@ -258,4 +259,50 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C { return mInputOverlay.isInEditMode(); } + + private static class EmulationState + { + private enum State + { + STOPPED, RUNNING, PAUSED + } + + private State state; + + EmulationState() + { + // Starting state is stopped. + state = State.STOPPED; + } + + public boolean isStopped() + { + return state == State.STOPPED; + } + + public boolean isRunning() + { + return state == State.RUNNING; + } + + public boolean isPaused() + { + return state == State.PAUSED; + } + + public void run() + { + state = State.RUNNING; + } + + public void pause() + { + state = State.PAUSED; + } + + public void stop() + { + state = State.STOPPED; + } + } } diff --git a/Source/Android/app/src/main/res/layout-television/activity_emulation.xml b/Source/Android/app/src/main/res/layout-television/activity_emulation.xml index 59e1ba02ec..ad67bcb05e 100644 --- a/Source/Android/app/src/main/res/layout-television/activity_emulation.xml +++ b/Source/Android/app/src/main/res/layout-television/activity_emulation.xml @@ -5,11 +5,11 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/frame_content"> - + android:layout_height="match_parent"/> - + android:layout_height="match_parent"/> GetStaticMethodID(s_jni_class, "displayAlertMsg", "(Ljava/lang/String;)V"); - s_jni_method_end = env->GetStaticMethodID(s_jni_class, "endEmulationActivity", "()V"); } // Surface Handling @@ -816,9 +814,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv* ANativeWindow_release(s_surf); s_surf = nullptr; } - - // Execute the Java method. - env->CallStaticVoidMethod(s_jni_class, s_jni_method_end); } #ifdef __cplusplus From c4d7814afa0e97784c6bc6b97dd9578c06867dd6 Mon Sep 17 00:00:00 2001 From: Mike Harris Date: Sat, 7 Oct 2017 23:12:08 -0700 Subject: [PATCH 10/13] Collapse layouts with a framelayout root then another viewgroup. There's no point to this, and it just slow things down (technically). --- .../layout-television/fragment_emulation.xml | 22 ++--- .../res/layout/fragment_saveload_state.xml | 98 +++++++++---------- 2 files changed, 52 insertions(+), 68 deletions(-) diff --git a/Source/Android/app/src/main/res/layout-television/fragment_emulation.xml b/Source/Android/app/src/main/res/layout-television/fragment_emulation.xml index 276f5ad133..27243e8411 100644 --- a/Source/Android/app/src/main/res/layout-television/fragment_emulation.xml +++ b/Source/Android/app/src/main/res/layout-television/fragment_emulation.xml @@ -1,15 +1,7 @@ - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/Source/Android/app/src/main/res/layout/fragment_saveload_state.xml b/Source/Android/app/src/main/res/layout/fragment_saveload_state.xml index 64ba331553..56bb02f01d 100644 --- a/Source/Android/app/src/main/res/layout/fragment_saveload_state.xml +++ b/Source/Android/app/src/main/res/layout/fragment_saveload_state.xml @@ -1,60 +1,52 @@ - - + - +