Merge pull request #6107 from hackbar/cleanup

Android UI: lots of cleanups, mainly around how Fragments are handled
This commit is contained in:
Pierre Bourdon 2017-10-17 10:12:56 +02:00 committed by GitHub
commit 774fca4d01
32 changed files with 503 additions and 503 deletions

View File

@ -1,8 +1,8 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 25 compileSdkVersion 26
buildToolsVersion '25.0.2' buildToolsVersion '26.0.2'
lintOptions { lintOptions {
// This is important as it will run lint but not abort on error // This is important as it will run lint but not abort on error
@ -71,7 +71,7 @@ android {
} }
ext { ext {
androidSupportVersion = '25.3.0' androidSupportVersion = '26.1.0'
} }
dependencies { dependencies {

View File

@ -12,13 +12,15 @@ import android.widget.Toast;
import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.utils.Log; import org.dolphinemu.dolphinemu.utils.Log;
import java.lang.ref.WeakReference;
/** /**
* Class which contains methods that interact * Class which contains methods that interact
* with the native side of the Dolphin code. * with the native side of the Dolphin code.
*/ */
public final class NativeLibrary public final class NativeLibrary
{ {
public static EmulationActivity sEmulationActivity; public static WeakReference<EmulationActivity> sEmulationActivity = new WeakReference<>(null);
/** /**
* Button type for use in onTouchEvent * Button type for use in onTouchEvent
@ -379,25 +381,34 @@ public final class NativeLibrary
public static void displayAlertMsg(final String alert) public static void displayAlertMsg(final String alert)
{ {
Log.error("[NativeLibrary] Alert: " + alert); Log.error("[NativeLibrary] Alert: " + alert);
sEmulationActivity.runOnUiThread(new Runnable() final EmulationActivity emulationActivity = sEmulationActivity.get();
if (emulationActivity != null)
{
emulationActivity.runOnUiThread(new Runnable()
{ {
@Override @Override
public void run() public void run()
{ {
Toast.makeText(sEmulationActivity, "Panic Alert: " + alert, Toast.LENGTH_LONG).show(); Toast.makeText(emulationActivity, "Panic Alert: " + alert, Toast.LENGTH_LONG).show();
} }
}); });
} }
else
public static void endEmulationActivity()
{ {
Log.verbose("[NativeLibrary]Ending EmulationActivity."); Log.warning("[NativeLibrary] EmulationActivity is null, can't do panic toast.");
sEmulationActivity.exitWithAnimation(); }
} }
public static void setEmulationActivity(EmulationActivity emulationActivity) public static void setEmulationActivity(EmulationActivity emulationActivity)
{ {
Log.verbose("[NativeLibrary]Registering EmulationActivity."); Log.verbose("[NativeLibrary] Registering EmulationActivity.");
sEmulationActivity = emulationActivity; sEmulationActivity = new WeakReference<>(emulationActivity);
}
public static void clearEmulationActivity()
{
Log.verbose("[NativeLibrary] Unregistering EmulationActivity.");
sEmulationActivity.clear();
} }
} }

View File

@ -1,12 +1,12 @@
package org.dolphinemu.dolphinemu.activities; package org.dolphinemu.dolphinemu.activities;
import android.app.Activity;
import android.content.AsyncQueryHandler; import android.content.AsyncQueryHandler;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.support.v4.app.FragmentActivity;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
@ -133,7 +133,7 @@ public class AddDirectoryActivity extends AppCompatActivity implements FileAdapt
mToolbar.setSubtitle(path); mToolbar.setSubtitle(path);
} }
public static void launch(Activity activity) public static void launch(FragmentActivity activity)
{ {
Intent fileChooser = new Intent(activity, AddDirectoryActivity.class); Intent fileChooser = new Intent(activity, AddDirectoryActivity.class);
activity.startActivityForResult(fileChooser, MainPresenter.REQUEST_ADD_DIRECTORY); activity.startActivityForResult(fileChooser, MainPresenter.REQUEST_ADD_DIRECTORY);

View File

@ -1,9 +1,6 @@
package org.dolphinemu.dolphinemu.activities; package org.dolphinemu.dolphinemu.activities;
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Fragment;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
@ -14,6 +11,10 @@ import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import android.view.InputDevice; import android.view.InputDevice;
@ -23,10 +24,7 @@ import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar; import android.widget.SeekBar;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
@ -44,7 +42,6 @@ import org.dolphinemu.dolphinemu.ui.platform.Platform;
import org.dolphinemu.dolphinemu.utils.Animations; import org.dolphinemu.dolphinemu.utils.Animations;
import org.dolphinemu.dolphinemu.utils.Java_GCAdapter; import org.dolphinemu.dolphinemu.utils.Java_GCAdapter;
import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter; import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter;
import org.dolphinemu.dolphinemu.utils.Log;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.util.List; import java.util.List;
@ -53,12 +50,11 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
public final class EmulationActivity extends AppCompatActivity public final class EmulationActivity extends AppCompatActivity
{ {
private static final String FRAGMENT_SUBMENU_TAG = "submenu"; private static final String BACKSTACK_NAME_MENU = "menu";
private static final String BACKSTACK_NAME_SUBMENU = "submenu";
private View mDecorView; private View mDecorView;
private ImageView mImageView; private ImageView mImageView;
private EmulationFragment mEmulationFragment;
private FrameLayout mFrameEmulation;
private LinearLayout mMenuLayout;
private SharedPreferences mPreferences; private SharedPreferences mPreferences;
@ -68,14 +64,8 @@ public final class EmulationActivity extends AppCompatActivity
private boolean mDeviceHasTouchScreen; private boolean mDeviceHasTouchScreen;
private boolean mSystemUiVisible; private boolean mSystemUiVisible;
private boolean mMenuVisible; private boolean mMenuVisible;
private boolean mSubMenuVisible = false;
private static boolean mIsGameCubeGame; private static boolean sIsGameCubeGame;
private enum MenuType
{
SAVE, LOAD
}
/** /**
* Handlers are a way to pass a message to an Activity telling it to do something * Handlers are a way to pass a message to an Activity telling it to do something
@ -91,7 +81,6 @@ public final class EmulationActivity extends AppCompatActivity
} }
}; };
private String mScreenPath; private String mScreenPath;
private FrameLayout mFrameContent;
private String mSelectedTitle; private String mSelectedTitle;
@Retention(SOURCE) @Retention(SOURCE)
@ -157,7 +146,7 @@ public final class EmulationActivity extends AppCompatActivity
buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT); 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); Intent launcher = new Intent(activity, EmulationActivity.class);
@ -166,17 +155,28 @@ public final class EmulationActivity extends AppCompatActivity
launcher.putExtra("ScreenPath", screenshotPath); launcher.putExtra("ScreenPath", screenshotPath);
launcher.putExtra("GridPosition", position); launcher.putExtra("GridPosition", position);
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation( ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
activity, activity,
sharedView, sharedView,
"image_game_screenshot"); "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()); activity.startActivityForResult(launcher, MainPresenter.REQUEST_EMULATE_GAME, options.toBundle());
} }
@Override @Override
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(savedInstanceState);
// Get params we were passed
Intent gameToEmulate = getIntent();
String path = gameToEmulate.getStringExtra("SelectedGame");
sIsGameCubeGame = Platform.fromNativeInt(NativeLibrary.GetPlatform(path)) == Platform.GAMECUBE;
mSelectedTitle = gameToEmulate.getStringExtra("SelectedTitle");
mScreenPath = gameToEmulate.getStringExtra("ScreenPath");
mPosition = gameToEmulate.getIntExtra("GridPosition", -1);
mDeviceHasTouchScreen = getPackageManager().hasSystemFeature("android.hardware.touchscreen"); mDeviceHasTouchScreen = getPackageManager().hasSystemFeature("android.hardware.touchscreen");
int themeId; int themeId;
@ -219,30 +219,22 @@ public final class EmulationActivity extends AppCompatActivity
} }
setTheme(themeId); setTheme(themeId);
super.onCreate(savedInstanceState);
Java_GCAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE); Java_GCAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE);
Java_WiimoteAdapter.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);
mEmulationFragment = (EmulationFragment) getSupportFragmentManager()
.findFragmentById(R.id.fragment_emulation);
if (savedInstanceState == null)
{
// Picasso will take a while to load these big-ass screenshots. So don't run // Picasso will take a while to load these big-ass screenshots. So don't run
// the animation until we say so. // the animation until we say so.
postponeEnterTransition(); postponeEnterTransition();
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);
Intent gameToEmulate = getIntent();
String path = gameToEmulate.getStringExtra("SelectedGame");
mSelectedTitle = gameToEmulate.getStringExtra("SelectedTitle");
mScreenPath = gameToEmulate.getStringExtra("ScreenPath");
mPosition = gameToEmulate.getIntExtra("GridPosition", -1);
if (savedInstanceState == null)
{
Picasso.with(this) Picasso.with(this)
.load(mScreenPath) .load(mScreenPath)
.noFade() .noFade()
@ -252,27 +244,19 @@ public final class EmulationActivity extends AppCompatActivity
@Override @Override
public void onSuccess() public void onSuccess()
{ {
scheduleStartPostponedTransition(mImageView); supportStartPostponedEnterTransition();
} }
@Override @Override
public void onError() public void onError()
{ {
// Still have to do this, or else the app will crash. // Still have to do this, or else the app will crash.
scheduleStartPostponedTransition(mImageView); supportStartPostponedEnterTransition();
} }
}); });
Animations.fadeViewOut(mImageView) Animations.fadeViewOut(mImageView)
.setStartDelay(2000) .setStartDelay(2000)
.withStartAction(new Runnable()
{
@Override
public void run()
{
mFrameEmulation.setVisibility(View.VISIBLE);
}
})
.withEndAction(new Runnable() .withEndAction(new Runnable()
{ {
@Override @Override
@ -282,55 +266,21 @@ public final class EmulationActivity extends AppCompatActivity
} }
}); });
// Instantiate an EmulationFragment. mEmulationFragment.setGamePath(path);
EmulationFragment emulationFragment = EmulationFragment.newInstance(path); mEmulationFragment.startEmulation();
// Add fragment to the activity - this triggers all its lifecycle callbacks.
getFragmentManager().beginTransaction()
.add(R.id.frame_emulation_fragment, emulationFragment, EmulationFragment.FRAGMENT_TAG)
.commit();
} }
else else
{ {
mImageView.setVisibility(View.GONE); mImageView.setVisibility(View.GONE);
mFrameEmulation.setVisibility(View.VISIBLE);
} }
if (mDeviceHasTouchScreen) if (mDeviceHasTouchScreen)
{ {
setTitle(mSelectedTitle); setTitle(mSelectedTitle);
} }
else
{
MenuFragment menuFragment = (MenuFragment) getFragmentManager()
.findFragmentById(R.id.fragment_menu);
if (menuFragment != null)
{
menuFragment.setTitleText(mSelectedTitle);
}
}
mPreferences = PreferenceManager.getDefaultSharedPreferences(this); mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
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.setEmulationActivity(null);
} }
@Override @Override
@ -370,56 +320,43 @@ public final class EmulationActivity extends AppCompatActivity
{ {
if (!mDeviceHasTouchScreen) if (!mDeviceHasTouchScreen)
{ {
if (mSubMenuVisible) boolean popResult = getSupportFragmentManager().popBackStackImmediate(
{ BACKSTACK_NAME_SUBMENU, FragmentManager.POP_BACK_STACK_INCLUSIVE);
removeSubMenu(); if (!popResult)
}
else
{ {
toggleMenu(); toggleMenu();
} }
} }
else else
{ {
stopEmulation(); mEmulationFragment.stopEmulation();
exitWithAnimation();
} }
} }
private void toggleMenu() private void toggleMenu()
{ {
if (mMenuVisible) boolean result = getSupportFragmentManager().popBackStackImmediate(
{ BACKSTACK_NAME_MENU, FragmentManager.POP_BACK_STACK_INCLUSIVE);
mMenuVisible = false; mMenuVisible = false;
Animations.fadeViewOutToLeft(mMenuLayout) if (!result) {
.withEndAction(new Runnable() // Removing the menu failed, so that means it wasn't visible. Add it.
{ Fragment fragment = MenuFragment.newInstance(mSelectedTitle);
@Override getSupportFragmentManager().beginTransaction()
public void run() .setCustomAnimations(
{ R.animator.menu_slide_in_from_left,
if (mMenuVisible) R.animator.menu_slide_out_to_left,
{ R.animator.menu_slide_in_from_left,
mMenuLayout.setVisibility(View.GONE); R.animator.menu_slide_out_to_left)
} .add(R.id.frame_menu, fragment)
} .addToBackStack(BACKSTACK_NAME_MENU)
}); .commit();
}
else
{
mMenuVisible = true; mMenuVisible = true;
Animations.fadeViewInFromLeft(mMenuLayout);
} }
} }
private void stopEmulation()
{
EmulationFragment fragment = (EmulationFragment) getFragmentManager()
.findFragmentByTag(EmulationFragment.FRAGMENT_TAG);
fragment.notifyEmulationStopped();
NativeLibrary.StopEmulation();
}
public void exitWithAnimation() public void exitWithAnimation()
{ {
runOnUiThread(new Runnable() runOnUiThread(new Runnable()
@ -463,9 +400,8 @@ public final class EmulationActivity extends AppCompatActivity
@Override @Override
public void run() public void run()
{ {
mFrameContent.removeView(mFrameEmulation);
setResult(mPosition); setResult(mPosition);
finishAfterTransition(); supportFinishAfterTransition();
} }
}; };
@ -473,7 +409,7 @@ public final class EmulationActivity extends AppCompatActivity
public boolean onCreateOptionsMenu(Menu menu) public boolean onCreateOptionsMenu(Menu menu)
{ {
// Inflate the menu; this adds items to the action bar if it is present. // Inflate the menu; this adds items to the action bar if it is present.
if (mIsGameCubeGame) if (sIsGameCubeGame)
{ {
getMenuInflater().inflate(R.menu.menu_emulation, menu); getMenuInflater().inflate(R.menu.menu_emulation, menu);
} }
@ -542,14 +478,14 @@ public final class EmulationActivity extends AppCompatActivity
case MENU_ACTION_SAVE_ROOT: case MENU_ACTION_SAVE_ROOT:
if (!mDeviceHasTouchScreen) if (!mDeviceHasTouchScreen)
{ {
showMenu(SaveLoadStateFragment.SaveOrLoad.SAVE); showSubMenu(SaveLoadStateFragment.SaveOrLoad.SAVE);
} }
return; return;
case MENU_ACTION_LOAD_ROOT: case MENU_ACTION_LOAD_ROOT:
if (!mDeviceHasTouchScreen) if (!mDeviceHasTouchScreen)
{ {
showMenu(SaveLoadStateFragment.SaveOrLoad.LOAD); showSubMenu(SaveLoadStateFragment.SaveOrLoad.LOAD);
} }
return; return;
@ -604,20 +540,23 @@ public final class EmulationActivity extends AppCompatActivity
return; return;
case MENU_ACTION_EXIT: case MENU_ACTION_EXIT:
toggleMenu(); toggleMenu(); // Hide the menu (it will be showing since we just clicked it)
stopEmulation(); mEmulationFragment.stopEmulation();
exitWithAnimation();
return; return;
} }
} }
private void editControlsPlacement() { private void editControlsPlacement()
EmulationFragment emulationFragment = (EmulationFragment) getFragmentManager() {
.findFragmentById(R.id.frame_emulation_fragment); if (mEmulationFragment.isConfiguringControls())
if (emulationFragment.isConfiguringControls()) { {
emulationFragment.stopConfiguringControls(); mEmulationFragment.stopConfiguringControls();
} else { }
emulationFragment.startConfiguringControls(); else
{
mEmulationFragment.startConfiguringControls();
} }
} }
@ -660,7 +599,7 @@ public final class EmulationActivity extends AppCompatActivity
boolean[] enabledButtons = new boolean[14]; boolean[] enabledButtons = new boolean[14];
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.emulation_toggle_controls); builder.setTitle(R.string.emulation_toggle_controls);
if (mIsGameCubeGame || mPreferences.getInt("wiiController", 3) == 0) { if (sIsGameCubeGame || mPreferences.getInt("wiiController", 3) == 0) {
for (int i = 0; i < enabledButtons.length; i++) { for (int i = 0; i < enabledButtons.length; i++) {
enabledButtons[i] = mPreferences.getBoolean("buttonToggleGc" + i, true); enabledButtons[i] = mPreferences.getBoolean("buttonToggleGc" + i, true);
} }
@ -706,20 +645,18 @@ public final class EmulationActivity extends AppCompatActivity
} }
builder.setNeutralButton(getString(R.string.emulation_toggle_all), new DialogInterface.OnClickListener() { builder.setNeutralButton(getString(R.string.emulation_toggle_all), new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialogInterface, int i) { public void onClick(DialogInterface dialogInterface, int i)
EmulationFragment emulationFragment = (EmulationFragment) getFragmentManager() {
.findFragmentByTag(EmulationFragment.FRAGMENT_TAG); mEmulationFragment.toggleInputOverlayVisibility();
emulationFragment.toggleInputOverlayVisibility();
} }
}); });
builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialogInterface, int i) { public void onClick(DialogInterface dialogInterface, int i)
{
editor.apply(); editor.apply();
EmulationFragment emulationFragment = (EmulationFragment) getFragmentManager() mEmulationFragment.refreshInputOverlay();
.findFragmentByTag(EmulationFragment.FRAGMENT_TAG);
emulationFragment.refreshInputOverlay();
} }
}); });
@ -764,9 +701,7 @@ public final class EmulationActivity extends AppCompatActivity
editor.putInt("controlScale", seekbar.getProgress()); editor.putInt("controlScale", seekbar.getProgress());
editor.apply(); editor.apply();
EmulationFragment emulationFragment = (EmulationFragment) getFragmentManager() mEmulationFragment.refreshInputOverlay();
.findFragmentByTag(EmulationFragment.FRAGMENT_TAG);
emulationFragment.refreshInputOverlay();
} }
}); });
@ -793,9 +728,7 @@ public final class EmulationActivity extends AppCompatActivity
public void onClick(DialogInterface dialogInterface, int i) { public void onClick(DialogInterface dialogInterface, int i) {
editor.apply(); editor.apply();
EmulationFragment emulationFragment = (EmulationFragment) getFragmentManager() mEmulationFragment.refreshInputOverlay();
.findFragmentByTag(EmulationFragment.FRAGMENT_TAG);
emulationFragment.refreshInputOverlay();
Toast.makeText(getApplication(), R.string.emulation_controller_changed, Toast.LENGTH_SHORT).show(); Toast.makeText(getApplication(), R.string.emulation_controller_changed, Toast.LENGTH_SHORT).show();
} }
@ -866,60 +799,22 @@ public final class EmulationActivity extends AppCompatActivity
hideSystemUiAfterDelay(); hideSystemUiAfterDelay();
} }
private void showSubMenu(SaveLoadStateFragment.SaveOrLoad saveOrLoad)
{
// Get rid of any visible submenu
getSupportFragmentManager().popBackStack(
BACKSTACK_NAME_SUBMENU, FragmentManager.POP_BACK_STACK_INCLUSIVE);
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); Fragment fragment = SaveLoadStateFragment.newInstance(saveOrLoad);
getFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.animator.menu_slide_in, R.animator.menu_slide_out) .setCustomAnimations(
.replace(R.id.frame_submenu, fragment, FRAGMENT_SUBMENU_TAG) R.animator.menu_slide_in_from_right,
R.animator.menu_slide_out_to_right,
R.animator.menu_slide_in_from_right,
R.animator.menu_slide_out_to_right)
.replace(R.id.frame_submenu, fragment)
.addToBackStack(BACKSTACK_NAME_SUBMENU)
.commit(); .commit();
mSubMenuVisible = true;
}
private void removeSubMenu()
{
final Fragment fragment = getFragmentManager().findFragmentByTag(FRAGMENT_SUBMENU_TAG);
if (fragment != null)
{
// When removing a fragment without replacement, its animation must be done
// manually beforehand.
Animations.fadeViewOutToRight(fragment.getView())
.withEndAction(new Runnable()
{
@Override
public void run()
{
if (mMenuVisible)
{
getFragmentManager().beginTransaction()
.remove(fragment)
.commit();
}
}
});
}
else
{
Log.error("[EmulationActivity] Fragment not found, can't remove.");
}
mSubMenuVisible = false;
} }
public String getSelectedTitle() public String getSelectedTitle()
@ -929,6 +824,6 @@ public final class EmulationActivity extends AppCompatActivity
public static boolean isGameCubeGame() public static boolean isGameCubeGame()
{ {
return mIsGameCubeGame; return sIsGameCubeGame;
} }
} }

View File

@ -1,9 +1,9 @@
package org.dolphinemu.dolphinemu.adapters; package org.dolphinemu.dolphinemu.adapters;
import android.app.Activity;
import android.database.Cursor; import android.database.Cursor;
import android.database.DataSetObserver; import android.database.DataSetObserver;
import android.graphics.Rect; import android.graphics.Rect;
import android.support.v4.app.FragmentActivity;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -202,7 +202,7 @@ public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> impl
{ {
GameViewHolder holder = (GameViewHolder) view.getTag(); GameViewHolder holder = (GameViewHolder) view.getTag();
EmulationActivity.launch((Activity) view.getContext(), EmulationActivity.launch((FragmentActivity) view.getContext(),
holder.path, holder.path,
holder.title, holder.title,
holder.screenshotPath, holder.screenshotPath,
@ -225,13 +225,13 @@ public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> impl
// TODO This should be all we need to pass in, eventually. // TODO This should be all we need to pass in, eventually.
// String gameId = (String) holder.gameId; // String gameId = (String) holder.gameId;
Activity activity = (Activity) view.getContext(); FragmentActivity activity = (FragmentActivity) view.getContext();
GameDetailsDialog.newInstance(holder.title, GameDetailsDialog.newInstance(holder.title,
holder.description, holder.description,
holder.country, holder.country,
holder.company, holder.company,
holder.path, holder.path,
holder.screenshotPath).show(activity.getFragmentManager(), "game_details"); holder.screenshotPath).show(activity.getSupportFragmentManager(), "game_details");
return true; return true;
} }

View File

@ -1,10 +1,10 @@
package org.dolphinemu.dolphinemu.adapters; package org.dolphinemu.dolphinemu.adapters;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable; 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.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.style.ImageSpan; import android.text.style.ImageSpan;

View File

@ -2,9 +2,9 @@ package org.dolphinemu.dolphinemu.dialogs;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.app.DialogFragment;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.DialogFragment;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;

View File

@ -1,9 +1,10 @@
package org.dolphinemu.dolphinemu.fragments; package org.dolphinemu.dolphinemu.fragments;
import android.app.Fragment; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Surface; import android.view.Surface;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
@ -14,15 +15,12 @@ import android.widget.Button;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.overlay.InputOverlay; import org.dolphinemu.dolphinemu.overlay.InputOverlay;
import org.dolphinemu.dolphinemu.utils.Log; import org.dolphinemu.dolphinemu.utils.Log;
public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback 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 SharedPreferences mPreferences;
private Surface mSurface; private Surface mSurface;
@ -31,18 +29,22 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
private Thread mEmulationThread; private Thread mEmulationThread;
private boolean mEmulationStarted; private String mGamePath;
private boolean mEmulationRunning; 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(); if (context instanceof EmulationActivity)
arguments.putString(ARG_GAME_PATH, path); {
fragment.setArguments(arguments); NativeLibrary.setEmulationActivity((EmulationActivity) context);
}
return fragment; 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); View contents = inflater.inflate(R.layout.fragment_emulation, container, false);
SurfaceView surfaceView = (SurfaceView) contents.findViewById(R.id.surface_emulation); SurfaceView surfaceView = contents.findViewById(R.id.surface_emulation);
mInputOverlay = (InputOverlay) contents.findViewById(R.id.surface_input_overlay);
surfaceView.getHolder().addCallback(this); 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 (mInputOverlay != null)
{ {
// If the input overlay was previously disabled, then don't show it.
if (!mPreferences.getBoolean("showInputOverlay", true)) if (!mPreferences.getBoolean("showInputOverlay", true))
{ {
mInputOverlay.setVisibility(View.GONE); mInputOverlay.setVisibility(View.GONE);
} }
} }
if (savedInstanceState == null) Button doneButton = contents.findViewById(R.id.done_control_config);
{
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);
if (doneButton != null) if (doneButton != null)
{ {
doneButton.setOnClickListener(new View.OnClickListener() doneButton.setOnClickListener(new View.OnClickListener()
@ -110,29 +94,29 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
} }
}); });
} }
}
@Override // The new Surface created here will get passed to the native code via onSurfaceChanged.
public void onStart()
{ return contents;
super.onStart();
startEmulation();
} }
@Override @Override
public void onStop() public void onStop()
{ {
pauseEmulation();
super.onStop(); super.onStop();
} }
@Override @Override
public void onDestroyView() public void onDetach()
{ {
super.onDestroyView(); NativeLibrary.clearEmulationActivity();
if (getActivity().isFinishing() && mEmulationStarted) super.onDetach();
{
NativeLibrary.StopEmulation();
} }
public void setGamePath(String gamePath)
{
mGamePath = gamePath;
} }
public void toggleInputOverlayVisibility() 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) public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{ {
Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height); Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height);
if (mEmulationState.isPaused())
{
NativeLibrary.UnPauseEmulation();
}
mSurface = holder.getSurface(); mSurface = holder.getSurface();
NativeLibrary.SurfaceChanged(mSurface); NativeLibrary.SurfaceChanged(mSurface);
} }
@ -181,45 +171,57 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
Log.debug("[EmulationFragment] Surface destroyed."); Log.debug("[EmulationFragment] Surface destroyed.");
NativeLibrary.SurfaceDestroyed(); NativeLibrary.SurfaceDestroyed();
if (mEmulationRunning) if (mEmulationState.isRunning())
{ {
pauseEmulation(); pauseEmulation();
} }
} }
private void startEmulation() public void startEmulation()
{ {
if (!mEmulationStarted) synchronized (mEmulationState)
{
if (mEmulationState.isStopped())
{ {
Log.debug("[EmulationFragment] Starting emulation thread."); Log.debug("[EmulationFragment] Starting emulation thread.");
mEmulationThread = new Thread(mEmulationRunner, "NativeEmulation");
mEmulationThread.start(); mEmulationThread.start();
// The thread will call mEmulationState.run()
} }
else else if (mEmulationState.isPaused())
{ {
Log.debug("[EmulationFragment] Resuming emulation."); Log.debug("[EmulationFragment] Resuming emulation.");
NativeLibrary.UnPauseEmulation(); NativeLibrary.UnPauseEmulation();
mEmulationState.run();
}
else
{
Log.debug("[EmulationFragment] Bug, startEmulation called while running.");
}
}
} }
mEmulationRunning = true; public void stopEmulation() {
synchronized (mEmulationState)
{
if (!mEmulationState.isStopped())
{
NativeLibrary.StopEmulation();
mEmulationState.stop();
}
}
} }
private void pauseEmulation() private void pauseEmulation()
{
synchronized (mEmulationState)
{ {
Log.debug("[EmulationFragment] Pausing emulation."); Log.debug("[EmulationFragment] Pausing emulation.");
NativeLibrary.PauseEmulation(); NativeLibrary.PauseEmulation();
mEmulationRunning = false; mEmulationState.pause();
} }
/**
* 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;
} }
private Runnable mEmulationRunner = new Runnable() private Runnable mEmulationRunner = new Runnable()
@ -227,18 +229,17 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
@Override @Override
public void run() public void run()
{ {
mEmulationRunning = true; // Busy-wait for surface to be set
mEmulationStarted = true; while (mSurface == null) {}
while (mSurface == null)
if (!mEmulationRunning)
return;
synchronized (mEmulationState)
{
Log.info("[EmulationFragment] Starting emulation: " + mSurface); Log.info("[EmulationFragment] Starting emulation: " + mSurface);
mEmulationState.run();
}
// Start emulation using the provided Surface. // Start emulation using the provided Surface.
String path = getArguments().getString(ARG_GAME_PATH); NativeLibrary.Run(mGamePath);
NativeLibrary.Run(path);
} }
}; };
@ -258,4 +259,50 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
{ {
return mInputOverlay.isInEditMode(); 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;
}
}
} }

View File

@ -1,8 +1,8 @@
package org.dolphinemu.dolphinemu.fragments; package org.dolphinemu.dolphinemu.fragments;
import android.app.Fragment;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -16,7 +16,7 @@ import org.dolphinemu.dolphinemu.activities.EmulationActivity;
public final class MenuFragment extends Fragment implements View.OnClickListener public final class MenuFragment extends Fragment implements View.OnClickListener
{ {
private TextView mTitleText; private static final String KEY_TITLE = "title";
private static SparseIntArray buttonsActionsMap = new SparseIntArray(); private static SparseIntArray buttonsActionsMap = new SparseIntArray();
static { static {
buttonsActionsMap.append(R.id.menu_take_screenshot, EmulationActivity.MENU_ACTION_TAKE_SCREENSHOT); buttonsActionsMap.append(R.id.menu_take_screenshot, EmulationActivity.MENU_ACTION_TAKE_SCREENSHOT);
@ -28,6 +28,17 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT); buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT);
} }
public static MenuFragment newInstance(String title)
{
MenuFragment fragment = new MenuFragment();
Bundle arguments = new Bundle();
arguments.putSerializable(KEY_TITLE, title);
fragment.setArguments(arguments);
return fragment;
}
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
@ -42,7 +53,12 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
button.setOnClickListener(this); button.setOnClickListener(this);
} }
mTitleText = (TextView) rootView.findViewById(R.id.text_game_title); TextView titleText = rootView.findViewById(R.id.text_game_title);
String title = getArguments().getString(KEY_TITLE);
if (title != null)
{
titleText.setText(title);
}
return rootView; return rootView;
} }
@ -57,9 +73,4 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
((EmulationActivity) getActivity()).handleMenuAction(action); ((EmulationActivity) getActivity()).handleMenuAction(action);
} }
} }
public void setTitleText(String title)
{
mTitleText.setText(title);
}
} }

View File

@ -1,8 +1,8 @@
package org.dolphinemu.dolphinemu.fragments; package org.dolphinemu.dolphinemu.fragments;
import android.app.Fragment;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;

View File

@ -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);
}
}

View File

@ -70,7 +70,8 @@ public final class MainActivity extends AppCompatActivity implements MainView
if (PermissionsHandler.hasWriteAccess(this)) if (PermissionsHandler.hasWriteAccess(this))
{ {
PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(getFragmentManager(), this); PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(
getSupportFragmentManager(), this);
mViewPager.setAdapter(platformPagerAdapter); mViewPager.setAdapter(platformPagerAdapter);
} else { } else {
mViewPager.setVisibility(View.INVISIBLE); mViewPager.setVisibility(View.INVISIBLE);
@ -162,7 +163,8 @@ public final class MainActivity extends AppCompatActivity implements MainView
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
StartupHandler.copyAssetsIfNeeded(this); StartupHandler.copyAssetsIfNeeded(this);
PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(getFragmentManager(), this); PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(
getSupportFragmentManager(), this);
mViewPager.setAdapter(platformPagerAdapter); mViewPager.setAdapter(platformPagerAdapter);
mTabLayout.setupWithViewPager(mViewPager); mTabLayout.setupWithViewPager(mViewPager);
mViewPager.setVisibility(View.VISIBLE); mViewPager.setVisibility(View.VISIBLE);
@ -205,6 +207,6 @@ public final class MainActivity extends AppCompatActivity implements MainView
{ {
String fragmentTag = "android:switcher:" + mViewPager.getId() + ":" + platform; String fragmentTag = "android:switcher:" + mViewPager.getId() + ":" + platform;
return (PlatformGamesView) getFragmentManager().findFragmentByTag(fragmentTag); return (PlatformGamesView) getSupportFragmentManager().findFragmentByTag(fragmentTag);
} }
} }

View File

@ -1,12 +1,11 @@
package org.dolphinemu.dolphinemu.ui.main; package org.dolphinemu.dolphinemu.ui.main;
import android.app.Activity;
import android.app.FragmentManager;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.Cursor; import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.support.v17.leanback.app.BrowseFragment; 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.database.CursorMapper;
import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.CursorObjectAdapter; 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.Presenter;
import android.support.v17.leanback.widget.Row; import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter; 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.support.v4.content.ContextCompat;
import android.widget.Toast; import android.widget.Toast;
@ -33,11 +34,11 @@ import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
import org.dolphinemu.dolphinemu.utils.StartupHandler; import org.dolphinemu.dolphinemu.utils.StartupHandler;
import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder; 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 MainPresenter mPresenter = new MainPresenter(this);
private BrowseFragment mBrowseFragment; private BrowseSupportFragment mBrowseFragment;
private ArrayObjectAdapter mRowsAdapter; private ArrayObjectAdapter mRowsAdapter;
@ -57,8 +58,8 @@ public final class TvMainActivity extends Activity implements MainView
} }
void setupUI() { void setupUI() {
final FragmentManager fragmentManager = getFragmentManager(); final FragmentManager fragmentManager = getSupportFragmentManager();
mBrowseFragment = new BrowseFragment(); mBrowseFragment = new BrowseSupportFragment();
fragmentManager fragmentManager
.beginTransaction() .beginTransaction()
.add(R.id.content, mBrowseFragment, "BrowseFragment") .add(R.id.content, mBrowseFragment, "BrowseFragment")

View File

@ -1,9 +1,9 @@
package org.dolphinemu.dolphinemu.ui.platform; package org.dolphinemu.dolphinemu.ui.platform;
import android.app.Fragment;
import android.database.Cursor; import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;

View File

@ -1,9 +1,9 @@
package org.dolphinemu.dolphinemu.ui.settings; package org.dolphinemu.dolphinemu.ui.settings;
import android.app.FragmentTransaction;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@ -88,7 +88,7 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
@Override @Override
public void showSettingsFragment(String menuTag, boolean addToStack) public void showSettingsFragment(String menuTag, boolean addToStack)
{ {
FragmentTransaction transaction = getFragmentManager().beginTransaction(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if (addToStack) if (addToStack)
{ {
@ -149,7 +149,7 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
@Override @Override
public void popBackStack() public void popBackStack()
{ {
getFragmentManager().popBackStackImmediate(); getSupportFragmentManager().popBackStackImmediate();
} }
@Override @Override
@ -178,6 +178,6 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
private SettingsFragment getFragment() private SettingsFragment getFragment()
{ {
return (SettingsFragment) getFragmentManager().findFragmentByTag(FRAGMENT_TAG); return (SettingsFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
} }
} }

View File

@ -1,10 +1,10 @@
package org.dolphinemu.dolphinemu.ui.settings; package org.dolphinemu.dolphinemu.ui.settings;
import android.app.Activity; import android.app.Activity;
import android.app.Fragment;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;

View File

@ -1,6 +1,6 @@
package org.dolphinemu.dolphinemu.ui.settings; 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.Setting;
import org.dolphinemu.dolphinemu.model.settings.SettingSection; import org.dolphinemu.dolphinemu.model.settings.SettingSection;
@ -48,7 +48,7 @@ public interface SettingsFragmentView
/** /**
* @return The Fragment's containing activity. * @return The Fragment's containing activity.
*/ */
Activity getActivity(); FragmentActivity getActivity();
/** /**
* Tell the Fragment to tell the containing Activity to show a new * Tell the Fragment to tell the containing Activity to show a new

View File

@ -2,55 +2,13 @@ package org.dolphinemu.dolphinemu.utils;
import android.view.View; import android.view.View;
import android.view.ViewPropertyAnimator; import android.view.ViewPropertyAnimator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
public final class Animations public final class Animations
{ {
private static final Interpolator DECELERATOR = new DecelerateInterpolator();
private static final Interpolator ACCELERATOR = new AccelerateInterpolator();
private Animations() private Animations()
{ {
} }
public static ViewPropertyAnimator fadeViewOutToRight(View view)
{
return view.animate()
.withLayer()
.setDuration(200)
.setInterpolator(ACCELERATOR)
.alpha(0.0f)
.translationX(view.getWidth());
}
public static ViewPropertyAnimator fadeViewOutToLeft(View view)
{
return view.animate()
.withLayer()
.setDuration(200)
.setInterpolator(ACCELERATOR)
.alpha(0.0f)
.translationX(-view.getWidth());
}
public static ViewPropertyAnimator fadeViewInFromLeft(View view)
{
view.setVisibility(View.VISIBLE);
view.setTranslationX(-view.getWidth());
view.setAlpha(0.0f);
return view.animate()
.withLayer()
.setDuration(300)
.setInterpolator(DECELERATOR)
.alpha(1.0f)
.translationX(0.0f);
}
public static ViewPropertyAnimator fadeViewIn(View view) public static ViewPropertyAnimator fadeViewIn(View view)
{ {
view.setVisibility(View.VISIBLE); view.setVisibility(View.VISIBLE);

View File

@ -1,6 +1,8 @@
package org.dolphinemu.dolphinemu.utils; package org.dolphinemu.dolphinemu.utils;
import android.app.Activity;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.hardware.usb.UsbConfiguration; import android.hardware.usb.UsbConfiguration;
import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbConstants;
@ -15,7 +17,6 @@ import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.services.USBPermService; import org.dolphinemu.dolphinemu.services.USBPermService;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
public class Java_GCAdapter { public class Java_GCAdapter {
@ -28,6 +29,9 @@ public class Java_GCAdapter {
static UsbEndpoint usb_out; static UsbEndpoint usb_out;
private static void RequestPermission() private static void RequestPermission()
{
Context context = NativeLibrary.sEmulationActivity.get();
if (context != null)
{ {
HashMap<String, UsbDevice> devices = manager.getDeviceList(); HashMap<String, UsbDevice> devices = manager.getDeviceList();
for (Map.Entry<String, UsbDevice> pair : devices.entrySet()) for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
@ -39,13 +43,19 @@ public class Java_GCAdapter {
{ {
Intent intent = new Intent(); Intent intent = new Intent();
PendingIntent pend_intent; PendingIntent pend_intent;
intent.setClass(NativeLibrary.sEmulationActivity, USBPermService.class); intent.setClass(context, USBPermService.class);
pend_intent = PendingIntent.getService(NativeLibrary.sEmulationActivity, 0, intent, 0); pend_intent = PendingIntent.getService(context, 0, intent, 0);
manager.requestPermission(dev, pend_intent); manager.requestPermission(dev, pend_intent);
} }
} }
} }
} }
else
{
Log.warning("Cannot request GameCube Adapter permission as EmulationActivity is null.");
}
}
public static void Shutdown() 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)
{
emulationActivity.runOnUiThread(new Runnable()
{ {
@Override @Override
public void run() public void run()
{ {
Toast.makeText(NativeLibrary.sEmulationActivity, "GameCube Adapter couldn't be opened. Please re-plug the device.", Toast.LENGTH_LONG).show(); 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(); usb_con.close();
} }
} }

View File

@ -1,6 +1,7 @@
package org.dolphinemu.dolphinemu.utils; package org.dolphinemu.dolphinemu.utils;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.hardware.usb.UsbConfiguration; import android.hardware.usb.UsbConfiguration;
import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDevice;
@ -32,6 +33,9 @@ public class Java_WiimoteAdapter
public static byte[][] wiimote_payload = new byte[MAX_WIIMOTES][MAX_PAYLOAD]; public static byte[][] wiimote_payload = new byte[MAX_WIIMOTES][MAX_PAYLOAD];
private static void RequestPermission() private static void RequestPermission()
{
Context context = NativeLibrary.sEmulationActivity.get();
if (context != null)
{ {
HashMap<String, UsbDevice> devices = manager.getDeviceList(); HashMap<String, UsbDevice> devices = manager.getDeviceList();
for (Map.Entry<String, UsbDevice> pair : devices.entrySet()) for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
@ -44,13 +48,18 @@ public class Java_WiimoteAdapter
Log.warning("Requesting permission for Wii Remote adapter"); Log.warning("Requesting permission for Wii Remote adapter");
Intent intent = new Intent(); Intent intent = new Intent();
PendingIntent pend_intent; PendingIntent pend_intent;
intent.setClass(NativeLibrary.sEmulationActivity, USBPermService.class); intent.setClass(context, USBPermService.class);
pend_intent = PendingIntent.getService(NativeLibrary.sEmulationActivity, 0, intent, 0); pend_intent = PendingIntent.getService(context, 0, intent, 0);
manager.requestPermission(dev, pend_intent); manager.requestPermission(dev, pend_intent);
} }
} }
} }
} }
else
{
Log.warning("Cannot request Wiimote adapter permission as EmulationActivity is null.");
}
}
public static boolean QueryAdapter() public static boolean QueryAdapter()
{ {

View File

@ -1,11 +1,11 @@
package org.dolphinemu.dolphinemu.utils; package org.dolphinemu.dolphinemu.utils;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.widget.Toast; import android.widget.Toast;
@ -17,7 +17,7 @@ public class PermissionsHandler {
public static final int REQUEST_CODE_WRITE_PERMISSION = 500; public static final int REQUEST_CODE_WRITE_PERMISSION = 500;
@TargetApi(Build.VERSION_CODES.M) @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) { if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true; return true;
} }
@ -45,7 +45,7 @@ public class PermissionsHandler {
return true; 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) { if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int hasWritePermission = ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE); int hasWritePermission = ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE);
return hasWritePermission == PackageManager.PERMISSION_GRANTED; return hasWritePermission == PackageManager.PERMISSION_GRANTED;
@ -54,7 +54,7 @@ public class PermissionsHandler {
return true; 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) new AlertDialog.Builder(activity)
.setMessage(message) .setMessage(message)
.setPositiveButton(android.R.string.ok, okListener) .setPositiveButton(android.R.string.ok, okListener)

View File

@ -1,10 +1,10 @@
package org.dolphinemu.dolphinemu.utils; package org.dolphinemu.dolphinemu.utils;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v4.app.FragmentActivity;
import android.text.TextUtils; import android.text.TextUtils;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
@ -13,7 +13,7 @@ import org.dolphinemu.dolphinemu.services.AssetCopyService;
public final class StartupHandler public final class StartupHandler
{ {
public static boolean HandleInit(Activity parent) public static boolean HandleInit(FragmentActivity parent)
{ {
NativeLibrary.SetUserDirectory(""); // Auto-Detect NativeLibrary.SetUserDirectory(""); // Auto-Detect
@ -46,7 +46,7 @@ public final class StartupHandler
return false; return false;
} }
public static void copyAssetsIfNeeded(Activity parent) { public static void copyAssetsIfNeeded(FragmentActivity parent) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(parent); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(parent);
boolean assetsCopied = preferences.getBoolean("assetsCopied", false); boolean assetsCopied = preferences.getBoolean("assetsCopied", false);

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:propertyName="translationX"
android:valueType="floatType"
android:valueFrom="-1280dp"
android:valueTo="0"
android:interpolator="@android:interpolator/decelerate_quad"
android:duration="300"/>
<objectAnimator
android:propertyName="alpha"
android:valueType="floatType"
android:valueFrom="0"
android:valueTo="1"
android:interpolator="@android:interpolator/accelerate_quad"
android:duration="300"/>
</set>

View File

@ -4,7 +4,7 @@
<objectAnimator <objectAnimator
android:propertyName="translationX" android:propertyName="translationX"
android:valueType="floatType" android:valueType="floatType"
android:valueFrom="1280" android:valueFrom="1280dp"
android:valueTo="0" android:valueTo="0"
android:interpolator="@android:interpolator/decelerate_quad" android:interpolator="@android:interpolator/decelerate_quad"
android:duration="300"/> android:duration="300"/>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- This animation is used ONLY when a submenu is replaced. -->
<objectAnimator
android:propertyName="translationX"
android:valueType="floatType"
android:valueFrom="0"
android:valueTo="-1280dp"
android:interpolator="@android:interpolator/decelerate_quad"
android:duration="200"/>
<objectAnimator
android:propertyName="alpha"
android:valueType="floatType"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:interpolator/decelerate_quad"
android:duration="200"/>
</set>

View File

@ -6,9 +6,9 @@
android:propertyName="translationX" android:propertyName="translationX"
android:valueType="floatType" android:valueType="floatType"
android:valueFrom="0" android:valueFrom="0"
android:valueTo="1280" android:valueTo="1280dp"
android:interpolator="@android:interpolator/decelerate_quad" android:interpolator="@android:interpolator/decelerate_quad"
android:duration="300"/> android:duration="200"/>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="alpha" android:propertyName="alpha"
@ -16,6 +16,6 @@
android:valueFrom="1" android:valueFrom="1"
android:valueTo="0" android:valueTo="0"
android:interpolator="@android:interpolator/decelerate_quad" android:interpolator="@android:interpolator/decelerate_quad"
android:duration="300"/> android:duration="200"/>
</set> </set>

View File

@ -5,11 +5,15 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frame_content"> android:id="@+id/frame_content">
<FrameLayout <fragment
android:id="@+id/frame_emulation_fragment" android:id="@+id/fragment_emulation"
android:name="org.dolphinemu.dolphinemu.fragments.EmulationFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"/>
android:visibility="invisible"/>
<org.dolphinemu.dolphinemu.ui.NVidiaShieldWorkaroundView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView <ImageView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -17,28 +21,24 @@
android:id="@+id/image_screenshot" android:id="@+id/image_screenshot"
android:transitionName="image_game_screenshot"/> android:transitionName="image_game_screenshot"/>
<LinearLayout <LinearLayout
android:id="@+id/layout_ingame_menu"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="horizontal" android:orientation="horizontal">
android:visibility="gone"
tools:visibility="visible"
android:baselineAligned="false">
<fragment <FrameLayout
android:id="@+id/fragment_menu" android:id="@+id/frame_menu"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight=".25"
android:name="org.dolphinemu.dolphinemu.fragments.MenuFragment"
tools:layout="@layout/fragment_ingame_menu"/> tools:layout="@layout/fragment_ingame_menu"/>
<FrameLayout <FrameLayout
android:id="@+id/frame_submenu" android:id="@+id/frame_submenu"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="3"/> android:layout_weight=".75"/>
</LinearLayout> </LinearLayout>

View File

@ -1,15 +1,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <SurfaceView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="org.dolphinemu.dolphinemu.fragments.EmulationFragment">
<!-- This is what everything is rendered to during emulation -->
<SurfaceView
android:id="@+id/surface_emulation" android:id="@+id/surface_emulation"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:focusable="false" android:focusable="false"
android:focusableInTouchMode="false"/> android:focusableInTouchMode="false"
/>
</FrameLayout>

View File

@ -3,11 +3,11 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/frame_content"> android:id="@+id/frame_content">
<FrameLayout <fragment
android:id="@+id/frame_emulation_fragment" android:id="@+id/fragment_emulation"
android:name="org.dolphinemu.dolphinemu.fragments.EmulationFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"/>
android:visibility="invisible"/>
<ImageView <ImageView
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,17 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#af000000"
android:orientation="vertical">
<GridLayout
android:id="@+id/grid_state_slots" android:id="@+id/grid_state_slots"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:columnCount="3" android:columnCount="3"
android:rowCount="2" android:rowCount="2"
android:layout_gravity="center"> android:layout_gravity="center"
android:background="#af000000">
<Button <Button
android:id="@+id/loadsave_state_button_1" android:id="@+id/loadsave_state_button_1"
@ -55,6 +49,4 @@
android:text="@string/emulation_slot6" android:text="@string/emulation_slot6"
style="@style/OverlayInGameMenuOption"/> style="@style/OverlayInGameMenuOption"/>
</GridLayout> </GridLayout>
</FrameLayout>

View File

@ -3,12 +3,15 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.3.0' classpath 'com.android.tools.build:gradle:2.3.3'
} }
} }
allprojects { allprojects {
repositories { repositories {
jcenter() jcenter()
maven {
url "https://maven.google.com"
}
} }
} }

View File

@ -58,7 +58,6 @@ std::string s_set_userpath;
jclass s_jni_class; jclass s_jni_class;
jmethodID s_jni_method_alert; jmethodID s_jni_method_alert;
jmethodID s_jni_method_end;
// The Core only supports using a single Host thread. // The Core only supports using a single Host thread.
// If multiple threads want to call host functions then they need to queue // If multiple threads want to call host functions then they need to queue
@ -732,7 +731,6 @@ Java_org_dolphinemu_dolphinemu_NativeLibrary_CacheClassesAndMethods(JNIEnv* env,
// Source/Android/app/build/intermediates/classes/arm/debug/org/dolphinemu/dolphinemu/NativeLibrary.class // Source/Android/app/build/intermediates/classes/arm/debug/org/dolphinemu/dolphinemu/NativeLibrary.class
s_jni_method_alert = s_jni_method_alert =
env->GetStaticMethodID(s_jni_class, "displayAlertMsg", "(Ljava/lang/String;)V"); env->GetStaticMethodID(s_jni_class, "displayAlertMsg", "(Ljava/lang/String;)V");
s_jni_method_end = env->GetStaticMethodID(s_jni_class, "endEmulationActivity", "()V");
} }
// Surface Handling // Surface Handling
@ -816,9 +814,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv*
ANativeWindow_release(s_surf); ANativeWindow_release(s_surf);
s_surf = nullptr; s_surf = nullptr;
} }
// Execute the Java method.
env->CallStaticVoidMethod(s_jni_class, s_jni_method_end);
} }
#ifdef __cplusplus #ifdef __cplusplus