mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-19 20:19:13 +12:00
some fixes
This commit is contained in:
parent
329fa7a158
commit
d0b4e09069
27 changed files with 342 additions and 240 deletions
|
@ -126,6 +126,16 @@ public class GameActivity extends BaseActivity implements EmulatorCallback {
|
|||
return super.dispatchGenericMotionEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
|
||||
super.onPictureInPictureModeChanged(isInPictureInPictureMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUserLeaveHint() {
|
||||
super.onUserLeaveHint();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (AlberDriver.HasRomLoaded()) {
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package com.panda3ds.pandroid.app.base;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.panda3ds.pandroid.R;
|
||||
import com.panda3ds.pandroid.utils.CompatUtils;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BaseSheetDialog extends BottomSheetDialog {
|
||||
private final LinearLayout contentView;
|
||||
public BaseSheetDialog(@NonNull Context context) {
|
||||
super(CompatUtils.findActivity(context));
|
||||
int width = CompatUtils.findActivity(context).getWindow().getDecorView().getMeasuredWidth();
|
||||
int height = CompatUtils.findActivity(context).getWindow().getDecorView().getMeasuredHeight();
|
||||
getBehavior().setPeekHeight((int) (height*0.87));
|
||||
getBehavior().setMaxWidth(width);
|
||||
getBehavior().setMaxHeight((int) (height*0.87));
|
||||
super.setContentView(R.layout.dialog_bottom_sheet);
|
||||
contentView = super.findViewById(R.id.content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view) {
|
||||
contentView.removeAllViews();
|
||||
contentView.addView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(int layoutResId) {
|
||||
setContentView(LayoutInflater.from(getContext()).inflate(layoutResId, null, false));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T extends View> T findViewById(int id) {
|
||||
return contentView.findViewById(id);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package com.panda3ds.pandroid.app.base;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
@ -14,28 +15,37 @@ import com.panda3ds.pandroid.utils.FileUtils;
|
|||
import com.panda3ds.pandroid.utils.GameUtils;
|
||||
import com.panda3ds.pandroid.view.gamesgrid.GameIconView;
|
||||
|
||||
public class GameAboutDialog extends BottomSheetDialog {
|
||||
public class GameAboutDialog extends BaseSheetDialog {
|
||||
private final GameMetadata game;
|
||||
public GameAboutDialog(@NonNull Context context, GameMetadata game) {
|
||||
super(context);
|
||||
View content = LayoutInflater.from(context).inflate(R.layout.dialog_game_about, null, false);
|
||||
setContentView(content);
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
((GameIconView)content.findViewById(R.id.game_icon)).setImageBitmap(game.getIcon());
|
||||
((TextView)content.findViewById(R.id.game_title)).setText(game.getTitle());
|
||||
((TextView)content.findViewById(R.id.game_publisher)).setText(game.getPublisher());
|
||||
((TextView)content.findViewById(R.id.region)).setText(game.getRegions()[0].name());
|
||||
((TextView)content.findViewById(R.id.directory)).setText(FileUtils.obtainUri(game.getRealPath()).getPath());
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.dialog_game_about);
|
||||
|
||||
content.findViewById(R.id.play).setOnClickListener(v -> {
|
||||
((GameIconView) findViewById(R.id.game_icon)).setImageBitmap(game.getIcon());
|
||||
((TextView) findViewById(R.id.game_title)).setText(game.getTitle());
|
||||
((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);
|
||||
});
|
||||
|
||||
if (game.getRomPath().startsWith("folder:")){
|
||||
content.findViewById(R.id.remove).setVisibility(View.GONE);
|
||||
if (game.getRomPath().startsWith("folder:")) {
|
||||
findViewById(R.id.remove).setVisibility(View.GONE);
|
||||
} else {
|
||||
content.findViewById(R.id.remove).setOnClickListener(v-> {
|
||||
findViewById(R.id.remove).setOnClickListener(v -> {
|
||||
dismiss();
|
||||
if (game.getRomPath().startsWith("elf:")) {
|
||||
FileUtils.delete(game.getRealPath());
|
||||
}
|
||||
GameUtils.removeGame(game);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import androidx.annotation.Nullable;
|
|||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import com.panda3ds.pandroid.R;
|
||||
import com.panda3ds.pandroid.app.base.GameAboutDialog;
|
||||
import com.panda3ds.pandroid.data.game.GameMetadata;
|
||||
import com.panda3ds.pandroid.utils.GameUtils;
|
||||
import com.panda3ds.pandroid.utils.SearchAgent;
|
||||
|
@ -33,6 +34,11 @@ public class SearchFragment extends Fragment {
|
|||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
gamesListView = view.findViewById(R.id.games);
|
||||
gamesListView.setItemLongClick((game)->{
|
||||
GameAboutDialog dialog = new GameAboutDialog(requireActivity(), game);
|
||||
dialog.setOnDismissListener((x)-> search(((AppCompatEditText) view.findViewById(R.id.search_bar)).getText().toString()));
|
||||
dialog.show();
|
||||
});
|
||||
((AppCompatEditText) view.findViewById(R.id.search_bar)).addTextChangedListener((SimpleTextWatcher) this::search);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ 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;
|
||||
import com.panda3ds.pandroid.data.game.GamesFolder;
|
||||
import com.panda3ds.pandroid.utils.FileUtils;
|
||||
import com.panda3ds.pandroid.utils.GameUtils;
|
||||
|
@ -34,13 +35,13 @@ public class GamesFoldersPreferences extends BasePreferenceFragment implements A
|
|||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
private void refreshList(){
|
||||
private void refreshList() {
|
||||
GamesFolder[] folders = GameUtils.getFolders();
|
||||
PreferenceScreen screen = getPreferenceScreen();
|
||||
screen.removeAll();
|
||||
for (GamesFolder folder: folders){
|
||||
for (GamesFolder folder : folders) {
|
||||
Preference preference = new Preference(screen.getContext());
|
||||
preference.setOnPreferenceClickListener((item)-> {
|
||||
preference.setOnPreferenceClickListener((item) -> {
|
||||
showFolderInfo(folder);
|
||||
screen.performClick();
|
||||
return false;
|
||||
|
@ -62,12 +63,12 @@ public class GamesFoldersPreferences extends BasePreferenceFragment implements A
|
|||
}
|
||||
|
||||
private void showFolderInfo(GamesFolder folder) {
|
||||
BottomSheetDialog dialog = new BottomSheetDialog(requireActivity());
|
||||
View layout = LayoutInflater.from(requireActivity()).inflate(R.layout.games_folder_about, null, false);
|
||||
BaseSheetDialog dialog = new BaseSheetDialog(requireActivity());
|
||||
View layout = LayoutInflater.from(requireActivity()).inflate(R.layout.dialog_games_folder, null, false);
|
||||
dialog.setContentView(layout);
|
||||
|
||||
((TextView) layout.findViewById(R.id.name)).setText(FileUtils.getName(folder.getPath()));
|
||||
((TextView) layout.findViewById(R.id.directory)).setText(folder.getPath());
|
||||
((TextView) layout.findViewById(R.id.directory)).setText(FileUtils.obtainUri(folder.getPath()).getPath());
|
||||
((TextView) layout.findViewById(R.id.games)).setText(String.valueOf(folder.getGames().size()));
|
||||
|
||||
layout.findViewById(R.id.ok).setOnClickListener(v -> dialog.dismiss());
|
||||
|
@ -83,7 +84,7 @@ public class GamesFoldersPreferences extends BasePreferenceFragment implements A
|
|||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (pickFolderRequest != null){
|
||||
if (pickFolderRequest != null) {
|
||||
pickFolderRequest.unregister();
|
||||
pickFolderRequest = null;
|
||||
}
|
||||
|
@ -91,7 +92,7 @@ public class GamesFoldersPreferences extends BasePreferenceFragment implements A
|
|||
|
||||
@Override
|
||||
public void onActivityResult(Uri result) {
|
||||
if (result != null){
|
||||
if (result != null) {
|
||||
FileUtils.makeUriPermanent(result.toString(), "r");
|
||||
GameUtils.registerFolder(result.toString());
|
||||
refreshList();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.panda3ds.pandroid.app.preferences;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -12,42 +12,43 @@ 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.app.base.BaseSheetDialog;
|
||||
import com.panda3ds.pandroid.utils.CompatUtils;
|
||||
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 {
|
||||
public class ThemeSelectorDialog extends BaseSheetDialog {
|
||||
|
||||
private final SimpleListAdapter<Theme> adapter;
|
||||
private final SimpleListAdapter<Theme> adapter = new SimpleListAdapter<>(R.layout.hold_theme_preview_base, this::bindItemView);
|
||||
private final int currentTheme = GlobalConfig.get(GlobalConfig.KEY_APP_THEME);
|
||||
private static final Theme[] themes = {
|
||||
private static final ArrayList<Theme> themes = new ArrayList<>(Arrays.asList(
|
||||
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));
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.dialog_select_theme);
|
||||
adapter.clear();
|
||||
themes.sort((o1, o2) -> o1.value == currentTheme ? -1 : 0);
|
||||
adapter.addAll(themes);
|
||||
|
||||
RecyclerView recycler = findViewById(R.id.recycler);
|
||||
recycler.setAdapter(adapter);
|
||||
recycler.setLayoutManager(new AutoFitGridLayout(getContext(), 150));
|
||||
}
|
||||
|
||||
private void bindItemView(int i, Theme theme, View view) {
|
||||
|
@ -55,12 +56,12 @@ public class ThemeSelectorDialog extends BottomSheetDialog {
|
|||
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);
|
||||
((AppCompatRadioButton) view.findViewById(R.id.checkbox)).setChecked(GlobalConfig.get(GlobalConfig.KEY_APP_THEME) == theme.value);
|
||||
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();
|
||||
if (theme.value != GlobalConfig.get(GlobalConfig.KEY_APP_THEME)) {
|
||||
GlobalConfig.set(GlobalConfig.KEY_APP_THEME, theme.value);
|
||||
CompatUtils.findActivity(getContext()).recreate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -68,12 +69,12 @@ public class ThemeSelectorDialog extends BottomSheetDialog {
|
|||
private static final class Theme {
|
||||
private final int style;
|
||||
private final int name;
|
||||
private final int id;
|
||||
private final int value;
|
||||
|
||||
private Theme(int style, int name, int value) {
|
||||
this.style = style;
|
||||
this.name = name;
|
||||
this.id = value;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ public class DsEditorPreferences extends Fragment {
|
|||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
layout = new LinearLayout(container.getContext());
|
||||
layout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|View.SYSTEM_UI_FLAG_FULLSCREEN|View.SYSTEM_UI_FLAG_IMMERSIVE);
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
@ -36,12 +37,6 @@ public class DsEditorPreferences extends Fragment {
|
|||
((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();
|
||||
|
|
|
@ -88,10 +88,14 @@ public class AppDataDocumentProvider extends DocumentsProvider {
|
|||
}
|
||||
|
||||
private void includeFile(MatrixCursor cursor, File file) {
|
||||
int flags = 0;
|
||||
if (file.isFile()) {
|
||||
flags = Document.FLAG_SUPPORTS_WRITE;
|
||||
}
|
||||
cursor.newRow()
|
||||
.add(Document.COLUMN_DOCUMENT_ID, obtainDocumentId(file))
|
||||
.add(Document.COLUMN_MIME_TYPE, file.isDirectory() ? Document.MIME_TYPE_DIR : "application/octect-stream")
|
||||
.add(Document.COLUMN_FLAGS, 0)
|
||||
.add(Document.COLUMN_FLAGS, flags)
|
||||
.add(Document.COLUMN_LAST_MODIFIED, file.lastModified())
|
||||
.add(Document.COLUMN_DISPLAY_NAME, file.getName())
|
||||
.add(Document.COLUMN_SIZE, file.length());
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.panda3ds.pandroid.data.game;
|
||||
|
||||
import com.panda3ds.pandroid.R;
|
||||
|
||||
public enum GameRegion {
|
||||
NorthAmerican,
|
||||
Japan,
|
||||
|
@ -8,5 +10,23 @@ public enum GameRegion {
|
|||
China,
|
||||
Korean,
|
||||
Taiwan,
|
||||
None
|
||||
None;
|
||||
|
||||
public int localizedName(){
|
||||
switch (this){
|
||||
case NorthAmerican:
|
||||
return R.string.region_north_armerican;
|
||||
case Japan:
|
||||
return R.string.region_japan;
|
||||
case Europe:
|
||||
return R.string.region_europe;
|
||||
case Australia:
|
||||
return R.string.region_australia;
|
||||
case Korean:
|
||||
return R.string.region_korean;
|
||||
case Taiwan:
|
||||
return R.string.region_taiwan;
|
||||
}
|
||||
return R.string.unknown;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.panda3ds.pandroid.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import androidx.annotation.AttrRes;
|
||||
|
||||
import com.panda3ds.pandroid.app.PandroidApplication;
|
||||
|
||||
public class CompatUtils {
|
||||
public static Activity findActivity(Context context) {
|
||||
if (context instanceof Activity) {
|
||||
return (Activity) context;
|
||||
} else if ((context instanceof ContextWrapper)) {
|
||||
return findActivity(((ContextWrapper) context).getBaseContext());
|
||||
}
|
||||
return ((Activity) context);
|
||||
}
|
||||
|
||||
public static int resolveColor(Context context, @AttrRes int id){
|
||||
try {
|
||||
TypedArray values = context.obtainStyledAttributes(new int[]{id});
|
||||
int color = values.getColor(0, Color.RED);
|
||||
values.recycle();
|
||||
return color;
|
||||
} catch (Exception e){
|
||||
return Color.rgb(255,0,255);
|
||||
}
|
||||
}
|
||||
|
||||
public static float applyDimen(int unit, int size) {
|
||||
return TypedValue.applyDimension(unit, size, PandroidApplication.getAppContext().getResources().getDisplayMetrics());
|
||||
}
|
||||
}
|
|
@ -24,7 +24,6 @@ import java.util.Objects;
|
|||
public class FileUtils {
|
||||
public static final String MODE_READ = "r";
|
||||
private static final String TREE_URI = "tree";
|
||||
public static final int CANONICAL_SEARCH_DEEP = 8;
|
||||
|
||||
private static DocumentFile parseFile(String path) {
|
||||
if (path.startsWith("/")) {
|
||||
|
@ -204,47 +203,6 @@ public class FileUtils {
|
|||
getContext().getContentResolver().takePersistableUriPermission(Uri.parse(uri), flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* When call ContentProvider.openFileDescriptor() android opens a file descriptor
|
||||
* on app process in /proc/self/fd/[file descriptor id] this is a link to real file path
|
||||
* can use File.getCanonicalPath() for get a link origin, but in some android version
|
||||
* need use Os.readlink(path) to get a real path.
|
||||
*/
|
||||
public static String obtainRealPath(String uri) {
|
||||
try {
|
||||
ParcelFileDescriptor parcelDescriptor = getContext().getContentResolver().openFileDescriptor(Uri.parse(uri), "r");
|
||||
int fd = parcelDescriptor.getFd();
|
||||
File file = new File("/proc/self/fd/" + fd).getAbsoluteFile();
|
||||
|
||||
for (int i = 0; i < CANONICAL_SEARCH_DEEP; i++) {
|
||||
try {
|
||||
String canonical = file.getCanonicalPath();
|
||||
if (!Objects.equals(canonical, file.getAbsolutePath())) {
|
||||
file = new File(canonical).getAbsoluteFile();
|
||||
}
|
||||
} catch (Exception x) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!file.getAbsolutePath().startsWith("/proc/self/")) {
|
||||
parcelDescriptor.close();
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
String path = Os.readlink(file.getAbsolutePath());
|
||||
parcelDescriptor.close();
|
||||
|
||||
if (new File(path).exists()) {
|
||||
return path;
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateFile(String path) {
|
||||
DocumentFile file = parseFile(path);
|
||||
Uri uri = file.getUri();
|
||||
|
|
|
@ -21,7 +21,7 @@ import java.util.Objects;
|
|||
|
||||
public class GameUtils {
|
||||
private static final Bitmap DEFAULT_ICON = Bitmap.createBitmap(48, 48, Bitmap.Config.ARGB_8888);
|
||||
private static GsonConfigParser parser = new GsonConfigParser(Constants.PREF_GAME_UTILS);
|
||||
private final static GsonConfigParser parser = new GsonConfigParser(Constants.PREF_GAME_UTILS);
|
||||
|
||||
private static DataModel data;
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package com.panda3ds.pandroid.view.ds;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
class Bounds {
|
||||
public int left = 0;
|
||||
public int right = 0;
|
||||
public int top = 0;
|
||||
public int bottom = 0;
|
||||
|
||||
public void normalize(){
|
||||
left = Math.abs(left);
|
||||
right = Math.abs(right);
|
||||
top = Math.abs(top);
|
||||
bottom = Math.abs(bottom);
|
||||
}
|
||||
|
||||
public void applyWithAspect(Rect rect, int width, double aspectRatio){
|
||||
normalize();
|
||||
rect.set(left, top, width-right, (int) Math.round((width-right-left)*aspectRatio)+top);
|
||||
}
|
||||
|
||||
public void apply(Rect rect, int width, int height){
|
||||
normalize();
|
||||
rect.set(left, top, width-right, height-bottom);
|
||||
}
|
||||
|
||||
public void move(int x, int y){
|
||||
left += x;
|
||||
right -= x;
|
||||
|
||||
top += y;
|
||||
bottom -= y;
|
||||
normalize();
|
||||
}
|
||||
|
||||
public void fixOverlay(int width, int height, int size) {
|
||||
if (left > (width-right) - size){
|
||||
right = (width-left) - size;
|
||||
}
|
||||
if (top > (height - bottom) - size){
|
||||
bottom = (height - top) - size;
|
||||
}
|
||||
normalize();
|
||||
}
|
||||
}
|
|
@ -26,14 +26,14 @@ 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.CompatUtils;
|
||||
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 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);
|
||||
|
@ -53,8 +53,9 @@ public class DsEditorView extends FrameLayout {
|
|||
public DsEditorView(Context context, int index) {
|
||||
super(context);
|
||||
layout = (DsLayout) DsLayoutManager.createLayout(index);
|
||||
|
||||
SIZE_DP = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
|
||||
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);
|
||||
|
||||
selectionPaint.setColor(COLOR_TOP_SELECTION);
|
||||
selectionPaint.setStrokeWidth(SIZE_DP * 2);
|
||||
|
@ -105,16 +106,11 @@ public class DsEditorView extends FrameLayout {
|
|||
});
|
||||
}
|
||||
|
||||
{
|
||||
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);
|
||||
|
@ -136,11 +132,13 @@ 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));
|
||||
|
||||
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));
|
||||
|
||||
topDisplayResizer = new PointView();
|
||||
|
@ -164,16 +162,10 @@ public class DsEditorView extends FrameLayout {
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
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();
|
||||
|
@ -194,9 +186,6 @@ public class DsEditorView extends FrameLayout {
|
|||
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);
|
||||
|
||||
|
@ -305,7 +294,6 @@ public class DsEditorView extends FrameLayout {
|
|||
|
||||
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) {
|
||||
|
@ -314,66 +302,39 @@ public class DsEditorView extends FrameLayout {
|
|||
|
||||
@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) {
|
||||
Bounds preferred = topScreen ? layout.getCurrentModel().preferredTop : layout.getCurrentModel().preferredBottom;
|
||||
if (layout.getCurrentModel().mode == Mode.ABSOLUTE && 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();
|
||||
|
||||
preferred.move((int) (event.getRawX() - downEvent.x), (int) (event.getRawY() - downEvent.y));
|
||||
downEvent.set(event.getRawX(), event.getRawY());
|
||||
refreshPoints();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
downEvent = null;
|
||||
return false;
|
||||
} else if (layout.getCurrentModel().mode == Mode.SINGLE && event.getAction() == MotionEvent.ACTION_UP) {
|
||||
callOnClick();
|
||||
}
|
||||
downEvent = null;
|
||||
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;
|
||||
Bounds 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));
|
||||
preferred.right = (int) (width - (((PointView) v).x() + event.getX()));
|
||||
preferred.bottom = (int) (height - (((PointView) v).y() + event.getY()));
|
||||
refreshPoints();
|
||||
return true;
|
||||
}
|
||||
downEvent = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -389,7 +350,7 @@ public class DsEditorView extends FrameLayout {
|
|||
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)));
|
||||
solidPaint.setColor(Color.argb(65, Color.red(color), Color.green(color), Color.blue(color)));
|
||||
canvas.drawRect(this.getBounds(), solidPaint);
|
||||
canvas.drawRect(this.getBounds(), selectionPaint);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ 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;
|
||||
|
||||
|
@ -70,14 +69,13 @@ class DsLayout implements ConsoleLayout {
|
|||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,13 +5,12 @@ 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 final Bounds preferredTop = new Bounds();
|
||||
public final Bounds preferredBottom = new Bounds();
|
||||
public boolean reverse = false;
|
||||
public boolean singleTop = true;
|
||||
public float space = 0.6f;
|
||||
|
|
39
src/pandroid/app/src/main/res/layout/dialog_bottom_sheet.xml
Normal file
39
src/pandroid/app/src/main/res/layout/dialog_bottom_sheet.xml
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
app:strokeWidth="1dp"
|
||||
app:cardCornerRadius="24dp"
|
||||
app:strokeColor="?colorSurfaceVariant"
|
||||
android:background="@drawable/alert_dialog_background">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:background="?colorSurface">
|
||||
|
||||
<View
|
||||
android:layout_width="128dp"
|
||||
android:layout_height="4dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:background="@drawable/medium_card_background"
|
||||
android:backgroundTint="?colorSurfaceVariant"/>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"/>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
|
@ -6,14 +6,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="top|center"
|
||||
android:padding="10dp"
|
||||
android:background="@drawable/alert_dialog_background">
|
||||
|
||||
<View
|
||||
android:layout_width="128dp"
|
||||
android:layout_height="4dp"
|
||||
android:background="?colorSurfaceVariant"
|
||||
android:layout_margin="5dp"/>
|
||||
android:padding="10dp">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="128dp"
|
||||
|
@ -111,9 +104,12 @@
|
|||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/play"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/play"
|
||||
android:textColor="?colorOnPrimary"
|
||||
android:backgroundTint="?colorPrimary"
|
||||
app:icon="@drawable/ic_play"
|
||||
app:iconTint="?colorOnPrimary"/>
|
||||
|
||||
|
|
|
@ -5,14 +5,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center|top"
|
||||
android:padding="10dp"
|
||||
android:background="@drawable/alert_dialog_background">
|
||||
|
||||
<View
|
||||
android:layout_width="128dp"
|
||||
android:layout_height="4dp"
|
||||
android:background="?colorSurfaceVariant"
|
||||
android:layout_margin="5dp"/>
|
||||
android:padding="10dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:layout_width="64dp"
|
|
@ -3,20 +3,13 @@
|
|||
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"/>
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="400dp"
|
||||
android:paddingHorizontal="20dp"/>
|
||||
android:layout_height="match_parent"
|
||||
android:paddingHorizontal="20dp" />
|
||||
|
||||
</LinearLayout>
|
|
@ -4,6 +4,8 @@
|
|||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="4dp">
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
android:layout_width="128dp"
|
||||
android:layout_height="35dp"
|
||||
android:paddingHorizontal="6dp"
|
||||
android:textColor="?colorOnPrimary"
|
||||
android:textColor="?colorOnSurfaceVariant"
|
||||
android:background="?colorSurfaceVariant"
|
||||
android:textSize="14sp"
|
||||
android:gravity="start|center">
|
||||
</TextView>
|
|
@ -7,13 +7,13 @@
|
|||
android:paddingVertical="10dp"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:foreground="@drawable/rounded_selectable_item_background"
|
||||
android:gravity="center">
|
||||
android:gravity="top|center">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/preview"
|
||||
android:layout_width="match_parent"
|
||||
app:strokeColor="?colorSurfaceVariant"
|
||||
android:layout_height="150dp">
|
||||
android:layout_height="125dp">
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:textSize="16sp"
|
||||
android:gravity="center"
|
||||
android:layout_margin="10dp"
|
||||
android:text="@string/app_name"/>
|
||||
|
|
@ -75,4 +75,14 @@
|
|||
<string name="pref_games_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>
|
||||
<string name="remove">Remover</string>
|
||||
<string name="play">Jogar</string>
|
||||
<string name="region">Região</string>
|
||||
<string name="region_north_armerican">Estados Unidos</string>
|
||||
<string name="region_japan">Japão</string>
|
||||
<string name="region_europe">Europa</string>
|
||||
<string name="region_australia">Australia</string>
|
||||
<string name="region_korean">Coréia</string>
|
||||
<string name="region_taiwan">Taiwan</string>
|
||||
</resources>
|
||||
|
|
|
@ -83,4 +83,10 @@
|
|||
<string name="remove">Remove</string>
|
||||
<string name="play">Play</string>
|
||||
<string name="region">Region</string>
|
||||
<string name="region_north_armerican">North American</string>
|
||||
<string name="region_japan">Japan</string>
|
||||
<string name="region_europe">Europe</string>
|
||||
<string name="region_australia">Australia</string>
|
||||
<string name="region_korean">Korean</string>
|
||||
<string name="region_taiwan">Taiwan</string>
|
||||
</resources>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue