Android: Scan on background thread and show progress
This commit is contained in:
parent
5084c90e08
commit
e22c7608e3
|
@ -1,6 +1,8 @@
|
|||
set(SRCS
|
||||
android_host_interface.cpp
|
||||
android_host_interface.h
|
||||
android_progress_callback.cpp
|
||||
android_progress_callback.h
|
||||
android_settings_interface.cpp
|
||||
android_settings_interface.h
|
||||
)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "android_host_interface.h"
|
||||
#include "android_progress_callback.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/audio_stream.h"
|
||||
#include "common/file_system.h"
|
||||
|
@ -522,10 +523,10 @@ void AndroidHostInterface::SetControllerAxisState(u32 index, s32 button_code, fl
|
|||
false);
|
||||
}
|
||||
|
||||
void AndroidHostInterface::RefreshGameList(bool invalidate_cache, bool invalidate_database)
|
||||
void AndroidHostInterface::RefreshGameList(bool invalidate_cache, bool invalidate_database, ProgressCallback* progress_callback)
|
||||
{
|
||||
m_game_list->SetSearchDirectoriesFromSettings(m_settings_interface);
|
||||
m_game_list->Refresh(invalidate_cache, invalidate_database);
|
||||
m_game_list->Refresh(invalidate_cache, invalidate_database, progress_callback);
|
||||
}
|
||||
|
||||
void AndroidHostInterface::ApplySettings(bool display_osd_messages)
|
||||
|
@ -709,9 +710,10 @@ DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getControllerAxisCode, jobject
|
|||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_refreshGameList, jobject obj, jboolean invalidate_cache,
|
||||
jboolean invalidate_database)
|
||||
jboolean invalidate_database, jobject progress_callback)
|
||||
{
|
||||
AndroidHelpers::GetNativeClass(env, obj)->RefreshGameList(invalidate_cache, invalidate_database);
|
||||
AndroidProgressCallback cb(env, progress_callback);
|
||||
AndroidHelpers::GetNativeClass(env, obj)->RefreshGameList(invalidate_cache, invalidate_database, &cb);
|
||||
}
|
||||
|
||||
static const char* DiscRegionToString(DiscRegion region)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include "android_settings_interface.h"
|
||||
#include "common/event.h"
|
||||
#include "common/progress_callback.h"
|
||||
#include "frontend-common/common_host_interface.h"
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
@ -49,7 +50,7 @@ public:
|
|||
void SetControllerButtonState(u32 index, s32 button_code, bool pressed);
|
||||
void SetControllerAxisState(u32 index, s32 button_code, float value);
|
||||
|
||||
void RefreshGameList(bool invalidate_cache, bool invalidate_database);
|
||||
void RefreshGameList(bool invalidate_cache, bool invalidate_database, ProgressCallback* progress_callback);
|
||||
void ApplySettings(bool display_osd_messages);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
#include "android_progress_callback.h"
|
||||
#include "android_host_interface.h"
|
||||
#include "common/log.h"
|
||||
#include "common/assert.h"
|
||||
Log_SetChannel(AndroidProgressCallback);
|
||||
|
||||
AndroidProgressCallback::AndroidProgressCallback(JNIEnv* env, jobject java_object)
|
||||
: m_java_object(java_object)
|
||||
{
|
||||
jclass cls = env->GetObjectClass(java_object);
|
||||
m_set_title_method = env->GetMethodID(cls, "setTitle", "(Ljava/lang/String;)V");
|
||||
m_set_status_text_method = env->GetMethodID(cls, "setStatusText", "(Ljava/lang/String;)V");
|
||||
m_set_progress_range_method = env->GetMethodID(cls, "setProgressRange", "(I)V");
|
||||
m_set_progress_value_method = env->GetMethodID(cls, "setProgressValue", "(I)V");
|
||||
m_modal_error_method = env->GetMethodID(cls, "modalError", "(Ljava/lang/String;)V");
|
||||
m_modal_information_method = env->GetMethodID(cls, "modalInformation", "(Ljava/lang/String;)V");
|
||||
m_modal_confirmation_method = env->GetMethodID(cls, "modalConfirmation", "(Ljava/lang/String;)Z");
|
||||
Assert(m_set_status_text_method && m_set_progress_range_method && m_set_progress_value_method && m_modal_error_method && m_modal_information_method && m_modal_confirmation_method);
|
||||
}
|
||||
|
||||
AndroidProgressCallback::~AndroidProgressCallback() = default;
|
||||
|
||||
bool AndroidProgressCallback::IsCancelled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::SetCancellable(bool cancellable)
|
||||
{
|
||||
if (m_cancellable == cancellable)
|
||||
return;
|
||||
|
||||
BaseProgressCallback::SetCancellable(cancellable);
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::SetTitle(const char* title)
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jstring text_jstr = env->NewStringUTF(title);
|
||||
env->CallVoidMethod(m_java_object, m_set_title_method, text_jstr);
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::SetStatusText(const char* text)
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jstring text_jstr = env->NewStringUTF(text);
|
||||
env->CallVoidMethod(m_java_object, m_set_status_text_method, text_jstr);
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::SetProgressRange(u32 range)
|
||||
{
|
||||
BaseProgressCallback::SetProgressRange(range);
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
env->CallVoidMethod(m_java_object, m_set_progress_range_method, static_cast<jint>(range));
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::SetProgressValue(u32 value)
|
||||
{
|
||||
BaseProgressCallback::SetProgressValue(value);
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
env->CallVoidMethod(m_java_object, m_set_progress_value_method, static_cast<jint>(value));
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::DisplayError(const char* message)
|
||||
{
|
||||
Log_ErrorPrintf("%s", message);
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::DisplayWarning(const char* message)
|
||||
{
|
||||
Log_WarningPrintf("%s", message);
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::DisplayInformation(const char* message)
|
||||
{
|
||||
Log_InfoPrintf("%s", message);
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::DisplayDebugMessage(const char* message)
|
||||
{
|
||||
Log_DevPrintf("%s", message);
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::ModalError(const char* message)
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jstring message_jstr = env->NewStringUTF(message);
|
||||
env->CallVoidMethod(m_java_object, m_modal_error_method, message_jstr);
|
||||
}
|
||||
|
||||
bool AndroidProgressCallback::ModalConfirmation(const char* message)
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jstring message_jstr = env->NewStringUTF(message);
|
||||
return env->CallBooleanMethod(m_java_object, m_modal_confirmation_method, message_jstr);
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::ModalInformation(const char* message)
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jstring message_jstr = env->NewStringUTF(message);
|
||||
env->CallVoidMethod(m_java_object, m_modal_information_method, message_jstr);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
#include "common/progress_callback.h"
|
||||
#include <jni.h>
|
||||
|
||||
class AndroidProgressCallback final : public BaseProgressCallback
|
||||
{
|
||||
public:
|
||||
AndroidProgressCallback(JNIEnv* env, jobject java_object);
|
||||
~AndroidProgressCallback();
|
||||
|
||||
bool IsCancelled() const override;
|
||||
|
||||
void SetCancellable(bool cancellable) override;
|
||||
void SetTitle(const char* title) override;
|
||||
void SetStatusText(const char* text) override;
|
||||
void SetProgressRange(u32 range) override;
|
||||
void SetProgressValue(u32 value) override;
|
||||
|
||||
void DisplayError(const char* message) override;
|
||||
void DisplayWarning(const char* message) override;
|
||||
void DisplayInformation(const char* message) override;
|
||||
void DisplayDebugMessage(const char* message) override;
|
||||
|
||||
void ModalError(const char* message) override;
|
||||
bool ModalConfirmation(const char* message) override;
|
||||
void ModalInformation(const char* message) override;
|
||||
|
||||
private:
|
||||
jobject m_java_object;
|
||||
|
||||
jmethodID m_set_title_method;
|
||||
jmethodID m_set_status_text_method;
|
||||
jmethodID m_set_progress_range_method;
|
||||
jmethodID m_set_progress_value_method;
|
||||
jmethodID m_modal_error_method;
|
||||
jmethodID m_modal_confirmation_method;
|
||||
jmethodID m_modal_information_method;
|
||||
};
|
|
@ -55,7 +55,7 @@ public class AndroidHostInterface {
|
|||
|
||||
public static native int getControllerAxisCode(String controllerType, String axisName);
|
||||
|
||||
public native void refreshGameList(boolean invalidateCache, boolean invalidateDatabase);
|
||||
public native void refreshGameList(boolean invalidateCache, boolean invalidateDatabase, AndroidProgressCallback progressCallback);
|
||||
|
||||
public native GameListEntry[] getGameListEntries();
|
||||
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
public class AndroidProgressCallback {
|
||||
private Activity mContext;
|
||||
private ProgressDialog mDialog;
|
||||
|
||||
public AndroidProgressCallback(Activity context) {
|
||||
mContext = context;
|
||||
mDialog = new ProgressDialog(context);
|
||||
mDialog.setMessage("Please wait...");
|
||||
mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
mDialog.setIndeterminate(false);
|
||||
mDialog.setMax(100);
|
||||
mDialog.setProgress(0);
|
||||
mDialog.show();
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
mDialog.dismiss();
|
||||
}
|
||||
|
||||
public void setTitle(String text) {
|
||||
mContext.runOnUiThread(() -> {
|
||||
mDialog.setTitle(text);
|
||||
});
|
||||
}
|
||||
public void setStatusText(String text) {
|
||||
mContext.runOnUiThread(() -> {
|
||||
mDialog.setMessage(text);
|
||||
});
|
||||
}
|
||||
|
||||
public void setProgressRange(int range) {
|
||||
mContext.runOnUiThread(() -> {
|
||||
mDialog.setMax(range);
|
||||
});
|
||||
}
|
||||
|
||||
public void setProgressValue(int value) {
|
||||
mContext.runOnUiThread(() -> {
|
||||
mDialog.setProgress(value);
|
||||
});
|
||||
}
|
||||
|
||||
public void modalError(String message) {
|
||||
Object lock = new Object();
|
||||
mContext.runOnUiThread(() -> {
|
||||
new AlertDialog.Builder(mContext)
|
||||
.setTitle("Error")
|
||||
.setMessage(message)
|
||||
.setPositiveButton("OK", (dialog, button) -> {
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setOnDismissListener((dialogInterface) -> {
|
||||
synchronized (lock) {
|
||||
lock.notify();
|
||||
}
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
});
|
||||
|
||||
synchronized (lock) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void modalInformation(String message) {
|
||||
Object lock = new Object();
|
||||
mContext.runOnUiThread(() -> {
|
||||
new AlertDialog.Builder(mContext)
|
||||
.setTitle("Information")
|
||||
.setMessage(message)
|
||||
.setPositiveButton("OK", (dialog, button) -> {
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setOnDismissListener((dialogInterface) -> {
|
||||
synchronized (lock) {
|
||||
lock.notify();
|
||||
}
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
});
|
||||
|
||||
synchronized (lock) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ConfirmationResult {
|
||||
public boolean result = false;
|
||||
}
|
||||
|
||||
public boolean modalConfirmation(String message) {
|
||||
ConfirmationResult result = new ConfirmationResult();
|
||||
mContext.runOnUiThread(() -> {
|
||||
new AlertDialog.Builder(mContext)
|
||||
.setTitle("Confirmation")
|
||||
.setMessage(message)
|
||||
.setPositiveButton("Yes", (dialog, button) -> {
|
||||
result.result = true;
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setNegativeButton("No", (dialog, button) -> {
|
||||
result.result = false;
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setOnDismissListener((dialogInterface) -> {
|
||||
synchronized (result) {
|
||||
result.notify();
|
||||
}
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
});
|
||||
|
||||
synchronized (result) {
|
||||
try {
|
||||
result.wait();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
return result.result;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package com.github.stenzek.duckstation;
|
|||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.ArraySet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -17,11 +18,11 @@ import java.util.Comparator;
|
|||
import java.util.Set;
|
||||
|
||||
public class GameList {
|
||||
private Context mContext;
|
||||
private Activity mContext;
|
||||
private GameListEntry[] mEntries;
|
||||
private ListViewAdapter mAdapter;
|
||||
|
||||
public GameList(Context context) {
|
||||
public GameList(Activity context) {
|
||||
mContext = context;
|
||||
mAdapter = new ListViewAdapter();
|
||||
mEntries = new GameListEntry[0];
|
||||
|
@ -35,12 +36,20 @@ public class GameList {
|
|||
}
|
||||
|
||||
|
||||
public void refresh(boolean invalidateCache, boolean invalidateDatabase) {
|
||||
public void refresh(boolean invalidateCache, boolean invalidateDatabase, Activity parentActivity) {
|
||||
// Search and get entries from native code
|
||||
AndroidHostInterface.getInstance().refreshGameList(invalidateCache, invalidateDatabase);
|
||||
mEntries = AndroidHostInterface.getInstance().getGameListEntries();
|
||||
Arrays.sort(mEntries, new GameListEntryComparator());
|
||||
mAdapter.notifyDataSetChanged();
|
||||
AndroidProgressCallback progressCallback = new AndroidProgressCallback(mContext);
|
||||
AsyncTask.execute(() -> {
|
||||
AndroidHostInterface.getInstance().refreshGameList(invalidateCache, invalidateDatabase, progressCallback);
|
||||
GameListEntry[] newEntries = AndroidHostInterface.getInstance().getGameListEntries();
|
||||
Arrays.sort(newEntries, new GameListEntryComparator());
|
||||
|
||||
mContext.runOnUiThread(() -> {
|
||||
progressCallback.dismiss();
|
||||
mEntries = newEntries;
|
||||
mAdapter.notifyDataSetChanged();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public int getEntryCount() {
|
||||
|
|
|
@ -127,7 +127,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
throw new RuntimeException("Failed to create host interface");
|
||||
}
|
||||
|
||||
mGameList.refresh(false, false);
|
||||
mGameList.refresh(false, false, this);
|
||||
}
|
||||
|
||||
private void startAddGameDirectory() {
|
||||
|
@ -163,9 +163,9 @@ public class MainActivity extends AppCompatActivity {
|
|||
} else if (id == R.id.action_add_game_directory) {
|
||||
startAddGameDirectory();
|
||||
} else if (id == R.id.action_scan_for_new_games) {
|
||||
mGameList.refresh(false, false);
|
||||
mGameList.refresh(false, false, this);
|
||||
} else if (id == R.id.action_rescan_all_games) {
|
||||
mGameList.refresh(true, true);
|
||||
mGameList.refresh(true, true, this);
|
||||
} else if (id == R.id.action_import_bios) {
|
||||
importBIOSImage();
|
||||
} else if (id == R.id.action_settings) {
|
||||
|
@ -210,7 +210,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
editor.putStringSet("GameList/RecursivePaths", currentValues);
|
||||
editor.apply();
|
||||
Log.i("MainActivity", "Added path '" + path + "' to game list search directories");
|
||||
mGameList.refresh(false, false);
|
||||
mGameList.refresh(false, false, this);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
Loading…
Reference in New Issue