Merge pull request #11808 from deReeperJosh/disneyinfinitybaseandroid

Android: Infinity Base UI
This commit is contained in:
Charles Lombardo 2023-05-28 17:09:31 -04:00 committed by GitHub
commit 55b9794e4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 623 additions and 55 deletions

View File

@ -38,7 +38,11 @@ import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.databinding.ActivityEmulationBinding;
import org.dolphinemu.dolphinemu.databinding.DialogInputAdjustBinding;
import org.dolphinemu.dolphinemu.databinding.DialogSkylandersManagerBinding;
import org.dolphinemu.dolphinemu.databinding.DialogNfcFiguresManagerBinding;
import org.dolphinemu.dolphinemu.features.infinitybase.InfinityConfig;
import org.dolphinemu.dolphinemu.features.infinitybase.model.Figure;
import org.dolphinemu.dolphinemu.features.infinitybase.ui.FigureSlot;
import org.dolphinemu.dolphinemu.features.infinitybase.ui.FigureSlotAdapter;
import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface;
import org.dolphinemu.dolphinemu.features.input.model.DolphinSensorEventListener;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
@ -78,6 +82,8 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
public static final int REQUEST_CHANGE_DISC = 1;
public static final int REQUEST_SKYLANDER_FILE = 2;
public static final int REQUEST_CREATE_SKYLANDER = 3;
public static final int REQUEST_INFINITY_FIGURE_FILE = 4;
public static final int REQUEST_CREATE_INFINITY_FIGURE = 5;
private EmulationFragment mEmulationFragment;
@ -107,6 +113,10 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
public static final String EXTRA_SKYLANDER_ID = "SkylanderId";
public static final String EXTRA_SKYLANDER_VAR = "SkylanderVar";
public static final String EXTRA_SKYLANDER_NAME = "SkylanderName";
public static final String EXTRA_INFINITY_POSITION = "FigurePosition";
public static final String EXTRA_INFINITY_LIST_POSITION = "FigureListPosition";
public static final String EXTRA_INFINITY_NUM = "FigureNum";
public static final String EXTRA_INFINITY_NAME = "FigureName";
@Retention(SOURCE)
@IntDef(
@ -121,7 +131,7 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
MENU_ACTION_RESET_OVERLAY, MENU_SET_IR_RECENTER, MENU_SET_IR_MODE,
MENU_ACTION_CHOOSE_DOUBLETAP, MENU_ACTION_PAUSE_EMULATION,
MENU_ACTION_UNPAUSE_EMULATION, MENU_ACTION_OVERLAY_CONTROLS, MENU_ACTION_SETTINGS,
MENU_ACTION_SKYLANDERS})
MENU_ACTION_SKYLANDERS, MENU_ACTION_INFINITY_BASE})
public @interface MenuAction
{
}
@ -160,14 +170,20 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
public static final int MENU_ACTION_OVERLAY_CONTROLS = 34;
public static final int MENU_ACTION_SETTINGS = 35;
public static final int MENU_ACTION_SKYLANDERS = 36;
public static final int MENU_ACTION_INFINITY_BASE = 37;
private Skylander mSkylanderData = new Skylander(-1, -1, "Slot");
private Figure mInfinityFigureData = new Figure(-1, "Position");
private int mSkylanderSlot = -1;
private int mInfinityPosition = -1;
private int mInfinityListPosition = -1;
private DialogSkylandersManagerBinding mSkylandersBinding;
private DialogNfcFiguresManagerBinding mSkylandersBinding;
private DialogNfcFiguresManagerBinding mInfinityBinding;
private static List<SkylanderSlot> sSkylanderSlots = new ArrayList<>();
private static List<FigureSlot> sInfinityFigures = new ArrayList<>();
private static final SparseIntArray buttonsActionsMap = new SparseIntArray();
@ -348,6 +364,17 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
sSkylanderSlots.add(new SkylanderSlot(getString(R.string.skylander_slot, i + 1), i));
}
}
if (sInfinityFigures.isEmpty())
{
sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_hexagon_label), 0));
sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_p1_label), 1));
sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_p1a1_label), 3));
sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_p1a2_label), 5));
sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_p2_label), 2));
sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_p2a1_label), 4));
sInfinityFigures.add(new FigureSlot(getString(R.string.infinity_p2a2_label), 6));
}
}
@Override
@ -364,6 +391,10 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
outState.putInt(EXTRA_SKYLANDER_ID, mSkylanderData.getId());
outState.putInt(EXTRA_SKYLANDER_VAR, mSkylanderData.getVariant());
outState.putString(EXTRA_SKYLANDER_NAME, mSkylanderData.getName());
outState.putInt(EXTRA_INFINITY_POSITION, mInfinityPosition);
outState.putInt(EXTRA_INFINITY_LIST_POSITION, mInfinityListPosition);
outState.putLong(EXTRA_INFINITY_NUM, mInfinityFigureData.getNumber());
outState.putString(EXTRA_INFINITY_NAME, mInfinityFigureData.getName());
super.onSaveInstanceState(outState);
}
@ -376,6 +407,10 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
mSkylanderData = new Skylander(savedInstanceState.getInt(EXTRA_SKYLANDER_ID),
savedInstanceState.getInt(EXTRA_SKYLANDER_VAR),
savedInstanceState.getString(EXTRA_SKYLANDER_NAME));
mInfinityPosition = savedInstanceState.getInt(EXTRA_INFINITY_POSITION);
mInfinityListPosition = savedInstanceState.getInt(EXTRA_INFINITY_LIST_POSITION);
mInfinityFigureData = new Figure(savedInstanceState.getLong(EXTRA_INFINITY_NUM),
savedInstanceState.getString(EXTRA_INFINITY_NAME));
}
@Override
@ -486,7 +521,7 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
clearSkylander(mSkylanderSlot);
sSkylanderSlots.get(mSkylanderSlot).setPortalSlot(slot.first);
sSkylanderSlots.get(mSkylanderSlot).setLabel(slot.second);
mSkylandersBinding.skylandersManager.getAdapter().notifyItemChanged(mSkylanderSlot);
mSkylandersBinding.figureManager.getAdapter().notifyItemChanged(mSkylanderSlot);
mSkylanderSlot = -1;
mSkylanderData = Skylander.BLANK_SKYLANDER;
}
@ -500,11 +535,48 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
clearSkylander(mSkylanderSlot);
sSkylanderSlots.get(mSkylanderSlot).setPortalSlot(slot.first);
sSkylanderSlots.get(mSkylanderSlot).setLabel(slot.second);
mSkylandersBinding.skylandersManager.getAdapter().notifyItemChanged(mSkylanderSlot);
mSkylandersBinding.figureManager.getAdapter().notifyItemChanged(mSkylanderSlot);
mSkylanderSlot = -1;
mSkylanderData = Skylander.BLANK_SKYLANDER;
}
}
else if (requestCode == REQUEST_INFINITY_FIGURE_FILE)
{
String label = InfinityConfig.loadFigure(mInfinityPosition, result.getData().toString());
if (label != null && !label.equals("Unknown Figure"))
{
clearInfinityFigure(mInfinityListPosition);
sInfinityFigures.get(mInfinityListPosition).setLabel(label);
mInfinityBinding.figureManager.getAdapter().notifyItemChanged(mInfinityListPosition);
mInfinityPosition = -1;
mInfinityListPosition = -1;
mInfinityFigureData = Figure.BLANK_FIGURE;
}
else
{
new MaterialAlertDialogBuilder(this)
.setTitle(R.string.incompatible_figure_selected)
.setMessage(R.string.select_compatible_figure)
.setPositiveButton(R.string.ok, null)
.show();
}
}
else if (requestCode == REQUEST_CREATE_INFINITY_FIGURE)
{
if (!(mInfinityFigureData.getNumber() == -1))
{
String label =
InfinityConfig.createFigure(mInfinityFigureData.getNumber(),
result.getData().toString(),
mInfinityPosition);
clearInfinityFigure(mInfinityListPosition);
sInfinityFigures.get(mInfinityListPosition).setLabel(label);
mInfinityBinding.figureManager.getAdapter().notifyItemChanged(mInfinityListPosition);
mInfinityPosition = -1;
mInfinityListPosition = -1;
mInfinityFigureData = Figure.BLANK_FIGURE;
}
}
}
}
@ -775,6 +847,10 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
showSkylanderPortalSettings();
break;
case MENU_ACTION_INFINITY_BASE:
showInfinityBaseSettings();
break;
case MENU_ACTION_EXIT:
mEmulationFragment.stopEmulation();
break;
@ -1036,10 +1112,10 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
private void showSkylanderPortalSettings()
{
mSkylandersBinding =
DialogSkylandersManagerBinding.inflate(getLayoutInflater());
mSkylandersBinding.skylandersManager.setLayoutManager(new LinearLayoutManager(this));
DialogNfcFiguresManagerBinding.inflate(getLayoutInflater());
mSkylandersBinding.figureManager.setLayoutManager(new LinearLayoutManager(this));
mSkylandersBinding.skylandersManager.setAdapter(
mSkylandersBinding.figureManager.setAdapter(
new SkylanderSlotAdapter(sSkylanderSlots, this));
new MaterialAlertDialogBuilder(this)
@ -1048,6 +1124,21 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
.show();
}
private void showInfinityBaseSettings()
{
mInfinityBinding =
DialogNfcFiguresManagerBinding.inflate(getLayoutInflater());
mInfinityBinding.figureManager.setLayoutManager(new LinearLayoutManager(this));
mInfinityBinding.figureManager.setAdapter(
new FigureSlotAdapter(sInfinityFigures, this));
new MaterialAlertDialogBuilder(this)
.setTitle(R.string.infinity_manager)
.setView(mInfinityBinding.getRoot())
.show();
}
public void setSkylanderData(int id, int var, String name, int slot)
{
mSkylanderData = new Skylander(id, var, name);
@ -1057,7 +1148,43 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
public void clearSkylander(int slot)
{
sSkylanderSlots.get(slot).setLabel(getString(R.string.skylander_slot, slot + 1));
mSkylandersBinding.skylandersManager.getAdapter().notifyItemChanged(slot);
mSkylandersBinding.figureManager.getAdapter().notifyItemChanged(slot);
}
public void setInfinityFigureData(Long num, String name, int position, int listPosition)
{
mInfinityFigureData = new Figure(num, name);
mInfinityPosition = position;
mInfinityListPosition = listPosition;
}
public void clearInfinityFigure(int position)
{
switch (position)
{
case 0:
sInfinityFigures.get(position).setLabel(getString(R.string.infinity_hexagon_label));
break;
case 1:
sInfinityFigures.get(position).setLabel(getString(R.string.infinity_p1_label));
break;
case 2:
sInfinityFigures.get(position).setLabel(getString(R.string.infinity_p1a1_label));
break;
case 3:
sInfinityFigures.get(position).setLabel(getString(R.string.infinity_p1a2_label));
break;
case 4:
sInfinityFigures.get(position).setLabel(getString(R.string.infinity_p2_label));
break;
case 5:
sInfinityFigures.get(position).setLabel(getString(R.string.infinity_p2a1_label));
break;
case 6:
sInfinityFigures.get(position).setLabel(getString(R.string.infinity_p2a2_label));
break;
}
mInfinityBinding.figureManager.getAdapter().notifyItemChanged(position);
}
private void resetOverlay()

View File

@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.infinitybase
object InfinityConfig {
var LIST_FIGURES: Map<Long, String> = getFigureMap()
var REVERSE_LIST_FIGURES: Map<String, Long> = getInverseFigureMap()
private external fun getFigureMap(): Map<Long, String>
private external fun getInverseFigureMap(): Map<String, Long>
@JvmStatic
external fun removeFigure(position: Int)
@JvmStatic
external fun loadFigure(position: Int, fileName: String): String?
@JvmStatic
external fun createFigure(
figureNumber: Long,
fileName: String,
position: Int
): String?
}

View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.infinitybase.model
data class Figure(var number: Long, var name: String) {
companion object {
@JvmField
val BLANK_FIGURE = Figure(-1, "Blank")
}
}

View File

@ -0,0 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.infinitybase.ui
data class FigureSlot(var label: String, val position: Int)

View File

@ -0,0 +1,149 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.infinitybase.ui
import android.app.AlertDialog
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.AdapterView.OnItemClickListener
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.activities.EmulationActivity
import org.dolphinemu.dolphinemu.databinding.DialogCreateInfinityFigureBinding
import org.dolphinemu.dolphinemu.databinding.ListItemNfcFigureSlotBinding
import org.dolphinemu.dolphinemu.features.infinitybase.InfinityConfig
import org.dolphinemu.dolphinemu.features.infinitybase.InfinityConfig.removeFigure
class FigureSlotAdapter(
private val figures: List<FigureSlot>,
private val activity: EmulationActivity
) : RecyclerView.Adapter<FigureSlotAdapter.ViewHolder>(),
OnItemClickListener {
class ViewHolder(var binding: ListItemNfcFigureSlotBinding) :
RecyclerView.ViewHolder(binding.getRoot())
private lateinit var binding: DialogCreateInfinityFigureBinding
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ListItemNfcFigureSlotBinding.inflate(inflater, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val figure = figures[position]
holder.binding.textFigureName.text = figure.label
holder.binding.buttonClearFigure.setOnClickListener {
removeFigure(figure.position)
activity.clearInfinityFigure(position)
}
holder.binding.buttonLoadFigure.setOnClickListener {
val loadFigure = Intent(Intent.ACTION_OPEN_DOCUMENT)
loadFigure.addCategory(Intent.CATEGORY_OPENABLE)
loadFigure.type = "*/*"
activity.setInfinityFigureData(0, "", figure.position, position)
activity.startActivityForResult(
loadFigure,
EmulationActivity.REQUEST_INFINITY_FIGURE_FILE
)
}
val inflater = LayoutInflater.from(activity)
binding = DialogCreateInfinityFigureBinding.inflate(inflater)
binding.infinityDropdown.onItemClickListener = this
holder.binding.buttonCreateFigure.setOnClickListener {
var validFigures = InfinityConfig.REVERSE_LIST_FIGURES
// Filter adapter list by position, either Hexagon Pieces, Characters or Abilities
validFigures = when (figure.position) {
0 -> {
// Hexagon Pieces
validFigures.filter { (_, value) -> value in 2000000..2999999 || value in 4000000..4999999 }
}
1, 2 -> {
// Characters
validFigures.filter { (_, value) -> value in 1000000..1999999 }
}
else -> {
// Abilities
validFigures.filter { (_, value) -> value in 3000000..3999999 }
}
}
val figureListKeys = validFigures.keys.toMutableList()
figureListKeys.sort()
val figureNames: ArrayList<String> = ArrayList(figureListKeys)
binding.infinityDropdown.setAdapter(
ArrayAdapter(
activity, R.layout.support_simple_spinner_dropdown_item,
figureNames
)
)
if (binding.getRoot().parent != null) {
(binding.getRoot().parent as ViewGroup).removeAllViews()
}
val createDialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.create_figure_title)
.setView(binding.getRoot())
.setPositiveButton(R.string.create_figure, null)
.setNegativeButton(R.string.cancel, null)
.show()
createDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
if (binding.infinityNum.text.toString().isNotBlank()) {
val createFigure = Intent(Intent.ACTION_CREATE_DOCUMENT)
createFigure.addCategory(Intent.CATEGORY_OPENABLE)
createFigure.type = "*/*"
val num = binding.infinityNum.text.toString().toLong()
val name = InfinityConfig.LIST_FIGURES[num]
if (name != null) {
createFigure.putExtra(
Intent.EXTRA_TITLE,
"$name.bin"
)
activity.setInfinityFigureData(num, name, figure.position, position)
} else {
createFigure.putExtra(
Intent.EXTRA_TITLE,
"Unknown(Number: $num).bin"
)
activity.setInfinityFigureData(num, "Unknown", figure.position, position)
}
activity.startActivityForResult(
createFigure,
EmulationActivity.REQUEST_CREATE_INFINITY_FIGURE
)
createDialog.dismiss()
} else {
Toast.makeText(
activity, R.string.invalid_infinity_figure,
Toast.LENGTH_SHORT
).show()
}
}
}
}
override fun getItemCount(): Int {
return figures.size
}
override fun onItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) {
val figureNumber = InfinityConfig.REVERSE_LIST_FIGURES[parent.getItemAtPosition(position)]
binding.infinityNum.setText(figureNumber.toString())
}
}

