Android: Implement disc changing via playlists
This commit is contained in:
parent
0ea5c870e7
commit
27a5da8a53
|
@ -28,6 +28,7 @@ Log_SetChannel(AndroidHostInterface);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static JavaVM* s_jvm;
|
static JavaVM* s_jvm;
|
||||||
|
static jclass s_String_class;
|
||||||
static jclass s_AndroidHostInterface_class;
|
static jclass s_AndroidHostInterface_class;
|
||||||
static jmethodID s_AndroidHostInterface_constructor;
|
static jmethodID s_AndroidHostInterface_constructor;
|
||||||
static jfieldID s_AndroidHostInterface_field_mNativePointer;
|
static jfieldID s_AndroidHostInterface_field_mNativePointer;
|
||||||
|
@ -618,7 +619,10 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||||
|
|
||||||
// Create global reference so it doesn't get cleaned up.
|
// Create global reference so it doesn't get cleaned up.
|
||||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||||
if ((s_AndroidHostInterface_class = env->FindClass("com/github/stenzek/duckstation/AndroidHostInterface")) ==
|
if ((s_String_class = env->FindClass("java/lang/String")) == nullptr ||
|
||||||
|
(s_String_class = static_cast<jclass>(env->NewGlobalRef(s_String_class))) ==
|
||||||
|
nullptr ||
|
||||||
|
(s_AndroidHostInterface_class = env->FindClass("com/github/stenzek/duckstation/AndroidHostInterface")) ==
|
||||||
nullptr ||
|
nullptr ||
|
||||||
(s_AndroidHostInterface_class = static_cast<jclass>(env->NewGlobalRef(s_AndroidHostInterface_class))) ==
|
(s_AndroidHostInterface_class = static_cast<jclass>(env->NewGlobalRef(s_AndroidHostInterface_class))) ==
|
||||||
nullptr ||
|
nullptr ||
|
||||||
|
@ -1011,3 +1015,46 @@ DEFINE_JNI_ARGS_METHOD(jstring, AndroidHostInterface_importBIOSImage, jobject ob
|
||||||
else
|
else
|
||||||
return env->NewStringUTF(hash.ToString().c_str());
|
return env->NewStringUTF(hash.ToString().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFINE_JNI_ARGS_METHOD(jobjectArray, AndroidHostInterface_getMediaPlaylistPaths, jobject obj)
|
||||||
|
{
|
||||||
|
if (!System::IsValid())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const u32 count = System::GetMediaPlaylistCount();
|
||||||
|
if (count == 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
jobjectArray arr = env->NewObjectArray(static_cast<jsize>(count), s_String_class, nullptr);
|
||||||
|
for (u32 i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
jstring str = env->NewStringUTF(System::GetMediaPlaylistPath(i).c_str());
|
||||||
|
env->SetObjectArrayElement(arr, static_cast<jsize>(i), str);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getMediaPlaylistIndex, jobject obj)
|
||||||
|
{
|
||||||
|
if (!System::IsValid())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return System::GetMediaPlaylistIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_JNI_ARGS_METHOD(jboolean, AndroidHostInterface_setMediaPlaylistIndex, jobject obj, jint index)
|
||||||
|
{
|
||||||
|
if (!System::IsValid() || index < 0 || static_cast<u32>(index) >= System::GetMediaPlaylistCount())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||||
|
hi->RunOnEmulationThread([index, hi]() {
|
||||||
|
if (System::IsValid()) {
|
||||||
|
if (!System::SwitchMediaFromPlaylist(index))
|
||||||
|
hi->AddOSDMessage("Disc switch failed. Please make sure the file exists.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -83,6 +83,10 @@ public class AndroidHostInterface {
|
||||||
public native boolean isFastForwardEnabled();
|
public native boolean isFastForwardEnabled();
|
||||||
public native void setFastForwardEnabled(boolean enabled);
|
public native void setFastForwardEnabled(boolean enabled);
|
||||||
|
|
||||||
|
public native String[] getMediaPlaylistPaths();
|
||||||
|
public native int getMediaPlaylistIndex();
|
||||||
|
public native boolean setMediaPlaylistIndex(int index);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
System.loadLibrary("duckstation-native");
|
System.loadLibrary("duckstation-native");
|
||||||
}
|
}
|
||||||
|
|
|
@ -409,7 +409,7 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||||
|
|
||||||
case 2: // Change Disc
|
case 2: // Change Disc
|
||||||
{
|
{
|
||||||
onMenuClosed();
|
showDiscChangeMenu();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,6 +482,30 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showDiscChangeMenu() {
|
||||||
|
final String[] paths = AndroidHostInterface.getInstance().getMediaPlaylistPaths();
|
||||||
|
final int currentPath = AndroidHostInterface.getInstance().getMediaPlaylistIndex();
|
||||||
|
if (paths == null)
|
||||||
|
{
|
||||||
|
onMenuClosed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
|
||||||
|
CharSequence[] items = new CharSequence[paths.length];
|
||||||
|
for (int i = 0; i < paths.length; i++)
|
||||||
|
items[i] = GameListEntry.getFileNameForPath(paths[i]);
|
||||||
|
|
||||||
|
builder.setSingleChoiceItems(items, currentPath, (dialogInterface, i) -> {
|
||||||
|
AndroidHostInterface.getInstance().setMediaPlaylistIndex(i);
|
||||||
|
dialogInterface.dismiss();
|
||||||
|
onMenuClosed();
|
||||||
|
});
|
||||||
|
builder.setOnCancelListener(dialogInterface -> onMenuClosed());
|
||||||
|
builder.create().show();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Touchscreen controller overlay
|
* Touchscreen controller overlay
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,7 +9,8 @@ import androidx.core.content.ContextCompat;
|
||||||
public class GameListEntry {
|
public class GameListEntry {
|
||||||
public enum EntryType {
|
public enum EntryType {
|
||||||
Disc,
|
Disc,
|
||||||
PSExe
|
PSExe,
|
||||||
|
Playlist
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum CompatibilityRating {
|
public enum CompatibilityRating {
|
||||||
|
@ -90,15 +91,17 @@ public class GameListEntry {
|
||||||
return mCompatibilityRating;
|
return mCompatibilityRating;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSubTitle() {
|
public static String getFileNameForPath(String path) {
|
||||||
String sizeString = String.format("%.2f MB", (double) mSize / 1048576.0);
|
int lastSlash = path.lastIndexOf('/');
|
||||||
String fileName;
|
if (lastSlash > 0 && lastSlash < path.length() - 1)
|
||||||
int lastSlash = mPath.lastIndexOf('/');
|
return path.substring(lastSlash + 1);
|
||||||
if (lastSlash > 0 && lastSlash < mPath.length() - 1)
|
|
||||||
fileName = mPath.substring(lastSlash + 1);
|
|
||||||
else
|
else
|
||||||
fileName = mPath;
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSubTitle() {
|
||||||
|
String fileName = getFileNameForPath(mPath);
|
||||||
|
String sizeString = String.format("%.2f MB", (double) mSize / 1048576.0);
|
||||||
return String.format("%s (%s)", fileName, sizeString);
|
return String.format("%s (%s)", fileName, sizeString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +137,10 @@ public class GameListEntry {
|
||||||
case PSExe:
|
case PSExe:
|
||||||
typeDrawableId = R.drawable.ic_emblem_system;
|
typeDrawableId = R.drawable.ic_emblem_system;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Playlist:
|
||||||
|
typeDrawableId = R.drawable.ic_baseline_playlist_play_24;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
((ImageView) view.findViewById(R.id.game_list_view_entry_type_icon))
|
((ImageView) view.findViewById(R.id.game_list_view_entry_type_icon))
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M4,10h12v2L4,12zM4,6h12v2L4,8zM4,14h8v2L4,16zM14,14v6l5,-3z"/>
|
||||||
|
</vector>
|
|
@ -25,7 +25,7 @@ GameList::~GameList() = default;
|
||||||
|
|
||||||
const char* GameList::EntryTypeToString(GameListEntryType type)
|
const char* GameList::EntryTypeToString(GameListEntryType type)
|
||||||
{
|
{
|
||||||
static std::array<const char*, 2> names = {{"Disc", "PSExe"}};
|
static std::array<const char*, 3> names = {{"Disc", "PSExe", "Playlist"}};
|
||||||
return names[static_cast<int>(type)];
|
return names[static_cast<int>(type)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue