mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 22:55:40 +12:00
Bonk
This commit is contained in:
parent
0ab7bf3b17
commit
f2fac171a0
22 changed files with 324 additions and 323 deletions
|
@ -1,7 +1,8 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
|
||||||
class Pandroid {
|
|
||||||
public:
|
namespace Pandroid {
|
||||||
static void onSmdhLoaded(const std::vector<u8> &smdh);
|
static void onSmdhLoaded(const std::vector<u8>& smdh);
|
||||||
};
|
};
|
||||||
|
|
|
@ -259,10 +259,9 @@ bool NCCH::parseSMDH(const std::vector<u8>& smdh) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
#ifdef __ANDROID__
|
|
||||||
Pandroid::onSmdhLoaded(smdh);
|
Pandroid::onSmdhLoaded(smdh);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Bitmask showing which regions are allowed.
|
// Bitmask showing which regions are allowed.
|
||||||
// https://www.3dbrew.org/wiki/SMDH#Region_Lockout
|
// https://www.3dbrew.org/wiki/SMDH#Region_Lockout
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include "jni_driver.hpp"
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
#include <EGL/egl.h>
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
@ -8,8 +10,6 @@
|
||||||
#include "renderer_gl/renderer_gl.hpp"
|
#include "renderer_gl/renderer_gl.hpp"
|
||||||
#include "services/hid.hpp"
|
#include "services/hid.hpp"
|
||||||
|
|
||||||
#include "jni_driver.hpp"
|
|
||||||
|
|
||||||
std::unique_ptr<Emulator> emulator = nullptr;
|
std::unique_ptr<Emulator> emulator = nullptr;
|
||||||
HIDService* hidService = nullptr;
|
HIDService* hidService = nullptr;
|
||||||
RendererGL* renderer = nullptr;
|
RendererGL* renderer = nullptr;
|
||||||
|
@ -24,41 +24,35 @@ void throwException(JNIEnv* env, const char* message) {
|
||||||
env->ThrowNew(exceptionClass, message);
|
env->ThrowNew(exceptionClass, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEnv* jniEnv(){
|
JNIEnv* jniEnv() {
|
||||||
JNIEnv* env;
|
JNIEnv* env;
|
||||||
auto status = jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
|
auto status = jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
|
||||||
if(status == JNI_EDETACHED){
|
if (status == JNI_EDETACHED) {
|
||||||
jvm->AttachCurrentThread(&env, nullptr);
|
jvm->AttachCurrentThread(&env, nullptr);
|
||||||
} else if(status != JNI_OK){
|
} else if (status != JNI_OK) {
|
||||||
throw std::runtime_error("Failed to obtain JNIEnv from JVM!!");
|
throw std::runtime_error("Failed to obtain JNIEnv from JVM!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Pandroid::onSmdhLoaded(const std::vector<u8>& smdh) {
|
||||||
void Pandroid::onSmdhLoaded(const std::vector<u8> &smdh){
|
|
||||||
JNIEnv* env = jniEnv();
|
JNIEnv* env = jniEnv();
|
||||||
int size = smdh.size();
|
int size = smdh.size();
|
||||||
|
|
||||||
jbyteArray result = env->NewByteArray(size);
|
jbyteArray result = env->NewByteArray(size);
|
||||||
|
|
||||||
env->SetByteArrayRegion(result, 0, size, (jbyte*)smdh.data());
|
env->SetByteArrayRegion(result, 0, size, (jbyte*)smdh.data());
|
||||||
|
|
||||||
|
auto classLoader = env->FindClass(alberClass);
|
||||||
|
auto method = env->GetStaticMethodID(classLoader, "OnSmdhLoaded", "([B)V");
|
||||||
|
|
||||||
auto clazz = env->FindClass(alberClass);
|
env->CallStaticVoidMethod(classLoader, method, result);
|
||||||
auto method = env->GetStaticMethodID(clazz, "OnSmdhLoaded", "([B)V");
|
|
||||||
|
|
||||||
env->CallStaticVoidMethod(clazz, method, result);
|
|
||||||
|
|
||||||
env->DeleteLocalRef(result);
|
env->DeleteLocalRef(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
AlberFunction(void, Setup)(JNIEnv* env, jobject obj) {
|
AlberFunction(void, Setup)(JNIEnv* env, jobject obj) { env->GetJavaVM(&jvm); }
|
||||||
env->GetJavaVM(&jvm);
|
|
||||||
}
|
|
||||||
|
|
||||||
AlberFunction(void, Initialize)(JNIEnv* env, jobject obj) {
|
AlberFunction(void, Initialize)(JNIEnv* env, jobject obj) {
|
||||||
emulator = std::make_unique<Emulator>();
|
emulator = std::make_unique<Emulator>();
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
package com.panda3ds.pandroid.app;
|
package com.panda3ds.pandroid.app;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import com.panda3ds.pandroid.R;
|
import com.panda3ds.pandroid.R;
|
||||||
import com.panda3ds.pandroid.data.config.GlobalConfig;
|
import com.panda3ds.pandroid.data.config.GlobalConfig;
|
||||||
|
|
||||||
|
|
||||||
public class BaseActivity extends AppCompatActivity {
|
public class BaseActivity extends AppCompatActivity {
|
||||||
private int currentTheme = GlobalConfig.get(GlobalConfig.KEY_APP_THEME);
|
private int currentTheme = GlobalConfig.get(GlobalConfig.KEY_APP_THEME);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
applyTheme();
|
applyTheme();
|
||||||
|
@ -19,26 +19,20 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
if (GlobalConfig.get(GlobalConfig.KEY_APP_THEME) != currentTheme){
|
|
||||||
|
if (GlobalConfig.get(GlobalConfig.KEY_APP_THEME) != currentTheme) {
|
||||||
recreate();
|
recreate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyTheme(){
|
private void applyTheme() {
|
||||||
switch (GlobalConfig.get(GlobalConfig.KEY_APP_THEME)){
|
switch (GlobalConfig.get(GlobalConfig.KEY_APP_THEME)) {
|
||||||
case GlobalConfig.THEME_ANDROID:
|
case GlobalConfig.THEME_ANDROID: setTheme(R.style.Theme_Pandroid); break;
|
||||||
setTheme(R.style.Theme_Pandroid);
|
case GlobalConfig.THEME_LIGHT: setTheme(R.style.Theme_Pandroid_Light); break;
|
||||||
break;
|
case GlobalConfig.THEME_DARK: setTheme(R.style.Theme_Pandroid_Dark); break;
|
||||||
case GlobalConfig.THEME_LIGHT:
|
case GlobalConfig.THEME_BLACK: setTheme(R.style.Theme_Pandroid_Black); break;
|
||||||
setTheme(R.style.Theme_Pandroid_Light);
|
|
||||||
break;
|
|
||||||
case GlobalConfig.THEME_DARK:
|
|
||||||
setTheme(R.style.Theme_Pandroid_Dark);
|
|
||||||
break;
|
|
||||||
case GlobalConfig.THEME_BLACK:
|
|
||||||
setTheme(R.style.Theme_Pandroid_Black);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTheme = GlobalConfig.get(GlobalConfig.KEY_APP_THEME);
|
currentTheme = GlobalConfig.get(GlobalConfig.KEY_APP_THEME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,7 @@ import android.view.WindowManager;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.panda3ds.pandroid.AlberDriver;
|
import com.panda3ds.pandroid.AlberDriver;
|
||||||
import com.panda3ds.pandroid.R;
|
import com.panda3ds.pandroid.R;
|
||||||
import com.panda3ds.pandroid.app.game.AlberInputListener;
|
import com.panda3ds.pandroid.app.game.AlberInputListener;
|
||||||
|
@ -24,7 +22,6 @@ import com.panda3ds.pandroid.view.PandaGlSurfaceView;
|
||||||
import com.panda3ds.pandroid.view.PandaLayoutController;
|
import com.panda3ds.pandroid.view.PandaLayoutController;
|
||||||
|
|
||||||
public class GameActivity extends BaseActivity {
|
public class GameActivity extends BaseActivity {
|
||||||
|
|
||||||
private final AlberInputListener inputListener = new AlberInputListener(this);
|
private final AlberInputListener inputListener = new AlberInputListener(this);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -75,25 +72,28 @@ public class GameActivity extends BaseActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||||
if (InputHandler.processKeyEvent(event))
|
if (InputHandler.processKeyEvent(event)) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return super.dispatchKeyEvent(event);
|
return super.dispatchKeyEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean dispatchGenericMotionEvent(MotionEvent ev) {
|
public boolean dispatchGenericMotionEvent(MotionEvent ev) {
|
||||||
if (InputHandler.processMotionEvent(ev))
|
if (InputHandler.processMotionEvent(ev)) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return super.dispatchGenericMotionEvent(ev);
|
return super.dispatchGenericMotionEvent(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
if(AlberDriver.HasRomLoaded()){
|
if (AlberDriver.HasRomLoaded()) {
|
||||||
AlberDriver.Finalize();
|
AlberDriver.Finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,18 +9,17 @@ import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
import com.google.android.material.navigation.NavigationBarView;
|
import com.google.android.material.navigation.NavigationBarView;
|
||||||
import com.panda3ds.pandroid.R;
|
import com.panda3ds.pandroid.R;
|
||||||
import com.panda3ds.pandroid.app.main.GamesFragment;
|
import com.panda3ds.pandroid.app.main.GamesFragment;
|
||||||
import com.panda3ds.pandroid.app.main.SearchFragment;
|
import com.panda3ds.pandroid.app.main.SearchFragment;
|
||||||
import com.panda3ds.pandroid.app.main.SettingsFragment;
|
import com.panda3ds.pandroid.app.main.SettingsFragment;
|
||||||
|
|
||||||
|
|
||||||
public class MainActivity extends BaseActivity implements NavigationBarView.OnItemSelectedListener {
|
public class MainActivity extends BaseActivity implements NavigationBarView.OnItemSelectedListener {
|
||||||
private static final int PICK_ROM = 2;
|
private static final int PICK_ROM = 2;
|
||||||
private static final int PERMISSION_REQUEST_CODE = 3;
|
private static final int PERMISSION_REQUEST_CODE = 3;
|
||||||
|
@ -45,8 +44,8 @@ public class MainActivity extends BaseActivity implements NavigationBarView.OnIt
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ActivityCompat.requestPermissions(this, new String[]{READ_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
|
ActivityCompat.requestPermissions(this, new String[] {READ_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
|
||||||
ActivityCompat.requestPermissions(this, new String[]{WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
|
ActivityCompat.requestPermissions(this, new String[] {WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
@ -71,9 +70,7 @@ public class MainActivity extends BaseActivity implements NavigationBarView.OnIt
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.beginTransaction()
|
manager.beginTransaction().replace(R.id.fragment_container, fragment).commitNow();
|
||||||
.replace(R.id.fragment_container, fragment)
|
|
||||||
.commitNow();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,12 +2,12 @@ package com.panda3ds.pandroid.app;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.panda3ds.pandroid.AlberDriver;
|
import com.panda3ds.pandroid.AlberDriver;
|
||||||
import com.panda3ds.pandroid.data.config.GlobalConfig;
|
import com.panda3ds.pandroid.data.config.GlobalConfig;
|
||||||
import com.panda3ds.pandroid.input.InputMap;
|
import com.panda3ds.pandroid.input.InputMap;
|
||||||
import com.panda3ds.pandroid.utils.GameUtils;
|
import com.panda3ds.pandroid.utils.GameUtils;
|
||||||
|
|
||||||
|
|
||||||
public class PandroidApplication extends Application {
|
public class PandroidApplication extends Application {
|
||||||
private static Context appContext;
|
private static Context appContext;
|
||||||
|
|
||||||
|
@ -15,13 +15,12 @@ public class PandroidApplication extends Application {
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
appContext = this;
|
appContext = this;
|
||||||
|
|
||||||
GlobalConfig.initialize();
|
GlobalConfig.initialize();
|
||||||
GameUtils.initialize();
|
GameUtils.initialize();
|
||||||
InputMap.initialize();
|
InputMap.initialize();
|
||||||
AlberDriver.Setup();
|
AlberDriver.Setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Context getAppContext() {
|
public static Context getAppContext() { return appContext; }
|
||||||
return appContext;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
@ -30,10 +29,7 @@ public class PreferenceActivity extends BaseActivity {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Class<?> clazz = getClassLoader().loadClass(intent.getStringExtra(Constants.ACTIVITY_PARAMETER_FRAGMENT));
|
Class<?> clazz = getClassLoader().loadClass(intent.getStringExtra(Constants.ACTIVITY_PARAMETER_FRAGMENT));
|
||||||
getSupportFragmentManager()
|
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, (Fragment) clazz.newInstance()).commitNow();
|
||||||
.beginTransaction()
|
|
||||||
.replace(R.id.fragment_container, (Fragment) clazz.newInstance())
|
|
||||||
.commitNow();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -47,8 +43,10 @@ public class PreferenceActivity extends BaseActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
if (item.getItemId() == android.R.id.home)
|
if (item.getItemId() == android.R.id.home) {
|
||||||
finish();
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
package com.panda3ds.pandroid.app.base;
|
package com.panda3ds.pandroid.app.base;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
|
|
||||||
import com.panda3ds.pandroid.lang.Function;
|
import com.panda3ds.pandroid.lang.Function;
|
||||||
|
|
||||||
|
|
||||||
public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
|
public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
protected void setItemClick(String key, Function<Preference> listener){
|
protected void setItemClick(String key, Function<Preference> listener) {
|
||||||
findPreference(key).setOnPreferenceClickListener(preference -> {
|
findPreference(key).setOnPreferenceClickListener(preference -> {
|
||||||
listener.run(preference);
|
listener.run(preference);
|
||||||
getPreferenceScreen().performClick();
|
getPreferenceScreen().performClick();
|
||||||
|
|
|
@ -2,21 +2,18 @@ package com.panda3ds.pandroid.app.game;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
import com.panda3ds.pandroid.AlberDriver;
|
import com.panda3ds.pandroid.AlberDriver;
|
||||||
import com.panda3ds.pandroid.input.InputEvent;
|
import com.panda3ds.pandroid.input.InputEvent;
|
||||||
import com.panda3ds.pandroid.input.InputMap;
|
import com.panda3ds.pandroid.input.InputMap;
|
||||||
import com.panda3ds.pandroid.input.KeyName;
|
import com.panda3ds.pandroid.input.KeyName;
|
||||||
import com.panda3ds.pandroid.lang.Function;
|
import com.panda3ds.pandroid.lang.Function;
|
||||||
import com.panda3ds.pandroid.math.Vector2;
|
import com.panda3ds.pandroid.math.Vector2;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
||||||
public class AlberInputListener implements Function<InputEvent> {
|
public class AlberInputListener implements Function<InputEvent> {
|
||||||
private final Activity activity;
|
private final Activity activity;
|
||||||
public AlberInputListener(Activity activity){
|
public AlberInputListener(Activity activity) { this.activity = activity; }
|
||||||
this.activity = activity;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Vector2 axis = new Vector2(0.0f, 0.0f);
|
private final Vector2 axis = new Vector2(0.0f, 0.0f);
|
||||||
|
|
||||||
|
@ -24,13 +21,14 @@ public class AlberInputListener implements Function<InputEvent> {
|
||||||
public void run(InputEvent event) {
|
public void run(InputEvent event) {
|
||||||
KeyName key = InputMap.relative(event.getName());
|
KeyName key = InputMap.relative(event.getName());
|
||||||
|
|
||||||
if (Objects.equals(event.getName(), "KEYCODE_BACK")){
|
if (Objects.equals(event.getName(), "KEYCODE_BACK")) {
|
||||||
activity.onBackPressed();
|
activity.onBackPressed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key == KeyName.NULL)
|
if (key == KeyName.NULL) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
boolean axisChanged = false;
|
boolean axisChanged = false;
|
||||||
|
|
||||||
|
@ -64,5 +62,4 @@ public class AlberInputListener implements Function<InputEvent> {
|
||||||
AlberDriver.SetCirclepadAxis(Math.round(axis.x * 0x9C), Math.round(axis.y * 0x9C));
|
AlberDriver.SetCirclepadAxis(Math.round(axis.x * 0x9C), Math.round(axis.y * 0x9C));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,22 +6,20 @@ import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.activity.result.ActivityResultCallback;
|
import androidx.activity.result.ActivityResultCallback;
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.panda3ds.pandroid.R;
|
import com.panda3ds.pandroid.R;
|
||||||
import com.panda3ds.pandroid.data.game.GameMetadata;
|
import com.panda3ds.pandroid.data.game.GameMetadata;
|
||||||
import com.panda3ds.pandroid.utils.FileUtils;
|
import com.panda3ds.pandroid.utils.FileUtils;
|
||||||
import com.panda3ds.pandroid.utils.GameUtils;
|
import com.panda3ds.pandroid.utils.GameUtils;
|
||||||
import com.panda3ds.pandroid.view.gamesgrid.GamesGridView;
|
import com.panda3ds.pandroid.view.gamesgrid.GamesGridView;
|
||||||
|
|
||||||
public class GamesFragment extends Fragment implements ActivityResultCallback<Uri> {
|
|
||||||
|
|
||||||
|
public class GamesFragment extends Fragment implements ActivityResultCallback<Uri> {
|
||||||
private final ActivityResultContracts.OpenDocument openRomContract = new ActivityResultContracts.OpenDocument();
|
private final ActivityResultContracts.OpenDocument openRomContract = new ActivityResultContracts.OpenDocument();
|
||||||
private ActivityResultLauncher<String[]> pickFileRequest;
|
private ActivityResultLauncher<String[]> pickFileRequest;
|
||||||
private GamesGridView gameListView;
|
private GamesGridView gameListView;
|
||||||
|
@ -37,7 +35,7 @@ public class GamesFragment extends Fragment implements ActivityResultCallback<Ur
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
gameListView = view.findViewById(R.id.games);
|
gameListView = view.findViewById(R.id.games);
|
||||||
|
|
||||||
view.findViewById(R.id.add_rom).setOnClickListener((v) -> pickFileRequest.launch(new String[]{"*/*"}));
|
view.findViewById(R.id.add_rom).setOnClickListener((v) -> pickFileRequest.launch(new String[] {"*/*"}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,12 +49,12 @@ public class GamesFragment extends Fragment implements ActivityResultCallback<Ur
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
String uri = result.toString();
|
String uri = result.toString();
|
||||||
if (GameUtils.findByRomPath(uri) == null) {
|
if (GameUtils.findByRomPath(uri) == null) {
|
||||||
if (FileUtils.obtainRealPath(uri) == null){
|
if (FileUtils.obtainRealPath(uri) == null) {
|
||||||
Toast.makeText(getContext(), "Invalid file path", Toast.LENGTH_LONG).show();
|
Toast.makeText(getContext(), "Invalid file path", Toast.LENGTH_LONG).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FileUtils.makeUriPermanent(uri, FileUtils.MODE_READ);
|
FileUtils.makeUriPermanent(uri, FileUtils.MODE_READ);
|
||||||
GameMetadata game = new GameMetadata(uri, FileUtils.getName(uri).split("\\.")[0],"Unknown");
|
GameMetadata game = new GameMetadata(uri, FileUtils.getName(uri).split("\\.")[0], "Unknown");
|
||||||
GameUtils.addGame(game);
|
GameUtils.addGame(game);
|
||||||
GameUtils.launch(requireActivity(), game);
|
GameUtils.launch(requireActivity(), game);
|
||||||
}
|
}
|
||||||
|
@ -75,6 +73,7 @@ public class GamesFragment extends Fragment implements ActivityResultCallback<Ur
|
||||||
pickFileRequest.unregister();
|
pickFileRequest.unregister();
|
||||||
pickFileRequest = null;
|
pickFileRequest = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,24 +4,21 @@ import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.widget.AppCompatEditText;
|
import androidx.appcompat.widget.AppCompatEditText;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.panda3ds.pandroid.R;
|
import com.panda3ds.pandroid.R;
|
||||||
import com.panda3ds.pandroid.data.game.GameMetadata;
|
import com.panda3ds.pandroid.data.game.GameMetadata;
|
||||||
import com.panda3ds.pandroid.utils.GameUtils;
|
import com.panda3ds.pandroid.utils.GameUtils;
|
||||||
import com.panda3ds.pandroid.utils.SearchAgent;
|
import com.panda3ds.pandroid.utils.SearchAgent;
|
||||||
import com.panda3ds.pandroid.view.SimpleTextWatcher;
|
import com.panda3ds.pandroid.view.SimpleTextWatcher;
|
||||||
import com.panda3ds.pandroid.view.gamesgrid.GamesGridView;
|
import com.panda3ds.pandroid.view.gamesgrid.GamesGridView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class SearchFragment extends Fragment {
|
|
||||||
|
|
||||||
|
public class SearchFragment extends Fragment {
|
||||||
private final SearchAgent searchAgent = new SearchAgent();
|
private final SearchAgent searchAgent = new SearchAgent();
|
||||||
private GamesGridView gamesListView;
|
private GamesGridView gamesListView;
|
||||||
|
|
||||||
|
@ -36,9 +33,7 @@ public class SearchFragment extends Fragment {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
gamesListView = view.findViewById(R.id.games);
|
gamesListView = view.findViewById(R.id.games);
|
||||||
|
((AppCompatEditText) view.findViewById(R.id.search_bar)).addTextChangedListener((SimpleTextWatcher) this::search);
|
||||||
((AppCompatEditText) view.findViewById(R.id.search_bar))
|
|
||||||
.addTextChangedListener((SimpleTextWatcher) this::search);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -48,19 +43,19 @@ public class SearchFragment extends Fragment {
|
||||||
for (GameMetadata game : GameUtils.getGames()) {
|
for (GameMetadata game : GameUtils.getGames()) {
|
||||||
searchAgent.addToBuffer(game.getId(), game.getTitle(), game.getPublisher());
|
searchAgent.addToBuffer(game.getId(), game.getTitle(), game.getPublisher());
|
||||||
}
|
}
|
||||||
|
|
||||||
search("");
|
search("");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void search(String query) {
|
private void search(String query) {
|
||||||
List<String> resultIds = searchAgent.search(query);
|
List<String> resultIds = searchAgent.search(query);
|
||||||
ArrayList<GameMetadata> games = new ArrayList<>(GameUtils.getGames());
|
ArrayList<GameMetadata> games = new ArrayList<>(GameUtils.getGames());
|
||||||
Object[] resultObj = games.stream()
|
Object[] resultObj = games.stream().filter(gameMetadata -> resultIds.contains(gameMetadata.getId())).toArray();
|
||||||
.filter(gameMetadata -> resultIds.contains(gameMetadata.getId()))
|
|
||||||
.toArray();
|
|
||||||
|
|
||||||
games.clear();
|
games.clear();
|
||||||
for (Object res : resultObj)
|
for (Object res : resultObj) {
|
||||||
games.add((GameMetadata) res);
|
games.add((GameMetadata) res);
|
||||||
|
}
|
||||||
|
|
||||||
gamesListView.setGameList(games);
|
gamesListView.setGameList(games);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,9 @@ public class InputMapActivity extends BaseActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
InputHandler.reset();
|
InputHandler.reset();
|
||||||
InputHandler.setMotionDeadZone(0.8F);
|
InputHandler.setMotionDeadZone(0.8f);
|
||||||
InputHandler.setEventListener(this::onInputEvent);
|
InputHandler.setEventListener(this::onInputEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +62,6 @@ public class InputMapActivity extends BaseActivity {
|
||||||
|
|
||||||
|
|
||||||
public static final class Contract extends ActivityResultContract<String, String> {
|
public static final class Contract extends ActivityResultContract<String, String> {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Intent createIntent(@NonNull Context context, String s) {
|
public Intent createIntent(@NonNull Context context, String s) {
|
||||||
|
|
|
@ -30,7 +30,10 @@ public class InputMapPreferences extends BasePreferenceFragment implements Activ
|
||||||
((BaseActivity) requireActivity()).getSupportActionBar().setTitle(R.string.controller_mapping);
|
((BaseActivity) requireActivity()).getSupportActionBar().setTitle(R.string.controller_mapping);
|
||||||
|
|
||||||
for (KeyName key : KeyName.values()) {
|
for (KeyName key : KeyName.values()) {
|
||||||
if (key == KeyName.NULL) continue;
|
if (key == KeyName.NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
setItemClick(key.name(), this::onItemPressed);
|
setItemClick(key.name(), this::onItemPressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +62,7 @@ public class InputMapPreferences extends BasePreferenceFragment implements Activ
|
||||||
@Override
|
@Override
|
||||||
public void onDetach() {
|
public void onDetach() {
|
||||||
super.onDetach();
|
super.onDetach();
|
||||||
|
|
||||||
if (requestKey != null) {
|
if (requestKey != null) {
|
||||||
requestKey.unregister();
|
requestKey.unregister();
|
||||||
requestKey = null;
|
requestKey = null;
|
||||||
|
@ -77,10 +81,14 @@ public class InputMapPreferences extends BasePreferenceFragment implements Activ
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshList() {
|
private void refreshList() {
|
||||||
deadZonePreference.setValue((int)(InputMap.getDeadZone()*100));
|
deadZonePreference.setValue((int)(InputMap.getDeadZone() * 100));
|
||||||
deadZonePreference.setSummary(deadZonePreference.getValue()+"%");
|
deadZonePreference.setSummary(deadZonePreference.getValue() + "%");
|
||||||
|
|
||||||
for (KeyName key : KeyName.values()) {
|
for (KeyName key : KeyName.values()) {
|
||||||
if (key == KeyName.NULL) continue;
|
if (key == KeyName.NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
findPreference(key.name()).setSummary(InputMap.relative(key));
|
findPreference(key.name()).setSummary(InputMap.relative(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ public class GsonConfigParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPath(){
|
private String getPath(){
|
||||||
return FileUtils.getConfigPath()+"/"+name+".json";
|
return FileUtils.getConfigPath()+ "/" + name + ".json";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void save(Object data){
|
public void save(Object data){
|
||||||
|
@ -26,7 +26,7 @@ public class GsonConfigParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T load(Class<T> clazz){
|
public <T> T load(Class<T> clazz){
|
||||||
String[] content = new String[]{"{}"};
|
String[] content = new String[] {"{}"};
|
||||||
new Task(()->{
|
new Task(()->{
|
||||||
if (FileUtils.exists(getPath())){
|
if (FileUtils.exists(getPath())){
|
||||||
content[0] = FileUtils.readTextFile(getPath());
|
content[0] = FileUtils.readTextFile(getPath());
|
||||||
|
|
|
@ -9,9 +9,8 @@ import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
public class SMDH {
|
public class SMDH {
|
||||||
|
|
||||||
public static final int LANGUAGE_ENGLISH = 1;
|
|
||||||
public static final int LANGUAGE_JAPANESE = 0;
|
public static final int LANGUAGE_JAPANESE = 0;
|
||||||
|
public static final int LANGUAGE_ENGLISH = 1;
|
||||||
public static final int LANGUAGE_CHINESE = 6;
|
public static final int LANGUAGE_CHINESE = 6;
|
||||||
public static final int LANGUAGE_KOREAN = 7;
|
public static final int LANGUAGE_KOREAN = 7;
|
||||||
|
|
||||||
|
@ -61,6 +60,8 @@ public class SMDH {
|
||||||
final boolean korea = (regionMasks & REGION_KOREAN_MASK) != 0;
|
final boolean korea = (regionMasks & REGION_KOREAN_MASK) != 0;
|
||||||
final boolean taiwan = (regionMasks & REGION_TAIWAN_MASK) != 0;
|
final boolean taiwan = (regionMasks & REGION_TAIWAN_MASK) != 0;
|
||||||
|
|
||||||
|
// Depending on the regions allowed in the region mask, pick one of the regions to use
|
||||||
|
// We prioritize English-speaking regions both here and in the emulator core, since users are most likely to speak English at least
|
||||||
if (northAmerica) {
|
if (northAmerica) {
|
||||||
region = GameRegion.NorthAmerican;
|
region = GameRegion.NorthAmerican;
|
||||||
} else if (europe) {
|
} else if (europe) {
|
||||||
|
@ -71,14 +72,14 @@ public class SMDH {
|
||||||
region = GameRegion.Japan;
|
region = GameRegion.Japan;
|
||||||
metaLanguage = LANGUAGE_JAPANESE;
|
metaLanguage = LANGUAGE_JAPANESE;
|
||||||
} else if (korea) {
|
} else if (korea) {
|
||||||
metaLanguage = LANGUAGE_KOREAN;
|
|
||||||
region = GameRegion.Korean;
|
region = GameRegion.Korean;
|
||||||
|
metaLanguage = LANGUAGE_KOREAN;
|
||||||
} else if (china) {
|
} else if (china) {
|
||||||
metaLanguage = LANGUAGE_CHINESE;
|
|
||||||
region = GameRegion.China;
|
region = GameRegion.China;
|
||||||
} else if (taiwan) {
|
|
||||||
metaLanguage = LANGUAGE_CHINESE;
|
metaLanguage = LANGUAGE_CHINESE;
|
||||||
|
} else if (taiwan) {
|
||||||
region = GameRegion.Taiwan;
|
region = GameRegion.Taiwan;
|
||||||
|
metaLanguage = LANGUAGE_CHINESE;
|
||||||
} else {
|
} else {
|
||||||
region = GameRegion.None;
|
region = GameRegion.None;
|
||||||
}
|
}
|
||||||
|
@ -111,29 +112,30 @@ public class SMDH {
|
||||||
int indexY = y & ~7;
|
int indexY = y & ~7;
|
||||||
int indexX = x & ~7;
|
int indexX = x & ~7;
|
||||||
|
|
||||||
int i = mortonInterleave(x, y);
|
int interleave = mortonInterleave(x, y);
|
||||||
int offset = (i + (indexX * 8)) * 2;
|
int offset = (interleave + (indexX * 8)) * 2;
|
||||||
|
|
||||||
offset = offset + indexY * ICON_SIZE * 2;
|
offset = offset + indexY * ICON_SIZE * 2;
|
||||||
|
|
||||||
smdh.position(offset + IMAGE_OFFSET);
|
smdh.position(offset + IMAGE_OFFSET);
|
||||||
|
|
||||||
int byte1 = smdh.get() & 0xFF;
|
int lowByte = smdh.get() & 0xFF;
|
||||||
int byte2 = smdh.get() & 0xFF;
|
int highByte = smdh.get() & 0xFF;
|
||||||
|
int texel = (highByte << 8) | lowByte;
|
||||||
|
|
||||||
int texel = byte1 | (byte2 << 8);
|
// Convert texel from RGB565 to RGB888
|
||||||
|
int r = (texel >> 11) & 0x1F;
|
||||||
|
int g = (texel >> 5) & 0x3F;
|
||||||
|
int b = texel & 0x1F;
|
||||||
|
|
||||||
int r = (texel >> 11) & 0x1f;
|
|
||||||
int g = (texel >> 5) & 0x3f;
|
|
||||||
int b = texel & 0x1f;
|
|
||||||
|
|
||||||
b = (b << 3) | (b >> 2);
|
|
||||||
g = (g << 2) | (g >> 4);
|
|
||||||
r = (r << 3) | (r >> 2);
|
r = (r << 3) | (r >> 2);
|
||||||
|
g = (g << 2) | (g >> 4);
|
||||||
|
b = (b << 3) | (b >> 2);
|
||||||
|
|
||||||
icon[x + ICON_SIZE * y] = Color.rgb(r, g, b);
|
icon[x + ICON_SIZE * y] = Color.rgb(r, g, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,7 +162,7 @@ public class SMDH {
|
||||||
return publisher[metaLanguage];
|
return publisher[metaLanguage];
|
||||||
}
|
}
|
||||||
|
|
||||||
// SMDH stores string in UTF-16LE format
|
// Strings in SMDH files are stored as UTF-16LE
|
||||||
private static String convertString(byte[] buffer) {
|
private static String convertString(byte[] buffer) {
|
||||||
try {
|
try {
|
||||||
return new String(buffer, 0, buffer.length, StandardCharsets.UTF_16LE)
|
return new String(buffer, 0, buffer.length, StandardCharsets.UTF_16LE)
|
||||||
|
@ -174,6 +176,7 @@ public class SMDH {
|
||||||
private static int mortonInterleave(int u, int v) {
|
private static int mortonInterleave(int u, int v) {
|
||||||
int[] xlut = {0, 1, 4, 5, 16, 17, 20, 21};
|
int[] xlut = {0, 1, 4, 5, 16, 17, 20, 21};
|
||||||
int[] ylut = {0, 2, 8, 10, 32, 34, 40, 42};
|
int[] ylut = {0, 2, 8, 10, 32, 34, 40, 42};
|
||||||
|
|
||||||
return xlut[u % 8] + ylut[v % 8];
|
return xlut[u % 8] + ylut[v % 8];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,13 @@ public class InputHandler {
|
||||||
|
|
||||||
private static final HashMap<String, Float> motionDownEvents = new HashMap<>();
|
private static final HashMap<String, Float> motionDownEvents = new HashMap<>();
|
||||||
|
|
||||||
private static boolean containsSource(int[] sources, int sourceMasked) {
|
private static boolean containsSource(int[] sources, int sourceMask) {
|
||||||
for (int source : sources) {
|
for (int source : sources) {
|
||||||
if ((sourceMasked & source) == source)
|
if ((source & sourceMask) == source) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,8 +59,9 @@ public class InputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean processMotionEvent(MotionEvent event) {
|
public static boolean processMotionEvent(MotionEvent event) {
|
||||||
if (!isSourceValid(event.getSource()))
|
if (!isSourceValid(event.getSource())) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (isGamepadSource(event.getSource())) {
|
if (isGamepadSource(event.getSource())) {
|
||||||
for (InputDevice.MotionRange range : event.getDevice().getMotionRanges()) {
|
for (InputDevice.MotionRange range : event.getDevice().getMotionRanges()) {
|
||||||
|
@ -87,8 +90,9 @@ public class InputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean processKeyEvent(KeyEvent event) {
|
public static boolean processKeyEvent(KeyEvent event) {
|
||||||
if (!isSourceValid(event.getSource()))
|
if (!isSourceValid(event.getSource())) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (isGamepadSource(event.getSource())) {
|
if (isGamepadSource(event.getSource())) {
|
||||||
// Dpad return motion event + key event, this remove the key event
|
// Dpad return motion event + key event, this remove the key event
|
||||||
|
@ -104,6 +108,7 @@ public class InputHandler {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEvent(new InputEvent(KeyEvent.keyCodeToString(event.getKeyCode()), event.getAction() == KeyEvent.ACTION_UP ? 0.0f : 1.0f));
|
handleEvent(new InputEvent(KeyEvent.keyCodeToString(event.getKeyCode()), event.getAction() == KeyEvent.ACTION_UP ? 0.0f : 1.0f));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,11 @@ public class Task extends Thread {
|
||||||
super(runnable);
|
super(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void runSync(){
|
public void runSync(){
|
||||||
start();
|
start();
|
||||||
waitFinish();
|
waitFinish();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waitFinish(){
|
public void waitFinish(){
|
||||||
try {
|
try {
|
||||||
join();
|
join();
|
||||||
|
|
|
@ -44,6 +44,7 @@ public class FileUtils {
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
file.mkdirs();
|
file.mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
return file.getAbsolutePath();
|
return file.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +53,7 @@ public class FileUtils {
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
file.mkdirs();
|
file.mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
return file.getAbsolutePath();
|
return file.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,8 +63,10 @@ public class FileUtils {
|
||||||
|
|
||||||
public static boolean createDir(String path, String name) {
|
public static boolean createDir(String path, String name) {
|
||||||
DocumentFile folder = parseFile(path);
|
DocumentFile folder = parseFile(path);
|
||||||
if (folder.findFile(name) != null)
|
if (folder.findFile(name) != null) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return folder.createDirectory(name) != null;
|
return folder.createDirectory(name) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,12 +90,14 @@ public class FileUtils {
|
||||||
Log.e(Constants.LOG_TAG, "Error on write text file: ", e);
|
Log.e(Constants.LOG_TAG, "Error on write text file: ", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String readTextFile(String path) {
|
public static String readTextFile(String path) {
|
||||||
if (!exists(path))
|
if (!exists(path)) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
InputStream stream = getInputStream(path);
|
InputStream stream = getInputStream(path);
|
||||||
|
@ -99,15 +105,15 @@ public class FileUtils {
|
||||||
|
|
||||||
int len;
|
int len;
|
||||||
byte[] buffer = new byte[1024 * 8];
|
byte[] buffer = new byte[1024 * 8];
|
||||||
while ((len = stream.read(buffer)) != -1)
|
while ((len = stream.read(buffer)) != -1) {
|
||||||
output.write(buffer, 0, len);
|
output.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
|
||||||
stream.close();
|
stream.close();
|
||||||
output.flush();
|
output.flush();
|
||||||
output.close();
|
output.close();
|
||||||
|
|
||||||
byte[] data = output.toByteArray();
|
byte[] data = output.toByteArray();
|
||||||
|
|
||||||
return new String(data, 0, data.length);
|
return new String(data, 0, data.length);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -124,8 +130,9 @@ public class FileUtils {
|
||||||
|
|
||||||
public static void makeUriPermanent(String uri, String mode) {
|
public static void makeUriPermanent(String uri, String mode) {
|
||||||
int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||||
if (mode.toLowerCase().contains("w"))
|
if (mode.toLowerCase().contains("w")) {
|
||||||
flags &= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
flags &= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
||||||
|
}
|
||||||
|
|
||||||
getContext().getContentResolver().takePersistableUriPermission(Uri.parse(uri), flags);
|
getContext().getContentResolver().takePersistableUriPermission(Uri.parse(uri), flags);
|
||||||
}
|
}
|
||||||
|
@ -141,6 +148,7 @@ public class FileUtils {
|
||||||
ParcelFileDescriptor parcelDescriptor = getContext().getContentResolver().openFileDescriptor(Uri.parse(uri), "r");
|
ParcelFileDescriptor parcelDescriptor = getContext().getContentResolver().openFileDescriptor(Uri.parse(uri), "r");
|
||||||
int fd = parcelDescriptor.getFd();
|
int fd = parcelDescriptor.getFd();
|
||||||
File file = new File("/proc/self/fd/" + fd).getAbsoluteFile();
|
File file = new File("/proc/self/fd/" + fd).getAbsoluteFile();
|
||||||
|
|
||||||
for (int i = 0; i < CANONICAL_SEARCH_DEEP; i++) {
|
for (int i = 0; i < CANONICAL_SEARCH_DEEP; i++) {
|
||||||
try {
|
try {
|
||||||
String canonical = file.getCanonicalPath();
|
String canonical = file.getCanonicalPath();
|
||||||
|
@ -156,11 +164,14 @@ public class FileUtils {
|
||||||
parcelDescriptor.close();
|
parcelDescriptor.close();
|
||||||
return file.getAbsolutePath();
|
return file.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
String path = Os.readlink(file.getAbsolutePath());
|
String path = Os.readlink(file.getAbsolutePath());
|
||||||
parcelDescriptor.close();
|
parcelDescriptor.close();
|
||||||
|
|
||||||
if (new File(path).exists())
|
if (new File(path).exists()) {
|
||||||
return path;
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -8,9 +8,8 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class SearchAgent {
|
public class SearchAgent {
|
||||||
|
// Store all results in a hashmap
|
||||||
// Store all possibles results in map
|
// Matches IDs -> Result string
|
||||||
// id->words
|
|
||||||
private final HashMap<String, String> searchBuffer = new HashMap<>();
|
private final HashMap<String, String> searchBuffer = new HashMap<>();
|
||||||
|
|
||||||
// Add search item to list
|
// Add search item to list
|
||||||
|
@ -19,15 +18,12 @@ public class SearchAgent {
|
||||||
for (String word : words) {
|
for (String word : words) {
|
||||||
string.append(normalize(word)).append(" ");
|
string.append(normalize(word)).append(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
searchBuffer.put(id, string.toString());
|
searchBuffer.put(id, string.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Convert string to lowercase alphanumeric string, converting all characters to ASCII and turning double spaces into single ones
|
||||||
* Convert string to simple string with only a-z 0-9 for do this first it get the input string
|
// For example, é will be converted to e
|
||||||
* and apply lower case, after convert all chars to ASCII
|
|
||||||
* Ex: ç => c, á => a
|
|
||||||
* after replace all double space for single space
|
|
||||||
*/
|
|
||||||
private String normalize(String string) {
|
private String normalize(String string) {
|
||||||
string = Normalizer.normalize(string, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", "");
|
string = Normalizer.normalize(string, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", "");
|
||||||
|
|
||||||
|
@ -40,26 +36,30 @@ public class SearchAgent {
|
||||||
public List<String> search(String query) {
|
public List<String> search(String query) {
|
||||||
String[] words = normalize(query).split("\\s");
|
String[] words = normalize(query).split("\\s");
|
||||||
|
|
||||||
if (words.length == 0)
|
if (words.length == 0) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
// Map for add all search result: id -> probability
|
// Map for add all search result: id -> probability
|
||||||
HashMap<String, Integer> results = new HashMap<>();
|
HashMap<String, Integer> results = new HashMap<>();
|
||||||
for (String key : searchBuffer.keySet()) {
|
for (String key : searchBuffer.keySet()) {
|
||||||
int probability = 0;
|
int probability = 0;
|
||||||
String value = searchBuffer.get(key);
|
String value = searchBuffer.get(key);
|
||||||
|
|
||||||
for (String word : words) {
|
for (String word : words) {
|
||||||
if (value.contains(word))
|
if (value.contains(word))
|
||||||
probability++;
|
probability++;
|
||||||
}
|
}
|
||||||
if (probability > 0)
|
|
||||||
|
if (probability > 0) {
|
||||||
results.put(key, probability);
|
results.put(key, probability);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Filter by probability average
|
// Filter by probability average, ie by how closely they match to our query
|
||||||
// Ex: A = 10% B = 30% C = 70% (calc is (10+30+70)/3=36)
|
// Ex: A = 10% B = 30% C = 70% (formula is (10+30+70)/3=36)
|
||||||
// After remove all result with probability < 36
|
// Afterwards remove all results with probability < 36
|
||||||
int average = 0;
|
int average = 0;
|
||||||
for (String key : results.keySet()) {
|
for (String key : results.keySet()) {
|
||||||
average += results.get(key);
|
average += results.get(key);
|
||||||
|
@ -76,6 +76,7 @@ public class SearchAgent {
|
||||||
i = 0;
|
i = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.widget.AppCompatImageView;
|
import androidx.appcompat.widget.AppCompatImageView;
|
||||||
|
|
||||||
public class GameIconView extends AppCompatImageView {
|
public class GameIconView extends AppCompatImageView {
|
||||||
|
|
||||||
public GameIconView(@NonNull Context context) {
|
public GameIconView(@NonNull Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ public class SingleSelectionPreferences extends PreferenceCategory implements Pr
|
||||||
@Override
|
@Override
|
||||||
public void onAttached() {
|
public void onAttached() {
|
||||||
super.onAttached();
|
super.onAttached();
|
||||||
|
|
||||||
for (int i = 0; i < getPreferenceCount();i++) {
|
for (int i = 0; i < getPreferenceCount();i++) {
|
||||||
getPreference(i).setOnPreferenceClickListener(this);
|
getPreference(i).setOnPreferenceClickListener(this);
|
||||||
}
|
}
|
||||||
|
@ -68,6 +69,7 @@ public class SingleSelectionPreferences extends PreferenceCategory implements Pr
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceClick(@NonNull Preference preference) {
|
public boolean onPreferenceClick(@NonNull Preference preference) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
for (int i = 0; i < getPreferenceCount(); i++){
|
for (int i = 0; i < getPreferenceCount(); i++){
|
||||||
Preference item = getPreference(i);
|
Preference item = getPreference(i);
|
||||||
if (item == preference){
|
if (item == preference){
|
||||||
|
|
Loading…
Add table
Reference in a new issue