Add game to launcher and some clear code.

This commit is contained in:
gabriel 2024-02-29 15:46:41 -04:00
parent 3a0ca3baa0
commit 94b9f3137d
39 changed files with 354 additions and 271 deletions

View file

@ -32,6 +32,8 @@
<activity
android:name=".app.GameActivity"
android:supportsPictureInPicture="true"
android:taskAffinity="emulator.GameActivity"
android:launchMode="singleTop"
android:configChanges="screenSize|screenLayout|smallestScreenSize|orientation|density|uiMode">
</activity>
<activity
@ -57,6 +59,15 @@
</intent-filter>
</provider>
<activity
android:name=".app.game.GameLauncher"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<data android:scheme="pandroid-game"/>
</intent-filter>
</activity>
<service android:name=".app.services.LoggerService" android:process=":logger_service"/>
</application>
</manifest>

View file

@ -1,8 +1,11 @@
package com.panda3ds.pandroid.app;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Rational;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@ -11,11 +14,9 @@ import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.FrameLayout;
import android.widget.Toast;
import androidx.annotation.Nullable;
import android.app.PictureInPictureParams;
import android.content.res.Configuration;
import android.util.DisplayMetrics;
import android.util.Rational;
import com.panda3ds.pandroid.AlberDriver;
import com.panda3ds.pandroid.R;
import com.panda3ds.pandroid.app.game.AlberInputListener;
@ -61,9 +62,7 @@ public class GameActivity extends BaseActivity implements EmulatorCallback {
controllerLayout.initialize();
((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();
changeOverlayVisibility(checked);
GlobalConfig.set(GlobalConfig.KEY_SCREEN_GAMEPAD_VISIBLE, checked);
});
((CheckBox) findViewById(R.id.hide_screen_controller)).setChecked(GlobalConfig.get(GlobalConfig.KEY_SCREEN_GAMEPAD_VISIBLE));
@ -74,6 +73,13 @@ public class GameActivity extends BaseActivity implements EmulatorCallback {
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));
}
swapScreens(GlobalConfig.get(GlobalConfig.KEY_CURRENT_DS_LAYOUT));
}
private void changeOverlayVisibility(boolean visible){
findViewById(R.id.overlay_controller).setVisibility(visible ? View.VISIBLE : View.GONE);
findViewById(R.id.overlay_controller).invalidate();
findViewById(R.id.overlay_controller).requestLayout();
}
@Override
@ -84,51 +90,20 @@ public class GameActivity extends BaseActivity implements EmulatorCallback {
InputHandler.reset();
InputHandler.setMotionDeadZone(InputMap.getDeadZone());
InputHandler.setEventListener(inputListener);
if (GlobalConfig.get(GlobalConfig.KEY_PICTURE_IN_PICTURE)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (!drawerFragment.isOpened()) {
setPictureInPictureParams(new PictureInPictureParams.Builder().setAutoEnterEnabled(true).build());
}
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
getTheme().applyStyle(R.style.GameActivityNavigationBar, true);
}
}
@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
if (isInPictureInPictureMode) {
findViewById(R.id.overlay_controller).setVisibility(View.GONE);
} else {
if (GlobalConfig.get(GlobalConfig.KEY_SCREEN_GAMEPAD_VISIBLE)) {
findViewById(R.id.overlay_controller).setVisibility(View.VISIBLE);
}
}
}
@Override
public void onUserLeaveHint() {
super.onUserLeaveHint();
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int widthPixels = displayMetrics.widthPixels;
int heightPixels = displayMetrics.heightPixels;
// Calculate aspect ratio
float aspectRatio = (float) widthPixels / (float) heightPixels;
if (GlobalConfig.get(GlobalConfig.KEY_PICTURE_IN_PICTURE)) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
if (!drawerFragment.isOpened()) {
Rational aspectRatioRational = new Rational(widthPixels, heightPixels);
PictureInPictureParams.Builder pipBuilder = new PictureInPictureParams.Builder();
pipBuilder.setAspectRatio(aspectRatioRational);
enterPictureInPictureMode(pipBuilder.build());
}
private void goToPictureInPicture() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
builder.setAutoEnterEnabled(true);
builder.setSeamlessResizeEnabled(true);
}
builder.setAspectRatio(new Rational(10, 14));
enterPictureInPictureMode(builder.build());
}
}
@ -137,8 +112,14 @@ public class GameActivity extends BaseActivity implements EmulatorCallback {
super.onPause();
InputHandler.reset();
if (GlobalConfig.get(GlobalConfig.KEY_PICTURE_IN_PICTURE)) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
goToPictureInPicture();
}
} else {
drawerFragment.open();
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
@ -159,8 +140,9 @@ public class GameActivity extends BaseActivity implements EmulatorCallback {
}
@Override
public void swapScreens() {
currentDsLayout = currentDsLayout + 1 < DsLayoutManager.getLayoutCount() ? currentDsLayout + 1 : 0;
public void swapScreens(int index) {
currentDsLayout = index;
GlobalConfig.set(GlobalConfig.KEY_CURRENT_DS_LAYOUT,index);
renderer.setLayout(DsLayoutManager.createLayout(currentDsLayout));
}
@ -176,11 +158,14 @@ public class GameActivity extends BaseActivity implements EmulatorCallback {
@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode);
changeOverlayVisibility(!isInPictureInPictureMode && GlobalConfig.get(GlobalConfig.KEY_SCREEN_GAMEPAD_VISIBLE));
findViewById(R.id.hide_screen_controller).setVisibility(isInPictureInPictureMode ? View.INVISIBLE : View.VISIBLE);
if (isInPictureInPictureMode){
getWindow().getDecorView().postDelayed(drawerFragment::close, 250);
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S){
ActivityManager manager = ((ActivityManager) getSystemService(ACTIVITY_SERVICE));
manager.getAppTasks().forEach(ActivityManager.AppTask::moveToFront);
}
@Override
protected void onUserLeaveHint() {
super.onUserLeaveHint();
}
@Override
@ -188,7 +173,6 @@ public class GameActivity extends BaseActivity implements EmulatorCallback {
if (AlberDriver.HasRomLoaded()) {
AlberDriver.Finalize();
}
super.onDestroy();
}
}

View file

@ -7,6 +7,7 @@ import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreferenceCompat;
import com.panda3ds.pandroid.lang.Function;
@ -21,6 +22,10 @@ public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
});
}
protected void setSwitchValue(String id, boolean value){
((SwitchPreferenceCompat)findPreference(id)).setChecked(value);
}
protected void setActivityTitle(@StringRes int titleId) {
ActionBar header = ((AppCompatActivity) requireActivity()).getSupportActionBar();
if (header != null) {

View file

@ -1,16 +1,23 @@
package com.panda3ds.pandroid.app.base;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.drawable.IconCompat;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.panda3ds.pandroid.R;
import com.panda3ds.pandroid.app.PandroidApplication;
import com.panda3ds.pandroid.app.game.GameLauncher;
import com.panda3ds.pandroid.data.game.GameMetadata;
import com.panda3ds.pandroid.utils.CompatUtils;
import com.panda3ds.pandroid.utils.FileUtils;
import com.panda3ds.pandroid.utils.GameUtils;
import com.panda3ds.pandroid.view.gamesgrid.GameIconView;
@ -32,11 +39,14 @@ public class GameAboutDialog extends BaseSheetDialog {
((TextView) findViewById(R.id.game_publisher)).setText(game.getPublisher());
((TextView) findViewById(R.id.region)).setText(game.getRegions()[0].localizedName());
((TextView) findViewById(R.id.directory)).setText(FileUtils.obtainUri(game.getRealPath()).getPath());
findViewById(R.id.play).setOnClickListener(v -> {
dismiss();
GameUtils.launch(getContext(), game);
});
findViewById(R.id.shortcut).setOnClickListener(v -> {
dismiss();
makeShortcut();
});
if (game.getRomPath().startsWith("folder:")) {
findViewById(R.id.remove).setVisibility(View.GONE);
@ -50,4 +60,22 @@ public class GameAboutDialog extends BaseSheetDialog {
});
}
}
private void makeShortcut() {
Context context = CompatUtils.findActivity(getContext());
ShortcutInfoCompat.Builder shortcut = new ShortcutInfoCompat.Builder(context, game.getId());
if (game.getIcon() != null){
shortcut.setIcon(IconCompat.createWithAdaptiveBitmap(game.getIcon()));
} else {
shortcut.setIcon(IconCompat.createWithResource(getContext(), R.mipmap.ic_launcher));
}
shortcut.setActivity(new ComponentName(context, GameLauncher.class));
shortcut.setLongLabel(game.getTitle());
shortcut.setShortLabel(game.getTitle());
Intent intent = new Intent(PandroidApplication.getAppContext(), GameLauncher.class);
intent.setAction(Intent.ACTION_VIEW);
intent.setData(new Uri.Builder().scheme("pandroid-game").authority(game.getId()).build());
shortcut.setIntent(intent);
ShortcutManagerCompat.requestPinShortcut(context,shortcut.build(),null);
}
}

View file

@ -25,7 +25,9 @@ import com.panda3ds.pandroid.view.gamesgrid.GameIconView;
public class DrawerFragment extends Fragment implements DrawerLayout.DrawerListener, NavigationView.OnNavigationItemSelectedListener {
private DrawerLayout drawerContainer;
private View drawerLayout;
private EmulatorCallback emulator;
private GameMetadata game;
@Nullable
@Override
@ -44,21 +46,21 @@ public class DrawerFragment extends Fragment implements DrawerLayout.DrawerListe
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
drawerContainer.setVisibility(View.GONE);
drawerLayout = view.findViewById(R.id.drawer_layout);
((NavigationView)view.findViewById(R.id.menu)).setNavigationItemSelectedListener(this);
refresh();
}
private void refresh(){
GameMetadata game = GameUtils.getCurrentGame();
View view = getView();
game = GameUtils.getCurrentGame();
if (game.getIcon() != null && !game.getIcon().isRecycled()) {
((GameIconView) view.findViewById(R.id.game_icon)).setImageBitmap(game.getIcon());
((GameIconView) drawerLayout.findViewById(R.id.game_icon)).setImageBitmap(game.getIcon());
} else {
((GameIconView) view.findViewById(R.id.game_icon)).setImageDrawable(new ColorDrawable(Color.TRANSPARENT));
((GameIconView) drawerLayout.findViewById(R.id.game_icon)).setImageDrawable(new ColorDrawable(Color.TRANSPARENT));
}
((AppCompatTextView)view.findViewById(R.id.game_title)).setText(game.getTitle());
((AppCompatTextView)view.findViewById(R.id.game_publisher)).setText(game.getPublisher());
((AppCompatTextView)drawerLayout.findViewById(R.id.game_title)).setText(game.getTitle());
((AppCompatTextView)drawerLayout.findViewById(R.id.game_publisher)).setText(game.getPublisher());
}
@ -119,7 +121,7 @@ public class DrawerFragment extends Fragment implements DrawerLayout.DrawerListe
emulator.swapScreens();
close();
} else if (id == R.id.exit) {
requireActivity().finish();
requireActivity().finishAndRemoveTask();
} else if (id == R.id.lua_script) {
new LuaDialogFragment().show(getParentFragmentManager(), null);
} else if (id == R.id.change_orientation) {

View file

@ -1,7 +1,11 @@
package com.panda3ds.pandroid.app.game;
import com.panda3ds.pandroid.data.config.GlobalConfig;
public interface EmulatorCallback {
void onBackPressed();
void swapScreens();
void swapScreens(int index);
default void swapScreens() {
swapScreens(GlobalConfig.get(GlobalConfig.KEY_CURRENT_DS_LAYOUT)+1);
}
}

View file

@ -0,0 +1,37 @@
package com.panda3ds.pandroid.app.game;
import android.net.Uri;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.core.content.pm.ShortcutManagerCompat;
import com.panda3ds.pandroid.R;
import com.panda3ds.pandroid.app.BaseActivity;
import com.panda3ds.pandroid.data.game.GameMetadata;
import com.panda3ds.pandroid.utils.GameUtils;
import java.util.Arrays;
public class GameLauncher extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new TextView(this));
Uri uri = getIntent().getData();
if(uri != null && uri.getScheme().equals("pandroid-game")){
String gameId = uri.getAuthority();
GameMetadata game = GameUtils.findGameById(gameId);
if (game != null){
GameUtils.launch(this, game);
} else {
Toast.makeText(this, R.string.invalid_game,Toast.LENGTH_LONG).show();
ShortcutManagerCompat.removeDynamicShortcuts(this, Arrays.asList(gameId));
ShortcutManagerCompat.removeLongLivedShortcuts(this, Arrays.asList(gameId));
}
}
finish();
}
}

View file

@ -21,7 +21,6 @@ public class AdvancedPreferences extends BasePreferenceFragment {
setActivityTitle(R.string.advanced_options);
setItemClick("performanceMonitor", pref -> GlobalConfig.set(GlobalConfig.KEY_SHOW_PERFORMANCE_OVERLAY, ((SwitchPreferenceCompat) pref).isChecked()));
setItemClick("pictureInPicture", pref -> GlobalConfig.set(GlobalConfig.KEY_PICTURE_IN_PICTURE, ((SwitchPreferenceCompat) pref).isChecked()));
setItemClick("shaderJit", pref -> GlobalConfig.set(GlobalConfig.KEY_SHADER_JIT, ((SwitchPreferenceCompat) pref).isChecked()));
setItemClick("loggerService", pref -> {
boolean checked = ((SwitchPreferenceCompat) pref).isChecked();
@ -45,14 +44,7 @@ public class AdvancedPreferences extends BasePreferenceFragment {
private void refresh() {
((SwitchPreferenceCompat) findPreference("performanceMonitor")).setChecked(GlobalConfig.get(GlobalConfig.KEY_SHOW_PERFORMANCE_OVERLAY));
((SwitchPreferenceCompat) findPreference("pictureInPicture")).setChecked(GlobalConfig.get(GlobalConfig.KEY_PICTURE_IN_PICTURE));
((SwitchPreferenceCompat) findPreference("loggerService")).setChecked(GlobalConfig.get(GlobalConfig.KEY_LOGGER_SERVICE));
((SwitchPreferenceCompat) findPreference("shaderJit")).setChecked(GlobalConfig.get(GlobalConfig.KEY_SHADER_JIT));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
findPreference("pictureInPicture").setEnabled(true);
} else {
findPreference("pictureInPicture").setEnabled(false);
findPreference("pictureInPicture").setSummary("Your device does not support this feature");
}
}
}

View file

@ -14,7 +14,6 @@ import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.panda3ds.pandroid.R;
import com.panda3ds.pandroid.app.base.BasePreferenceFragment;
import com.panda3ds.pandroid.app.base.BaseSheetDialog;
@ -29,7 +28,7 @@ public class GamesFoldersPreferences extends BasePreferenceFragment implements A
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
setPreferencesFromResource(R.xml.empty_preferences, rootKey);
setActivityTitle(R.string.pref_games_folders);
setActivityTitle(R.string.pref_game_folders);
refreshList();
pickFolderRequest = registerForActivityResult(openFolderContract, this);
}

View file

@ -3,19 +3,33 @@ package com.panda3ds.pandroid.app.preferences;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.preference.SwitchPreferenceCompat;
import com.panda3ds.pandroid.R;
import com.panda3ds.pandroid.app.PreferenceActivity;
import com.panda3ds.pandroid.app.base.BasePreferenceFragment;
import com.panda3ds.pandroid.app.preferences.ds.DsListPreferences;
import com.panda3ds.pandroid.app.preferences.screen_editor.ScreenLayoutsPreference;
import com.panda3ds.pandroid.data.config.GlobalConfig;
public class GeneralPreferences extends BasePreferenceFragment {
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
setPreferencesFromResource(R.xml.general_preference, rootKey);
setItemClick("appearance.theme", (pref) -> new ThemeSelectorDialog(requireActivity()).show());
setItemClick("appearance.ds", (pref) -> PreferenceActivity.launch(requireActivity(), DsListPreferences.class));
setItemClick("appearance.ds", (pref) -> PreferenceActivity.launch(requireActivity(), ScreenLayoutsPreference.class));
setItemClick("games.folders", (pref) -> PreferenceActivity.launch(requireActivity(), GamesFoldersPreferences.class));
setItemClick("behavior.pictureInPicture", (pref)-> GlobalConfig.set(GlobalConfig.KEY_PICTURE_IN_PICTURE, ((SwitchPreferenceCompat)pref).isChecked()));
setActivityTitle(R.string.general);
refresh();
}
@Override
public void onResume() {
super.onResume();
refresh();
}
private void refresh(){
setSwitchValue("behavior.pictureInPicture", GlobalConfig.get(GlobalConfig.KEY_PICTURE_IN_PICTURE));
}
}

View file

@ -1,4 +1,4 @@
package com.panda3ds.pandroid.app.preferences.ds;
package com.panda3ds.pandroid.app.preferences.screen_editor;
import android.os.Bundle;
import android.view.LayoutInflater;
@ -16,9 +16,8 @@ import com.panda3ds.pandroid.app.BaseActivity;
import com.panda3ds.pandroid.view.ds.DsEditorView;
import com.panda3ds.pandroid.view.ds.DsLayoutManager;
public class DsEditorPreferences extends Fragment {
public class ScreenEditorPreference extends Fragment {
private LinearLayout layout;
private DsEditorView editor;
@Nullable
@Override
@ -33,7 +32,7 @@ public class DsEditorPreferences extends Fragment {
super.onViewCreated(view, savedInstanceState);
int index = getArguments().getInt("index");
layout.removeAllViews();
layout.addView(editor = new DsEditorView(view.getContext(), index), new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
layout.addView(new DsEditorView(view.getContext(), index), new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
((BaseActivity)requireActivity()).getSupportActionBar().hide();
}

View file

@ -1,4 +1,4 @@
package com.panda3ds.pandroid.app.preferences.ds;
package com.panda3ds.pandroid.app.preferences.screen_editor;
import android.content.Intent;
import android.os.Bundle;
@ -12,7 +12,7 @@ import com.panda3ds.pandroid.app.PreferenceActivity;
import com.panda3ds.pandroid.app.base.BasePreferenceFragment;
import com.panda3ds.pandroid.view.ds.DsLayoutManager;
public class DsListPreferences extends BasePreferenceFragment {
public class ScreenLayoutsPreference extends BasePreferenceFragment {
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
setPreferencesFromResource(R.xml.empty_preferences, rootKey);
@ -33,7 +33,7 @@ public class DsListPreferences extends BasePreferenceFragment {
final int index = i;
pref.setOnPreferenceClickListener(preference -> {
PreferenceActivity.launch(requireContext(), DsEditorPreferences.class, new Intent().putExtra("index", index));
PreferenceActivity.launch(requireContext(), ScreenEditorPreference.class, new Intent().putExtra("index", index));
return false;
});
screen.addPreference(pref);

View file

@ -13,9 +13,12 @@ import androidx.annotation.Nullable;
import com.panda3ds.pandroid.R;
import com.panda3ds.pandroid.app.PandroidApplication;
import com.panda3ds.pandroid.utils.FileUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Objects;
public class AppDataDocumentProvider extends DocumentsProvider {
private static final String ROOT_ID = "root";
@ -89,8 +92,10 @@ public class AppDataDocumentProvider extends DocumentsProvider {
private void includeFile(MatrixCursor cursor, File file) {
int flags = 0;
if (file.isFile()) {
flags = Document.FLAG_SUPPORTS_WRITE;
if (file.isDirectory()) {
flags = Document.FLAG_DIR_SUPPORTS_CREATE;
} else {
flags = Document.FLAG_SUPPORTS_WRITE | Document.FLAG_SUPPORTS_REMOVE | Document.FLAG_SUPPORTS_DELETE;
}
cursor.newRow()
.add(Document.COLUMN_DOCUMENT_ID, obtainDocumentId(file))
@ -116,6 +121,40 @@ public class AppDataDocumentProvider extends DocumentsProvider {
return cursor;
}
@Override
public String createDocument(String parentDocumentId, String mimeType, String displayName) throws FileNotFoundException {
File parent = obtainFile(parentDocumentId);
File file = new File(parent, displayName);
if (!parent.exists()){
throw new FileNotFoundException("Parent don't exists");
}
if (Objects.equals(mimeType, Document.MIME_TYPE_DIR)){
if (!file.mkdirs()){
throw new FileNotFoundException("Error on create directory");
}
} else {
try {
if (!file.createNewFile()){
throw new Exception("Error on create file");
}
} catch (Exception e){
throw new FileNotFoundException(e.getMessage());
}
}
return obtainDocumentId(file);
}
@Override
public void deleteDocument(String documentId) throws FileNotFoundException {
File file = obtainFile(documentId);
if (file.exists()){
FileUtils.delete(file.getAbsolutePath());
} else {
throw new FileNotFoundException("File not exists");
}
}
@Override
public ParcelFileDescriptor openDocument(String documentId, String mode, @Nullable CancellationSignal signal) throws FileNotFoundException {
return ParcelFileDescriptor.open(obtainFile(documentId), ParcelFileDescriptor.parseMode(mode));

View file

@ -93,7 +93,7 @@ public class SMDH {
smdh.position(META_OFFSET + (512 * i) + 0x80);
data = new byte[0x100];
smdh.get(data);
title[i] = convertString(data).replaceAll("\n", " ");
title[i] = convertString(data);
smdh.position(META_OFFSET + (512 * i) + 0x180);
data = new byte[0x80];

View file

@ -22,11 +22,12 @@ public class GlobalConfig {
public static DataModel data;
public static final Key<Boolean> KEY_SHADER_JIT = new Key<>("emu.shader_jit", false);
public static final Key<Boolean> KEY_PICTURE_IN_PICTURE = new Key<>("app.behavior.pictureInPicture", false);
public static final Key<Boolean> KEY_SHOW_PERFORMANCE_OVERLAY = new Key<>("dev.performanceOverlay", false);
public static final Key<Boolean> KEY_PICTURE_IN_PICTURE = new Key<>("dev.pictureInPicture", false);
public static final Key<Boolean> KEY_LOGGER_SERVICE = new Key<>("dev.loggerService", false);
public static final Key<Integer> KEY_APP_THEME = new Key<>("app.theme", THEME_ANDROID);
public static final Key<Boolean> KEY_SCREEN_GAMEPAD_VISIBLE = new Key<>("app.screen_gamepad.visible", true);
public static final Key<Integer> KEY_CURRENT_DS_LAYOUT = new Key<>("app.ds.current_layout",0);
public static final Key<String> KEY_DS_LAYOUTS = new Key<>("app.ds.layouts", "");
public static void initialize() {

View file

@ -1,6 +1,7 @@
package com.panda3ds.pandroid.utils;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.TypedArray;

View file

@ -157,6 +157,15 @@ public class GameUtils {
writeChanges();
}
public static GameMetadata findGameById(String id) {
for (GameMetadata game: getGames()){
if (game.getId().equals(id)){
return game;
}
}
return null;
}
private static class DataModel {
public final List<GameMetadata> games = new ArrayList<>();
public final HashMap<String, GamesFolder> folders = new HashMap<>();

View file

@ -16,6 +16,7 @@ import com.panda3ds.pandroid.app.base.BottomAlertDialog;
import com.panda3ds.pandroid.data.SMDH;
import com.panda3ds.pandroid.data.config.GlobalConfig;
import com.panda3ds.pandroid.data.game.GameMetadata;
import com.panda3ds.pandroid.utils.CompatUtils;
import com.panda3ds.pandroid.utils.Constants;
import com.panda3ds.pandroid.utils.GameUtils;
import com.panda3ds.pandroid.utils.PerformanceMonitor;
@ -103,7 +104,7 @@ public class PandaGlRenderer implements GLSurfaceView.Renderer, ConsoleRenderer
.setMessage(R.string.dialog_message_invalid_rom)
.setPositiveButton(android.R.string.ok, (dialog, witch) -> {
dialog.dismiss();
((Activity) context).finish();
CompatUtils.findActivity(context).finishAndRemoveTask();
})
.setCancelable(false)
.show();

View file

@ -30,10 +30,8 @@ import com.panda3ds.pandroid.math.Vector2;
import com.panda3ds.pandroid.utils.CompatUtils;
import com.panda3ds.pandroid.utils.Constants;
@SuppressLint("ViewConstructor")
public class DsEditorView extends FrameLayout {
private final int COLOR_TOP_SELECTION;
private final int COLOR_BOTTOM_SELECTION;
private final float SIZE_DP;
private final Paint selectionPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@ -54,10 +52,10 @@ public class DsEditorView extends FrameLayout {
super(context);
layout = (DsLayout) DsLayoutManager.createLayout(index);
SIZE_DP = CompatUtils.applyDimen(TypedValue.COMPLEX_UNIT_DIP, 1);
COLOR_BOTTOM_SELECTION = CompatUtils.resolveColor(context, androidx.appcompat.R.attr.colorPrimary);
COLOR_TOP_SELECTION = CompatUtils.resolveColor(context, com.google.android.material.R.attr.colorAccent);
int colorBottomSelection = CompatUtils.resolveColor(context, androidx.appcompat.R.attr.colorPrimary);
int colorTopSelection = CompatUtils.resolveColor(context, com.google.android.material.R.attr.colorAccent);
selectionPaint.setColor(COLOR_TOP_SELECTION);
selectionPaint.setColor(colorTopSelection);
selectionPaint.setStrokeWidth(SIZE_DP * 2);
selectionPaint.setPathEffect(new DashPathEffect(new float[]{SIZE_DP * 10, SIZE_DP * 10}, 0.0f));
selectionPaint.setStyle(Paint.Style.STROKE);
@ -100,9 +98,7 @@ public class DsEditorView extends FrameLayout {
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
public void onNothingSelected(AdapterView<?> parent) {}
});
}
@ -113,7 +109,7 @@ public class DsEditorView extends FrameLayout {
});
spacePoint = new PointView();
spacePoint.setColor(Color.WHITE, COLOR_TOP_SELECTION);
spacePoint.setColor(CompatUtils.resolveColor(context, com.google.android.material.R.attr.colorOnPrimary), colorTopSelection);
spacePoint.setOnTouchListener((view, motion) -> {
layout.getCurrentModel().space = (motion.getX() + spacePoint.x()) / (float) width;
refreshPoints();
@ -124,7 +120,7 @@ public class DsEditorView extends FrameLayout {
setOnClickListener(v -> {
if (layout.getCurrentModel().mode == Mode.SINGLE) {
layout.getCurrentModel().singleTop = !layout.getCurrentModel().singleTop;
layout.getCurrentModel().onlyTop = !layout.getCurrentModel().onlyTop;
refreshPoints();
}
});
@ -132,40 +128,38 @@ public class DsEditorView extends FrameLayout {
topDisplay = new PointView();
topDisplay.setText(R.string.top_display);
topDisplay.setOnTouchListener(new DisplayTouchEvent(true));
topDisplay.setTextColor(COLOR_TOP_SELECTION);
topDisplay.setBackground(new SelectionDrawable(COLOR_TOP_SELECTION));
topDisplay.setTextColor(colorTopSelection);
topDisplay.setBackground(new SelectionDrawable(colorTopSelection));
bottomDisplay = new PointView();
bottomDisplay.setText(R.string.bottom_display);
bottomDisplay.setOnTouchListener(new DisplayTouchEvent(false));
bottomDisplay.setTextColor(COLOR_BOTTOM_SELECTION);
bottomDisplay.setBackground(new SelectionDrawable(COLOR_BOTTOM_SELECTION));
bottomDisplay.setTextColor(colorBottomSelection);
bottomDisplay.setBackground(new SelectionDrawable(colorBottomSelection));
topDisplayResizer = new PointView();
topDisplayResizer.setColor(0, COLOR_TOP_SELECTION);
topDisplayResizer.setColor(0, colorTopSelection);
topDisplayResizer.setOnTouchListener(new DisplayResizeTouchEvent(true));
bottomDisplayResizer = new PointView();
bottomDisplayResizer.setColor(0, COLOR_BOTTOM_SELECTION);
bottomDisplayResizer.setColor(0, colorBottomSelection);
bottomDisplayResizer.setOnTouchListener(new DisplayResizeTouchEvent(false));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (this.width != width || this.height != height) {
this.width = width;
this.height = height;
public void draw(Canvas canvas) {
super.draw(canvas);
if (this.width != getWidth() || this.height != getHeight()) {
this.width = getWidth();
this.height = getHeight();
refreshLayout();
}
}
private void refreshPoints() {
Model data = layout.getCurrentModel();
data.preferredTop.fixOverlay(width, height, (int) (SIZE_DP*5));
data.preferredBottom.fixOverlay(width, height, (int) (SIZE_DP*30));
data.preferredTop.fixOverlay(width, height, (int) (SIZE_DP * 5));
data.preferredBottom.fixOverlay(width, height, (int) (SIZE_DP * 30));
layout.update(width, height);
Rect bottomDisplay = layout.getBottomDisplayBounds();
Rect topDisplay = layout.getTopDisplayBounds();
@ -322,6 +316,7 @@ public class DsEditorView extends FrameLayout {
private class DisplayResizeTouchEvent implements OnTouchListener {
private final boolean topScreen;
private DisplayResizeTouchEvent(boolean topScreen) {
this.topScreen = topScreen;
}

View file

@ -10,17 +10,17 @@ class DsLayout implements ConsoleLayout {
private final Rect topDisplay = new Rect();
private final Rect bottomDisplay = new Rect();
private final Vector2 screenSize = new Vector2(0,0);
private final Vector2 sourceTop = new Vector2(0,0);
private final Vector2 sourceBottom = new Vector2(0,0);
private final Vector2 screenSize = new Vector2(0, 0);
private final Vector2 sourceTop = new Vector2(0, 0);
private final Vector2 sourceBottom = new Vector2(0, 0);
private final Model[] modes = new Model[2];
public DsLayout(Model landscape, Model portrait){
public DsLayout(Model landscape, Model portrait) {
modes[0] = landscape;
modes[1] = portrait;
}
public DsLayout(){
public DsLayout() {
this(new Model(), new Model());
}
@ -52,10 +52,10 @@ class DsLayout implements ConsoleLayout {
return topDisplay;
}
public void update(){
public void update() {
Model data = getCurrentModel();
Mode mode = data.mode;
switch (mode){
switch (mode) {
case RELATIVE:
relative(data);
break;
@ -69,22 +69,18 @@ class DsLayout implements ConsoleLayout {
}
private void absolute(Model data) {
if (data.lockAspect){
data.preferredTop.applyWithAspect(topDisplay, (int) screenSize.x, (double) sourceTop.y/sourceTop.x);
data.preferredBottom.applyWithAspect(bottomDisplay, (int) screenSize.x, (double) sourceBottom.y/sourceBottom.x);
if (data.lockAspect) {
data.preferredTop.applyWithAspect(topDisplay, (int) screenSize.x, (double) sourceTop.y / sourceTop.x);
data.preferredBottom.applyWithAspect(bottomDisplay, (int) screenSize.x, (double) sourceBottom.y / sourceBottom.x);
} else {
data.preferredTop.apply(topDisplay, (int) screenSize.x, (int) screenSize.y);
data.preferredBottom.apply(bottomDisplay, (int) screenSize.x, (int) screenSize.y);
}
}
/**
* SINGLE LAYOUT:
* SHOW ONLY SCREEN IN FIT MODE
*/
private void single(Model data) {
Vector2 source = data.singleTop ? sourceTop : sourceBottom;
Rect dest = data.singleTop ? topDisplay : bottomDisplay;
Vector2 source = data.onlyTop ? sourceTop : sourceBottom;
Rect dest = data.onlyTop ? topDisplay : bottomDisplay;
if (data.lockAspect) {
int x = 0, y = 0;
@ -99,11 +95,11 @@ class DsLayout implements ConsoleLayout {
height = (int) screenSize.y;
x = (int) ((screenSize.x - width) / 2);
}
dest.set(x,y,x+width,y+height);
dest.set(x, y, x + width, y + height);
} else {
dest.set(0,0, (int) screenSize.x, (int) screenSize.y);
dest.set(0, 0, (int) screenSize.x, (int) screenSize.y);
}
(data.singleTop ? bottomDisplay : topDisplay).set(0,0,0,0);
(data.onlyTop ? bottomDisplay : topDisplay).set(0, 0, 0, 0);
}
@ -122,7 +118,7 @@ class DsLayout implements ConsoleLayout {
Rect topDisplay = this.topDisplay;
Rect bottomDisplay = this.bottomDisplay;
if (data.reverse){
if (data.reverse) {
topSourceSize = this.sourceBottom;
bottomSourceSize = this.sourceTop;
@ -144,15 +140,17 @@ class DsLayout implements ConsoleLayout {
topDisplay.set(0, 0, topDisplayWidth, topDisplayHeight);
bottomDisplay.set(topDisplayWidth, 0, topDisplayWidth + (screenWidth - topDisplayWidth), bottomDisplayHeight);
switch (data.gravity){
case Gravity.CENTER:{
bottomDisplay.offset(0, (screenHeight-bottomDisplay.height())/2);
topDisplay.offset(0, (screenHeight-topDisplay.height())/2);
}break;
case Gravity.BOTTOM:{
bottomDisplay.offset(0, (screenHeight-bottomDisplay.height()));
topDisplay.offset(0, (screenHeight-topDisplay.height()));
}break;
switch (data.gravity) {
case Gravity.CENTER: {
bottomDisplay.offset(0, (screenHeight - bottomDisplay.height()) / 2);
topDisplay.offset(0, (screenHeight - topDisplay.height()) / 2);
}
break;
case Gravity.BOTTOM: {
bottomDisplay.offset(0, (screenHeight - bottomDisplay.height()));
topDisplay.offset(0, (screenHeight - topDisplay.height()));
}
break;
}
} else {

View file

@ -20,11 +20,11 @@ public class DsLayoutManager {
Model model2 = new Model();
model2.mode = Mode.SINGLE;
model2.singleTop = false;
model2.onlyTop = false;
Model model3 = new Model();
model3.mode = Mode.SINGLE;
model3.singleTop = true;
model3.onlyTop = true;
data.models.add(new Model[]{model1, model1.clone()});
data.models.add(new Model[]{model2, model2.clone()});

View file

@ -12,7 +12,7 @@ class Model implements Cloneable {
public final Bounds preferredTop = new Bounds();
public final Bounds preferredBottom = new Bounds();
public boolean reverse = false;
public boolean singleTop = true;
public boolean onlyTop = true;
public float space = 0.6f;
public int gravity = Gravity.CENTER;
public boolean lockAspect = true;

View file

@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:fillColor="#FFF"
android:pathData="M8,19h3v4h2v-4h3l-4,-4 -4,4zM16,5h-3L13,1h-2v4L8,5l4,4 4,-4zM4,11v2h16v-2L4,11z"/>
</vector>

View file

@ -2,8 +2,9 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:viewportHeight="24"
android:tint="?colorOnSurface">
<path
android:fillColor="#FF000000"
android:fillColor="#FFF"
android:pathData="M7.41,8.59 L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
</vector>

View file

@ -2,8 +2,9 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:viewportHeight="24"
android:tint="?colorOnSurface">
<path
android:fillColor="#FF000000"
android:fillColor="#FFF"
android:pathData="M7.41,15.41 L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
</vector>

View file

@ -1,4 +1,4 @@
<vector android:height="24dp" android:tint="#000000"
<vector android:height="24dp" android:tint="?colorOnSurface"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M9.4,16.6L4.8,12l4.6,-4.6L8,6l-6,6 6,6 1.4,-1.4zM14.6,16.6l4.6,-4.6 -4.6,-4.6L16,6l6,6 -6,6 -1.4,-1.4z"/>

View file

@ -2,8 +2,9 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:viewportHeight="24"
android:tint="?colorOnSurface">
<path
android:fillColor="#FF000000"
android:fillColor="#FFF"
android:pathData="M9.01,14L2,14v2h7.01v3L13,15l-3.99,-4v3zM14.99,13v-3L22,10L22,8h-7.01L14.99,5L11,9l3.99,4z"/>
</vector>

View file

@ -1,4 +1,4 @@
<vector android:height="24dp" android:tint="#000000"
<vector android:height="24dp" android:tint="?colorOnSurface"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>

View file

@ -1,5 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#000000" android:viewportHeight="24"
android:tint="?colorOnSurface" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
</vector>

View file

@ -2,8 +2,9 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:viewportHeight="24"
android:tint="?colorOnSurface">
<path
android:fillColor="?colorOnSurface"
android:fillColor="#FFF"
android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z"/>
</vector>

View file

@ -1,5 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#000000" android:viewportHeight="24"
android:tint="?colorOnSurface" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M21,11l-6,-6v5H8c-2.76,0 -5,2.24 -5,5v4h2v-4c0,-1.65 1.35,-3 3,-3h7v5L21,11z"/>
</vector>

View file

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true">
<layer-list>
<item>
<shape>
<padding android:bottom="3dp" android:top="3dp" android:right="3dp" android:left="3dp"/>
<solid android:color="#0000"/>
</shape>
</item>
<item>
<shape>
<size android:width="18dp" android:height="18dp"/>
<corners android:radius="999dp"/>
<solid android:color="?colorPrimary"/>
</shape>
</item>
</layer-list>
</item>
<item>
<layer-list>
<item>
<shape>
<padding android:bottom="3dp" android:top="3dp" android:right="3dp" android:left="3dp"/>
<solid android:color="#0000"/>
</shape>
</item>
<item>
<shape android:tintMode="multiply" android:tint="#2FFF">
<size android:width="18dp" android:height="18dp"/>
<corners android:radius="999dp"/>
<solid android:color="?colorOnSurface"/>
</shape>
</item>
</layer-list>
</item>
</selector>

View file

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true">
<shape android:tintMode="multiply" android:tint="#5FFF">
<padding android:left="5dp" android:right="5dp" />
<solid android:color="?colorPrimary"/>
<corners android:radius="24dp"/>
<size android:width="32dp" android:height="22dp"/>
</shape>
</item>
<item>
<shape android:tintMode="multiply" android:tint="#2FFF">
<padding android:left="5dp" android:right="5dp" />
<solid android:color="?colorOnSurface"/>
<corners android:radius="24dp"/>
<size android:width="32dp" android:height="22dp"/>
</shape>
</item>
</selector>

View file

@ -92,15 +92,26 @@
android:gravity="end|center">
<com.google.android.material.button.MaterialButton
android:id="@+id/remove"
android:id="@+id/shortcut"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/remove"
app:icon="@drawable/ic_delete"
app:icon="@drawable/ic_shortcut"
app:iconTint="?colorOnSurfaceVariant"
android:textColor="?colorOnSurfaceVariant"
android:backgroundTint="?colorSurfaceVariant"
android:layout_marginHorizontal="10dp"/>
android:layout_marginHorizontal="10dp"
style="@style/Widget.Material3.Button.IconButton"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:icon="@drawable/ic_delete"
app:iconTint="?colorSurfaceVariant"
android:textColor="?colorOnSurfaceVariant"
android:backgroundTint="?colorOnSurfaceVariant"
android:layout_marginHorizontal="10dp"
style="@style/Widget.Material3.Button.IconButton"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/play"
@ -111,6 +122,7 @@
android:textColor="?colorOnPrimary"
android:backgroundTint="?colorPrimary"
app:icon="@drawable/ic_play"
app:rippleColor="?colorOnPrimary"
app:iconTint="?colorOnPrimary"/>
</LinearLayout>

View file

@ -31,6 +31,7 @@
android:textColor="?colorOnSurface"
android:text="@string/app_name"
android:textStyle="bold"
android:textSize="14sp"
android:gravity="center"
android:layout_marginTop="10dp"/>

View file

@ -53,6 +53,8 @@
<string name="pref_logger_service_summary">Grave os registros para um arquivo.</string>
<string name="pref_shader_jit_title">Shader Jit</string>
<string name="pref_shader_jit_summary">Usar recompilador de shaders.</string>
<string name="pref_picture_in_picture_title">Picture In Picture</string>
<string name="pref_picture_in_picture_summary">Entrar em modo picture in picture quando a janela sai de foco</string>
<string name="graphics">Gráficos</string>
<string name="loading">Carregando</string>
<string name="rotate">Rotacionar</string>
@ -67,12 +69,12 @@
<string name="bottom_display">Tela inferior</string>
<string name="top_display">Tela superior</string>
<string name="fix_aspect">Manter porporção</string>
<string name="dual_screen_layouts">Disposições para as duas telas.</string>
<string name="dual_screen_layouts">Disposição das telas</string>
<string name="dual_screen_layouts_summary">Altere as disposições disponiveis para as telas do console</string>
<string name="click_to_change">Clique para mudar</string>
<string name="swap_screen">Mudar telas</string>
<string name="pref_games_folders_summary">Pastas usadas para importar os jogos</string>
<string name="pref_games_folders">Pastas de jogos</string>
<string name="pref_game_folders_summary">Pastas usadas para importar os jogos</string>
<string name="pref_game_folders">Pastas de jogos</string>
<string name="import_folder">Adicionar pasta</string>
<string name="games_count_f">%d Jogos</string>
<string name="directory">Diretorio</string>
@ -85,4 +87,7 @@
<string name="region_australia">Australia</string>
<string name="region_korean">Coréia</string>
<string name="region_taiwan">Taiwan</string>
<string name="behavior">Comportamento</string>
<string name="invalid_game">Jogo invalido</string>
<string name="tools">Ferramentas</string>
</resources>

View file

@ -52,7 +52,6 @@
<!-- Advanced Settings -->
<string name="advanced_options">Advanced options</string>
<string name="pref_advanced_summary">Logger, performance statistics, etc.</string>
<string name="display">Display</string>
<string name="pref_performance_monitor_title">Performance monitor</string>
<string name="pref_performance_monitor_summary">Show overlay with fps, memory, etc.</string>
<string name="pref_picture_in_picture_title">Picture In Picture</string>
@ -60,7 +59,7 @@
<string name="graphics">Graphics</string>
<string name="pref_shader_jit_title">Shader JIT</string>
<string name="pref_shader_jit_summary">Use shader recompiler.</string>
<string name="debug">Debug</string>
<string name="tools">Tools</string>
<string name="pref_logger_service_title">Logger</string>
<string name="pref_logger_service_summary">Store application logs to file.</string>
<string name="loading">Loading</string>
@ -71,21 +70,23 @@
<string name="dialog_message_invalid_rom">Make sure it\'s a valid 3DS ROM and that storage permissions are configured properly.</string>
<string name="system">System</string>
<string name="general">General</string>
<string name="pref_general_summary">General application configurations.</string>
<string name="bottom_display">Bottom Display</string>
<string name="top_display">Top Display</string>
<string name="fix_aspect">Fix aspect</string>
<string name="dual_screen_layouts">Dual Screen layouts</string>
<string name="pref_general_summary">General application configuration.</string>
<string name="dual_screen_layouts">Screen layouts</string>
<string name="dual_screen_layouts_summary">Change layout of console screens.</string>
<string name="click_to_change">Click to change</string>
<string name="swap_screen">Swap screen</string>
<string name="pref_games_folders_summary">Folders for auto import games</string>
<string name="pref_games_folders">Games folders</string>
<string name="pref_game_folders_summary">Folders for importing games</string>
<string name="pref_game_folders">Game folders</string>
<string name="import_folder">Import folder</string>
<string name="games_count_f">%d Games</string>
<string name="directory">Directory</string>
<string name="remove">Remove</string>
<string name="play">Play</string>
<!-- Screen layout editor -->
<string name="fix_aspect">Maintain aspect ratio</string>
<string name="bottom_display">Bottom Display</string>
<string name="top_display">Top Display</string>
<!-- Game about -->
<string name="region">Region</string>
<string name="region_north_armerican">North American</string>
<string name="region_japan">Japan</string>
@ -93,4 +94,6 @@
<string name="region_australia">Australia</string>
<string name="region_korean">Korean</string>
<string name="region_taiwan">Taiwan</string>
<string name="behavior">Behavior</string>
<string name="invalid_game">Invalid game</string>
</resources>

View file

@ -3,9 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
app:iconSpaceReserved="false"
app:title="@string/display">
app:title="@string/tools"
app:iconSpaceReserved="false">
<SwitchPreferenceCompat
android:key="performanceMonitor"
app:title="@string/pref_performance_monitor_title"
@ -30,17 +29,4 @@
app:iconSpaceReserved="false"/>
</PreferenceCategory>
<PreferenceCategory
app:iconSpaceReserved="false"
app:title="@string/debug">
<SwitchPreferenceCompat
android:key="loggerService"
app:iconSpaceReserved="false"
app:title="@string/pref_logger_service_title"
android:defaultValue="true"
android:summaryOn="@string/pref_logger_service_summary"
android:summaryOff="@string/pref_logger_service_summary"/>
</PreferenceCategory>
</PreferenceScreen>

View file

@ -20,8 +20,17 @@
app:iconSpaceReserved="false">
<Preference
android:key="games.folders"
app:title="@string/pref_games_folders"
app:summary="@string/pref_games_folders_summary"
app:title="@string/pref_game_folders"
app:summary="@string/pref_game_folders_summary"
app:iconSpaceReserved="false"/>
</PreferenceCategory>
<PreferenceCategory
app:title="@string/behavior"
app:iconSpaceReserved="false">
<SwitchPreferenceCompat
android:key="behavior.pictureInPicture"
app:title="@string/pref_picture_in_picture_title"
app:summary="@string/pref_picture_in_picture_summary"
app:iconSpaceReserved="false"/>
</PreferenceCategory>
</PreferenceScreen>