Android TV: Implement Save and Load state menus

This commit is contained in:
sigmabeta 2015-07-04 16:32:15 -04:00
parent d191d8851a
commit c0315fcf78
9 changed files with 375 additions and 5 deletions

View File

@ -1,5 +1,6 @@
package org.dolphinemu.dolphinemu.activities; package org.dolphinemu.dolphinemu.activities;
import android.app.Fragment;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@ -26,6 +27,8 @@ import com.squareup.picasso.Picasso;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.fragments.EmulationFragment; import org.dolphinemu.dolphinemu.fragments.EmulationFragment;
import org.dolphinemu.dolphinemu.fragments.LoadStateFragment;
import org.dolphinemu.dolphinemu.fragments.SaveStateFragment;
import java.util.List; import java.util.List;
@ -37,13 +40,15 @@ public final class EmulationActivity extends AppCompatActivity
private FrameLayout mFrameEmulation; private FrameLayout mFrameEmulation;
private LinearLayout mMenuLayout; private LinearLayout mMenuLayout;
private boolean mDeviceHasTouchScreen; private String mMenuFragmentTag;
private boolean mSystemUiVisible;
private boolean mMenuVisible;
// So that MainActivity knows which view to invalidate before the return animation. // So that MainActivity knows which view to invalidate before the return animation.
private int mPosition; private int mPosition;
private boolean mDeviceHasTouchScreen;
private boolean mSystemUiVisible;
private boolean mMenuVisible;
private static Interpolator sDecelerator = new DecelerateInterpolator(); private static Interpolator sDecelerator = new DecelerateInterpolator();
private static Interpolator sAccelerator = new AccelerateInterpolator(); private static Interpolator sAccelerator = new AccelerateInterpolator();
@ -235,7 +240,14 @@ public final class EmulationActivity extends AppCompatActivity
{ {
if (!mDeviceHasTouchScreen) if (!mDeviceHasTouchScreen)
{ {
toggleMenu(); if (mMenuFragmentTag != null)
{
removeMenu();
}
else
{
toggleMenu();
}
} }
else else
{ {
@ -390,6 +402,15 @@ public final class EmulationActivity extends AppCompatActivity
NativeLibrary.LoadState(9); NativeLibrary.LoadState(9);
return; return;
// TV Menu only
case R.id.menu_emulation_save_root:
showMenu(SaveStateFragment.FRAGMENT_ID);
return;
case R.id.menu_emulation_load_root:
showMenu(LoadStateFragment.FRAGMENT_ID);
return;
// Save state slots // Save state slots
case R.id.menu_emulation_save_1: case R.id.menu_emulation_save_1:
NativeLibrary.SaveState(0); NativeLibrary.SaveState(0);
@ -549,4 +570,73 @@ public final class EmulationActivity extends AppCompatActivity
} }
}); });
} }
private void showMenu(int menuId)
{
Fragment fragment;
switch (menuId)
{
case SaveStateFragment.FRAGMENT_ID:
fragment = SaveStateFragment.newInstance();
mMenuFragmentTag = SaveStateFragment.FRAGMENT_TAG;
break;
case LoadStateFragment.FRAGMENT_ID:
fragment = LoadStateFragment.newInstance();
mMenuFragmentTag = LoadStateFragment.FRAGMENT_TAG;
break;
default:
return;
}
getFragmentManager().beginTransaction()
.setCustomAnimations(R.animator.menu_slide_in, R.animator.menu_slide_out)
.replace(R.id.frame_submenu, fragment, mMenuFragmentTag)
.commit();
}
private void removeMenu()
{
if (mMenuFragmentTag != null)
{
final Fragment fragment = getFragmentManager().findFragmentByTag(mMenuFragmentTag);
if (fragment != null)
{
// When removing a fragment without replacement, its aniimation must be done
// manually beforehand.
fragment.getView().animate()
.withLayer()
.setDuration(200)
.setInterpolator(sAccelerator)
.alpha(0.0f)
.translationX(600.0f)
.withEndAction(new Runnable()
{
@Override
public void run()
{
if (mMenuVisible)
{
getFragmentManager().beginTransaction()
.remove(fragment)
.commit();
}
}
});
}
else
{
Log.e("DolphinEmu", "[EmulationActivity] Fragment not found, can't remove.");
}
mMenuFragmentTag = null;
}
else
{
Log.e("DolphinEmu", "[EmulationActivity] Fragment Tag empty.");
}
}
} }

View File

@ -0,0 +1,55 @@
package org.dolphinemu.dolphinemu.fragments;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.GridLayout;
import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
public final class LoadStateFragment extends Fragment implements View.OnClickListener
{
public static final String FRAGMENT_TAG = BuildConfig.APPLICATION_ID + ".load_state";
public static final int FRAGMENT_ID = R.layout.fragment_state_load;
public static LoadStateFragment newInstance()
{
LoadStateFragment fragment = new LoadStateFragment();
// TODO Add any appropriate arguments to this fragment.
return fragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(FRAGMENT_ID, container, false);
GridLayout grid = (GridLayout) rootView.findViewById(R.id.grid_state_slots);
for (int childIndex = 0; childIndex < grid.getChildCount(); childIndex++)
{
Button button = (Button) grid.getChildAt(childIndex);
button.setOnClickListener(this);
}
// So that item clicked to start this Fragment is no longer the focused item.
grid.requestFocus();
return rootView;
}
@Override
public void onClick(View button)
{
((EmulationActivity) getActivity()).onMenuItemClicked(button.getId());
}
}

View File

@ -9,16 +9,20 @@ import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.activities.EmulationActivity;
public final class MenuFragment extends Fragment implements View.OnClickListener public final class MenuFragment extends Fragment implements View.OnClickListener
{ {
public static final String FRAGMENT_TAG = BuildConfig.APPLICATION_ID + ".ingame_menu";
public static final int FRAGMENT_ID = R.layout.fragment_ingame_menu;
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{ {
LinearLayout rootView = (LinearLayout) inflater.inflate(R.layout.fragment_ingame_menu, container, false); LinearLayout rootView = (LinearLayout) inflater.inflate(FRAGMENT_ID, container, false);
for (int childIndex = 0; childIndex < rootView.getChildCount(); childIndex++) for (int childIndex = 0; childIndex < rootView.getChildCount(); childIndex++)
{ {

View File

@ -0,0 +1,55 @@
package org.dolphinemu.dolphinemu.fragments;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.GridLayout;
import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
public final class SaveStateFragment extends Fragment implements View.OnClickListener
{
public static final String FRAGMENT_TAG = BuildConfig.APPLICATION_ID + ".save_state";
public static final int FRAGMENT_ID = R.layout.fragment_state_save;
public static SaveStateFragment newInstance()
{
SaveStateFragment fragment = new SaveStateFragment();
// TODO Add any appropriate arguments to this fragment.
return fragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(FRAGMENT_ID, container, false);
GridLayout grid = (GridLayout) rootView.findViewById(R.id.grid_state_slots);
for (int childIndex = 0; childIndex < grid.getChildCount(); childIndex++)
{
Button button = (Button) grid.getChildAt(childIndex);
button.setOnClickListener(this);
}
// So that item clicked to start this Fragment is no longer the focused item.
grid.requestFocus();
return rootView;
}
@Override
public void onClick(View button)
{
((EmulationActivity) getActivity()).onMenuItemClicked(button.getId());
}
}

View File

@ -0,0 +1,17 @@
<?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="1280"
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

@ -0,0 +1,18 @@
<?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 xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="translationX"
android:valueType="floatType"
android:valueFrom="0"
android:valueTo="1280"
android:interpolator="@android:interpolator/decelerate_quad"
android:duration="300"/>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="alpha"
android:valueType="floatType"
android:valueFrom="1"
android:valueTo="0"
android:interpolator="@android:interpolator/decelerate_quad"
android:duration="300"/>
</set>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:columnCount="3"
android:rowCount="2"
android:layout_gravity="center">
<Button
android:id="@+id/menu_emulation_load_1"
android:layout_width="128dp"
android:layout_height="128dp"
android:text="@string/overlay_slot1"
style="@style/InGameMenuOption"
/>
<Button
android:id="@+id/menu_emulation_load_2"
android:layout_width="128dp"
android:layout_height="128dp"
android:text="@string/overlay_slot2"
style="@style/InGameMenuOption"
/>
<Button
android:id="@+id/menu_emulation_load_3"
android:layout_width="128dp"
android:layout_height="128dp"
android:text="@string/overlay_slot3"
style="@style/InGameMenuOption"
/>
<Button
android:id="@+id/menu_emulation_load_4"
android:layout_width="128dp"
android:layout_height="128dp"
android:text="@string/overlay_slot4"
style="@style/InGameMenuOption"
/>
<Button
android:id="@+id/menu_emulation_load_5"
android:layout_width="128dp"
android:layout_height="128dp"
android:text="@string/overlay_slot5"
style="@style/InGameMenuOption"
/>
<Button
android:id="@+id/menu_emulation_load_6"
android:layout_width="128dp"
android:layout_height="128dp"
android:text="@string/overlay_slot6"
style="@style/InGameMenuOption"
/>
</GridLayout>
</FrameLayout>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:columnCount="3"
android:rowCount="2"
android:layout_gravity="center">
<Button
android:id="@+id/menu_emulation_save_1"
android:layout_width="128dp"
android:layout_height="128dp"
android:text="@string/overlay_slot1"
style="@style/InGameMenuOption"
/>
<Button
android:id="@+id/menu_emulation_save_2"
android:layout_width="128dp"
android:layout_height="128dp"
android:text="@string/overlay_slot2"
style="@style/InGameMenuOption"
/>
<Button
android:id="@+id/menu_emulation_save_3"
android:layout_width="128dp"
android:layout_height="128dp"
android:text="@string/overlay_slot3"
style="@style/InGameMenuOption"
/>
<Button
android:id="@+id/menu_emulation_save_4"
android:layout_width="128dp"
android:layout_height="128dp"
android:text="@string/overlay_slot4"
style="@style/InGameMenuOption"
/>
<Button
android:id="@+id/menu_emulation_save_5"
android:layout_width="128dp"
android:layout_height="128dp"
android:text="@string/overlay_slot5"
style="@style/InGameMenuOption"
/>
<Button
android:id="@+id/menu_emulation_save_6"
android:layout_width="128dp"
android:layout_height="128dp"
android:text="@string/overlay_slot6"
style="@style/InGameMenuOption"
/>
</GridLayout>
</FrameLayout>

View File

@ -66,6 +66,7 @@
<string name="overlay_slot3">Slot 3</string> <string name="overlay_slot3">Slot 3</string>
<string name="overlay_slot4">Slot 4</string> <string name="overlay_slot4">Slot 4</string>
<string name="overlay_slot5">Slot 5</string> <string name="overlay_slot5">Slot 5</string>
<string name="overlay_slot6">Slot 6</string>
<!-- Input Config Fragment --> <!-- Input Config Fragment -->
<string name="input_settings">Input</string> <string name="input_settings">Input</string>