Skip to content

Commit

Permalink
Merge pull request #351 from GabrielBRDeveloper/ui
Browse files Browse the repository at this point in the history
Pandroid: UI
  • Loading branch information
wheremyfoodat authored Dec 26, 2023
2 parents bf05002 + adb8ccc commit dc32e9a
Show file tree
Hide file tree
Showing 74 changed files with 2,679 additions and 202 deletions.
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -388,8 +388,10 @@ if(ENABLE_VULKAN)
set(ALL_SOURCES ${ALL_SOURCES} ${RENDERER_VK_SOURCE_FILES})
endif()

if(ANDROID)
if(ANDROID AND NOT BUILD_HYDRA_CORE)
set(HEADER_FILES ${HEADER_FILES} include/jni_driver.hpp)
set(ALL_SOURCES ${ALL_SOURCES} src/jni_driver.cpp)
target_compile_definitions(Alber PRIVATE PANDA3DS_FRONTEND_PANDROID=1)
endif()

if(BUILD_HYDRA_CORE)
Expand Down
8 changes: 8 additions & 0 deletions include/jni_driver.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <vector>

#include "helpers.hpp"

class Pandroid {
public:
static void onSmdhLoaded(const std::vector<u8>& smdh);
};
9 changes: 9 additions & 0 deletions src/core/loader/ncch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
#include "loader/ncch.hpp"
#include "memory.hpp"

#ifdef PANDA3DS_FRONTEND_PANDROID
#include "jni_driver.hpp"
#endif

#include <iostream>

bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSInfo &info) {
Expand Down Expand Up @@ -255,6 +259,11 @@ bool NCCH::parseSMDH(const std::vector<u8>& smdh) {
return false;
}

// In the Android version, notify the application that we're loading an SMDH file, to extract data for the title list
#ifdef PANDA3DS_FRONTEND_PANDROID
Pandroid::onSmdhLoaded(smdh);
#endif

// Bitmask showing which regions are allowed.
// https://www.3dbrew.org/wiki/SMDH#Region_Lockout
const u32 regionMasks = *(u32*)&smdh[0x2018];
Expand Down
35 changes: 34 additions & 1 deletion src/jni_driver.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include "jni_driver.hpp"

#include <EGL/egl.h>
#include <android/log.h>
#include <jni.h>
Expand All @@ -12,6 +14,8 @@ std::unique_ptr<Emulator> emulator = nullptr;
HIDService* hidService = nullptr;
RendererGL* renderer = nullptr;
bool romLoaded = false;
JavaVM* jvm = nullptr;
const char* alberClass = "com/panda3ds/pandroid/AlberDriver";

#define AlberFunction(type, name) JNIEXPORT type JNICALL Java_com_panda3ds_pandroid_AlberDriver_##name

Expand All @@ -20,7 +24,36 @@ void throwException(JNIEnv* env, const char* message) {
env->ThrowNew(exceptionClass, message);
}

JNIEnv* jniEnv() {
JNIEnv* env;
auto status = jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
if (status == JNI_EDETACHED) {
jvm->AttachCurrentThread(&env, nullptr);
} else if (status != JNI_OK) {
throw std::runtime_error("Failed to obtain JNIEnv from JVM!!");
}

return env;
}

void Pandroid::onSmdhLoaded(const std::vector<u8>& smdh) {
JNIEnv* env = jniEnv();
int size = smdh.size();

jbyteArray result = env->NewByteArray(size);
env->SetByteArrayRegion(result, 0, size, (jbyte*)smdh.data());

auto classLoader = env->FindClass(alberClass);
auto method = env->GetStaticMethodID(classLoader, "OnSmdhLoaded", "([B)V");

env->CallStaticVoidMethod(classLoader, method, result);
env->DeleteLocalRef(result);
}

extern "C" {

AlberFunction(void, Setup)(JNIEnv* env, jobject obj) { env->GetJavaVM(&jvm); }

AlberFunction(void, Initialize)(JNIEnv* env, jobject obj) {
emulator = std::make_unique<Emulator>();

Expand Down Expand Up @@ -73,4 +106,4 @@ AlberFunction(void, SetCirclepadAxis)(JNIEnv* env, jobject obj, jint x, jint y)
}
}

#undef AlberFunction
#undef AlberFunction
2 changes: 2 additions & 0 deletions src/pandroid/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,7 @@ android {
dependencies {
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.8.0")
implementation("androidx.preference:preference:1.2.1")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("com.google.code.gson:gson:2.10.1")
}
9 changes: 8 additions & 1 deletion src/pandroid/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
tools:targetApi="31">
<activity
android:name=".app.MainActivity"
android:exported="true">
android:exported="true"
android:configChanges="orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand All @@ -34,5 +35,11 @@
android:name=".app.GameActivity"
android:configChanges="screenSize|screenLayout|orientation|density|uiMode">
</activity>
<activity android:name=".app.PreferenceActivity"
android:launchMode="standard"
android:configChanges="screenSize|screenLayout|orientation|density"/>

<activity android:name=".app.preferences.InputMapActivity"
android:configChanges="density|orientation|screenSize"/>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
package com.panda3ds.pandroid;

import android.util.Log;

import com.panda3ds.pandroid.data.SMDH;
import com.panda3ds.pandroid.data.game.GameMetadata;
import com.panda3ds.pandroid.utils.Constants;
import com.panda3ds.pandroid.utils.GameUtils;

public class AlberDriver {
AlberDriver() { super(); }

public static native void Setup();
public static native void Initialize();
public static native void RunFrame(int fbo);
public static native boolean HasRomLoaded();
Expand All @@ -15,5 +23,14 @@ public class AlberDriver {
public static native void TouchScreenUp();
public static native void TouchScreenDown(int x, int y);

public static void OnSmdhLoaded(byte[] buffer) {
SMDH smdh = new SMDH(buffer);
Log.i(Constants.LOG_TAG, "Loaded rom SDMH");
Log.i(Constants.LOG_TAG, String.format("Are you playing '%s' published by '%s'", smdh.getTitle(), smdh.getPublisher()));
GameMetadata game = GameUtils.getCurrentGame();
GameUtils.removeGame(game);
GameUtils.addGame(GameMetadata.applySMDH(game, smdh));
}

static { System.loadLibrary("Alber"); }
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
package com.panda3ds.pandroid.app;

import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.panda3ds.pandroid.R;
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);

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
applyTheme();
super.onCreate(savedInstanceState);
}

@Override
protected void onResume() {
super.onResume();

if (GlobalConfig.get(GlobalConfig.KEY_APP_THEME) != currentTheme) {
recreate();
}
}

private void applyTheme() {
switch (GlobalConfig.get(GlobalConfig.KEY_APP_THEME)) {
case GlobalConfig.THEME_ANDROID: setTheme(R.style.Theme_Pandroid); break;
case GlobalConfig.THEME_LIGHT: 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,28 @@

import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.FrameLayout;
import android.widget.Toast;

import androidx.annotation.Nullable;

import com.panda3ds.pandroid.AlberDriver;
import com.panda3ds.pandroid.R;
import com.panda3ds.pandroid.app.game.AlberInputListener;
import com.panda3ds.pandroid.data.config.GlobalConfig;
import com.panda3ds.pandroid.input.InputHandler;
import com.panda3ds.pandroid.input.InputMap;
import com.panda3ds.pandroid.utils.Constants;
import com.panda3ds.pandroid.view.PandaGlSurfaceView;
import com.panda3ds.pandroid.view.PandaLayoutController;

public class GameActivity extends BaseActivity {
private final AlberInputListener inputListener = new AlberInputListener(this);

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand All @@ -38,13 +45,55 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
PandaLayoutController controllerLayout = findViewById(R.id.controller_layout);
controllerLayout.initialize();

((CheckBox) findViewById(R.id.hide_screen_controller)).setOnCheckedChangeListener((buttonView, isChecked) -> findViewById(R.id.overlay_controller).setVisibility(isChecked ? View.VISIBLE : View.INVISIBLE));
((CheckBox) findViewById(R.id.hide_screen_controller)).setOnCheckedChangeListener((buttonView, checked) -> {
findViewById(R.id.overlay_controller).setVisibility(checked ? View.VISIBLE : View.GONE);
findViewById(R.id.overlay_controller).invalidate();
findViewById(R.id.overlay_controller).requestLayout();
GlobalConfig.set(GlobalConfig.KEY_SCREEN_GAMEPAD_VISIBLE, checked);
});
((CheckBox) findViewById(R.id.hide_screen_controller)).setChecked(GlobalConfig.get(GlobalConfig.KEY_SCREEN_GAMEPAD_VISIBLE));
}

@Override
protected void onResume() {
super.onResume();
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
InputHandler.reset();
InputHandler.setMotionDeadZone(InputMap.getDeadZone());
InputHandler.setEventListener(inputListener);
}

@Override
protected void onPause() {
super.onPause();
InputHandler.reset();
}

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (InputHandler.processKeyEvent(event)) {
return true;
}

return super.dispatchKeyEvent(event);
}

@Override
public boolean dispatchGenericMotionEvent(MotionEvent ev) {
if (InputHandler.processMotionEvent(ev)) {
return true;
}

return super.dispatchGenericMotionEvent(ev);
}

@Override
protected void onDestroy() {
if (AlberDriver.HasRomLoaded()) {
AlberDriver.Finalize();
}

super.onDestroy();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,26 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import com.google.android.material.navigation.NavigationBarView;
import com.panda3ds.pandroid.R;
import com.panda3ds.pandroid.utils.Constants;
import com.panda3ds.pandroid.utils.PathUtils;
import com.panda3ds.pandroid.app.main.GamesFragment;
import com.panda3ds.pandroid.app.main.SearchFragment;
import com.panda3ds.pandroid.app.main.SettingsFragment;

public class MainActivity extends BaseActivity {

public class MainActivity extends BaseActivity implements NavigationBarView.OnItemSelectedListener {
private static final int PICK_ROM = 2;
private static final int PERMISSION_REQUEST_CODE = 3;

private final GamesFragment gamesFragment = new GamesFragment();
private final SearchFragment searchFragment = new SearchFragment();
private final SettingsFragment settingsFragment = new SettingsFragment();

private void openFile() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
Expand All @@ -40,18 +49,28 @@ protected void onCreate(Bundle savedInstanceState) {
}

setContentView(R.layout.activity_main);
findViewById(R.id.load_rom).setOnClickListener(v -> { openFile(); });

NavigationBarView bar = findViewById(R.id.navigation);
bar.setOnItemSelectedListener(this);
bar.postDelayed(() -> bar.setSelectedItemId(bar.getSelectedItemId()), 5);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PICK_ROM) {
if (resultCode == RESULT_OK) {
String path = PathUtils.getPath(getApplicationContext(), data.getData());
Toast.makeText(getApplicationContext(), "pandroid opening " + path, Toast.LENGTH_LONG).show();
startActivity(new Intent(this, GameActivity.class).putExtra(Constants.ACTIVITY_PARAMETER_PATH, path));
}
super.onActivityResult(requestCode, resultCode, data);
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
int id = item.getItemId();
FragmentManager manager = getSupportFragmentManager();
Fragment fragment;
if (id == R.id.games) {
fragment = gamesFragment;
} else if (id == R.id.search) {
fragment = searchFragment;
} else if (id == R.id.settings) {
fragment = settingsFragment;
} else {
return false;
}

manager.beginTransaction().replace(R.id.fragment_container, fragment).commitNow();
return true;
}
}
Loading

0 comments on commit dc32e9a

Please sign in to comment.