mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 22:05:38 +00:00
Android: Multiple changes
- Fix game list display of NTSC-J region - Hook up quick load/save/reset options in emulation view. - Add speed limiter toggle to emulation view. - Add game list scanning options to main menu. - Add resume button (not yet hooked up to save states, it'll start the BIOS shell)
This commit is contained in:
parent
8665a24eee
commit
a7e24da7fe
|
@ -130,6 +130,7 @@ void AndroidHostInterface::SetUserDirectory()
|
|||
void AndroidHostInterface::LoadSettings()
|
||||
{
|
||||
CommonHostInterface::LoadSettings(m_settings_interface);
|
||||
CommonHostInterface::UpdateInputMap(m_settings_interface);
|
||||
}
|
||||
|
||||
void AndroidHostInterface::UpdateInputMap()
|
||||
|
@ -396,6 +397,19 @@ void AndroidHostInterface::SetControllerButtonState(u32 index, s32 button_code,
|
|||
false);
|
||||
}
|
||||
|
||||
void AndroidHostInterface::RefreshGameList(bool invalidate_cache, bool invalidate_database)
|
||||
{
|
||||
m_game_list->SetSearchDirectoriesFromSettings(m_settings_interface);
|
||||
m_game_list->Refresh(invalidate_cache, invalidate_database);
|
||||
}
|
||||
|
||||
void AndroidHostInterface::ApplySettings()
|
||||
{
|
||||
Settings old_settings = std::move(m_settings);
|
||||
CommonHostInterface::LoadSettings(m_settings_interface);
|
||||
CheckForSettingsChanges(old_settings);
|
||||
}
|
||||
|
||||
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||
{
|
||||
Log::SetDebugOutputParams(true, nullptr, LOGLEVEL_DEV);
|
||||
|
@ -530,28 +544,18 @@ DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getControllerButtonCode, jobje
|
|||
return code.value_or(-1);
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_cache_path, jstring j_redump_dat_path,
|
||||
jarray j_search_directories, jboolean search_recursively)
|
||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_refreshGameList, jobject obj, jboolean invalidate_cache, jboolean invalidate_database)
|
||||
{
|
||||
// const std::string cache_path = AndroidHelpers::JStringToString(env, j_cache_path);
|
||||
std::string redump_dat_path = AndroidHelpers::JStringToString(env, j_redump_dat_path);
|
||||
AndroidHelpers::GetNativeClass(env, obj)->RefreshGameList(invalidate_cache, invalidate_database);
|
||||
}
|
||||
|
||||
// TODO: This should use the base HostInterface.
|
||||
GameList gl;
|
||||
if (!redump_dat_path.empty())
|
||||
gl.SetDatabaseFilename(std::move(redump_dat_path));
|
||||
|
||||
const jsize search_directories_size = env->GetArrayLength(j_search_directories);
|
||||
for (jsize i = 0; i < search_directories_size; i++)
|
||||
{
|
||||
jobject search_dir_obj = env->GetObjectArrayElement(reinterpret_cast<jobjectArray>(j_search_directories), i);
|
||||
const std::string search_dir = AndroidHelpers::JStringToString(env, reinterpret_cast<jstring>(search_dir_obj));
|
||||
if (!search_dir.empty())
|
||||
gl.AddDirectory(search_dir.c_str(), search_recursively);
|
||||
}
|
||||
|
||||
gl.Refresh(false, false, nullptr);
|
||||
static const char* DiscRegionToString(DiscRegion region) {
|
||||
static std::array<const char*, 4> names = {{"NTSC_J", "NTSC_U", "PAL", "Other"}};
|
||||
return names[static_cast<int>(region)];
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jarray, AndroidHostInterface_getGameListEntries, jobject obj)
|
||||
{
|
||||
jclass entry_class = env->FindClass("com/github/stenzek/duckstation/GameListEntry");
|
||||
Assert(entry_class != nullptr);
|
||||
|
||||
|
@ -560,11 +564,12 @@ DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_ca
|
|||
"String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
|
||||
Assert(entry_constructor != nullptr);
|
||||
|
||||
jobjectArray entry_array = env->NewObjectArray(gl.GetEntryCount(), entry_class, nullptr);
|
||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
jobjectArray entry_array = env->NewObjectArray(hi->GetGameList()->GetEntryCount(), entry_class, nullptr);
|
||||
Assert(entry_array != nullptr);
|
||||
|
||||
u32 counter = 0;
|
||||
for (const GameListEntry& entry : gl.GetEntries())
|
||||
for (const GameListEntry& entry : hi->GetGameList()->GetEntries())
|
||||
{
|
||||
const Timestamp modified_ts(
|
||||
Timestamp::FromUnixTimestamp(static_cast<Timestamp::UnixTimestampValue>(entry.last_modified_time)));
|
||||
|
@ -572,7 +577,7 @@ DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_ca
|
|||
jstring path = env->NewStringUTF(entry.path.c_str());
|
||||
jstring code = env->NewStringUTF(entry.code.c_str());
|
||||
jstring title = env->NewStringUTF(entry.title.c_str());
|
||||
jstring region = env->NewStringUTF(Settings::GetDiscRegionName(entry.region));
|
||||
jstring region = env->NewStringUTF(DiscRegionToString(entry.region));
|
||||
jstring type = env->NewStringUTF(GameList::EntryTypeToString(entry.type));
|
||||
jstring compatibility_rating =
|
||||
env->NewStringUTF(GameList::EntryCompatibilityRatingToString(entry.compatibility_rating));
|
||||
|
@ -587,3 +592,42 @@ DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_ca
|
|||
|
||||
return entry_array;
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_applySettings, jobject obj)
|
||||
{
|
||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
if (hi->IsEmulationThreadRunning())
|
||||
{
|
||||
hi->RunOnEmulationThread([hi]() {
|
||||
hi->ApplySettings();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
hi->ApplySettings();
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_resetSystem, jobject obj, jboolean global, jint slot)
|
||||
{
|
||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
hi->RunOnEmulationThread([hi]() {
|
||||
hi->ResetSystem();
|
||||
});
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_loadState, jobject obj, jboolean global, jint slot)
|
||||
{
|
||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
hi->RunOnEmulationThread([hi, global, slot]() {
|
||||
hi->LoadState(global, slot);
|
||||
});
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_saveState, jobject obj, jboolean global, jint slot)
|
||||
{
|
||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
hi->RunOnEmulationThread([hi, global, slot]() {
|
||||
hi->SaveState(global, slot);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -44,6 +44,9 @@ public:
|
|||
void SetControllerType(u32 index, std::string_view type_name);
|
||||
void SetControllerButtonState(u32 index, s32 button_code, bool pressed);
|
||||
|
||||
void RefreshGameList(bool invalidate_cache, bool invalidate_database);
|
||||
void ApplySettings();
|
||||
|
||||
protected:
|
||||
void SetUserDirectory() override;
|
||||
void LoadSettings() override;
|
||||
|
|
|
@ -141,7 +141,7 @@ void AndroidSettingsInterface::DeleteValue(const char* section, const char* key)
|
|||
std::vector<std::string> AndroidSettingsInterface::GetStringList(const char* section, const char* key)
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jobject values_set = env->CallObjectMethod(m_java_shared_preferences, m_get_string_set, nullptr);
|
||||
jobject values_set = env->CallObjectMethod(m_java_shared_preferences, m_get_string_set, env->NewStringUTF(GetSettingKey(section, key)), nullptr);
|
||||
if (!values_set)
|
||||
return {};
|
||||
|
||||
|
|
|
@ -7,10 +7,6 @@ public class AndroidHostInterface
|
|||
{
|
||||
private long nativePointer;
|
||||
|
||||
static {
|
||||
System.loadLibrary("duckstation-native");
|
||||
}
|
||||
|
||||
static public native AndroidHostInterface create(Context context);
|
||||
|
||||
public AndroidHostInterface(long nativePointer)
|
||||
|
@ -28,4 +24,26 @@ public class AndroidHostInterface
|
|||
public native void setControllerType(int index, String typeName);
|
||||
public native void setControllerButtonState(int index, int buttonCode, boolean pressed);
|
||||
public static native int getControllerButtonCode(String controllerType, String buttonName);
|
||||
|
||||
public native void refreshGameList(boolean invalidateCache, boolean invalidateDatabase);
|
||||
public native GameListEntry[] getGameListEntries();
|
||||
|
||||
public native void resetSystem();
|
||||
public native void loadState(boolean global, int slot);
|
||||
public native void saveState(boolean global, int slot);
|
||||
public native void applySettings();
|
||||
|
||||
static {
|
||||
System.loadLibrary("duckstation-native");
|
||||
}
|
||||
|
||||
static private AndroidHostInterface mInstance;
|
||||
static public boolean createInstance(Context context) {
|
||||
mInstance = create(context);
|
||||
return mInstance != null;
|
||||
}
|
||||
|
||||
static public AndroidHostInterface getInstance() {
|
||||
return mInstance;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package com.github.stenzek.duckstation;
|
||||
|
||||
public enum DiscRegion {
|
||||
NTSC_J,
|
||||
NTSC_U,
|
||||
PAL,
|
||||
Other
|
||||
}
|
|
@ -6,6 +6,7 @@ import androidx.appcompat.app.ActionBar;
|
|||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
|
@ -18,6 +19,7 @@ import android.view.MenuItem;
|
|||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.core.app.NavUtils;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
/**
|
||||
* An example full-screen activity that shows and hides the system UI (i.e.
|
||||
|
@ -25,9 +27,17 @@ import androidx.core.app.NavUtils;
|
|||
*/
|
||||
public class EmulationActivity extends AppCompatActivity implements SurfaceHolder.Callback {
|
||||
/**
|
||||
* Interface to the native emulator core
|
||||
* Settings interfaces.
|
||||
*/
|
||||
AndroidHostInterface mHostInterface;
|
||||
SharedPreferences mPreferences;
|
||||
private boolean getBooleanSetting(String key, boolean defaultValue) {
|
||||
return mPreferences.getBoolean(key, defaultValue);
|
||||
}
|
||||
private void setBooleanSetting(String key, boolean value) {
|
||||
SharedPreferences.Editor editor = mPreferences.edit();
|
||||
editor.putBoolean(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Touchscreen controller overlay
|
||||
|
@ -96,15 +106,16 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
|||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
// Once we get a surface, we can boot.
|
||||
if (mHostInterface.isEmulationThreadRunning()) {
|
||||
mHostInterface.surfaceChanged(holder.getSurface(), format, width, height);
|
||||
if (AndroidHostInterface.getInstance().isEmulationThreadRunning()) {
|
||||
AndroidHostInterface.getInstance().surfaceChanged(holder.getSurface(), format, width, height);
|
||||
return;
|
||||
}
|
||||
|
||||
String bootPath = getIntent().getStringExtra("bootPath");
|
||||
String bootSaveStatePath = getIntent().getStringExtra("bootSaveStatePath");
|
||||
boolean resumeState = getIntent().getBooleanExtra("resumeState", false);
|
||||
|
||||
if (!mHostInterface
|
||||
if (!AndroidHostInterface.getInstance()
|
||||
.startEmulationThread(holder.getSurface(), bootPath, bootSaveStatePath)) {
|
||||
Log.e("EmulationActivity", "Failed to start emulation thread");
|
||||
finishActivity(0);
|
||||
|
@ -114,16 +125,17 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
|||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
if (!mHostInterface.isEmulationThreadRunning())
|
||||
if (!AndroidHostInterface.getInstance().isEmulationThreadRunning())
|
||||
return;
|
||||
|
||||
Log.i("EmulationActivity", "Stopping emulation thread");
|
||||
mHostInterface.stopEmulationThread();
|
||||
AndroidHostInterface.getInstance().stopEmulationThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
setContentView(R.layout.activity_emulation);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
|
@ -142,19 +154,15 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
|||
}
|
||||
});
|
||||
|
||||
mHostInterface = AndroidHostInterface.create(this);
|
||||
if (mHostInterface == null)
|
||||
throw new InstantiationError("Failed to create host interface");
|
||||
|
||||
// Create touchscreen controller.
|
||||
FrameLayout activityLayout = findViewById(R.id.frameLayout);
|
||||
mTouchscreenController = new TouchscreenControllerView(this);
|
||||
activityLayout.addView(mTouchscreenController);
|
||||
mTouchscreenController.init(0, "DigitalController", mHostInterface);
|
||||
mTouchscreenController.init(0, "DigitalController", AndroidHostInterface.getInstance());
|
||||
setTouchscreenControllerVisibility(true);
|
||||
|
||||
// Hook up controller input.
|
||||
mContentView.initControllerKeyMapping(mHostInterface, "DigitalController");
|
||||
mContentView.initControllerKeyMapping("DigitalController");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -172,6 +180,7 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
|||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
getMenuInflater().inflate(R.menu.menu_emulation, menu);
|
||||
menu.findItem(R.id.show_controller).setChecked(mTouchscreenControllerVisible);
|
||||
menu.findItem(R.id.enable_speed_limiter).setChecked(getBooleanSetting("Main/SpeedLimiterEnabled", true));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -191,6 +200,18 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
|||
setTouchscreenControllerVisibility(!mTouchscreenControllerVisible);
|
||||
item.setChecked(mTouchscreenControllerVisible);
|
||||
return true;
|
||||
} else if (id == R.id.enable_speed_limiter) {
|
||||
boolean newSetting = !getBooleanSetting("Main/SpeedLimiterEnabled", true);
|
||||
setBooleanSetting("Main/SpeedLimiterEnabled", newSetting);
|
||||
item.setChecked(newSetting);
|
||||
AndroidHostInterface.getInstance().applySettings();
|
||||
return true;
|
||||
} else if (id == R.id.reset) {
|
||||
AndroidHostInterface.getInstance().resetSystem();
|
||||
} else if (id == R.id.quick_load) {
|
||||
AndroidHostInterface.getInstance().loadState(false, 0);
|
||||
} else if (id == R.id.quick_save) {
|
||||
AndroidHostInterface.getInstance().saveState(false, 0);
|
||||
} else if (id == R.id.quit) {
|
||||
finish();
|
||||
return true;
|
||||
|
|
|
@ -48,7 +48,6 @@ public class EmulationSurfaceView extends SurfaceView {
|
|||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
private AndroidHostInterface mHostInterface;
|
||||
private ArrayMap<Integer, Integer> mControllerKeyMapping;
|
||||
|
||||
private void addControllerKeyMapping(int keyCode, String controllerType, String buttonName) {
|
||||
|
@ -59,9 +58,7 @@ public class EmulationSurfaceView extends SurfaceView {
|
|||
mControllerKeyMapping.put(keyCode, mapping);
|
||||
}
|
||||
|
||||
public void initControllerKeyMapping(AndroidHostInterface hostInterface,
|
||||
String controllerType) {
|
||||
mHostInterface = hostInterface;
|
||||
public void initControllerKeyMapping(String controllerType) {
|
||||
mControllerKeyMapping = new ArrayMap<>();
|
||||
|
||||
// TODO: Don't hardcode...
|
||||
|
@ -86,7 +83,7 @@ public class EmulationSurfaceView extends SurfaceView {
|
|||
return false;
|
||||
|
||||
final int mapping = mControllerKeyMapping.get(keyCode);
|
||||
mHostInterface.setControllerButtonState(0, mapping, pressed);
|
||||
AndroidHostInterface.getInstance().setControllerButtonState(0, mapping, pressed);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,44 +15,21 @@ import androidx.preference.PreferenceManager;
|
|||
import java.util.Set;
|
||||
|
||||
public class GameList {
|
||||
static {
|
||||
System.loadLibrary("duckstation-native");
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
private String mCachePath;
|
||||
private String mRedumpDatPath;
|
||||
private String[] mSearchDirectories;
|
||||
private boolean mSearchRecursively;
|
||||
private GameListEntry[] mEntries;
|
||||
|
||||
static private native GameListEntry[] getEntries(String cachePath, String redumpDatPath,
|
||||
String[] searchDirectories,
|
||||
boolean searchRecursively);
|
||||
private ListViewAdapter mAdapter;
|
||||
|
||||
public GameList(Context context) {
|
||||
mContext = context;
|
||||
refresh();
|
||||
mAdapter = new ListViewAdapter();
|
||||
mEntries = new GameListEntry[0];
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
mCachePath = preferences.getString("GameList/CachePath", "");
|
||||
mRedumpDatPath = preferences.getString("GameList/RedumpDatPath", "");
|
||||
|
||||
Set<String> searchDirectories =
|
||||
preferences.getStringSet("GameList/SearchDirectories", null);
|
||||
if (searchDirectories != null) {
|
||||
mSearchDirectories = new String[searchDirectories.size()];
|
||||
searchDirectories.toArray(mSearchDirectories);
|
||||
} else {
|
||||
mSearchDirectories = new String[0];
|
||||
}
|
||||
|
||||
mSearchRecursively = preferences.getBoolean("GameList/SearchRecursively", true);
|
||||
|
||||
public void refresh(boolean invalidateCache, boolean invalidateDatabase) {
|
||||
// Search and get entries from native code
|
||||
mEntries = getEntries(mCachePath, mRedumpDatPath, mSearchDirectories, mSearchRecursively);
|
||||
AndroidHostInterface.getInstance().refreshGameList(invalidateCache, invalidateDatabase);
|
||||
mEntries = AndroidHostInterface.getInstance().getGameListEntries();
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public int getEntryCount() {
|
||||
|
@ -97,6 +74,6 @@ public class GameList {
|
|||
}
|
||||
|
||||
public BaseAdapter getListViewAdapter() {
|
||||
return new ListViewAdapter();
|
||||
return mAdapter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ public class GameListEntry {
|
|||
private String mTitle;
|
||||
private long mSize;
|
||||
private String mModifiedTime;
|
||||
private ConsoleRegion mRegion;
|
||||
private DiscRegion mRegion;
|
||||
private EntryType mType;
|
||||
private CompatibilityRating mCompatibilityRating;
|
||||
|
||||
|
@ -42,9 +42,9 @@ public class GameListEntry {
|
|||
mModifiedTime = modifiedTime;
|
||||
|
||||
try {
|
||||
mRegion = ConsoleRegion.valueOf(region);
|
||||
mRegion = DiscRegion.valueOf(region);
|
||||
} catch (IllegalArgumentException e) {
|
||||
mRegion = ConsoleRegion.NTSC_U;
|
||||
mRegion = DiscRegion.NTSC_U;
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -74,7 +74,7 @@ public class GameListEntry {
|
|||
|
||||
public String getModifiedTime() { return mModifiedTime; }
|
||||
|
||||
public ConsoleRegion getRegion() {
|
||||
public DiscRegion getRegion() {
|
||||
return mRegion;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,22 +43,26 @@ public class MainActivity extends AppCompatActivity {
|
|||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (!AndroidHostInterface.createInstance(this)) {
|
||||
Log.i("MainActivity", "Failed to create host interface");
|
||||
throw new RuntimeException("Failed to create host interface");
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
FloatingActionButton fab = findViewById(R.id.fab);
|
||||
fab.setOnClickListener(new View.OnClickListener() {
|
||||
findViewById(R.id.fab_add_game_directory).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (!checkForExternalStoragePermissions())
|
||||
return;
|
||||
|
||||
Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
i.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
i.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
|
||||
startActivityForResult(Intent.createChooser(i, "Choose directory"),
|
||||
REQUEST_ADD_DIRECTORY_TO_GAME_LIST);
|
||||
startAddGameDirectory();
|
||||
}
|
||||
});
|
||||
findViewById(R.id.fab_resume).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
startEmulation(null, true);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -69,7 +73,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
mGameListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
startEmulation(mGameList.getEntry(position).getPath());
|
||||
startEmulation(mGameList.getEntry(position).getPath(), true);
|
||||
}
|
||||
});
|
||||
mGameListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
|
||||
|
@ -89,6 +93,18 @@ public class MainActivity extends AppCompatActivity {
|
|||
return true;
|
||||
}
|
||||
});
|
||||
mGameList.refresh(false, false);
|
||||
}
|
||||
|
||||
private void startAddGameDirectory() {
|
||||
if (!checkForExternalStoragePermissions())
|
||||
return;
|
||||
|
||||
Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
i.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
i.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
|
||||
startActivityForResult(Intent.createChooser(i, "Choose directory"),
|
||||
REQUEST_ADD_DIRECTORY_TO_GAME_LIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -106,6 +122,13 @@ public class MainActivity extends AppCompatActivity {
|
|||
int id = item.getItemId();
|
||||
|
||||
//noinspection SimplifiableIfStatement
|
||||
if (id == R.id.action_add_game_directory) {
|
||||
startAddGameDirectory();
|
||||
} else if (id == R.id.action_scan_for_new_games) {
|
||||
mGameList.refresh(false, false);
|
||||
} if (id == R.id.action_rescan_all_games) {
|
||||
mGameList.refresh(true, false);
|
||||
}
|
||||
if (id == R.id.action_settings) {
|
||||
Intent intent = new Intent(this, SettingsActivity.class);
|
||||
startActivity(intent);
|
||||
|
@ -132,16 +155,16 @@ public class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
Set<String> currentValues = prefs.getStringSet("GameList/SearchDirectories", null);
|
||||
Set<String> currentValues = prefs.getStringSet("GameList/RecursivePaths", null);
|
||||
if (currentValues == null)
|
||||
currentValues = new HashSet<String>();
|
||||
|
||||
currentValues.add(path);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putStringSet("GameList/SearchDirectories", currentValues);
|
||||
editor.putStringSet("GameList/RecursivePaths", currentValues);
|
||||
editor.apply();
|
||||
Log.i("MainActivity", "Added path '" + path + "' to game list search directories");
|
||||
mGameList.refresh();
|
||||
mGameList.refresh(false, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -175,13 +198,14 @@ public class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean startEmulation(String bootPath) {
|
||||
private boolean startEmulation(String bootPath, boolean resumeState) {
|
||||
if (!checkForExternalStoragePermissions()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(this, EmulationActivity.class);
|
||||
intent.putExtra("bootPath", bootPath);
|
||||
intent.putExtra("resumeState", resumeState);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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="M8,5v14l11,-7z"/>
|
||||
</vector>
|
|
@ -24,7 +24,17 @@
|
|||
<include layout="@layout/content_main"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:id="@+id/fab_resume"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginBottom="@dimen/fab_margin"
|
||||
android:layout_marginRight="96dp"
|
||||
app:backgroundTint="@android:color/background_light"
|
||||
app:srcCompat="@drawable/ic_baseline_play_arrow_24" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_add_game_directory"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
|
|
|
@ -2,14 +2,22 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<group android:id="@+id/quick_load_save">
|
||||
<item android:title="Quick Load" />
|
||||
<item android:title="Quick Save" />
|
||||
<group android:id="@+id/actions">
|
||||
<item android:id="@+id/reset"
|
||||
android:title="Reset" />
|
||||
<item android:id="@+id/quick_load"
|
||||
android:title="Quick Load" />
|
||||
<item android:id="@+id/quick_save"
|
||||
android:title="Quick Save" />
|
||||
</group>
|
||||
<group android:id="@+id/quick_settings">
|
||||
<item
|
||||
android:id="@+id/change_disc"
|
||||
android:title="Change Disc" />
|
||||
<item
|
||||
android:id="@+id/enable_speed_limiter"
|
||||
android:title="Enable Speed Limiter"
|
||||
android:checkable="true" />
|
||||
<item
|
||||
android:id="@+id/show_controller"
|
||||
android:checkable="true"
|
||||
|
|
|
@ -2,6 +2,14 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context="com.github.stenzek.duckstation.MainActivity" >
|
||||
<group android:id="@+id/game_list">
|
||||
<item android:id="@+id/action_add_game_directory"
|
||||
android:title="Add Game Directory" />
|
||||
<item android:id="@+id/action_scan_for_new_games"
|
||||
android:title="Scan For New Games" />
|
||||
<item android:id="@+id/action_rescan_all_games"
|
||||
android:title="Rescan All Games" />
|
||||
</group>
|
||||
<item android:id="@+id/action_settings"
|
||||
android:title="@string/action_settings"
|
||||
android:orderInCategory="100"
|
||||
|
|
Loading…
Reference in a new issue