mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-19 20:19:13 +12:00
basic implement, ds editor, and remake theme selector
This commit is contained in:
parent
b0be9f5e60
commit
dcda78de97
49 changed files with 1526 additions and 190 deletions
|
@ -1,9 +1,11 @@
|
|||
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.google.android.material.color.DynamicColors;
|
||||
import com.panda3ds.pandroid.data.config.GlobalConfig;
|
||||
|
||||
|
||||
|
@ -28,5 +30,8 @@ public class BaseActivity extends AppCompatActivity {
|
|||
private void applyTheme() {
|
||||
currentTheme = PandroidApplication.getThemeId();
|
||||
setTheme(currentTheme);
|
||||
if (GlobalConfig.get(GlobalConfig.KEY_APP_THEME) == GlobalConfig.THEME_ANDROID){
|
||||
DynamicColors.applyToActivityIfAvailable(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.panda3ds.pandroid.app;
|
|||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
@ -16,17 +17,22 @@ import com.panda3ds.pandroid.AlberDriver;
|
|||
import com.panda3ds.pandroid.R;
|
||||
import com.panda3ds.pandroid.app.game.AlberInputListener;
|
||||
import com.panda3ds.pandroid.app.game.DrawerFragment;
|
||||
import com.panda3ds.pandroid.app.game.EmulatorListener;
|
||||
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;
|
||||
import com.panda3ds.pandroid.view.ds.DsLayoutManager;
|
||||
import com.panda3ds.pandroid.view.renderer.ConsoleRenderer;
|
||||
import com.panda3ds.pandroid.view.utils.PerformanceView;
|
||||
|
||||
public class GameActivity extends BaseActivity {
|
||||
public class GameActivity extends BaseActivity implements EmulatorListener {
|
||||
private final DrawerFragment drawerFragment = new DrawerFragment();
|
||||
private final AlberInputListener inputListener = new AlberInputListener(this::onBackPressed);
|
||||
private final AlberInputListener inputListener = new AlberInputListener(this);
|
||||
private ConsoleRenderer renderer;
|
||||
private int currentDsLayout;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
|
@ -43,6 +49,8 @@ public class GameActivity extends BaseActivity {
|
|||
PandaGlSurfaceView pandaSurface = new PandaGlSurfaceView(this, intent.getStringExtra(Constants.ACTIVITY_PARAMETER_PATH));
|
||||
setContentView(R.layout.game_activity);
|
||||
|
||||
renderer = pandaSurface.getRenderer();
|
||||
|
||||
((FrameLayout) findViewById(R.id.panda_gl_frame))
|
||||
.addView(pandaSurface, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
|
@ -104,6 +112,12 @@ public class GameActivity extends BaseActivity {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchDualScreenLayout() {
|
||||
currentDsLayout = currentDsLayout + 1 < DsLayoutManager.getLayoutCount() ? currentDsLayout + 1 : 0;
|
||||
renderer.setLayout(DsLayoutManager.createLayout(currentDsLayout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchGenericMotionEvent(MotionEvent ev) {
|
||||
if ((!drawerFragment.isOpened()) && InputHandler.processMotionEvent(ev)) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.panda3ds.pandroid.app;
|
|||
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
@ -16,15 +17,25 @@ public class MainActivity extends BaseActivity implements NavigationBarView.OnIt
|
|||
private final GamesFragment gamesFragment = new GamesFragment();
|
||||
private final SearchFragment searchFragment = new SearchFragment();
|
||||
private final SettingsFragment settingsFragment = new SettingsFragment();
|
||||
private NavigationBarView navigationBar;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
NavigationBarView bar = findViewById(R.id.navigation);
|
||||
bar.setOnItemSelectedListener(this);
|
||||
bar.postDelayed(() -> bar.setSelectedItemId(bar.getSelectedItemId()), 5);
|
||||
navigationBar = findViewById(R.id.navigation);
|
||||
navigationBar.setOnItemSelectedListener(this);
|
||||
navigationBar.postDelayed(() -> navigationBar.setSelectedItemId(navigationBar.getSelectedItemId()), 5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (navigationBar.getSelectedItemId() != R.id.games){
|
||||
navigationBar.setSelectedItemId(R.id.games);
|
||||
return;
|
||||
}
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.appcompat.app.ActionBar;
|
|||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.panda3ds.pandroid.lang.Function;
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ import java.util.Objects;
|
|||
|
||||
|
||||
public class AlberInputListener implements Function<InputEvent> {
|
||||
private final Runnable backListener;
|
||||
public AlberInputListener(Runnable backListener) { this.backListener = backListener; }
|
||||
private final EmulatorListener emulator;
|
||||
public AlberInputListener(EmulatorListener emulator) { this.emulator = emulator; }
|
||||
|
||||
private final Vector2 axis = new Vector2(0.0f, 0.0f);
|
||||
|
||||
|
@ -21,7 +21,7 @@ public class AlberInputListener implements Function<InputEvent> {
|
|||
KeyName key = InputMap.relative(event.getName());
|
||||
|
||||
if (Objects.equals(event.getName(), "KEYCODE_BACK")) {
|
||||
backListener.run();
|
||||
emulator.onBackPressed();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,11 @@ public class AlberInputListener implements Function<InputEvent> {
|
|||
axis.x = event.getValue();
|
||||
axisChanged = true;
|
||||
break;
|
||||
case CHANGE_DS_LAYOUT:
|
||||
if (!event.isDown()){
|
||||
emulator.switchDualScreenLayout();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (event.isDown()) {
|
||||
AlberDriver.KeyDown(key.getKeyId());
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.panda3ds.pandroid.app.game;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
|
@ -42,13 +43,21 @@ public class DrawerFragment extends Fragment implements DrawerLayout.DrawerListe
|
|||
super.onViewCreated(view, savedInstanceState);
|
||||
drawerContainer.setVisibility(View.GONE);
|
||||
|
||||
GameMetadata game = GameUtils.getCurrentGame();
|
||||
((NavigationView)view.findViewById(R.id.menu)).setNavigationItemSelectedListener(this);
|
||||
refresh();
|
||||
}
|
||||
|
||||
((GameIconView)view.findViewById(R.id.game_icon)).setImageBitmap(game.getIcon());
|
||||
private void refresh(){
|
||||
GameMetadata game = GameUtils.getCurrentGame();
|
||||
View view = getView();
|
||||
if (game.getIcon() != null && !game.getIcon().isRecycled()) {
|
||||
((GameIconView) view.findViewById(R.id.game_icon)).setImageBitmap(game.getIcon());
|
||||
} else {
|
||||
((GameIconView) view.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());
|
||||
|
||||
((NavigationView)view.findViewById(R.id.menu)).setNavigationItemSelectedListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -72,6 +81,7 @@ public class DrawerFragment extends Fragment implements DrawerLayout.DrawerListe
|
|||
drawerContainer.setVisibility(View.VISIBLE);
|
||||
drawerContainer.open();
|
||||
drawerContainer.postDelayed(this::refreshLayout, 20);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package com.panda3ds.pandroid.app.game;
|
||||
|
||||
public interface EmulatorListener {
|
||||
void onBackPressed();
|
||||
|
||||
void switchDualScreenLayout();
|
||||
}
|
|
@ -1,13 +1,15 @@
|
|||
package com.panda3ds.pandroid.app.main;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.panda3ds.pandroid.R;
|
||||
import com.panda3ds.pandroid.app.PandroidApplication;
|
||||
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.GeneralPreferences;
|
||||
import com.panda3ds.pandroid.app.preferences.AdvancedPreferences;
|
||||
import com.panda3ds.pandroid.app.preferences.InputPreferences;
|
||||
|
||||
|
@ -15,8 +17,18 @@ public class SettingsFragment extends BasePreferenceFragment {
|
|||
@Override
|
||||
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
|
||||
setPreferencesFromResource(R.xml.start_preferences, rootKey);
|
||||
findPreference("application").setSummary(getVersionName());
|
||||
setItemClick("input", (item) -> PreferenceActivity.launch(requireContext(), InputPreferences.class));
|
||||
setItemClick("appearance", (item)-> PreferenceActivity.launch(requireContext(), AppearancePreferences.class));
|
||||
setItemClick("general", (item)-> PreferenceActivity.launch(requireContext(), GeneralPreferences.class));
|
||||
setItemClick("advanced", (item)-> PreferenceActivity.launch(requireContext(), AdvancedPreferences.class));
|
||||
}
|
||||
|
||||
private String getVersionName() {
|
||||
try {
|
||||
Context context = PandroidApplication.getAppContext();
|
||||
return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
|
||||
} catch (Exception e){
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
package com.panda3ds.pandroid.app.preferences;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.panda3ds.pandroid.R;
|
||||
import com.panda3ds.pandroid.app.BaseActivity;
|
||||
import com.panda3ds.pandroid.app.base.BasePreferenceFragment;
|
||||
import com.panda3ds.pandroid.data.config.GlobalConfig;
|
||||
import com.panda3ds.pandroid.view.preferences.SingleSelectionPreferences;
|
||||
|
||||
public class AppearancePreferences extends BasePreferenceFragment {
|
||||
@Override
|
||||
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
|
||||
setPreferencesFromResource(R.xml.appearance_preference, rootKey);
|
||||
|
||||
setActivityTitle(R.string.appearance);
|
||||
|
||||
SingleSelectionPreferences themePreference = findPreference("theme");
|
||||
themePreference.setSelectedItem(GlobalConfig.get(GlobalConfig.KEY_APP_THEME));
|
||||
themePreference.setOnPreferenceChangeListener((preference, value) -> {
|
||||
GlobalConfig.set(GlobalConfig.KEY_APP_THEME, (int) value);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.panda3ds.pandroid.app.preferences;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
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;
|
||||
|
||||
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));
|
||||
setActivityTitle(R.string.general);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package com.panda3ds.pandroid.app.preferences;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.AppCompatRadioButton;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.panda3ds.pandroid.R;
|
||||
import com.panda3ds.pandroid.data.config.GlobalConfig;
|
||||
import com.panda3ds.pandroid.view.recycler.AutoFitGridLayout;
|
||||
import com.panda3ds.pandroid.view.recycler.SimpleListAdapter;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ThemeSelectorDialog extends BottomSheetDialog {
|
||||
|
||||
private final SimpleListAdapter<Theme> adapter;
|
||||
private final int currentTheme = GlobalConfig.get(GlobalConfig.KEY_APP_THEME);
|
||||
private static final Theme[] themes = {
|
||||
new Theme(R.style.Theme_Pandroid, R.string.theme_device, GlobalConfig.THEME_ANDROID),
|
||||
new Theme(R.style.Theme_Pandroid_Light, R.string.light, GlobalConfig.THEME_LIGHT),
|
||||
new Theme(R.style.Theme_Pandroid_Dark, R.string.dark, GlobalConfig.THEME_DARK),
|
||||
new Theme(R.style.Theme_Pandroid_Black, R.string.black, GlobalConfig.THEME_BLACK)
|
||||
};
|
||||
|
||||
|
||||
public ThemeSelectorDialog(@NonNull Context context) {
|
||||
super(context);
|
||||
View content = LayoutInflater.from(context).inflate(R.layout.dialog_select_theme, null, false);
|
||||
setContentView(content);
|
||||
adapter = new SimpleListAdapter<>(R.layout.hold_theme_preview_summary, this::bindItemView);
|
||||
adapter.clear();
|
||||
ArrayList<Theme> themeList = new ArrayList<>(Arrays.asList(themes));
|
||||
themeList.sort((o1, o2) -> o1.id == currentTheme ? -1 : 0);
|
||||
adapter.addAll(themeList);
|
||||
|
||||
((RecyclerView) content.findViewById(R.id.recycler)).setAdapter(adapter);
|
||||
((RecyclerView) content.findViewById(R.id.recycler)).setLayoutManager(new AutoFitGridLayout(getContext(), 150));
|
||||
}
|
||||
|
||||
private void bindItemView(int i, Theme theme, View view) {
|
||||
ViewGroup container = view.findViewById(R.id.preview);
|
||||
container.removeAllViews();
|
||||
container.addView(LayoutInflater.from(new ContextThemeWrapper(getContext(), theme.style)).inflate(R.layout.hold_theme_preview, null, false));
|
||||
((TextView) view.findViewById(R.id.title)).setText(theme.name);
|
||||
((AppCompatRadioButton) view.findViewById(R.id.checkbox)).setChecked(GlobalConfig.get(GlobalConfig.KEY_APP_THEME) == theme.id);
|
||||
view.setOnClickListener(v -> {
|
||||
dismiss();
|
||||
if (theme.id != GlobalConfig.get(GlobalConfig.KEY_APP_THEME)) {
|
||||
GlobalConfig.set(GlobalConfig.KEY_APP_THEME, theme.id);
|
||||
((Activity) v.getContext()).recreate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static final class Theme {
|
||||
private final int style;
|
||||
private final int name;
|
||||
private final int id;
|
||||
|
||||
private Theme(int style, int name, int value) {
|
||||
this.style = style;
|
||||
this.name = name;
|
||||
this.id = value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.panda3ds.pandroid.app.preferences.ds;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.panda3ds.pandroid.R;
|
||||
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 {
|
||||
private LinearLayout layout;
|
||||
private DsEditorView editor;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
layout = new LinearLayout(container.getContext());
|
||||
return layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
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));
|
||||
((BaseActivity)requireActivity()).getSupportActionBar().hide();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
requireActivity().getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN|View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|View.SYSTEM_UI_FLAG_IMMERSIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
DsLayoutManager.save();
|
||||
Toast.makeText(requireActivity(), R.string.saved, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package com.panda3ds.pandroid.app.preferences.ds;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.panda3ds.pandroid.R;
|
||||
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 {
|
||||
@Override
|
||||
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
|
||||
setPreferencesFromResource(R.xml.empty_preferences, rootKey);
|
||||
setActivityTitle(R.string.dual_screen_layouts);
|
||||
refresh();
|
||||
}
|
||||
|
||||
public void refresh(){
|
||||
PreferenceScreen screen = getPreferenceScreen();
|
||||
screen.removeAll();
|
||||
for (int i = 0; i < DsLayoutManager.getLayoutCount(); i++){
|
||||
Preference pref = new Preference(getPreferenceScreen().getContext());
|
||||
pref.setIconSpaceReserved(false);
|
||||
pref.setTitle("Layout "+(i+1));
|
||||
pref.setSummary(R.string.click_to_change);
|
||||
pref.setIcon(R.drawable.ic_edit);
|
||||
pref.setKey(String.valueOf(i));
|
||||
|
||||
final int index = i;
|
||||
pref.setOnPreferenceClickListener(preference -> {
|
||||
PreferenceActivity.launch(requireContext(), DsEditorPreferences.class, new Intent().putExtra("index", index));
|
||||
return false;
|
||||
});
|
||||
screen.addPreference(pref);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package com.panda3ds.pandroid.data;
|
||||
|
||||
import com.google.gson.ExclusionStrategy;
|
||||
import com.google.gson.FieldAttributes;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.panda3ds.pandroid.lang.Task;
|
||||
|
@ -30,7 +32,10 @@ public class GsonConfigParser {
|
|||
String[] content = new String[] {"{}"};
|
||||
new Task(()->{
|
||||
if (FileUtils.exists(getPath())) {
|
||||
content[0] = FileUtils.readTextFile(getPath());
|
||||
String src = FileUtils.readTextFile(getPath());
|
||||
if(src != null && src.length() > 2){
|
||||
content[0] = src;
|
||||
}
|
||||
}
|
||||
}).runSync();
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.panda3ds.pandroid.data.config;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.internal.LinkedTreeMap;
|
||||
import com.panda3ds.pandroid.data.GsonConfigParser;
|
||||
import com.panda3ds.pandroid.utils.Constants;
|
||||
|
@ -10,6 +12,7 @@ import java.util.Map;
|
|||
public class GlobalConfig {
|
||||
|
||||
private static final GsonConfigParser parser = new GsonConfigParser(Constants.PREF_GLOBAL_CONFIG);
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
public static final int THEME_ANDROID = 0;
|
||||
public static final int THEME_LIGHT = 1;
|
||||
|
@ -23,6 +26,7 @@ public class GlobalConfig {
|
|||
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<String> KEY_DS_LAYOUTS = new Key<>("app.ds.layouts", "");
|
||||
|
||||
public static void initialize() {
|
||||
data = parser.load(DataModel.class);
|
||||
|
@ -54,6 +58,21 @@ public class GlobalConfig {
|
|||
writeChanges();
|
||||
}
|
||||
|
||||
public static <T extends Object> T getExtra(Key<String> key, Class<T> dataClass){
|
||||
if (data.extras.has(key.name)){
|
||||
return gson.fromJson(data.extras.getAsJsonObject(key.name), dataClass);
|
||||
}
|
||||
return gson.fromJson("{}", dataClass);
|
||||
}
|
||||
|
||||
public static synchronized void putExtra(Key<String> key, Object value){
|
||||
if (data.extras.has(key.name)){
|
||||
data.extras.remove(key.name);
|
||||
}
|
||||
data.extras.add(key.name, gson.toJsonTree(value));
|
||||
writeChanges();
|
||||
}
|
||||
|
||||
private static void writeChanges() {
|
||||
parser.save(data);
|
||||
}
|
||||
|
@ -70,6 +89,7 @@ public class GlobalConfig {
|
|||
|
||||
private static class DataModel {
|
||||
private final Map<String, Object> configs = new LinkedTreeMap<>();
|
||||
private final JsonObject extras = new JsonObject();
|
||||
|
||||
public Object get(String key) {
|
||||
return configs.get(key);
|
||||
|
|
|
@ -25,6 +25,7 @@ public class InputHandler {
|
|||
};
|
||||
|
||||
private static final HashMap<String, Float> motionDownEvents = new HashMap<>();
|
||||
private static final HashMap<String, InputEvent> keyDownEvents = new HashMap<>();
|
||||
|
||||
private static boolean containsSource(int[] sources, int sourceMask) {
|
||||
for (int source : sources) {
|
||||
|
@ -108,8 +109,17 @@ public class InputHandler {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
String code = KeyEvent.keyCodeToString(event.getKeyCode());
|
||||
if (event.getAction() == KeyEvent.ACTION_UP){
|
||||
keyDownEvents.remove(code);
|
||||
handleEvent(new InputEvent(code, 0.0f));
|
||||
} else if (!keyDownEvents.containsKey(code)){
|
||||
keyDownEvents.put(code, new InputEvent(code, 1.0f));
|
||||
}
|
||||
for (InputEvent env: keyDownEvents.values()){
|
||||
handleEvent(env);
|
||||
}
|
||||
|
||||
handleEvent(new InputEvent(KeyEvent.keyCodeToString(event.getKeyCode()), event.getAction() == KeyEvent.ACTION_UP ? 0.0f : 1.0f));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -117,5 +127,6 @@ public class InputHandler {
|
|||
eventListener = null;
|
||||
motionDeadZone = 0.0f;
|
||||
motionDownEvents.clear();
|
||||
keyDownEvents.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ public enum KeyName {
|
|||
SELECT(Constants.INPUT_KEY_SELECT),
|
||||
L(Constants.INPUT_KEY_L),
|
||||
R(Constants.INPUT_KEY_R),
|
||||
NULL;
|
||||
NULL,
|
||||
CHANGE_DS_LAYOUT;
|
||||
|
||||
private final int keyId;
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package com.panda3ds.pandroid.math;
|
||||
|
||||
public class Shape {
|
||||
public int x = 0, y = 0, width = 1, height = 1;
|
||||
|
||||
public Shape() {
|
||||
}
|
||||
|
||||
public Shape(int x, int y, int width, int height) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public void move(int x, int y) {
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
}
|
||||
|
||||
public void normalize() {
|
||||
this.x = Math.max(x, 0);
|
||||
this.y = Math.max(y, 0);
|
||||
this.width = Math.max(width, 1);
|
||||
this.height = Math.max(height, 1);
|
||||
}
|
||||
|
||||
public void maxSize(int w, int h) {
|
||||
this.width = Math.max(w, this.width);
|
||||
this.height = Math.max(h, this.height);
|
||||
}
|
||||
}
|
|
@ -2,24 +2,26 @@ package com.panda3ds.pandroid.view;
|
|||
|
||||
import static android.opengl.GLES32.*;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.panda3ds.pandroid.AlberDriver;
|
||||
import com.panda3ds.pandroid.R;
|
||||
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.Constants;
|
||||
import com.panda3ds.pandroid.utils.GameUtils;
|
||||
import com.panda3ds.pandroid.utils.PerformanceMonitor;
|
||||
import com.panda3ds.pandroid.view.ds.DsLayoutManager;
|
||||
import com.panda3ds.pandroid.view.renderer.ConsoleRenderer;
|
||||
import com.panda3ds.pandroid.view.renderer.layout.ConsoleLayout;
|
||||
import com.panda3ds.pandroid.view.renderer.layout.DefaultScreenLayout;
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
|
@ -38,7 +40,7 @@ public class PandaGlRenderer implements GLSurfaceView.Renderer, ConsoleRenderer
|
|||
|
||||
screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
|
||||
screenHeight = Resources.getSystem().getDisplayMetrics().heightPixels;
|
||||
setLayout(new DefaultScreenLayout());
|
||||
setLayout(DsLayoutManager.createLayout(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -74,9 +76,9 @@ public class PandaGlRenderer implements GLSurfaceView.Renderer, ConsoleRenderer
|
|||
glGenTextures(1, generateBuffer, 0);
|
||||
screenTexture = generateBuffer[0];
|
||||
glBindTexture(GL_TEXTURE_2D, screenTexture);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, screenWidth, screenHeight);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, Constants.N3DS_WIDTH, Constants.N3DS_FULL_HEIGHT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST_MIPMAP_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glGenFramebuffers(1, generateBuffer, 0);
|
||||
|
@ -95,19 +97,17 @@ public class PandaGlRenderer implements GLSurfaceView.Renderer, ConsoleRenderer
|
|||
if (!AlberDriver.LoadRom(romPath)) {
|
||||
// Get a handler that can be used to post to the main thread
|
||||
Handler mainHandler = new Handler(context.getMainLooper());
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setTitle("Failed to load ROM")
|
||||
.setMessage("Make sure it's a valid 3DS ROM and that storage permissions are configured properly.")
|
||||
.setPositiveButton("OK", null)
|
||||
mainHandler.post(()-> {
|
||||
new BottomAlertDialog(context)
|
||||
.setTitle(R.string.failed_load_rom)
|
||||
.setMessage(R.string.dialog_message_invalid_rom)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, witch) -> {
|
||||
dialog.dismiss();
|
||||
((Activity) context).finish();
|
||||
})
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
}
|
||||
};
|
||||
mainHandler.post(runnable);
|
||||
});
|
||||
|
||||
GameMetadata game = GameUtils.getCurrentGame();
|
||||
GameUtils.removeGame(game);
|
||||
|
@ -131,6 +131,9 @@ public class PandaGlRenderer implements GLSurfaceView.Renderer, ConsoleRenderer
|
|||
}
|
||||
|
||||
public void onDrawFrame(GL10 unused) {
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (AlberDriver.HasRomLoaded()) {
|
||||
AlberDriver.RunFrame(screenFbo);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
|
|
|
@ -0,0 +1,397 @@
|
|||
package com.panda3ds.pandroid.view.ds;
|
||||
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.DashPathEffect;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatSpinner;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
|
||||
import com.google.android.material.checkbox.MaterialCheckBox;
|
||||
import com.panda3ds.pandroid.R;
|
||||
import com.panda3ds.pandroid.math.Shape;
|
||||
import com.panda3ds.pandroid.math.Vector2;
|
||||
import com.panda3ds.pandroid.utils.Constants;
|
||||
|
||||
public class DsEditorView extends FrameLayout {
|
||||
|
||||
private static final int COLOR_TOP_SELECTION = Color.RED;
|
||||
private static final int COLOR_BOTTOM_SELECTION = Color.BLUE;
|
||||
private final float SIZE_DP;
|
||||
|
||||
private final Paint selectionPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final DsLayout layout;
|
||||
private int width = 1, height = 1;
|
||||
private final LinearLayout gravityAnchor;
|
||||
private final LinearLayout aspectRatioFixLayout;
|
||||
private final LinearLayout modeSelectorLayout;
|
||||
private final AppCompatSpinner modeSelector;
|
||||
private final PointView spacePoint;
|
||||
private final PointView topDisplay;
|
||||
private final PointView bottomDisplay;
|
||||
private final PointView topDisplayResizer;
|
||||
private final PointView bottomDisplayResizer;
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
public DsEditorView(Context context, int index) {
|
||||
super(context);
|
||||
layout = (DsLayout) DsLayoutManager.createLayout(index);
|
||||
|
||||
SIZE_DP = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
|
||||
|
||||
selectionPaint.setColor(COLOR_TOP_SELECTION);
|
||||
selectionPaint.setStrokeWidth(SIZE_DP * 2);
|
||||
selectionPaint.setPathEffect(new DashPathEffect(new float[]{SIZE_DP * 10, SIZE_DP * 10}, 0.0f));
|
||||
selectionPaint.setStyle(Paint.Style.STROKE);
|
||||
|
||||
layout.setTopDisplaySourceSize(Constants.N3DS_WIDTH, Constants.N3DS_HALF_HEIGHT);
|
||||
layout.setBottomDisplaySourceSize(Constants.N3DS_WIDTH - 40 - 40, Constants.N3DS_HALF_HEIGHT);
|
||||
setBackgroundColor(Color.argb(2, 0, 0, 0));
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
|
||||
gravityAnchor = (LinearLayout) inflater.inflate(R.layout.ds_editor_gravity_anchor, this, false);
|
||||
gravityAnchor.findViewById(R.id.up).setOnClickListener(v -> {
|
||||
layout.getCurrentModel().gravity = Gravity.TOP;
|
||||
refreshLayout();
|
||||
});
|
||||
gravityAnchor.findViewById(R.id.center).setOnClickListener(v -> {
|
||||
layout.getCurrentModel().gravity = Gravity.CENTER;
|
||||
refreshLayout();
|
||||
});
|
||||
gravityAnchor.findViewById(R.id.down).setOnClickListener(v -> {
|
||||
layout.getCurrentModel().gravity = Gravity.BOTTOM;
|
||||
refreshLayout();
|
||||
});
|
||||
gravityAnchor.findViewById(R.id.revert).setOnClickListener(v -> {
|
||||
layout.getCurrentModel().reverse = !layout.getCurrentModel().reverse;
|
||||
refreshLayout();
|
||||
});
|
||||
|
||||
{
|
||||
modeSelectorLayout = (LinearLayout) inflater.inflate(R.layout.ds_editor_spinner, this, false);
|
||||
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(getContext(), R.layout.ds_editor_spinner_label);
|
||||
spinnerAdapter.addAll("SINGLE", "RELATIVE", "ABSOLUTE");
|
||||
modeSelector = modeSelectorLayout.findViewById(R.id.spinner);
|
||||
modeSelector.setAdapter(spinnerAdapter);
|
||||
modeSelector.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
layout.getCurrentModel().mode = Mode.values()[position];
|
||||
refreshLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
aspectRatioFixLayout = (LinearLayout) inflater.inflate(R.layout.ds_editor_lock_aspect, this, false);
|
||||
((MaterialCheckBox) aspectRatioFixLayout.findViewById(R.id.checkbox)).setOnCheckedChangeListener((buttonView, checked) -> {
|
||||
layout.getCurrentModel().lockAspect = checked;
|
||||
if (checked) {
|
||||
fixAspect();
|
||||
}
|
||||
refreshPoints();
|
||||
});
|
||||
}
|
||||
|
||||
spacePoint = new PointView();
|
||||
spacePoint.setColor(Color.WHITE, COLOR_TOP_SELECTION);
|
||||
spacePoint.setOnTouchListener((view, motion) -> {
|
||||
layout.getCurrentModel().space = (motion.getX() + spacePoint.x()) / (float) width;
|
||||
refreshPoints();
|
||||
return true;
|
||||
});
|
||||
|
||||
spacePoint.setLayoutGravity(Gravity.START | Gravity.CENTER);
|
||||
|
||||
setOnClickListener(v -> {
|
||||
if (layout.getCurrentModel().mode == Mode.SINGLE) {
|
||||
layout.getCurrentModel().singleTop = !layout.getCurrentModel().singleTop;
|
||||
refreshPoints();
|
||||
}
|
||||
});
|
||||
|
||||
topDisplay = new PointView();
|
||||
topDisplay.setText(R.string.top_display);
|
||||
topDisplay.setOnTouchListener(new DisplayTouchEvent(true));
|
||||
topDisplay.setBackground(new SelectionDrawable(COLOR_TOP_SELECTION));
|
||||
|
||||
bottomDisplay = new PointView();
|
||||
bottomDisplay.setText(R.string.bottom_display);
|
||||
bottomDisplay.setOnTouchListener(new DisplayTouchEvent(false));
|
||||
bottomDisplay.setBackground(new SelectionDrawable(COLOR_BOTTOM_SELECTION));
|
||||
|
||||
topDisplayResizer = new PointView();
|
||||
topDisplayResizer.setColor(0, COLOR_TOP_SELECTION);
|
||||
topDisplayResizer.setOnTouchListener(new DisplayResizeTouchEvent(true));
|
||||
|
||||
bottomDisplayResizer = new PointView();
|
||||
bottomDisplayResizer.setColor(0, COLOR_BOTTOM_SELECTION);
|
||||
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;
|
||||
refreshLayout();
|
||||
}
|
||||
}
|
||||
|
||||
private void fixAspect() {
|
||||
Shape top = layout.getCurrentModel().preferredTop;
|
||||
Shape bottom = layout.getCurrentModel().preferredBottom;
|
||||
|
||||
top.height = (int) (((float) top.width / Constants.N3DS_WIDTH) * Constants.N3DS_HALF_HEIGHT);
|
||||
bottom.height = (int) (((float) bottom.width / (Constants.N3DS_WIDTH - 80)) * Constants.N3DS_HALF_HEIGHT);
|
||||
}
|
||||
|
||||
private void refreshPoints() {
|
||||
Model data = layout.getCurrentModel();
|
||||
layout.update(width, height);
|
||||
Rect bottomDisplay = layout.getBottomDisplayBounds();
|
||||
Rect topDisplay = layout.getTopDisplayBounds();
|
||||
|
||||
switch (data.mode) {
|
||||
case RELATIVE: {
|
||||
if (width > height) {
|
||||
Rect primaryDisplay = data.reverse ? bottomDisplay : topDisplay;
|
||||
data.space = primaryDisplay.width() / (float) width;
|
||||
spacePoint.setCenterPosition(primaryDisplay.width(), (int) (SIZE_DP * 15));
|
||||
spacePoint.setText(String.valueOf((int) (data.space * 100)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SINGLE:
|
||||
case ABSOLUTE: {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
data.preferredTop.maxSize((int) (SIZE_DP * 64), (int) (SIZE_DP * 64));
|
||||
data.preferredBottom.maxSize((int) (SIZE_DP * 64), (int) (SIZE_DP * 64));
|
||||
|
||||
this.topDisplay.setSize(topDisplay.width(), topDisplay.height());
|
||||
this.topDisplay.setPosition(topDisplay.left, topDisplay.top);
|
||||
|
||||
this.bottomDisplay.setSize(bottomDisplay.width(), bottomDisplay.height());
|
||||
this.bottomDisplay.setPosition(bottomDisplay.left, bottomDisplay.top);
|
||||
|
||||
if (data.lockAspect) {
|
||||
topDisplayResizer.setCenterPosition(topDisplay.right, topDisplay.top + (topDisplay.height() / 2));
|
||||
bottomDisplayResizer.setCenterPosition(bottomDisplay.right, bottomDisplay.top + (bottomDisplay.height() / 2));
|
||||
} else {
|
||||
topDisplayResizer.setCenterPosition(topDisplay.right, topDisplay.bottom);
|
||||
bottomDisplayResizer.setCenterPosition(bottomDisplay.right, bottomDisplay.bottom);
|
||||
}
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void refreshLayout() {
|
||||
removeAllViews();
|
||||
layout.update(width, height);
|
||||
boolean landscape = width > height;
|
||||
addView(topDisplay);
|
||||
addView(bottomDisplay);
|
||||
|
||||
gravityAnchor.setOrientation(LinearLayout.HORIZONTAL);
|
||||
addView(modeSelectorLayout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.BOTTOM | Gravity.CENTER));
|
||||
switch (layout.getCurrentModel().mode) {
|
||||
case RELATIVE: {
|
||||
addView(gravityAnchor, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.CENTER | Gravity.TOP));
|
||||
if (landscape) {
|
||||
addView(spacePoint);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ABSOLUTE: {
|
||||
addView(aspectRatioFixLayout, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.CENTER | Gravity.TOP));
|
||||
addView(topDisplayResizer);
|
||||
addView(bottomDisplayResizer);
|
||||
}
|
||||
break;
|
||||
case SINGLE: {
|
||||
addView(aspectRatioFixLayout, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.CENTER | Gravity.TOP));
|
||||
}
|
||||
break;
|
||||
}
|
||||
((MaterialCheckBox) aspectRatioFixLayout.findViewById(R.id.checkbox)).setChecked(layout.getCurrentModel().lockAspect);
|
||||
|
||||
modeSelector.setSelection(layout.getCurrentModel().mode.ordinal());
|
||||
gravityAnchor.findViewById(R.id.revert).setRotation(landscape ? 0 : 90);
|
||||
refreshPoints();
|
||||
}
|
||||
|
||||
private class PointView extends AppCompatTextView {
|
||||
|
||||
public PointView() {
|
||||
super(DsEditorView.this.getContext());
|
||||
setLayoutParams(new FrameLayout.LayoutParams((int) (SIZE_DP * 30), (int) (SIZE_DP * 30)));
|
||||
setBackgroundResource(R.drawable.medium_card_background);
|
||||
setGravity(Gravity.CENTER);
|
||||
this.setFocusable(true);
|
||||
this.setClickable(true);
|
||||
}
|
||||
|
||||
public int x() {
|
||||
return ((LayoutParams) getLayoutParams()).leftMargin;
|
||||
}
|
||||
|
||||
public int y() {
|
||||
return ((LayoutParams) getLayoutParams()).topMargin;
|
||||
}
|
||||
|
||||
public int width() {
|
||||
return ((LayoutParams) getLayoutParams()).width;
|
||||
}
|
||||
|
||||
public void setColor(int text, int background) {
|
||||
setTextColor(text);
|
||||
setBackgroundTintList(ColorStateList.valueOf(background));
|
||||
}
|
||||
|
||||
public void setSize(int width, int height) {
|
||||
LayoutParams params = (LayoutParams) getLayoutParams();
|
||||
params.width = Math.max(0, width);
|
||||
params.height = Math.max(0, height);
|
||||
setLayoutParams(params);
|
||||
}
|
||||
|
||||
public void setPosition(int x, int y) {
|
||||
LayoutParams params = (LayoutParams) getLayoutParams();
|
||||
params.leftMargin = x;
|
||||
params.topMargin = y;
|
||||
setLayoutParams(params);
|
||||
}
|
||||
|
||||
public void setCenterPosition(int x, int y) {
|
||||
int middle = this.width() / 2;
|
||||
setPosition(Math.max(-middle, Math.min(x - middle, width - middle)), Math.max(-middle, Math.min(y - middle, height - middle)));
|
||||
}
|
||||
|
||||
public void setLayoutGravity(int gravity) {
|
||||
FrameLayout.LayoutParams params = (LayoutParams) getLayoutParams();
|
||||
params.gravity = gravity;
|
||||
setLayoutParams(params);
|
||||
}
|
||||
}
|
||||
|
||||
private class DisplayTouchEvent implements OnTouchListener {
|
||||
private final boolean topScreen;
|
||||
private final Vector2 inner = new Vector2(0, 0);
|
||||
private Vector2 downEvent = null;
|
||||
|
||||
private DisplayTouchEvent(boolean topScreen) {
|
||||
this.topScreen = topScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
Shape preferred = topScreen ? layout.getCurrentModel().preferredTop : layout.getCurrentModel().preferredBottom;
|
||||
if (layout.getCurrentModel().mode == Mode.ABSOLUTE) {
|
||||
PointView point = (PointView) v;
|
||||
if (event.getAction() != MotionEvent.ACTION_UP) {
|
||||
if (downEvent == null) {
|
||||
downEvent = new Vector2(event.getRawX(), event.getRawY());
|
||||
inner.set(point.x(), point.y());
|
||||
return true;
|
||||
}
|
||||
|
||||
preferred.x = (int) ((event.getRawX() - downEvent.x) + inner.x);
|
||||
preferred.y = (int) ((event.getRawY() - downEvent.y) + inner.y);
|
||||
preferred.normalize();
|
||||
|
||||
refreshPoints();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
downEvent = null;
|
||||
return false;
|
||||
} else if (layout.getCurrentModel().mode == Mode.SINGLE && event.getAction() == MotionEvent.ACTION_UP) {
|
||||
callOnClick();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class DisplayResizeTouchEvent implements OnTouchListener {
|
||||
private final boolean topScreen;
|
||||
private final Vector2 size = new Vector2(0, 0);
|
||||
private Vector2 downEvent = null;
|
||||
|
||||
private DisplayResizeTouchEvent(boolean topScreen) {
|
||||
this.topScreen = topScreen;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
Shape preferred = topScreen ? layout.getCurrentModel().preferredTop : layout.getCurrentModel().preferredBottom;
|
||||
if (event.getAction() != MotionEvent.ACTION_UP) {
|
||||
if (downEvent == null) {
|
||||
downEvent = new Vector2(event.getRawX(), event.getRawY());
|
||||
size.set(preferred.width, preferred.height);
|
||||
return true;
|
||||
}
|
||||
|
||||
preferred.width = (int) (size.x + ((event.getRawX() - downEvent.x)));
|
||||
|
||||
if (layout.getCurrentModel().lockAspect) {
|
||||
fixAspect();
|
||||
} else {
|
||||
preferred.height = (int) (size.y + ((event.getRawY() - downEvent.y)));
|
||||
}
|
||||
preferred.maxSize((int) (SIZE_DP * 32), (int) (SIZE_DP * 32));
|
||||
refreshPoints();
|
||||
return true;
|
||||
}
|
||||
downEvent = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class SelectionDrawable extends ColorDrawable {
|
||||
private final Paint solidPaint = new Paint();
|
||||
|
||||
public SelectionDrawable(int color) {
|
||||
super(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
int color = this.getColor();
|
||||
selectionPaint.setColor(color);
|
||||
solidPaint.setColor(Color.argb(50, Color.red(color), Color.green(color), Color.blue(color)));
|
||||
canvas.drawRect(this.getBounds(), solidPaint);
|
||||
canvas.drawRect(this.getBounds(), selectionPaint);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
package com.panda3ds.pandroid.view.ds;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.Gravity;
|
||||
|
||||
import com.panda3ds.pandroid.math.Shape;
|
||||
import com.panda3ds.pandroid.math.Vector2;
|
||||
import com.panda3ds.pandroid.view.renderer.layout.ConsoleLayout;
|
||||
|
||||
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 Model[] modes = new Model[2];
|
||||
|
||||
public DsLayout(Model landscape, Model portrait){
|
||||
modes[0] = landscape;
|
||||
modes[1] = portrait;
|
||||
}
|
||||
|
||||
public DsLayout(){
|
||||
this(new Model(), new Model());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(int screenWidth, int screenHeight) {
|
||||
screenSize.set(screenWidth, screenHeight);
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBottomDisplaySourceSize(int width, int height) {
|
||||
sourceBottom.set(width, height);
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTopDisplaySourceSize(int width, int height) {
|
||||
sourceTop.set(width, height);
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getBottomDisplayBounds() {
|
||||
return bottomDisplay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getTopDisplayBounds() {
|
||||
return topDisplay;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
Model data = getCurrentModel();
|
||||
Mode mode = data.mode;
|
||||
switch (mode){
|
||||
case RELATIVE:
|
||||
relative(data);
|
||||
break;
|
||||
case SINGLE:
|
||||
single(data);
|
||||
break;
|
||||
case ABSOLUTE:
|
||||
absolute(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void absolute(Model data) {
|
||||
Shape top = data.preferredTop;
|
||||
Shape bottom = data.preferredBottom;
|
||||
|
||||
top.normalize();
|
||||
bottom.normalize();
|
||||
|
||||
topDisplay.set(top.x, top.y, top.x +top.width, top.y + top.height);
|
||||
bottomDisplay.set(bottom.x, bottom.y, bottom.x +bottom.width, bottom.y + bottom.height);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
if (data.lockAspect) {
|
||||
int x = 0, y = 0;
|
||||
int width = (int) ((screenSize.y / source.y) * source.x);
|
||||
int height;
|
||||
|
||||
if (width > screenSize.x) {
|
||||
height = (int) ((screenSize.x / source.x) * source.y);
|
||||
width = (int) screenSize.x;
|
||||
y = (int) ((screenSize.y - height) / 2);
|
||||
} else {
|
||||
height = (int) screenSize.y;
|
||||
x = (int) ((screenSize.x - width) / 2);
|
||||
}
|
||||
dest.set(x,y,x+width,y+height);
|
||||
} else {
|
||||
dest.set(0,0, (int) screenSize.x, (int) screenSize.y);
|
||||
}
|
||||
(data.singleTop ? bottomDisplay : topDisplay).set(0,0,0,0);
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* RELATIVE LAYOUT:
|
||||
* ORGANIZE SCREEN IN POSITION BASED IN GRAVITY
|
||||
* AND SPACE, THE SPACE DETERMINE LANDSCAPE TOP SCREEN SIZE
|
||||
*/
|
||||
private void relative(Model data) {
|
||||
int screenWidth = (int) screenSize.x;
|
||||
int screenHeight = (int) screenSize.y;
|
||||
|
||||
Vector2 topSourceSize = this.sourceTop;
|
||||
Vector2 bottomSourceSize = this.sourceBottom;
|
||||
|
||||
Rect topDisplay = this.topDisplay;
|
||||
Rect bottomDisplay = this.bottomDisplay;
|
||||
|
||||
if (data.reverse){
|
||||
topSourceSize = this.sourceBottom;
|
||||
bottomSourceSize = this.sourceTop;
|
||||
|
||||
topDisplay = this.bottomDisplay;
|
||||
bottomDisplay = this.topDisplay;
|
||||
}
|
||||
|
||||
if (screenWidth > screenHeight) {
|
||||
int topDisplayWidth = (int) ((screenHeight / topSourceSize.y) * topSourceSize.x);
|
||||
int topDisplayHeight = screenHeight;
|
||||
|
||||
if (topDisplayWidth > (screenWidth * data.space)) {
|
||||
topDisplayWidth = (int) (screenWidth * data.space);
|
||||
topDisplayHeight = (int) ((topDisplayWidth / topSourceSize.x) * topSourceSize.y);
|
||||
}
|
||||
|
||||
int bottomDisplayHeight = (int) (((screenWidth - topDisplayWidth) / bottomSourceSize.x) * bottomSourceSize.y);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} else {
|
||||
int topScreenHeight = (int) ((screenWidth / topSourceSize.x) * topSourceSize.y);
|
||||
topDisplay.set(0, 0, screenWidth, topScreenHeight);
|
||||
|
||||
int bottomDisplayHeight = (int) ((screenWidth / bottomSourceSize.x) * bottomSourceSize.y);
|
||||
int bottomDisplayWidth = screenWidth;
|
||||
int bottomDisplayX = 0;
|
||||
|
||||
if (topScreenHeight + bottomDisplayHeight > screenHeight) {
|
||||
bottomDisplayHeight = (screenHeight - topScreenHeight);
|
||||
bottomDisplayWidth = (int) ((bottomDisplayHeight / bottomSourceSize.y) * bottomSourceSize.x);
|
||||
bottomDisplayX = (screenWidth - bottomDisplayX) / 2;
|
||||
}
|
||||
|
||||
topDisplay.set(0, 0, screenWidth, topScreenHeight);
|
||||
bottomDisplay.set(bottomDisplayX, topScreenHeight, bottomDisplayX + bottomDisplayWidth, topScreenHeight + bottomDisplayHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public Model getCurrentModel() {
|
||||
return screenSize.x > screenSize.y ? modes[0] : modes[1];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.panda3ds.pandroid.view.ds;
|
||||
|
||||
import com.panda3ds.pandroid.data.config.GlobalConfig;
|
||||
import com.panda3ds.pandroid.view.renderer.layout.ConsoleLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DsLayoutManager {
|
||||
private static final DataModel data;
|
||||
|
||||
static {
|
||||
data = GlobalConfig.getExtra(GlobalConfig.KEY_DS_LAYOUTS, DataModel.class);
|
||||
if (data.models.size() == 0){
|
||||
setupBasicModels();
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupBasicModels() {
|
||||
Model model1 = new Model();
|
||||
|
||||
Model model2 = new Model();
|
||||
model2.mode = Mode.SINGLE;
|
||||
model2.singleTop = false;
|
||||
|
||||
Model model3 = new Model();
|
||||
model3.mode = Mode.SINGLE;
|
||||
model3.singleTop = true;
|
||||
|
||||
data.models.add(new Model[]{model1, model1.clone()});
|
||||
data.models.add(new Model[]{model2, model2.clone()});
|
||||
data.models.add(new Model[]{model3, model3.clone()});
|
||||
|
||||
save();
|
||||
}
|
||||
|
||||
public static synchronized void save(){
|
||||
GlobalConfig.putExtra(GlobalConfig.KEY_DS_LAYOUTS, data);
|
||||
}
|
||||
|
||||
public static int getLayoutCount(){
|
||||
return data.models.size();
|
||||
}
|
||||
|
||||
public static ConsoleLayout createLayout(int index){
|
||||
index = Math.min(getLayoutCount()-1, index);
|
||||
return new DsLayout(data.models.get(index)[0],data.models.get(index)[1]);
|
||||
}
|
||||
|
||||
private static class DataModel {
|
||||
private final ArrayList<Model[]> models = new ArrayList<>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.panda3ds.pandroid.view.ds;
|
||||
|
||||
enum Mode {
|
||||
SINGLE,
|
||||
RELATIVE,
|
||||
ABSOLUTE
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.panda3ds.pandroid.view.ds;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.panda3ds.pandroid.math.Shape;
|
||||
import com.panda3ds.pandroid.utils.Constants;
|
||||
|
||||
class Model implements Cloneable {
|
||||
public Mode mode = Mode.RELATIVE;
|
||||
public final Shape preferredTop = new Shape();
|
||||
public final Shape preferredBottom = new Shape();
|
||||
public boolean reverse = false;
|
||||
public boolean singleTop = true;
|
||||
public float space = 0.6f;
|
||||
public int gravity = Gravity.CENTER;
|
||||
public boolean lockAspect = true;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Model clone() {
|
||||
try {
|
||||
return (Model) super.clone();
|
||||
} catch (Exception e){
|
||||
Log.e(Constants.LOG_TAG, "Error on clone DsModel!", e);
|
||||
return new Model();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
package com.panda3ds.pandroid.view.preferences;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
|
||||
import com.panda3ds.pandroid.R;
|
||||
import com.panda3ds.pandroid.utils.Constants;
|
||||
|
||||
public class SingleSelectionPreferences extends PreferenceCategory implements Preference.OnPreferenceClickListener {
|
||||
private final Drawable transparent = new ColorDrawable(Color.TRANSPARENT);
|
||||
private final Drawable doneDrawable = ContextCompat.getDrawable(getContext(), R.drawable.ic_done);
|
||||
|
||||
public SingleSelectionPreferences(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SingleSelectionPreferences(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SingleSelectionPreferences(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public SingleSelectionPreferences(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
{
|
||||
try {
|
||||
TypedArray color = getContext().obtainStyledAttributes(new int[]{
|
||||
android.R.attr.textColorSecondary
|
||||
});
|
||||
doneDrawable.setTint(color.getColor(0, Color.RED));
|
||||
color.recycle();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
color.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(Constants.LOG_TAG, "Error on obtain text color secondary: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttached() {
|
||||
super.onAttached();
|
||||
|
||||
for (int i = 0; i < getPreferenceCount();i++) {
|
||||
getPreference(i).setOnPreferenceClickListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSelectedItem(int index) {
|
||||
onPreferenceClick(getPreference(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(@NonNull Preference preference) {
|
||||
int index = 0;
|
||||
|
||||
for (int i = 0; i < getPreferenceCount(); i++) {
|
||||
Preference item = getPreference(i);
|
||||
if (item == preference) {
|
||||
index = i;
|
||||
item.setIcon(R.drawable.ic_done);
|
||||
} else {
|
||||
item.setIcon(transparent);
|
||||
}
|
||||
}
|
||||
|
||||
callChangeListener(index);
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
package com.panda3ds.pandroid.view.renderer.layout;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.Gravity;
|
||||
|
||||
import com.panda3ds.pandroid.math.Vector2;
|
||||
|
||||
public class DefaultScreenLayout implements ConsoleLayout {
|
||||
public class RelativeScreenLayout implements ConsoleLayout {
|
||||
private final Rect topDisplay = new Rect();
|
||||
private final Rect bottomDisplay = new Rect();
|
||||
|
||||
|
@ -12,6 +13,12 @@ public class DefaultScreenLayout implements ConsoleLayout {
|
|||
private final Vector2 topSourceSize = new Vector2(1.0f, 1.0f);
|
||||
private final Vector2 bottomSourceSize = new Vector2(1.0f, 1.0f);
|
||||
|
||||
private boolean landscapeReverse = false;
|
||||
private boolean portraitReverse = false;
|
||||
private float landscapeSpace = 0.6f;
|
||||
private int landscapeGravity = Gravity.CENTER;
|
||||
private int portraitGravity = Gravity.TOP;
|
||||
|
||||
@Override
|
||||
public void update(int screenWidth, int screenHeight) {
|
||||
screenSize.set(screenWidth, screenHeight);
|
||||
|
@ -29,16 +36,50 @@ public class DefaultScreenLayout implements ConsoleLayout {
|
|||
updateBounds();
|
||||
}
|
||||
|
||||
public void setPortraitGravity(int portraitGravity) {
|
||||
this.portraitGravity = portraitGravity;
|
||||
}
|
||||
|
||||
public void setLandscapeGravity(int landscapeGravity) {
|
||||
this.landscapeGravity = landscapeGravity;
|
||||
}
|
||||
|
||||
public void setLandscapeSpace(float landscapeSpace) {
|
||||
this.landscapeSpace = landscapeSpace;
|
||||
}
|
||||
|
||||
public void setLandscapeReverse(boolean landscapeReverse) {
|
||||
this.landscapeReverse = landscapeReverse;
|
||||
}
|
||||
|
||||
public void setPortraitReverse(boolean portraitReverse) {
|
||||
this.portraitReverse = portraitReverse;
|
||||
}
|
||||
|
||||
private void updateBounds() {
|
||||
int screenWidth = (int) screenSize.x;
|
||||
int screenHeight = (int) screenSize.y;
|
||||
|
||||
Vector2 topSourceSize = this.topSourceSize;
|
||||
Vector2 bottomSourceSize = this.bottomSourceSize;
|
||||
|
||||
Rect topDisplay = this.topDisplay;
|
||||
Rect bottomDisplay = this.bottomDisplay;
|
||||
|
||||
if ((landscapeReverse && screenWidth > screenHeight) || (portraitReverse && screenWidth < screenHeight)){
|
||||
topSourceSize = this.bottomSourceSize;
|
||||
bottomSourceSize = this.topSourceSize;
|
||||
|
||||
topDisplay = this.bottomDisplay;
|
||||
bottomDisplay = this.topDisplay;
|
||||
}
|
||||
|
||||
if (screenWidth > screenHeight) {
|
||||
int topDisplayWidth = (int) ((screenHeight / topSourceSize.y) * topSourceSize.x);
|
||||
int topDisplayHeight = screenHeight;
|
||||
|
||||
if (topDisplayWidth > (screenWidth * 0.7)) {
|
||||
topDisplayWidth = (int) (screenWidth * 0.7);
|
||||
if (topDisplayWidth > (screenWidth * landscapeSpace)) {
|
||||
topDisplayWidth = (int) (screenWidth * landscapeSpace);
|
||||
topDisplayHeight = (int) ((topDisplayWidth / topSourceSize.x) * topSourceSize.y);
|
||||
}
|
||||
|
||||
|
@ -46,6 +87,7 @@ public class DefaultScreenLayout implements ConsoleLayout {
|
|||
|
||||
topDisplay.set(0, 0, topDisplayWidth, topDisplayHeight);
|
||||
bottomDisplay.set(topDisplayWidth, 0, topDisplayWidth + (screenWidth - topDisplayWidth), bottomDisplayHeight);
|
||||
adjustHorizontalGravity();
|
||||
} else {
|
||||
int topScreenHeight = (int) ((screenWidth / topSourceSize.x) * topSourceSize.y);
|
||||
topDisplay.set(0, 0, screenWidth, topScreenHeight);
|
||||
|
@ -62,9 +104,42 @@ public class DefaultScreenLayout implements ConsoleLayout {
|
|||
|
||||
topDisplay.set(0, 0, screenWidth, topScreenHeight);
|
||||
bottomDisplay.set(bottomDisplayX, topScreenHeight, bottomDisplayX + bottomDisplayWidth, topScreenHeight + bottomDisplayHeight);
|
||||
adjustVerticalGravity();
|
||||
}
|
||||
}
|
||||
|
||||
private void adjustHorizontalGravity(){
|
||||
int topOffset = 0;
|
||||
int bottomOffset = 0;
|
||||
switch (landscapeGravity){
|
||||
case Gravity.CENTER:{
|
||||
topOffset = (int) (screenSize.y - topDisplay.height())/2;
|
||||
bottomOffset = (int) (screenSize.y - bottomDisplay.height())/2;
|
||||
}break;
|
||||
case Gravity.BOTTOM:{
|
||||
topOffset = (int) (screenSize.y - topDisplay.height());
|
||||
bottomOffset = (int) (screenSize.y - bottomDisplay.height());
|
||||
}break;
|
||||
}
|
||||
topDisplay.offset(0, topOffset);
|
||||
bottomDisplay.offset(0, bottomOffset);
|
||||
}
|
||||
|
||||
private void adjustVerticalGravity(){
|
||||
int height = (topDisplay.height() + bottomDisplay.height());
|
||||
int space = 0;
|
||||
switch (portraitGravity){
|
||||
case Gravity.CENTER:{
|
||||
space = (int) (screenSize.y - height)/2;
|
||||
}break;
|
||||
case Gravity.BOTTOM:{
|
||||
space = (int) (screenSize.y - height);
|
||||
}break;
|
||||
}
|
||||
topDisplay.offset(0, space);
|
||||
bottomDisplay.offset(0,space);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getBottomDisplayBounds() {
|
||||
return bottomDisplay;
|
|
@ -0,0 +1,63 @@
|
|||
package com.panda3ds.pandroid.view.renderer.layout;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.Gravity;
|
||||
|
||||
import com.panda3ds.pandroid.math.Vector2;
|
||||
|
||||
public class SingleScreenLayout implements ConsoleLayout {
|
||||
private final Rect topDisplay = new Rect();
|
||||
private final Rect bottomDisplay = new Rect();
|
||||
private final Vector2 screenSize = new Vector2(1.0f, 1.0f);
|
||||
private final Vector2 topSourceSize = new Vector2(1.0f, 1.0f);
|
||||
private final Vector2 bottomSourceSize = new Vector2(1.0f, 1.0f);
|
||||
private boolean top = true;
|
||||
|
||||
@Override
|
||||
public void update(int screenWidth, int screenHeight) {
|
||||
screenSize.set(screenWidth, screenHeight);
|
||||
updateBounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBottomDisplaySourceSize(int width, int height) {
|
||||
bottomSourceSize.set(width, height);
|
||||
updateBounds();
|
||||
}
|
||||
@Override
|
||||
public void setTopDisplaySourceSize(int width, int height) {
|
||||
topSourceSize.set(width, height);
|
||||
updateBounds();
|
||||
}
|
||||
|
||||
private void updateBounds() {
|
||||
int screenWidth = (int) screenSize.x;
|
||||
int screenHeight = (int) screenSize.y;
|
||||
Vector2 source = top ? topSourceSize : bottomSourceSize;
|
||||
Rect dest = top ? topDisplay : bottomDisplay;
|
||||
|
||||
int width = Math.round((screenHeight / source.y) * source.x);
|
||||
int height = screenHeight;
|
||||
int y = 0;
|
||||
int x = (screenWidth - width) / 2;
|
||||
if (width > screenWidth){
|
||||
width = screenWidth;
|
||||
height = Math.round((screenWidth / source.x) * source.y);
|
||||
x = 0;
|
||||
y = (screenHeight - height)/2;
|
||||
}
|
||||
dest.set(x, y, x + width, y+height);
|
||||
|
||||
(top ? bottomDisplay : topDisplay).set(0,0,0,0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getBottomDisplayBounds() {
|
||||
return bottomDisplay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getTopDisplayBounds() {
|
||||
return topDisplay;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
<item>
|
||||
<shape>
|
||||
<corners android:radius="20dp"/>
|
||||
<stroke android:width="1dp" android:color="?colorSurfaceVariant"/>
|
||||
<solid android:color="?colorSurface"/>
|
||||
</shape>
|
||||
</item>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="8dp"/>
|
||||
<solid android:color="?colorSurfaceVariant"/>
|
||||
</shape>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M8,19h3v4h2v-4h3l-4,-4 -4,4zM16,5h-3L13,1h-2v4L8,5l4,4 4,-4zM4,11v2h16v-2L4,11z"/>
|
||||
</vector>
|
9
src/pandroid/app/src/main/res/drawable/ic_arrow_down.xml
Normal file
9
src/pandroid/app/src/main/res/drawable/ic_arrow_down.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M7.41,8.59 L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
|
||||
</vector>
|
9
src/pandroid/app/src/main/res/drawable/ic_arrow_up.xml
Normal file
9
src/pandroid/app/src/main/res/drawable/ic_arrow_up.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M7.41,15.41 L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M9.01,14L2,14v2h7.01v3L13,15l-3.99,-4v3zM14.99,13v-3L22,10L22,8h-7.01L14.99,5L11,9l3.99,4z"/>
|
||||
</vector>
|
|
@ -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="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||
|
|
22
src/pandroid/app/src/main/res/layout/dialog_select_theme.xml
Normal file
22
src/pandroid/app/src/main/res/layout/dialog_select_theme.xml
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/theme_selector"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/alert_dialog_background">
|
||||
|
||||
<View
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="4dp"
|
||||
android:background="?colorSurfaceVariant"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="400dp"
|
||||
android:paddingHorizontal="20dp"/>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/up"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_arrow_up"
|
||||
android:background="@drawable/simple_card_background"
|
||||
android:padding="5dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:elevation="4dp"
|
||||
android:backgroundTint="?colorPrimary"
|
||||
android:layout_margin="2dp"
|
||||
android:tint="?colorOnPrimary"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_align_center"
|
||||
android:background="@drawable/simple_card_background"
|
||||
android:padding="5dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:elevation="4dp"
|
||||
android:backgroundTint="?colorPrimary"
|
||||
android:layout_margin="2dp"
|
||||
android:tint="?colorOnPrimary"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/down"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_arrow_down"
|
||||
android:background="@drawable/simple_card_background"
|
||||
android:padding="5dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:elevation="4dp"
|
||||
android:backgroundTint="?colorPrimary"
|
||||
android:layout_margin="2dp"
|
||||
android:tint="?colorOnPrimary"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/revert"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_compare_arrow"
|
||||
android:background="@drawable/simple_card_background"
|
||||
android:padding="5dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:elevation="4dp"
|
||||
android:backgroundTint="?colorPrimary"
|
||||
android:layout_margin="2dp"
|
||||
android:tint="?colorOnPrimary"/>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:clickable="true">
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingVertical="4dp"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:background="@drawable/simple_card_background"
|
||||
android:backgroundTint="?colorSurfaceVariant"
|
||||
android:layout_margin="5dp"
|
||||
android:elevation="4dp">
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/checkbox"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
android:padding="0dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:text="@string/fix_aspect"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
28
src/pandroid/app/src/main/res/layout/ds_editor_spinner.xml
Normal file
28
src/pandroid/app/src/main/res/layout/ds_editor_spinner.xml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp">
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/simple_card_background"
|
||||
android:layout_margin="4dp"
|
||||
android:elevation="4dp"
|
||||
android:backgroundTint="?colorSurfaceVariant">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
android:id="@+id/spinner"
|
||||
android:layout_width="140dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_margin="6dp"
|
||||
android:dropDownWidth="140dp"
|
||||
android:popupElevation="2dp"
|
||||
android:popupBackground="@drawable/ds_editor_popup_background"
|
||||
android:backgroundTint="?colorOnSurfaceVariant"
|
||||
android:foregroundTint="?colorOnSurfaceVariant"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="128dp"
|
||||
android:layout_height="35dp"
|
||||
android:paddingHorizontal="6dp"
|
||||
android:textColor="?colorOnPrimary"
|
||||
android:textSize="14sp"
|
||||
android:gravity="start|center">
|
||||
</TextView>
|
|
@ -69,16 +69,12 @@
|
|||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Drawer content layout -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="start"
|
||||
android:layout_marginTop="195dp">
|
||||
android:gravity="start">
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:id="@+id/menu"
|
||||
|
@ -91,4 +87,7 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
|
|
34
src/pandroid/app/src/main/res/layout/hold_theme_preview.xml
Normal file
34
src/pandroid/app/src/main/res/layout/hold_theme_preview.xml
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp"
|
||||
android:background="?colorSurface">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="16dp"
|
||||
android:padding="10dp"
|
||||
android:background="@drawable/simple_card_background"
|
||||
android:backgroundTint="?colorSurfaceVariant">
|
||||
|
||||
<View
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="10dp"
|
||||
android:background="@drawable/medium_card_background"
|
||||
android:backgroundTint="?colorOnSurfaceVariant"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_gravity="center|top"
|
||||
android:background="@drawable/medium_card_background"
|
||||
android:backgroundTint="?colorPrimary"/>
|
||||
|
||||
|
||||
</FrameLayout>
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:paddingVertical="10dp"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:foreground="@drawable/rounded_selectable_item_background"
|
||||
android:gravity="center">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/preview"
|
||||
android:layout_width="match_parent"
|
||||
app:strokeColor="?colorSurfaceVariant"
|
||||
android:layout_height="150dp">
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:textSize="16sp"
|
||||
android:layout_margin="10dp"
|
||||
android:text="@string/app_name"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/checkbox"
|
||||
android:layout_width="26dp"
|
||||
android:layout_height="26dp"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:gravity="center"
|
||||
android:checked="false"/>
|
||||
|
||||
</LinearLayout>
|
|
@ -56,4 +56,17 @@
|
|||
<string name="graphics">Gráficos</string>
|
||||
<string name="loading">Carregando</string>
|
||||
<string name="rotate">Rotacionar</string>
|
||||
<string name="apply">Aplicar</string>
|
||||
<string name="pref_theme_summary">Definir o tema do aplicativo</string>
|
||||
<string name="pref_theme_title">Tema do aplicativo</string>
|
||||
<string name="failed_load_rom">Falha ao carregar a ROM.</string>
|
||||
<string name="dialog_message_invalid_rom">Falha ao carregar o arquivo ROM, por favor verifique se o arquivo não esta corrompido ou se o emulator tem permição para acessa-lo.</string>
|
||||
<string name="system">Sistema</string>
|
||||
<string name="general">Geral</string>
|
||||
<string name="pref_general_summary">Configurações gerais do emulador.</string>
|
||||
<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_summary">Altere as disposições disponiveis para as telas do console</string>
|
||||
</resources>
|
||||
|
|
|
@ -60,4 +60,18 @@
|
|||
<string name="pref_shader_jit_summary">Use shader recompiler.</string>
|
||||
<string name="graphics">Graphics</string>
|
||||
<string name="loading">Loading</string>
|
||||
<string name="apply">Apply</string>
|
||||
<string name="pref_theme_summary">Set application theme</string>
|
||||
<string name="pref_theme_title">Application theme</string>
|
||||
<string name="failed_load_rom">Failed to load ROM</string>
|
||||
<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="dual_screen_layouts_summary">Change layout of console screens.</string>
|
||||
<string name="click_to_change">Click to change</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<com.panda3ds.pandroid.view.preferences.SingleSelectionPreferences
|
||||
app:key="theme"
|
||||
app:title="@string/theme"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<Preference app:title="@string/theme_device"/>
|
||||
<Preference app:title="@string/light"/>
|
||||
<Preference app:title="@string/dark"/>
|
||||
<Preference app:title="@string/black"/>
|
||||
|
||||
</com.panda3ds.pandroid.view.preferences.SingleSelectionPreferences>
|
||||
</PreferenceScreen>
|
4
src/pandroid/app/src/main/res/xml/empty_preferences.xml
Normal file
4
src/pandroid/app/src/main/res/xml/empty_preferences.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</PreferenceScreen>
|
22
src/pandroid/app/src/main/res/xml/general_preference.xml
Normal file
22
src/pandroid/app/src/main/res/xml/general_preference.xml
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<PreferenceCategory
|
||||
app:title="@string/appearance"
|
||||
app:iconSpaceReserved="false">
|
||||
<Preference
|
||||
android:key="appearance.theme"
|
||||
app:title="@string/theme"
|
||||
app:summary="@string/theme"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
android:key="appearance.ds"
|
||||
app:title="@string/dual_screen_layouts"
|
||||
app:summary="@string/dual_screen_layouts_summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
app:title="@string/graphics"
|
||||
app:iconSpaceReserved="false">
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
|
@ -133,6 +133,12 @@
|
|||
app:summary="none"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
<Preference
|
||||
app:title="Switch dual screen layout"
|
||||
app:key="CHANGE_DS_LAYOUT"
|
||||
app:summary="none"
|
||||
app:iconSpaceReserved="false"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
|
@ -4,6 +4,7 @@
|
|||
android:divider="#F00">
|
||||
|
||||
<Preference
|
||||
android:key="application"
|
||||
app:title="@string/app_name"
|
||||
app:enabled="false"
|
||||
app:summary="1.0"
|
||||
|
@ -17,10 +18,10 @@
|
|||
app:layout="@layout/preference_start_item"/>
|
||||
|
||||
<Preference
|
||||
app:key="appearance"
|
||||
app:icon="@drawable/ic_theme"
|
||||
app:title="@string/appearance"
|
||||
app:summary="@string/pref_appearance_summary"
|
||||
app:key="general"
|
||||
app:icon="@drawable/ic_settings"
|
||||
app:title="@string/general"
|
||||
app:summary="@string/pref_general_summary"
|
||||
app:layout="@layout/preference_start_item"/>
|
||||
|
||||
<Preference
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue