Android: Implement disc changing via playlists

This commit is contained in:
Connor McLaughlin 2020-11-08 00:04:09 +10:00
parent 4701d2480d
commit 3e6bc0e10d
6 changed files with 103 additions and 11 deletions

View file

@ -28,6 +28,7 @@ Log_SetChannel(AndroidHostInterface);
#endif
static JavaVM* s_jvm;
static jclass s_String_class;
static jclass s_AndroidHostInterface_class;
static jmethodID s_AndroidHostInterface_constructor;
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.
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 ||
(s_AndroidHostInterface_class = static_cast<jclass>(env->NewGlobalRef(s_AndroidHostInterface_class))) ==
nullptr ||
@ -1011,3 +1015,46 @@ DEFINE_JNI_ARGS_METHOD(jstring, AndroidHostInterface_importBIOSImage, jobject ob
else
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;
}

View file

@ -83,6 +83,10 @@ public class AndroidHostInterface {
public native boolean isFastForwardEnabled();
public native void setFastForwardEnabled(boolean enabled);
public native String[] getMediaPlaylistPaths();
public native int getMediaPlaylistIndex();
public native boolean setMediaPlaylistIndex(int index);
static {
System.loadLibrary("duckstation-native");
}

View file

@ -409,7 +409,7 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
case 2: // Change Disc
{
onMenuClosed();
showDiscChangeMenu();
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
*/

View file

@ -9,7 +9,8 @@ import androidx.core.content.ContextCompat;
public class GameListEntry {
public enum EntryType {
Disc,
PSExe
PSExe,
Playlist
}
public enum CompatibilityRating {
@ -90,15 +91,17 @@ public class GameListEntry {
return mCompatibilityRating;
}
private String getSubTitle() {
String sizeString = String.format("%.2f MB", (double) mSize / 1048576.0);
String fileName;
int lastSlash = mPath.lastIndexOf('/');
if (lastSlash > 0 && lastSlash < mPath.length() - 1)
fileName = mPath.substring(lastSlash + 1);
public static String getFileNameForPath(String path) {
int lastSlash = path.lastIndexOf('/');
if (lastSlash > 0 && lastSlash < path.length() - 1)
return path.substring(lastSlash + 1);
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);
}
@ -134,6 +137,10 @@ public class GameListEntry {
case PSExe:
typeDrawableId = R.drawable.ic_emblem_system;
break;
case Playlist:
typeDrawableId = R.drawable.ic_baseline_playlist_play_24;
break;
}
((ImageView) view.findViewById(R.id.game_list_view_entry_type_icon))

View file

@ -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>

View file

@ -25,7 +25,7 @@ GameList::~GameList() = default;
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)];
}