View File

@ -217,6 +217,12 @@ enum class BooleanSetting(
"EmulateSkylanderPortal",
false
),
MAIN_EMULATE_INFINITY_BASE(
Settings.FILE_DOLPHIN,
Settings.SECTION_EMULATED_USB_DEVICES,
"EmulateInfinityBase",
false
),
MAIN_SHOW_GAME_TITLES(
Settings.FILE_DOLPHIN,
Settings.SECTION_INI_ANDROID,
@ -719,7 +725,8 @@ enum class BooleanSetting(
MAIN_RAM_OVERRIDE_ENABLE,
MAIN_CUSTOM_RTC_ENABLE,
MAIN_DSP_JIT,
MAIN_EMULATE_SKYLANDER_PORTAL
MAIN_EMULATE_SKYLANDER_PORTAL,
MAIN_EMULATE_INFINITY_BASE
)
private val NOT_RUNTIME_EDITABLE: Set<BooleanSetting> =
HashSet(listOf(*NOT_RUNTIME_EDITABLE_ARRAY))

View File

@ -115,6 +115,7 @@ class SettingsFragmentPresenter(
controllerNumber,
controllerType
)
MenuTag.WIIMOTE_1,
MenuTag.WIIMOTE_2,
MenuTag.WIIMOTE_3,
@ -122,6 +123,7 @@ class SettingsFragmentPresenter(
sl,
controllerNumber
)
MenuTag.WIIMOTE_EXTENSION_1,
MenuTag.WIIMOTE_EXTENSION_2,
MenuTag.WIIMOTE_EXTENSION_3,
@ -130,6 +132,7 @@ class SettingsFragmentPresenter(
controllerNumber,
controllerType
)
MenuTag.WIIMOTE_GENERAL_1,
MenuTag.WIIMOTE_GENERAL_2,
MenuTag.WIIMOTE_GENERAL_3,
@ -137,6 +140,7 @@ class SettingsFragmentPresenter(
sl,
controllerNumber
)
MenuTag.WIIMOTE_MOTION_SIMULATION_1,
MenuTag.WIIMOTE_MOTION_SIMULATION_2,
MenuTag.WIIMOTE_MOTION_SIMULATION_3,
@ -144,6 +148,7 @@ class SettingsFragmentPresenter(
sl,
controllerNumber
)
MenuTag.WIIMOTE_MOTION_INPUT_1,
MenuTag.WIIMOTE_MOTION_INPUT_2,
MenuTag.WIIMOTE_MOTION_INPUT_3,
@ -151,6 +156,7 @@ class SettingsFragmentPresenter(
sl,
controllerNumber
)
else -> throw UnsupportedOperationException("Unimplemented menu")
}
@ -454,10 +460,12 @@ class SettingsFragmentPresenter(
BooleanSetting.MAIN_DSP_HLE.setBoolean(settings, true)
BooleanSetting.MAIN_DSP_JIT.setBoolean(settings, true)
}
DSP_LLE_RECOMPILER -> {
BooleanSetting.MAIN_DSP_HLE.setBoolean(settings, false)
BooleanSetting.MAIN_DSP_JIT.setBoolean(settings, true)
}
DSP_LLE_INTERPRETER -> {
BooleanSetting.MAIN_DSP_HLE.setBoolean(settings, false)
BooleanSetting.MAIN_DSP_JIT.setBoolean(settings, false)
@ -834,6 +842,14 @@ class SettingsFragmentPresenter(
0
)
)
sl.add(
SwitchSetting(
context,
BooleanSetting.MAIN_EMULATE_INFINITY_BASE,
R.string.emulate_infinity_base,
0
)
)
}
private fun addAdvancedSettings(sl: ArrayList<SettingsItem>) {
@ -856,10 +872,12 @@ class SettingsFragmentPresenter(
BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.setBoolean(settings, false)
BooleanSetting.MAIN_SYNC_GPU.setBoolean(settings, false)
}
SYNC_GPU_ON_IDLE_SKIP -> {
BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.setBoolean(settings, true)
BooleanSetting.MAIN_SYNC_GPU.setBoolean(settings, false)
}
SYNC_GPU_ALWAYS -> {
BooleanSetting.MAIN_SYNC_ON_SKIP_IDLE.setBoolean(settings, true)
BooleanSetting.MAIN_SYNC_GPU.setBoolean(settings, true)
@ -893,10 +911,12 @@ class SettingsFragmentPresenter(
emuCoresEntries = R.array.emuCoresEntriesX86_64
emuCoresValues = R.array.emuCoresValuesX86_64
}
4 -> {
emuCoresEntries = R.array.emuCoresEntriesARM64
emuCoresValues = R.array.emuCoresValuesARM64
}
else -> {
emuCoresEntries = R.array.emuCoresEntriesGeneric
emuCoresValues = R.array.emuCoresValuesGeneric
@ -2246,6 +2266,7 @@ class SettingsFragmentPresenter(
setting.uiSuffix
)
)
NumericSetting.TYPE_BOOLEAN -> sl.add(
SwitchSetting(
InputMappingBooleanSetting(setting),

View File

@ -16,7 +16,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.activities.EmulationActivity
import org.dolphinemu.dolphinemu.databinding.DialogCreateSkylanderBinding
import org.dolphinemu.dolphinemu.databinding.ListItemSkylanderSlotBinding
import org.dolphinemu.dolphinemu.databinding.ListItemNfcFigureSlotBinding
import org.dolphinemu.dolphinemu.features.skylanders.SkylanderConfig
import org.dolphinemu.dolphinemu.features.skylanders.SkylanderConfig.removeSkylander
import org.dolphinemu.dolphinemu.features.skylanders.model.SkylanderPair
@ -25,27 +25,27 @@ class SkylanderSlotAdapter(
private val slots: List<SkylanderSlot>,
private val activity: EmulationActivity
) : RecyclerView.Adapter<SkylanderSlotAdapter.ViewHolder>(), OnItemClickListener {
class ViewHolder(var binding: ListItemSkylanderSlotBinding) :
class ViewHolder(var binding: ListItemNfcFigureSlotBinding) :
RecyclerView.ViewHolder(binding.getRoot())
private lateinit var binding: DialogCreateSkylanderBinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ListItemSkylanderSlotBinding.inflate(inflater, parent, false)
val binding = ListItemNfcFigureSlotBinding.inflate(inflater, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val slot = slots[position]
holder.binding.textSkylanderName.text = slot.label
holder.binding.textFigureName.text = slot.label
holder.binding.buttonClearSkylander.setOnClickListener {
holder.binding.buttonClearFigure.setOnClickListener {
removeSkylander(slot.portalSlot)
activity.clearSkylander(slot.slotNum)
}
holder.binding.buttonLoadSkylander.setOnClickListener {
holder.binding.buttonLoadFigure.setOnClickListener {
val loadSkylander = Intent(Intent.ACTION_OPEN_DOCUMENT)
loadSkylander.addCategory(Intent.CATEGORY_OPENABLE)
loadSkylander.type = "*/*"
@ -71,14 +71,14 @@ class SkylanderSlotAdapter(
)
binding.skylanderDropdown.onItemClickListener = this
holder.binding.buttonCreateSkylander.setOnClickListener {
holder.binding.buttonCreateFigure.setOnClickListener {
if (binding.getRoot().parent != null) {
(binding.getRoot().parent as ViewGroup).removeAllViews()
}
val createDialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.create_skylander_title)
.setView(binding.getRoot())
.setPositiveButton(R.string.create_skylander, null)
.setPositiveButton(R.string.create_figure, null)
.setNegativeButton(R.string.cancel, null)
.show()
createDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {

View File

@ -61,6 +61,7 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT);
buttonsActionsMap.append(R.id.menu_settings, EmulationActivity.MENU_ACTION_SETTINGS);
buttonsActionsMap.append(R.id.menu_skylanders, EmulationActivity.MENU_ACTION_SKYLANDERS);
buttonsActionsMap.append(R.id.menu_infinitybase, EmulationActivity.MENU_ACTION_INFINITY_BASE);
}
private FragmentIngameMenuBinding mBinding;

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/spacing_medlarge">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_infinity_dropdown"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/spacing_medlarge"
android:paddingHorizontal="@dimen/spacing_medlarge"
android:hint="@string/infinity_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/infinity_dropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:spinnerMode="dialog"
android:imeOptions="actionDone" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@+id/layout_infinity_dropdown"
android:gravity="center_vertical"
android:baselineAligned="false">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_infinity_num"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/infinity_number"
android:paddingHorizontal="@dimen/spacing_medlarge">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/infinity_num"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -4,10 +4,10 @@
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/skylanders_manager"
android:id="@+id/figure_manager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:scrollbars="vertical"
android:fadeScrollbars="false"/>
android:fadeScrollbars="false"
android:scrollbars="vertical" />
</androidx.appcompat.widget.LinearLayoutCompat>

View File

@ -106,6 +106,11 @@
android:text="@string/emulate_skylander_portal"
style="@style/InGameMenuOption" />
<Button
android:id="@+id/menu_infinitybase"
android:text="@string/emulate_infinity_base"
style="@style/InGameMenuOption" />
</LinearLayout>
</ScrollView>

View File

@ -8,43 +8,43 @@
android:padding="@dimen/spacing_medlarge">
<Button
android:id="@+id/button_create_skylander"
android:id="@+id/button_create_figure"
style="?attr/materialIconButtonFilledTonalStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/spacing_small"
android:layout_toStartOf="@id/button_load_skylander"
android:contentDescription="@string/create_skylander"
android:tooltipText="@string/create_skylander"
android:layout_toStartOf="@id/button_load_figure"
android:contentDescription="@string/create_figure"
android:tooltipText="@string/create_figure"
app:icon="@drawable/ic_add" />
<Button
android:id="@+id/button_load_skylander"
android:id="@+id/button_load_figure"
style="?attr/materialIconButtonFilledTonalStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/spacing_small"
android:layout_toStartOf="@+id/button_clear_skylander"
android:contentDescription="@string/load_skylander"
android:tooltipText="@string/load_skylander"
android:layout_toStartOf="@+id/button_clear_figure"
android:contentDescription="@string/load_figure"
android:tooltipText="@string/load_figure"
app:icon="@drawable/ic_load" />
<Button
android:id="@+id/button_clear_skylander"
android:id="@+id/button_clear_figure"
style="?attr/materialIconButtonFilledTonalStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/spacing_large"
android:contentDescription="@string/remove_skylander"
android:tooltipText="@string/remove_skylander"
android:contentDescription="@string/remove_figure"
android:tooltipText="@string/remove_figure"
app:icon="@drawable/ic_clear" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_skylander_name"
android:id="@+id/text_figure_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
@ -52,7 +52,7 @@
android:layout_marginBottom="@dimen/spacing_medlarge"
android:layout_marginEnd="@dimen/spacing_large"
android:layout_marginStart="@dimen/spacing_large"
android:layout_toStartOf="@+id/button_create_skylander"
android:layout_toStartOf="@+id/button_create_figure"
android:textAlignment="viewStart"
android:textColor="?attr/colorOnSurface"
android:textSize="16sp"

View File

@ -846,9 +846,6 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="emulated_usb_devices">Emulated USB Devices</string>
<string name="emulate_skylander_portal">Skylanders Portal</string>
<string name="skylanders_manager">Skylanders Manager</string>
<string name="load_skylander">Load</string>
<string name="remove_skylander">Remove</string>
<string name="create_skylander">Create</string>
<string name="create_skylander_title">Create Skylander</string>
<string name="skylander_label">Skylander</string>
<string name="skylander_slot">Slot %1$d</string>
@ -856,4 +853,23 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="skylander_variant">Variant</string>
<string name="invalid_skylander">Invalid Skylander Selection</string>
<string name="emulate_infinity_base">Infinity Base</string>
<string name="infinity_manager">Infinity Manager</string>
<string name="load_figure">Load</string>
<string name="remove_figure">Remove</string>
<string name="create_figure">Create</string>
<string name="create_figure_title">Create Figure</string>
<string name="infinity_label">Infinity Figure</string>
<string name="infinity_number">Figure Number</string>
<string name="invalid_infinity_figure">Invalid Figure Selection</string>
<string name="infinity_hexagon_label">Power Disc/Play Set</string>
<string name="infinity_p1_label">Player One</string>
<string name="infinity_p2_label">Player Two</string>
<string name="infinity_p1a1_label">P1 Ability One</string>
<string name="infinity_p2a1_label">P2 Ability One</string>
<string name="infinity_p1a2_label">P1 Ability Two</string>
<string name="infinity_p2a2_label">P2 Ability Two</string>
<string name="incompatible_figure_selected">Incompatible Figure Selected</string>
<string name="select_compatible_figure">Please select a compatible figure file</string>
</resources>

View File

@ -32,6 +32,10 @@ static jclass s_linked_hash_map_class;
static jmethodID s_linked_hash_map_init;
static jmethodID s_linked_hash_map_put;
static jclass s_hash_map_class;
static jmethodID s_hash_map_init;
static jmethodID s_hash_map_put;
static jclass s_ini_file_class;
static jfieldID s_ini_file_pointer;
static jclass s_ini_file_section_class;
@ -221,6 +225,21 @@ jmethodID GetLinkedHashMapPut()
return s_linked_hash_map_put;
}
jclass GetHashMapClass()
{
return s_hash_map_class;
}
jmethodID GetHashMapInit()
{
return s_hash_map_init;
}
jmethodID GetHashMapPut()
{
return s_hash_map_put;
}
jclass GetIniFileClass()
{
return s_ini_file_class;
@ -575,12 +594,19 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
ini_file_section_class, "<init>", "(Lorg/dolphinemu/dolphinemu/utils/IniFile;J)V");
env->DeleteLocalRef(ini_file_section_class);
const jclass map_class = env->FindClass("java/util/LinkedHashMap");
s_linked_hash_map_class = reinterpret_cast<jclass>(env->NewGlobalRef(map_class));
const jclass linked_hash_map_class = env->FindClass("java/util/LinkedHashMap");
s_linked_hash_map_class = reinterpret_cast<jclass>(env->NewGlobalRef(linked_hash_map_class));
s_linked_hash_map_init = env->GetMethodID(s_linked_hash_map_class, "<init>", "(I)V");
s_linked_hash_map_put = env->GetMethodID(
s_linked_hash_map_class, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
env->DeleteLocalRef(map_class);
env->DeleteLocalRef(linked_hash_map_class);
const jclass hash_map_class = env->FindClass("java/util/HashMap");
s_hash_map_class = reinterpret_cast<jclass>(env->NewGlobalRef(hash_map_class));
s_hash_map_init = env->GetMethodID(s_hash_map_class, "<init>", "(I)V");
s_hash_map_put = env->GetMethodID(s_hash_map_class, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
env->DeleteLocalRef(hash_map_class);
const jclass compress_cb_class =
env->FindClass("org/dolphinemu/dolphinemu/utils/CompressCallback");
@ -741,6 +767,7 @@ JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved)
env->DeleteGlobalRef(s_game_file_cache_class);
env->DeleteGlobalRef(s_analytics_class);
env->DeleteGlobalRef(s_linked_hash_map_class);
env->DeleteGlobalRef(s_hash_map_class);
env->DeleteGlobalRef(s_ini_file_class);
env->DeleteGlobalRef(s_ini_file_section_class);
env->DeleteGlobalRef(s_compress_cb_class);

View File

@ -32,6 +32,10 @@ jclass GetLinkedHashMapClass();
jmethodID GetLinkedHashMapInit();
jmethodID GetLinkedHashMapPut();
jclass GetHashMapClass();
jmethodID GetHashMapInit();
jmethodID GetHashMapPut();
jclass GetIniFileClass();
jfieldID GetIniFilePointer();
jclass GetIniFileSectionClass();

View File

@ -10,6 +10,7 @@ add_library(main SHARED
GameList/GameFile.cpp
GameList/GameFile.h
GameList/GameFileCache.cpp
InfinityConfig.cpp
Input/Control.cpp
Input/Control.h
Input/ControlGroup.cpp

View File

@ -0,0 +1,121 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <jni.h>
#include <array>
#include "AndroidCommon/AndroidCommon.h"
#include "AndroidCommon/IDCache.h"
#include "Core/IOS/USB/Emulated/Infinity.h"
#include "Core/System.h"
extern "C" {
JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_getFigureMap(JNIEnv* env,
jobject obj)
{
auto& system = Core::System::GetInstance();
jobject hash_map_obj = env->NewObject(IDCache::GetHashMapClass(), IDCache::GetHashMapInit(),
system.GetInfinityBase().GetFigureList().size());
jclass long_class = env->FindClass("java/lang/Long");
jmethodID long_init = env->GetMethodID(long_class, "<init>", "(J)V");
for (const auto& it : system.GetInfinityBase().GetFigureList())
{
const std::string& name = it.first;
jobject figure_number = env->NewObject(long_class, long_init, (jlong)it.second);
env->CallObjectMethod(hash_map_obj, IDCache::GetHashMapPut(), figure_number,
ToJString(env, name));
env->DeleteLocalRef(figure_number);
}
return hash_map_obj;
}
JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_getInverseFigureMap(JNIEnv* env,
jobject obj)
{
auto& system = Core::System::GetInstance();
jobject hash_map_obj = env->NewObject(IDCache::GetHashMapClass(), IDCache::GetHashMapInit(),
system.GetInfinityBase().GetFigureList().size());
jclass long_class = env->FindClass("java/lang/Long");
jmethodID long_init = env->GetMethodID(long_class, "<init>", "(J)V");
for (const auto& it : system.GetInfinityBase().GetFigureList())
{
const std::string& name = it.first;
jobject figure_number = env->NewObject(long_class, long_init, (jlong)it.second);
env->CallObjectMethod(hash_map_obj, IDCache::GetHashMapPut(), ToJString(env, name),
figure_number);
env->DeleteLocalRef(figure_number);
}
return hash_map_obj;
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_removeFigure(JNIEnv* env,
jclass clazz,
jint position)
{
auto& system = Core::System::GetInstance();
system.GetInfinityBase().RemoveFigure(position);
}
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_loadFigure(JNIEnv* env,
jclass clazz,
jint position,
jstring file_name)
{
File::IOFile inf_file(GetJString(env, file_name), "r+b");
if (!inf_file)
{
return nullptr;
}
std::array<u8, 0x14 * 0x10> file_data{};
if (!inf_file.ReadBytes(file_data.data(), file_data.size()))
{
return nullptr;
}
auto& system = Core::System::GetInstance();
system.GetInfinityBase().RemoveFigure(position);
return ToJString(env,
system.GetInfinityBase().LoadFigure(file_data, std::move(inf_file), position));
}
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_createFigure(
JNIEnv* env, jclass clazz, jlong figure_number, jstring fileName, jint position)
{
u32 fig_num = static_cast<u32>(figure_number);
std::string file_name = GetJString(env, fileName);
auto& system = Core::System::GetInstance();
system.GetInfinityBase().CreateFigure(file_name, fig_num);
system.GetInfinityBase().RemoveFigure(position);
File::IOFile inf_file(file_name, "r+b");
if (!inf_file)
{
return nullptr;
}
std::array<u8, 0x14 * 0x10> file_data{};
if (!inf_file.ReadBytes(file_data.data(), file_data.size()))
{
return nullptr;
}
return ToJString(env,
system.GetInfinityBase().LoadFigure(file_data, std::move(inf_file), position));
}
}

View File

@ -6,6 +6,7 @@
#include <array>
#include "AndroidCommon/AndroidCommon.h"
#include "AndroidCommon/IDCache.h"
#include "Core/IOS/USB/Emulated/Skylander.h"
#include "Core/System.h"
@ -15,12 +16,8 @@ JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_skylanders_SkylanderConfig_getSkylanderMap(JNIEnv* env,
jclass clazz)
{
jclass hash_map_class = env->FindClass("java/util/HashMap");
jmethodID hash_map_init = env->GetMethodID(hash_map_class, "<init>", "(I)V");
jobject hash_map_obj = env->NewObject(hash_map_class, hash_map_init,
jobject hash_map_obj = env->NewObject(IDCache::GetHashMapClass(), IDCache::GetHashMapInit(),
static_cast<u16>(IOS::HLE::USB::list_skylanders.size()));
jmethodID hash_map_put = env->GetMethodID(
hash_map_class, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
jclass skylander_class =
env->FindClass("org/dolphinemu/dolphinemu/features/skylanders/model/SkylanderPair");
@ -32,7 +29,8 @@ Java_org_dolphinemu_dolphinemu_features_skylanders_SkylanderConfig_getSkylanderM
const std::string& name = it.second;
jobject skylander_obj =
env->NewObject(skylander_class, skylander_init, it.first.first, it.first.second);
env->CallObjectMethod(hash_map_obj, hash_map_put, skylander_obj, ToJString(env, name));
env->CallObjectMethod(hash_map_obj, IDCache::GetHashMapPut(), skylander_obj,
ToJString(env, name));
env->DeleteLocalRef(skylander_obj);
}
@ -43,12 +41,8 @@ JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_skylanders_SkylanderConfig_getInverseSkylanderMap(
JNIEnv* env, jclass clazz)
{
jclass hash_map_class = env->FindClass("java/util/HashMap");
jmethodID hash_map_init = env->GetMethodID(hash_map_class, "<init>", "(I)V");
jobject hash_map_obj = env->NewObject(hash_map_class, hash_map_init,
jobject hash_map_obj = env->NewObject(IDCache::GetHashMapClass(), IDCache::GetHashMapInit(),
static_cast<u16>(IOS::HLE::USB::list_skylanders.size()));
jmethodID hash_map_put = env->GetMethodID(
hash_map_class, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
jclass skylander_class =
env->FindClass("org/dolphinemu/dolphinemu/features/skylanders/model/SkylanderPair");
@ -60,7 +54,8 @@ Java_org_dolphinemu_dolphinemu_features_skylanders_SkylanderConfig_getInverseSky
const std::string& name = it.second;
jobject skylander_obj =
env->NewObject(skylander_class, skylander_init, it.first.first, it.first.second);
env->CallObjectMethod(hash_map_obj, hash_map_put, ToJString(env, name), skylander_obj);
env->CallObjectMethod(hash_map_obj, IDCache::GetHashMapPut(), ToJString(env, name),
skylander_obj);
env->DeleteLocalRef(skylander_obj);
}

View File

@ -738,7 +738,7 @@ std::string InfinityBase::FindFigure(u32 number) const
return it->first;
}
}
return "";
return "Unknown Figure";
}
u8 InfinityBase::DeriveFigurePosition(u8 position)

View File

@ -139,8 +139,6 @@ void InfinityBaseWindow::LoadFigure(u8 slot)
s_last_figure_path = QFileInfo(file_path).absolutePath() + QLatin1Char('/');
m_edit_figures[slot]->setText(QFileInfo(file_path).fileName());
LoadFigurePath(slot, file_path);
}