From b214d6d1943825e9e6b0c56619572d0d6a5bb7e2 Mon Sep 17 00:00:00 2001
From: Gabriel Machado <97042217+GabrielBRDeveloper@users.noreply.github.com>
Date: Tue, 30 Jan 2024 01:32:06 -0400
Subject: [PATCH] Initial commit
---
src/pandroid/app/src/main/AndroidManifest.xml | 2 +
.../panda3ds/pandroid/app/GameActivity.java | 6 +
.../pandroid/app/PandroidApplication.java | 6 +
.../app/base/BasePreferenceFragment.java | 9 ++
.../pandroid/app/main/SettingsFragment.java | 2 +
.../app/preferences/DeveloperPreferences.java | 47 ++++++++
.../pandroid/app/services/LoggerService.java | 104 ++++++++++++++++++
.../pandroid/data/config/GlobalConfig.java | 3 +-
.../pandroid/lang/PipeStreamTask.java | 40 +++++++
.../java/com/panda3ds/pandroid/lang/Task.java | 2 +
.../panda3ds/pandroid/utils/FileUtils.java | 17 +++
.../pandroid/utils/PerformanceMonitor.java | 64 +++++++++++
.../pandroid/view/PandaGlRenderer.java | 4 +
.../pandroid/view/utils/PerformanceView.java | 61 ++++++++++
.../src/main/res/values-pt-rBR/strings.xml | 6 +
.../app/src/main/res/values/strings.xml | 6 +
.../main/res/xml/developer_preferences.xml | 17 +++
.../src/main/res/xml/start_preferences.xml | 7 ++
18 files changed, 402 insertions(+), 1 deletion(-)
create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/preferences/DeveloperPreferences.java
create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/services/LoggerService.java
create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/PipeStreamTask.java
create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/PerformanceMonitor.java
create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/utils/PerformanceView.java
create mode 100644 src/pandroid/app/src/main/res/xml/developer_preferences.xml
diff --git a/src/pandroid/app/src/main/AndroidManifest.xml b/src/pandroid/app/src/main/AndroidManifest.xml
index 9f767654..b234a571 100644
--- a/src/pandroid/app/src/main/AndroidManifest.xml
+++ b/src/pandroid/app/src/main/AndroidManifest.xml
@@ -46,5 +46,7 @@
+
+
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java
index aced6faa..f7050e99 100644
--- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java
@@ -21,6 +21,7 @@ import com.panda3ds.pandroid.input.InputMap;
import com.panda3ds.pandroid.utils.Constants;
import com.panda3ds.pandroid.view.PandaGlSurfaceView;
import com.panda3ds.pandroid.view.PandaLayoutController;
+import com.panda3ds.pandroid.view.utils.PerformanceView;
public class GameActivity extends BaseActivity {
private final DrawerFragment drawerFragment = new DrawerFragment();
@@ -56,6 +57,11 @@ public class GameActivity extends BaseActivity {
((CheckBox) findViewById(R.id.hide_screen_controller)).setChecked(GlobalConfig.get(GlobalConfig.KEY_SCREEN_GAMEPAD_VISIBLE));
getSupportFragmentManager().beginTransaction().replace(R.id.drawer_fragment, drawerFragment).commitNow();
+
+ if (GlobalConfig.get(GlobalConfig.KEY_SHOW_PERFORMANCE_OVERLAY)) {
+ PerformanceView view = new PerformanceView(this);
+ ((FrameLayout) findViewById(R.id.panda_gl_frame)).addView(view, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ }
}
@Override
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/PandroidApplication.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/PandroidApplication.java
index 02fbbbcc..b0cdc935 100644
--- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/PandroidApplication.java
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/PandroidApplication.java
@@ -2,11 +2,13 @@ package com.panda3ds.pandroid.app;
import android.app.Application;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import com.panda3ds.pandroid.AlberDriver;
import com.panda3ds.pandroid.R;
+import com.panda3ds.pandroid.app.services.LoggerService;
import com.panda3ds.pandroid.data.config.GlobalConfig;
import com.panda3ds.pandroid.input.InputMap;
import com.panda3ds.pandroid.utils.GameUtils;
@@ -24,6 +26,10 @@ public class PandroidApplication extends Application {
GameUtils.initialize();
InputMap.initialize();
AlberDriver.Setup();
+
+ if (GlobalConfig.get(GlobalConfig.KEY_LOGGER_SERVICE)) {
+ startService(new Intent(this, LoggerService.class));
+ }
}
public static int getThemeId() {
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/base/BasePreferenceFragment.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/base/BasePreferenceFragment.java
index 3cd28f4b..9482df1d 100644
--- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/base/BasePreferenceFragment.java
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/base/BasePreferenceFragment.java
@@ -1,8 +1,13 @@
package com.panda3ds.pandroid.app.base;
import android.annotation.SuppressLint;
+
+import androidx.annotation.StringRes;
+import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.SwitchPreference;
+
import com.panda3ds.pandroid.lang.Function;
@@ -15,4 +20,8 @@ public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
return false;
});
}
+
+ protected void setActivityTitle(@StringRes int titleId) {
+ ((AppCompatActivity) requireActivity()).getSupportActionBar().setTitle(titleId);
+ }
}
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/main/SettingsFragment.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/main/SettingsFragment.java
index b3bebf8f..bfe33a2b 100644
--- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/main/SettingsFragment.java
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/main/SettingsFragment.java
@@ -8,6 +8,7 @@ import com.panda3ds.pandroid.R;
import com.panda3ds.pandroid.app.PreferenceActivity;
import com.panda3ds.pandroid.app.base.BasePreferenceFragment;
import com.panda3ds.pandroid.app.preferences.AppearancePreferences;
+import com.panda3ds.pandroid.app.preferences.DeveloperPreferences;
import com.panda3ds.pandroid.app.preferences.InputPreferences;
public class SettingsFragment extends BasePreferenceFragment {
@@ -16,5 +17,6 @@ public class SettingsFragment extends BasePreferenceFragment {
setPreferencesFromResource(R.xml.start_preferences, rootKey);
setItemClick("input", (item) -> PreferenceActivity.launch(requireContext(), InputPreferences.class));
setItemClick("appearance", (item)-> PreferenceActivity.launch(requireContext(), AppearancePreferences.class));
+ setItemClick("developer", (item)-> PreferenceActivity.launch(requireContext(), DeveloperPreferences.class));
}
}
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/preferences/DeveloperPreferences.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/preferences/DeveloperPreferences.java
new file mode 100644
index 00000000..0d15ba32
--- /dev/null
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/preferences/DeveloperPreferences.java
@@ -0,0 +1,47 @@
+package com.panda3ds.pandroid.app.preferences;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.preference.SwitchPreference;
+
+import com.panda3ds.pandroid.R;
+import com.panda3ds.pandroid.app.PandroidApplication;
+import com.panda3ds.pandroid.app.base.BasePreferenceFragment;
+import com.panda3ds.pandroid.app.services.LoggerService;
+import com.panda3ds.pandroid.data.config.GlobalConfig;
+
+public class DeveloperPreferences extends BasePreferenceFragment {
+ @Override
+ public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
+ setPreferencesFromResource(R.xml.developer_preferences, rootKey);
+ setActivityTitle(R.string.developer_options);
+
+ setItemClick("performanceMonitor", pref -> GlobalConfig.set(GlobalConfig.KEY_SHOW_PERFORMANCE_OVERLAY, ((SwitchPreference) pref).isChecked()));
+ setItemClick("loggerService", pref -> {
+ boolean checked = ((SwitchPreference) pref).isChecked();
+ Context ctx = PandroidApplication.getAppContext();
+ if (checked) {
+ ctx.startService(new Intent(ctx, LoggerService.class));
+ } else {
+ ctx.stopService(new Intent(ctx, LoggerService.class));
+ }
+ GlobalConfig.set(GlobalConfig.KEY_LOGGER_SERVICE, checked);
+ });
+
+ refresh();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ refresh();
+ }
+
+ private void refresh() {
+ ((SwitchPreference) findPreference("performanceMonitor")).setChecked(GlobalConfig.get(GlobalConfig.KEY_SHOW_PERFORMANCE_OVERLAY));
+ ((SwitchPreference) findPreference("loggerService")).setChecked(GlobalConfig.get(GlobalConfig.KEY_LOGGER_SERVICE));
+ }
+}
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/services/LoggerService.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/services/LoggerService.java
new file mode 100644
index 00000000..bf73ec3b
--- /dev/null
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/services/LoggerService.java
@@ -0,0 +1,104 @@
+package com.panda3ds.pandroid.app.services;
+
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.os.Build;
+import android.os.IBinder;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.panda3ds.pandroid.lang.PipeStreamTask;
+import com.panda3ds.pandroid.lang.Task;
+import com.panda3ds.pandroid.utils.Constants;
+import com.panda3ds.pandroid.utils.FileUtils;
+
+import java.io.OutputStream;
+import java.util.Arrays;
+
+public class LoggerService extends Service {
+ private static final long MAX_LOG_SIZE = 1024 * 1024 * 4; // 4MB
+
+ private PipeStreamTask errorTask;
+ private PipeStreamTask outputTask;
+ private Process logcat;
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ try {
+ Runtime.getRuntime().exec(new String[]{"logcat", "-c"}).waitFor();
+ logcat = Runtime.getRuntime().exec(new String[]{"logcat"});
+ String logPath = getExternalMediaDirs()[0].getAbsolutePath();
+ FileUtils.createDir(logPath, "logs");
+ logPath = logPath + "/logs";
+
+ if (FileUtils.exists(logPath + "/last.txt")) {
+ FileUtils.delete(logPath + "/last.txt");
+ }
+
+ if (FileUtils.exists(logPath + "/current.txt")) {
+ FileUtils.rename(logPath + "/current.txt", "last.txt");
+ }
+
+ OutputStream stream = FileUtils.getOutputStream(logPath + "/current.txt");
+ errorTask = new PipeStreamTask(logcat.getErrorStream(), stream, MAX_LOG_SIZE);
+ outputTask = new PipeStreamTask(logcat.getInputStream(), stream, MAX_LOG_SIZE);
+
+ errorTask.start();
+ outputTask.start();
+
+ Log.i(Constants.LOG_TAG, "STARTED LOGGER SERVICE");
+ printDeviceAbout();
+ } catch (Exception e) {
+ stopSelf();
+ Log.e(Constants.LOG_TAG, "Failed to start LoggerService!");
+ }
+ }
+
+ private void printDeviceAbout() {
+ Log.i(Constants.LOG_TAG, "----------------------");
+ Log.i(Constants.LOG_TAG, "Android SDK: " + Build.VERSION.SDK_INT);
+ Log.i(Constants.LOG_TAG, "Device: " + Build.DEVICE);
+ Log.i(Constants.LOG_TAG, "Model: " + Build.MANUFACTURER + " " + Build.MODEL);
+ Log.i(Constants.LOG_TAG, "ABIs: " + Arrays.toString(Build.SUPPORTED_ABIS));
+ try {
+ PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0);
+ Log.i(Constants.LOG_TAG, "");
+ Log.i(Constants.LOG_TAG, "Package: " + info.packageName);
+ Log.i(Constants.LOG_TAG, "Install location: " + info.installLocation);
+ Log.i(Constants.LOG_TAG, "App version: " + info.versionName + " (" + info.versionCode + ")");
+ } catch (Exception e) {
+ Log.e(Constants.LOG_TAG, "Error on obtain package info: " + e);
+ }
+ Log.i(Constants.LOG_TAG, "----------------------");
+ }
+
+ @Override
+ public void onTaskRemoved(Intent rootIntent) {
+ stopSelf();
+ //This is a time for app save save log file
+ try {
+ Thread.sleep(1000);
+ } catch (Exception e) {}
+ super.onTaskRemoved(rootIntent);
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.i(Constants.LOG_TAG, "FINISHED LOGGER SERVICE");
+ errorTask.close();
+ outputTask.close();
+ try {
+ logcat.destroy();
+ } catch (Throwable t) {}
+ super.onDestroy();
+ }
+}
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/data/config/GlobalConfig.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/data/config/GlobalConfig.java
index d6dbe3b8..38efdb20 100644
--- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/data/config/GlobalConfig.java
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/data/config/GlobalConfig.java
@@ -5,7 +5,6 @@ import com.panda3ds.pandroid.data.GsonConfigParser;
import com.panda3ds.pandroid.utils.Constants;
import java.io.Serializable;
-import java.util.HashMap;
import java.util.Map;
public class GlobalConfig {
@@ -19,6 +18,8 @@ public class GlobalConfig {
public static DataModel data;
+ public static final Key KEY_SHOW_PERFORMANCE_OVERLAY = new Key<>("dev.performanceOverlay", false);
+ public static final Key KEY_LOGGER_SERVICE = new Key<>("dev.loggerService", false);
public static final Key KEY_APP_THEME = new Key<>("app.theme", THEME_ANDROID);
public static final Key KEY_SCREEN_GAMEPAD_VISIBLE = new Key<>("app.screen_gamepad.visible", true);
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/PipeStreamTask.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/PipeStreamTask.java
new file mode 100644
index 00000000..e4bbda98
--- /dev/null
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/PipeStreamTask.java
@@ -0,0 +1,40 @@
+package com.panda3ds.pandroid.lang;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class PipeStreamTask extends Task {
+ private final InputStream input;
+ private final OutputStream output;
+ private final long limit;
+ private long size;
+
+ public PipeStreamTask(InputStream input, OutputStream output, long limit) {
+ this.input = input;
+ this.output = output;
+ this.limit = limit;
+ }
+
+ @Override
+ public void run() {
+ super.run();
+ int data;
+ try {
+ while ((data = input.read()) != -1) {
+ output.write(data);
+ if (++size > limit) {
+ break;
+ }
+ }
+ } catch (Exception e) {}
+ close();
+ }
+
+ public void close() {
+ try {
+ output.flush();
+ output.close();
+ input.close();
+ } catch (Exception e) {}
+ }
+}
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/Task.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/Task.java
index 7745883d..8de344b4 100644
--- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/Task.java
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/Task.java
@@ -5,6 +5,8 @@ public class Task extends Thread {
super(runnable);
}
+ protected Task() {}
+
public void runSync() {
start();
waitFinish();
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/FileUtils.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/FileUtils.java
index 45faf5a4..7e6003d1 100644
--- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/FileUtils.java
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/FileUtils.java
@@ -70,6 +70,23 @@ public class FileUtils {
return parseFile(path).exists();
}
+ public static void rename(String path, String newName){
+ parseFile(path).renameTo(newName);
+ }
+
+ public static void delete(String path){
+ DocumentFile file = parseFile(path);
+ if (file.exists()){
+ if (file.isDirectory()){
+ String[] children = listFiles(path);
+ for (String child: children){
+ delete(path+"/"+child);
+ }
+ }
+ file.delete();
+ }
+ }
+
public static boolean createDir(String path, String name) {
DocumentFile folder = parseFile(path);
if (folder.findFile(name) != null) {
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/PerformanceMonitor.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/PerformanceMonitor.java
new file mode 100644
index 00000000..288dbc39
--- /dev/null
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/PerformanceMonitor.java
@@ -0,0 +1,64 @@
+package com.panda3ds.pandroid.utils;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.Debug;
+import android.os.Process;
+
+import com.panda3ds.pandroid.app.PandroidApplication;
+import com.panda3ds.pandroid.data.config.GlobalConfig;
+
+public class PerformanceMonitor {
+ private static int fps = 1;
+ private static String backend = "";
+ private static int frames = 0;
+ private static long lastUpdate = 0;
+ private static long totalMemory = 1;
+ private static long availMemory = 0;
+
+ public static void initialize(String backendName) {
+ fps = 1;
+ backend = backendName;
+ }
+
+ public static void runFrame() {
+ if (GlobalConfig.get(GlobalConfig.KEY_SHOW_PERFORMANCE_OVERLAY)) {
+ frames++;
+ if (System.currentTimeMillis() - lastUpdate > 1000) {
+ lastUpdate = System.currentTimeMillis();
+ fps = frames;
+ frames = 0;
+ try {
+ Context ctx = PandroidApplication.getAppContext();
+ ActivityManager manager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
+ ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
+ manager.getMemoryInfo(info);
+ totalMemory = info.totalMem;
+ availMemory = info.availMem;
+ } catch (Exception e) {/**/}
+ }
+ }
+ }
+
+ public static long getUsageMemory() {
+ return Math.max(1, totalMemory - availMemory);
+ }
+
+ public static long getTotalMemory() {
+ return totalMemory;
+ }
+
+ public static long getAvailMemory() {
+ return availMemory;
+ }
+
+ public static int getFps() {
+ return fps;
+ }
+
+ public static String getBackend() {
+ return backend;
+ }
+
+ public static void destroy() {}
+}
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlRenderer.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlRenderer.java
index 52e609a3..224bb080 100644
--- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlRenderer.java
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlRenderer.java
@@ -9,6 +9,7 @@ import android.util.Log;
import com.panda3ds.pandroid.AlberDriver;
import com.panda3ds.pandroid.utils.Constants;
import com.panda3ds.pandroid.utils.GameUtils;
+import com.panda3ds.pandroid.utils.PerformanceMonitor;
import com.panda3ds.pandroid.view.renderer.ConsoleRenderer;
import com.panda3ds.pandroid.view.renderer.layout.ConsoleLayout;
import com.panda3ds.pandroid.view.renderer.layout.DefaultScreenLayout;
@@ -41,6 +42,7 @@ public class PandaGlRenderer implements GLSurfaceView.Renderer, ConsoleRenderer
if (screenFbo != 0) {
glDeleteFramebuffers(1, new int[] {screenFbo}, 0);
}
+ PerformanceMonitor.destroy();
super.finalize();
}
@@ -92,6 +94,7 @@ public class PandaGlRenderer implements GLSurfaceView.Renderer, ConsoleRenderer
GameUtils.removeGame(game);
GameUtils.addGame(GameMetadata.applySMDH(game, smdh));
}
+ PerformanceMonitor.initialize(getBackendName());
}
public void onDrawFrame(GL10 unused) {
@@ -114,6 +117,7 @@ public class PandaGlRenderer implements GLSurfaceView.Renderer, ConsoleRenderer
screenHeight - bottomScreen.bottom, GL_COLOR_BUFFER_BIT, GL_LINEAR
);
}
+ PerformanceMonitor.runFrame();
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/utils/PerformanceView.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/utils/PerformanceView.java
new file mode 100644
index 00000000..7f712e4e
--- /dev/null
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/utils/PerformanceView.java
@@ -0,0 +1,61 @@
+package com.panda3ds.pandroid.view.utils;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.text.Html;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatTextView;
+
+import com.panda3ds.pandroid.utils.PerformanceMonitor;
+
+public class PerformanceView extends AppCompatTextView {
+ private boolean running = false;
+
+ public PerformanceView(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public PerformanceView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs,0);
+ }
+
+ public PerformanceView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics());
+ setPadding(padding,padding,padding,padding);
+ setTextColor(Color.WHITE);
+ setShadowLayer(padding,0,0,Color.BLACK);
+ }
+
+ public void refresh(){
+ running = isShown();
+ if (!running){
+ return;
+ }
+
+ String debug = "";
+
+ int memoryTotalMb = (int) Math.round((PerformanceMonitor.getTotalMemory()/1024.0)/1024.0);
+ int memoryUsageMb = (int) Math.round((PerformanceMonitor.getUsageMemory()/1024.0)/1024.0);
+
+ debug += "FPS: "+PerformanceMonitor.getFps()+"
";
+ debug += "RAM: "+Math.round(((float) memoryUsageMb / memoryTotalMb)*100)+"% ("+memoryUsageMb+"MB/"+memoryTotalMb+"MB)
";
+ debug += "BACKEND: "+PerformanceMonitor.getBackend()+"
";
+ setText(Html.fromHtml(debug, Html.FROM_HTML_MODE_COMPACT));
+ postDelayed(this::refresh, 250);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ if (!running){
+ refresh();
+ }
+ }
+}
diff --git a/src/pandroid/app/src/main/res/values-pt-rBR/strings.xml b/src/pandroid/app/src/main/res/values-pt-rBR/strings.xml
index 065b9e4f..aafe1810 100644
--- a/src/pandroid/app/src/main/res/values-pt-rBR/strings.xml
+++ b/src/pandroid/app/src/main/res/values-pt-rBR/strings.xml
@@ -45,4 +45,10 @@
Abrir arquivo
Criar novo
Executando \"%s\" ...
+ Opções de desenvolvedor
+ Depuração, mostrar fps, etc.
+ Monitor de desempenho
+ Mostrar um overlay com fps, memoria, etc.
+ Depuração
+ Grave os registros para um arquivo.
\ No newline at end of file
diff --git a/src/pandroid/app/src/main/res/values/strings.xml b/src/pandroid/app/src/main/res/values/strings.xml
index e0de62e1..baf89752 100644
--- a/src/pandroid/app/src/main/res/values/strings.xml
+++ b/src/pandroid/app/src/main/res/values/strings.xml
@@ -46,4 +46,10 @@
Open file
Create new
Running \"%s\" ...
+ Developer options
+ Logger, FPS Counter, etc.
+ Performance monitor
+ Show overlay with fps, memory, etc.
+ Logger
+ Store application logs to file.
diff --git a/src/pandroid/app/src/main/res/xml/developer_preferences.xml b/src/pandroid/app/src/main/res/xml/developer_preferences.xml
new file mode 100644
index 00000000..fae82279
--- /dev/null
+++ b/src/pandroid/app/src/main/res/xml/developer_preferences.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/pandroid/app/src/main/res/xml/start_preferences.xml b/src/pandroid/app/src/main/res/xml/start_preferences.xml
index 878aa920..5eeb1954 100644
--- a/src/pandroid/app/src/main/res/xml/start_preferences.xml
+++ b/src/pandroid/app/src/main/res/xml/start_preferences.xml
@@ -23,4 +23,11 @@
app:summary="@string/pref_appearance_summary"
app:layout="@layout/preference_start_item"/>
+
+
\ No newline at end of file