diff --git a/app_pojavlauncher/src/main/AndroidManifest.xml b/app_pojavlauncher/src/main/AndroidManifest.xml index e2b3cef98..4d5134275 100644 --- a/app_pojavlauncher/src/main/AndroidManifest.xml +++ b/app_pojavlauncher/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -38,6 +39,25 @@ + + + + + + + + + + + + 0) { - mouse_x += (e.getX()*scaleFactor); - mouse_y += (e.getY()*scaleFactor); - } - CallbackBridge.mouseX = (int) mouse_x; - CallbackBridge.mouseY = (int) mouse_y; - if(!CallbackBridge.isGrabbing()){ - view.releasePointerCapture(); - } + mouse_x += (e.getX()*scaleFactor); + mouse_y += (e.getY()*scaleFactor); + CallbackBridge.mouseX = (int) mouse_x; + CallbackBridge.mouseY = (int) mouse_y; + if(!CallbackBridge.isGrabbing()){ + view.releasePointerCapture(); + view.clearFocus(); + } - if (debugText.getVisibility() == View.VISIBLE && !debugErrored) { - StringBuilder builder = new StringBuilder(); - try { - builder.append("PointerCapture debug\n"); - builder.append("MotionEvent=").append(e.getActionMasked()).append("\n"); - builder.append("PressingBtn=").append(MotionEvent.class.getDeclaredMethod("buttonStateToString").invoke(null, e.getButtonState())).append("\n\n"); + if (debugText.getVisibility() == View.VISIBLE && !debugErrored) { + StringBuilder builder = new StringBuilder(); + try { + builder.append("PointerCapture debug\n"); + builder.append("MotionEvent=").append(e.getActionMasked()).append("\n"); + builder.append("PressingBtn=").append(MotionEvent.class.getDeclaredMethod("buttonStateToString").invoke(null, e.getButtonState())).append("\n\n"); - builder.append("PointerX=").append(e.getX()).append("\n"); - builder.append("PointerY=").append(e.getY()).append("\n"); - builder.append("RawX=").append(e.getRawX()).append("\n"); - builder.append("RawY=").append(e.getRawY()).append("\n\n"); + builder.append("PointerX=").append(e.getX()).append("\n"); + builder.append("PointerY=").append(e.getY()).append("\n"); + builder.append("RawX=").append(e.getRawX()).append("\n"); + builder.append("RawY=").append(e.getRawY()).append("\n\n"); - builder.append("XPos=").append(mouse_x).append("\n"); - builder.append("YPos=").append(mouse_y).append("\n\n"); - builder.append("MovingX=").append(getMoving(e.getX(), true)).append("\n"); - builder.append("MovingY=").append(getMoving(e.getY(), false)).append("\n"); - } catch (Throwable th) { - debugErrored = true; - builder.append("Error getting debug. The debug will be stopped!\n").append(Log.getStackTraceString(th)); - } finally { - debugText.setText(builder.toString()); - builder.setLength(0); - } - } - debugText.setText(CallbackBridge.DEBUG_STRING.toString()); - CallbackBridge.DEBUG_STRING.setLength(0); - switch (e.getActionMasked()) { - case MotionEvent.ACTION_MOVE: - CallbackBridge.sendCursorPos(mouse_x, mouse_y); - return true; - case MotionEvent.ACTION_BUTTON_PRESS: - return sendMouseButtonUnconverted(e.getActionButton(), true); - case MotionEvent.ACTION_BUTTON_RELEASE: - return sendMouseButtonUnconverted(e.getActionButton(), false); - case MotionEvent.ACTION_SCROLL: - CallbackBridge.sendScroll(e.getAxisValue(MotionEvent.AXIS_HSCROLL), e.getAxisValue(MotionEvent.AXIS_VSCROLL)); - return true; - default: - return false; + builder.append("XPos=").append(mouse_x).append("\n"); + builder.append("YPos=").append(mouse_y).append("\n\n"); + builder.append("MovingX=").append(getMoving(e.getX(), true)).append("\n"); + builder.append("MovingY=").append(getMoving(e.getY(), false)).append("\n"); + } catch (Throwable th) { + debugErrored = true; + builder.append("Error getting debug. The debug will be stopped!\n").append(Log.getStackTraceString(th)); + } finally { + debugText.setText(builder.toString()); + builder.setLength(0); } } - }); + debugText.setText(CallbackBridge.DEBUG_STRING.toString()); + CallbackBridge.DEBUG_STRING.setLength(0); + switch (e.getActionMasked()) { + case MotionEvent.ACTION_MOVE: + CallbackBridge.sendCursorPos(mouse_x, mouse_y); + return true; + case MotionEvent.ACTION_BUTTON_PRESS: + return sendMouseButtonUnconverted(e.getActionButton(), true); + case MotionEvent.ACTION_BUTTON_RELEASE: + return sendMouseButtonUnconverted(e.getActionButton(), false); + case MotionEvent.ACTION_SCROLL: + CallbackBridge.sendScroll(e.getAxisValue(MotionEvent.AXIS_HSCROLL), e.getAxisValue(MotionEvent.AXIS_VSCROLL)); + return true; + default: + return false; + } + } + }); } minecraftGLView.setOnTouchListener(glTouchListener); minecraftGLView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener(){ @@ -636,9 +657,10 @@ public class BaseMainActivity extends LoggableActivity { //Filtering useless events by order of probability if((event.getFlags() & KeyEvent.FLAG_FALLBACK) == KeyEvent.FLAG_FALLBACK) return true; - if(event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) return true; - if(event.getAction() == KeyEvent.KEYCODE_VOLUME_DOWN) return false; - if(event.getAction() == KeyEvent.KEYCODE_VOLUME_UP) return false; + int eventKeycode = event.getKeyCode(); + if(eventKeycode == KeyEvent.KEYCODE_UNKNOWN) return true; + if(eventKeycode == KeyEvent.KEYCODE_VOLUME_DOWN) return false; + if(eventKeycode == KeyEvent.KEYCODE_VOLUME_UP) return false; if(event.getRepeatCount() != 0) return true; if(event.getAction() == KeyEvent.ACTION_MULTIPLE) return true; diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/DoubleTapDetector.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/DoubleTapDetector.java new file mode 100644 index 000000000..e87c893f9 --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/DoubleTapDetector.java @@ -0,0 +1,67 @@ +package net.kdt.pojavlaunch; + +import android.view.MotionEvent; + +import static android.view.MotionEvent.ACTION_DOWN; +import static android.view.MotionEvent.ACTION_POINTER_DOWN; + +/** + * Class aiming at better detecting double tap events for EVERY POINTER + * Only uses the least amount of events possible, + * since we aren't guaranteed to have all events in order + */ +public class DoubleTapDetector { + + private final static int DOUBLE_TAP_MIN_DELTA_MS = 50; + private final static int DOUBLE_TAP_MAX_DELTA_MS = 300; + private final static int DOUBLE_TAP_SLOP_SQUARE_PX = (int) Math.pow(Tools.dpToPx(100), 2); + + private long mLastEventTime = 0; + private float mLastX = 9999; + private float mLastY = 9999; + + /** + * A function to call when you have a touch event. + * @param e The MotionEvent to inspect + * @return whether or not a double tap happened for a pointer + */ + public boolean onTouchEvent(MotionEvent e){ + int eventAction = e.getActionMasked(); + int pointerIndex; + + //Get the pointer index we want to look at + if(eventAction == ACTION_DOWN) pointerIndex = 0; + else if(eventAction == ACTION_POINTER_DOWN) pointerIndex = e.getActionIndex(); + else return false; + + float eventX = e.getX(pointerIndex); + float eventY = e.getY(pointerIndex); + long eventTime = e.getEventTime(); + + long deltaTime = eventTime - mLastEventTime; + if(deltaTime > DOUBLE_TAP_MIN_DELTA_MS && deltaTime < DOUBLE_TAP_MAX_DELTA_MS){ + int deltaX = (int) mLastX - (int) eventX; + int deltaY = (int) mLastY - (int) eventY; + if((deltaX*deltaX + deltaY*deltaY) < DOUBLE_TAP_SLOP_SQUARE_PX){ + //Then I guess there is a double tap :thonk: + resetDoubleTapState(); + return true; + } + } + + mLastEventTime = eventTime; + mLastX = eventX; + mLastY = eventY; + return false; + } + + /** + * Reset the double tap values. + */ + private void resetDoubleTapState(){ + mLastEventTime = 0; + mLastX = 9999; + mLastY = 9999; + } + +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/ImportControlActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/ImportControlActivity.java new file mode 100644 index 000000000..16afb9246 --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/ImportControlActivity.java @@ -0,0 +1,204 @@ +package net.kdt.pojavlaunch; + +import android.app.Activity; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.provider.OpenableColumns; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.Toast; + +import androidx.annotation.Nullable; + +import net.kdt.pojavlaunch.utils.FileUtils; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * An activity dedicated to importing control files. + */ +public class ImportControlActivity extends Activity { + + private Uri mUriData; + private boolean mHasIntentChanged = true; + private volatile boolean mIsFileVerified = false; + + private EditText mEditText; + + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Tools.initContextConstants(getApplicationContext()); + + setContentView(R.layout.import_control_layout); + mEditText = findViewById(R.id.editText_import_control_file_name); + } + + /** + * Override the previous loaded intent + * @param intent the intent used to replace the old one. + */ + @Override + protected void onNewIntent(Intent intent) { + if(intent != null) setIntent(intent); + mHasIntentChanged = true; + } + + /** + * Update all over again if the intent changed. + */ + @Override + protected void onPostResume() { + super.onPostResume(); + if(!mHasIntentChanged) return; + mIsFileVerified = false; + getUriData(); + mEditText.setText(getNameFromURI(mUriData)); + mHasIntentChanged = false; + + //Import and verify thread + //Kill the app if the file isn't valid. + new Thread(() -> { + importControlFile("TMP_IMPORT_FILE"); + + if(verify())mIsFileVerified = true; + else runOnUiThread(() -> { + Toast.makeText( + ImportControlActivity.this, + getText(R.string.import_control_invalid_file), + Toast.LENGTH_SHORT).show(); + finishAndRemoveTask(); + }); + }).start(); + + //Auto show the keyboard + new Handler(Looper.getMainLooper()).postDelayed(() -> { + InputMethodManager imm = (InputMethodManager) getApplicationContext().getSystemService(INPUT_METHOD_SERVICE); + imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0); + mEditText.setSelection(mEditText.getText().length()); + }, 100); + } + + /** + * Start the import. + * @param view the view which called the function + */ + public void startImport(View view) { + String fileName = trimFileName(mEditText.getText().toString()); + //Step 1 check for suffixes. + if(!isFileNameValid(fileName)){ + Toast.makeText(this, getText(R.string.import_control_invalid_name), Toast.LENGTH_SHORT).show(); + return; + } + if(!mIsFileVerified){ + Toast.makeText(this, getText(R.string.import_control_verifying_file), Toast.LENGTH_LONG).show(); + return; + } + + new File(Tools.CTRLMAP_PATH + "/TMP_IMPORT_FILE.json").renameTo(new File(Tools.CTRLMAP_PATH + "/" + fileName + ".json")); + Toast.makeText(getApplicationContext(), getText(R.string.import_control_done), Toast.LENGTH_SHORT).show(); + finishAndRemoveTask(); + } + + /** + * Copy a the file from the Intent data with a provided name into the controlmap folder. + * @param fileName The file name to use. + * @return whether the file was successfully imported + */ + private boolean importControlFile(String fileName){ + InputStream is; + try { + is = getContentResolver().openInputStream(mUriData); + + OutputStream os = new FileOutputStream(Tools.CTRLMAP_PATH + "/" + fileName + ".json"); + byte[] buffer = new byte[1024]; + while(is.read(buffer) != -1) + os.write(buffer); + + os.close(); + is.close(); + return true; + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + /** + * Tell if the clean version of the filename is valid. + * @param fileName the string to test + * @return whether the filename is valid + */ + private static boolean isFileNameValid(String fileName){ + fileName = trimFileName(fileName); + + if(fileName.isEmpty()) return false; + if (FileUtils.exists(Tools.CTRLMAP_PATH + "/" + fileName + ".json")) return false; + + return true; + } + + /** + * Remove or undesirable chars from the string + * @param fileName The string to trim + * @return The trimmed string + */ + private static String trimFileName(String fileName){ + return fileName + .replace(".json", "") + .replaceAll("%..", "/") + .replace("/", "") + .replace("\\", "") + .trim(); + } + + /** + * Tries to get an Uri from the various sources + */ + private void getUriData(){ + mUriData = getIntent().getData(); + if(mUriData != null) return; + try { + mUriData = getIntent().getClipData().getItemAt(0).getUri(); + }catch (Exception ignored){} + } + + /** + * Verify if the control file is valid + * @return Whether the control file is valid + */ + private static boolean verify(){ + try{ + String jsonLayoutData = Tools.read(Tools.CTRLMAP_PATH + "/TMP_IMPORT_FILE.json"); + JSONObject layoutJobj = new JSONObject(jsonLayoutData); + return layoutJobj.has("version") && layoutJobj.has("mControlDataList"); + + }catch (JSONException | IOException e) { + e.printStackTrace(); + return false; + } + + } + + public String getNameFromURI(Uri uri) { + Cursor c = getContentResolver().query(uri, null, null, null, null); + c.moveToFirst(); + String fileName = c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME)); + c.close(); + return trimFileName(fileName); + } + +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavLauncherActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavLauncherActivity.java index ce17fbea3..bc6037f5d 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavLauncherActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavLauncherActivity.java @@ -317,6 +317,7 @@ public class PojavLauncherActivity extends BaseLauncherActivity PREF_NOTCH_SIZE = getWindow().getDecorView().getRootWindowInsets().getDisplayCutout().getBoundingRects().get(0).width(); }catch (Exception e){ Log.i("NOTCH DETECTION", "No notch detected, or the device if in split screen mode"); + PREF_NOTCH_SIZE = -1; } Tools.updateWindowSize(this); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java index a19904931..dfa198dfb 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java @@ -29,7 +29,9 @@ import org.lwjgl.glfw.*; import android.view.*; import android.widget.Toast; +import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION_CODES.P; +import static android.os.Build.VERSION_CODES.Q; import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_IGNORE_NOTCH; import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_NOTCH_SIZE; @@ -79,7 +81,7 @@ public final class Tools { public static void initContextConstants(Context ctx){ DIR_DATA = ctx.getFilesDir().getParent(); MULTIRT_HOME = DIR_DATA+"/runtimes"; - if(Build.VERSION.SDK_INT >= 29) { + if(SDK_INT >= 29) { DIR_GAME_HOME = ctx.getExternalFilesDir(null).getAbsolutePath(); }else{ DIR_GAME_HOME = new File(Environment.getExternalStorageDirectory(),"games/PojavLauncher").getAbsolutePath(); @@ -338,11 +340,12 @@ public final class Tools { public static DisplayMetrics getDisplayMetrics(Activity ctx) { DisplayMetrics displayMetrics = new DisplayMetrics(); - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && (ctx.isInMultiWindowMode() || ctx.isInPictureInPictureMode())){ + if(SDK_INT >= Build.VERSION_CODES.N && (ctx.isInMultiWindowMode() || ctx.isInPictureInPictureMode()) + || PREF_NOTCH_SIZE == -1 ){ //For devices with free form/split screen, we need window size, not screen size. displayMetrics = ctx.getResources().getDisplayMetrics(); }else{ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (SDK_INT >= Build.VERSION_CODES.R) { ctx.getDisplay().getRealMetrics(displayMetrics); } else { ctx.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics); @@ -828,7 +831,7 @@ public final class Tools { } public static void ignoreNotch(boolean shouldIgnore, Activity ctx){ - if (Build.VERSION.SDK_INT >= P) { + if (SDK_INT >= P) { if (shouldIgnore) { ctx.getWindow().getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; } else { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlData.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlData.java index 0c3938fb9..fd4ba4762 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlData.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlData.java @@ -1,6 +1,10 @@ package net.kdt.pojavlaunch.customcontrols; import android.util.*; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.*; import net.kdt.pojavlaunch.*; import net.kdt.pojavlaunch.prefs.LauncherPreferences; @@ -28,7 +32,16 @@ public class ControlData { // Internal usage only public boolean isHideable; - + + private static WeakReference builder = new WeakReference<>(null); + private static WeakReference expression = new WeakReference<>(null); + private static WeakReference> conversionMap = new WeakReference<>(null); + static { + bypassExpressionBuilder(); + buildConversionMap(); + } + + /** * Both fields below are dynamic position data, auto updates * X and Y position, unlike the original one which uses fixed @@ -167,41 +180,16 @@ public class ControlData { public float insertDynamicPos(String dynamicPos) { - // Values in the map below may be always changed - Map keyValueMap = new ArrayMap<>(); - keyValueMap.put("top", "0"); - keyValueMap.put("left", "0"); - keyValueMap.put("right", Float.toString(CallbackBridge.physicalWidth - getWidth())); - keyValueMap.put("bottom", Float.toString(CallbackBridge.physicalHeight - getHeight())); - keyValueMap.put("width", Float.toString(getWidth())); - keyValueMap.put("height", Float.toString(getHeight())); - keyValueMap.put("screen_width", Integer.toString(CallbackBridge.physicalWidth)); - keyValueMap.put("screen_height", Integer.toString(CallbackBridge.physicalHeight)); - keyValueMap.put("margin", Integer.toString((int) Tools.dpToPx(2))); - keyValueMap.put("preferred_scale", Float.toString(LauncherPreferences.PREF_BUTTONSIZE)); - // Insert value to ${variable} - String insertedPos = JSONUtils.insertSingleJSONValue(dynamicPos, keyValueMap); + String insertedPos = JSONUtils.insertSingleJSONValue(dynamicPos, fillConversionMap()); // Calculate, because the dynamic position contains some math equations return calculate(insertedPos); } private static float calculate(String math) { - return (float) new ExpressionBuilder(math) - .function(new Function("dp", 1) { - @Override - public double apply(double... args) { - return Tools.pxToDp((float) args[0]); - } - }) - .function(new Function("px", 1) { - @Override - public double apply(double... args) { - return Tools.dpToPx((float) args[0]); - } - }) - .build().evaluate(); + setExpression(math); + return (float) builder.get().build().evaluate(); } private static int[] inflateKeycodeArray(int[] keycodes){ @@ -236,4 +224,84 @@ public class ControlData { public void setHeight(float heightInPx){ height = Tools.pxToDp(heightInPx); } + + /** + * Create a weak reference to a builder and its expression field. + * Although VERY bad practice it isn't slower due to saved GC time. + * The normal way requires us to create ONE builder and TWO functions for EACH button. + */ + private static void bypassExpressionBuilder(){ + ExpressionBuilder expressionBuilder = new ExpressionBuilder("1 + 1") + .function(new Function("dp", 1) { + @Override + public double apply(double... args) { + return Tools.pxToDp((float) args[0]); + } + }) + .function(new Function("px", 1) { + @Override + public double apply(double... args) { + return Tools.dpToPx((float) args[0]); + } + }); + builder = new WeakReference<>(expressionBuilder); + try { + expression = new WeakReference<>(builder.get().getClass().getDeclaredField("expression")); + expression.get().setAccessible(true); + expression.get().set(expression.get(), expression.get().getModifiers() & ~Modifier.FINAL); + }catch (Exception ignored){} + } + + /** + * wrapper for the WeakReference to the expressionField. + * @param stringExpression the expression to set. + */ + private static void setExpression(String stringExpression){ + if(builder.get() == null) bypassExpressionBuilder(); + try { + expression.get().set(builder.get(), stringExpression); + }catch (IllegalAccessException e){} + } + + /** + * Build a shared conversion map without the ControlData dependent values + * You need to set the view dependent values before using it. + */ + private static void buildConversionMap() { + // Values in the map below may be always changed + ArrayMap keyValueMap = new ArrayMap<>(10); + keyValueMap.put("top", "0"); + keyValueMap.put("left", "0"); + keyValueMap.put("right", "DUMMY_RIGHT"); + keyValueMap.put("bottom", "DUMMY_BOTTOM"); + keyValueMap.put("width", "DUMMY_WIDTH"); + keyValueMap.put("height", "DUMMY_HEIGHT"); + keyValueMap.put("screen_width", Integer.toString(CallbackBridge.physicalWidth)); + keyValueMap.put("screen_height", Integer.toString(CallbackBridge.physicalHeight)); + keyValueMap.put("margin", Integer.toString((int) Tools.dpToPx(2))); + keyValueMap.put("preferred_scale", Float.toString(LauncherPreferences.PREF_BUTTONSIZE)); + + conversionMap = new WeakReference<>(keyValueMap); + } + + /** + * Fill the conversionMap with controlData dependent values. + * The returned valueMap should NOT be kept in memory. + * @return the valueMap to use. + */ + private Map fillConversionMap(){ + ArrayMap valueMap = conversionMap.get(); + if (valueMap == null){ + buildConversionMap(); + valueMap = conversionMap.get(); + } + + valueMap.put("right", Float.toString(CallbackBridge.physicalWidth - getWidth())); + valueMap.put("bottom", Float.toString(CallbackBridge.physicalHeight - getHeight())); + valueMap.put("width", Float.toString(getWidth())); + valueMap.put("height", Float.toString(getHeight())); + + return valueMap; + } + } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlDrawerData.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlDrawerData.java index aa1478f1a..4db6526e7 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlDrawerData.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlDrawerData.java @@ -8,6 +8,7 @@ import static net.kdt.pojavlaunch.customcontrols.ControlDrawerData.Orientation.D import static net.kdt.pojavlaunch.customcontrols.ControlDrawerData.Orientation.LEFT; import static net.kdt.pojavlaunch.customcontrols.ControlDrawerData.Orientation.RIGHT; import static net.kdt.pojavlaunch.customcontrols.ControlDrawerData.Orientation.UP; +import static net.kdt.pojavlaunch.customcontrols.ControlDrawerData.Orientation.FREE; import androidx.annotation.NonNull; @@ -21,37 +22,32 @@ public class ControlDrawerData { DOWN, LEFT, UP, - RIGHT + RIGHT, + FREE } public static Orientation[] getOrientations(){ - return new Orientation[]{DOWN,LEFT,UP,RIGHT}; + return new Orientation[]{DOWN,LEFT,UP,RIGHT,FREE}; } public static int orientationToInt(Orientation orientation){ switch (orientation){ - case DOWN: - return 0; - case LEFT: - return 1; - case UP: - return 2; - case RIGHT: - return 3; + case DOWN: return 0; + case LEFT: return 1; + case UP: return 2; + case RIGHT: return 3; + case FREE: return 4; } return -1; } public static Orientation intToOrientation(int by){ switch (by){ - case 0: - return Orientation.DOWN; - case 1: - return Orientation.LEFT; - case 2: - return Orientation.UP; - case 3: - return RIGHT; + case 0: return DOWN; + case 1: return LEFT; + case 2: return UP; + case 3: return RIGHT; + case 4: return FREE; } return null; } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlLayout.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlLayout.java index 9cdb61725..dfaf130d8 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlLayout.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlLayout.java @@ -146,7 +146,10 @@ public class ControlLayout extends FrameLayout view.setAlpha(view.getProperties().opacity); view.setFocusable(false); view.setFocusableInTouchMode(false); + }else{ + view.setVisible(drawer.areButtonsVisible); } + drawer.addButton(view); addView(view); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/CustomControls.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/CustomControls.java index f5651ed40..66d264aeb 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/CustomControls.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/CustomControls.java @@ -51,13 +51,13 @@ public class CustomControls { this.mControlDataList.add(new ControlData(ctx, R.string.control_jump, new int[]{LWJGLGLFWKeycode.GLFW_KEY_SPACE}, "${right} - ${margin} * 2 - ${width}", "${bottom} - ${margin} * 2 - ${height}", true)); //The default controls are conform to the V2 - version = 3; + version = 4; } public void save(String path) throws IOException { - //Current version is the V2.3 so the version as to be marked as 3 ! - version = 3; + //Current version is the V2.4 so the version as to be marked as 4 ! + version = 4; Tools.write(path, Tools.GLOBAL_GSON.toJson(this)); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/LayoutConverter.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/LayoutConverter.java index b94911342..c0035ebac 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/LayoutConverter.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/LayoutConverter.java @@ -28,7 +28,7 @@ public class LayoutConverter { CustomControls layout = LayoutConverter.convertV2Layout(layoutJobj); layout.save(jsonPath); return layout; - }else if (layoutJobj.getInt("version") == 3) { + }else if (layoutJobj.getInt("version") == 3 || layoutJobj.getInt("version") == 4) { return Tools.GLOBAL_GSON.fromJson(jsonLayoutData, CustomControls.class); }else{ return null; diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/TouchCharInput.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/TouchCharInput.java index f20a6d0e5..5cb26354a 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/TouchCharInput.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/TouchCharInput.java @@ -46,23 +46,18 @@ public class TouchCharInput extends androidx.appcompat.widget.AppCompatEditText @Override protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { super.onTextChanged(text, start, lengthBefore, lengthAfter); - if(isDoingInternalChanges){ - isDoingInternalChanges = false; - return; + if(isDoingInternalChanges)return; + + for(int i=0; i< lengthBefore; ++i){ + CallbackBridge.sendKeycode(LWJGLGLFWKeycode.GLFW_KEY_BACKSPACE, '\u0008', 0, 0, true); } - if(lengthAfter < lengthBefore){ - for(int i=0; i< lengthBefore-lengthAfter; ++i){ - CallbackBridge.sendKeycode(LWJGLGLFWKeycode.GLFW_KEY_BACKSPACE, '\u0008', 0, 0, true); - } - }else{ - for(int i=lengthBefore, index=lengthBefore+start; i < lengthAfter; ++i){ - //I didn't know F25 existed before that. I just need a full fat keycode for mc 1.13+ - CallbackBridge.sendKeycode(LWJGLGLFWKeycode.GLFW_KEY_F25, text.charAt(index), 0, 0, true); - index ++; - } + for(int i=start, count = 0; count < lengthAfter; ++i){ + CallbackBridge.sendChar(text.charAt(i), 0); + ++count; } - clear(); + //Reset the keyboard state + if(text.length() < 1) clear(); } @@ -88,10 +83,6 @@ public class TouchCharInput extends androidx.appcompat.widget.AppCompatEditText return super.onKeyPreIme(keyCode, event); } - @Override - public void setSelection(int index) { - super.setSelection(5); - } /** * Toggle on and off the soft keyboard, depending of the state @@ -125,8 +116,9 @@ public class TouchCharInput extends androidx.appcompat.widget.AppCompatEditText isDoingInternalChanges = true; //Braille space, doesn't trigger keyboard auto-complete //replacing directly the text without though setText avoids notifying changes - getText().replace(0, getText().length(),"\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800"); - setSelection(5); + setText(" "); + setSelection(getText().length()); + isDoingInternalChanges = false; } /** @@ -159,7 +151,6 @@ public class TouchCharInput extends androidx.appcompat.widget.AppCompatEditText } - /** * This function deals with anything that has to be executed when the constructor is called */ diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlButton.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlButton.java index 0e86230f3..22c25e977 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlButton.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlButton.java @@ -144,6 +144,12 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp setVisibility(isVisible ? VISIBLE : GONE); } + @Override + protected void onVisibilityChanged(View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + setWillNotDraw(visibility == GONE); + } + @Override public void setX(float x) { super.setX(x); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlDrawer.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlDrawer.java index 5b5ea5472..a7fbb154c 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlDrawer.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlDrawer.java @@ -59,6 +59,7 @@ public class ControlDrawer extends ControlButton { private void alignButtons(){ if(buttons == null) return; + if(drawerData.orientation == ControlDrawerData.Orientation.FREE) return; for(int i=0; i < buttons.size(); ++i){ switch (drawerData.orientation){ case RIGHT: diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlSubButton.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlSubButton.java index 1692df4a3..ac349471f 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlSubButton.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlSubButton.java @@ -9,6 +9,7 @@ import android.view.ViewGroup; import net.kdt.pojavlaunch.SingleTapConfirm; import net.kdt.pojavlaunch.customcontrols.ControlData; +import net.kdt.pojavlaunch.customcontrols.ControlDrawerData; import net.kdt.pojavlaunch.customcontrols.ControlLayout; public class ControlSubButton extends ControlButton { @@ -19,9 +20,7 @@ public class ControlSubButton extends ControlButton { super(layout, properties); this.parentDrawer = parentDrawer; - //Delayed to let the button inflate first - if(!layout.getModifiable()) - new Handler(Looper.getMainLooper()).postDelayed(() -> setVisibility(parentDrawer.areButtonsVisible ? VISIBLE : GONE), 0); + filterProperties(); } @@ -50,7 +49,7 @@ public class ControlSubButton extends ControlButton { @Override public boolean onTouchEvent(MotionEvent event) { - if(!mModifiable){ + if(!mModifiable || parentDrawer.drawerData.orientation == ControlDrawerData.Orientation.FREE){ return super.onTouchEvent(event); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/gamepad/Gamepad.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/gamepad/Gamepad.java index e2b0f58fa..dfb56b474 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/gamepad/Gamepad.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/gamepad/Gamepad.java @@ -46,7 +46,6 @@ public class Gamepad { private float lastVerticalValue = 0.0f; private final double mouseMaxAcceleration = 2f; - private double acceleration = 0.0f; private double mouseMagnitude; private double mouseAngle; @@ -57,17 +56,32 @@ public class Gamepad { private GamepadMap currentMap = gameMap; private boolean lastGrabbingState = true; - private final boolean hasDigitalTriggers; + private final boolean mModifierDigitalTriggers; + private boolean mModifierSwappedAxis = true; //Triggers and right stick axis are swapped. - private final Handler handler = new Handler(Looper.getMainLooper()); + private final Handler inputHandler = new Handler(Looper.getMainLooper()); private final Runnable switchStateRunnable; public Gamepad(BaseMainActivity gameActivity, InputDevice inputDevice){ //Toast.makeText(gameActivity.getApplicationContext(),"GAMEPAD CREATED", Toast.LENGTH_LONG).show(); + for(InputDevice.MotionRange range : inputDevice.getMotionRanges()){ + if(range.getAxis() == MotionEvent.AXIS_RTRIGGER + || range.getAxis() == MotionEvent.AXIS_LTRIGGER + || range.getAxis() == MotionEvent.AXIS_GAS + || range.getAxis() == MotionEvent.AXIS_BRAKE){ + mModifierSwappedAxis = false; + break; + } + + } leftJoystick = new GamepadJoystick(MotionEvent.AXIS_X, MotionEvent.AXIS_Y, inputDevice); - rightJoystick = new GamepadJoystick(MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ, inputDevice); - hasDigitalTriggers = inputDevice.hasKeys(KeyEvent.KEYCODE_BUTTON_R2)[0]; + if(!mModifierSwappedAxis) + rightJoystick = new GamepadJoystick(MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ, inputDevice); + else + rightJoystick = new GamepadJoystick(MotionEvent.AXIS_RX, MotionEvent.AXIS_RY, inputDevice); + + mModifierDigitalTriggers = inputDevice.hasKeys(KeyEvent.KEYCODE_BUTTON_R2)[0]; this.gameActivity = gameActivity; pointerView = this.gameActivity.findViewById(R.id.console_pointer); @@ -81,11 +95,11 @@ public class Gamepad { updateGrabbingState(); tick(); - handler.postDelayed(this, 16); + inputHandler.postDelayed(this, 16); } }; - handler.postDelayed(handlerRunnable, 16); + inputHandler.postDelayed(handlerRunnable, 16); //Initialize runnables to be used by the input system, avoiding generating one each time is better memory. switchStateRunnable = () -> { @@ -93,7 +107,7 @@ public class Gamepad { if(lastGrabbingState){ currentMap = gameMap; pointerView.setVisibility(View.INVISIBLE); - mouseSensitivity = 22 / gameActivity.sensitivityFactor; //sensitivity in menus is resolution dependent. + mouseSensitivity = 18; return; } @@ -105,7 +119,8 @@ public class Gamepad { CallbackBridge.sendCursorPos(gameActivity.mouse_x, gameActivity.mouse_y); placePointerView(CallbackBridge.physicalWidth/2, CallbackBridge.physicalHeight/2); pointerView.setVisibility(View.VISIBLE); - mouseSensitivity = 14; //sensitivity in game doesn't need to be resolution dependent + //sensitivity in menu is MC and HARDWARE resolution dependent + mouseSensitivity = 19 * gameActivity.scaleFactor / gameActivity.sensitivityFactor; }; } @@ -117,7 +132,7 @@ public class Gamepad { if(lastHorizontalValue != 0 || lastVerticalValue != 0){ GamepadJoystick currentJoystick = lastGrabbingState ? leftJoystick : rightJoystick; - acceleration = (mouseMagnitude - currentJoystick.getDeadzone())/(1 - currentJoystick.getDeadzone()); + double acceleration = (mouseMagnitude - currentJoystick.getDeadzone()) / (1 - currentJoystick.getDeadzone()); acceleration = Math.pow(acceleration, mouseMaxAcceleration); if(acceleration > 1) acceleration = 1; @@ -180,9 +195,15 @@ public class Gamepad { } private void updateAnalogTriggers(MotionEvent event){ - if(!hasDigitalTriggers){ - getCurrentMap().TRIGGER_LEFT.update((event.getAxisValue(MotionEvent.AXIS_LTRIGGER) > 0.5) || (event.getAxisValue(MotionEvent.AXIS_BRAKE) > 0.5)); - getCurrentMap().TRIGGER_RIGHT.update((event.getAxisValue(MotionEvent.AXIS_RTRIGGER) > 0.5) || (event.getAxisValue(MotionEvent.AXIS_GAS) > 0.5)); + if(!mModifierDigitalTriggers){ + getCurrentMap().TRIGGER_LEFT.update( + (event.getAxisValue(MotionEvent.AXIS_LTRIGGER) > 0.5) + || (event.getAxisValue(MotionEvent.AXIS_BRAKE) > 0.5) + || (mModifierSwappedAxis &&(event.getAxisValue(MotionEvent.AXIS_Z) > 0.5)) ); + getCurrentMap().TRIGGER_RIGHT.update( + (event.getAxisValue( MotionEvent.AXIS_RTRIGGER) > 0.5) + || (event.getAxisValue(MotionEvent.AXIS_GAS) > 0.5) + || (mModifierSwappedAxis && event.getAxisValue(MotionEvent.AXIS_RZ) > 0.5) ); } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/gamepad/GamepadJoystick.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/gamepad/GamepadJoystick.java index 6a8afdf12..22cb0b999 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/gamepad/GamepadJoystick.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/gamepad/GamepadJoystick.java @@ -20,7 +20,7 @@ public class GamepadJoystick { public static final int DIRECTION_SOUTH = 6; public static final int DIRECTION_SOUTH_EAST = 7; - private float deadzone; + private final InputDevice device; private final int verticalAxis; private final int horizontalAxis; @@ -28,13 +28,9 @@ public class GamepadJoystick { public GamepadJoystick(int horizontalAxis, int verticalAxis, InputDevice device){ this.verticalAxis = verticalAxis; this.horizontalAxis = horizontalAxis; + this.device = device; - //Some controllers aren't recognized as such by android, so we fallback to a default value of 0.2 - //And some others don't report their MotionRange. This was the case with the xbox one series S controller. - //try { deadzone = Math.max(device.getMotionRange(verticalAxis).getFlat(), device.getMotionRange(horizontalAxis).getFlat()) * 1.9f; } - //catch (NullPointerException e){ deadzone = 0.2f; } - deadzone = 0.2f; } public double getAngleRadian(MotionEvent event){ @@ -71,6 +67,7 @@ public class GamepadJoystick { //to make it seem like there was no deadzone in the first place double magnitude = getMagnitude(event); + float deadzone = getDeadzone(); if (magnitude < deadzone) return 0; return (float) ( (event.getAxisValue(axis) / magnitude) * ((magnitude - deadzone) / (1 - deadzone)) ); @@ -83,12 +80,20 @@ public class GamepadJoystick { public int getHeightDirection(MotionEvent event){ - if(getMagnitude(event) <= deadzone) return DIRECTION_NONE; + if(getMagnitude(event) <= getDeadzone()) return DIRECTION_NONE; return ((int) ((getAngleDegree(event)+22.5)/45)) % 8; } - + /** + * Get the deadzone from the Input device linked to this joystick + * Some controller aren't supported, fallback to 0.2 if that the case. + * @return the deadzone of the joystick + */ public float getDeadzone() { - return deadzone; + try{ + return Math.max(device.getMotionRange(horizontalAxis).getFlat() * 1.9f, 0.2f); + }catch (Exception e){ + return 0.2f; + } } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/ActionPopupWindow.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/ActionPopupWindow.java index f4052356b..f64d3f8c6 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/ActionPopupWindow.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/ActionPopupWindow.java @@ -43,7 +43,7 @@ public class ActionPopupWindow extends PinnedPopupWindow implements OnClickListe private TextView mDeleteTextView; private TextView mCloneTextView; - private ControlButton editedButton; + private final ControlButton editedButton; public ActionPopupWindow(HandleView handleView, ControlButton button){ super(handleView); @@ -145,6 +145,11 @@ public class ActionPopupWindow extends PinnedPopupWindow implements OnClickListe cloneData.properties.dynamicX = "0.5 * ${screen_width}"; cloneData.properties.dynamicY = "0.5 * ${screen_height}"; ((ControlLayout) mHandleView.mView.getParent()).addDrawer(cloneData); + }else if(editedButton instanceof ControlSubButton){ + ControlData cloneData = new ControlData(editedButton.getProperties()); + cloneData.dynamicX = "0.5 * ${screen_width}"; + cloneData.dynamicY = "0.5 * ${screen_height}"; + ((ControlLayout) mHandleView.mView.getParent()).addSubButton(((ControlSubButton) editedButton).parentDrawer, cloneData); }else{ ControlData cloneData = new ControlData(editedButton.getProperties()); cloneData.dynamicX = "0.5 * ${screen_width}"; diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/FileUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/FileUtils.java index 3467b7ca9..44cc8c5fb 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/FileUtils.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/FileUtils.java @@ -1,5 +1,9 @@ package net.kdt.pojavlaunch.utils; -public class FileUtils -{ +import java.io.File; + +public class FileUtils { + public static boolean exists(String filePath){ + return new File(filePath).exists(); + } } \ No newline at end of file diff --git a/app_pojavlauncher/src/main/java/org/lwjgl/glfw/CallbackBridge.java b/app_pojavlauncher/src/main/java/org/lwjgl/glfw/CallbackBridge.java index fc4538fb2..7582e7468 100644 --- a/app_pojavlauncher/src/main/java/org/lwjgl/glfw/CallbackBridge.java +++ b/app_pojavlauncher/src/main/java/org/lwjgl/glfw/CallbackBridge.java @@ -79,6 +79,11 @@ public class CallbackBridge { // sendData(JRE_TYPE_KEYCODE_CONTROL, keycode, Character.toString(keychar), Boolean.toString(isDown), modifiers); } + public static void sendChar(char keychar, int modifiers){ + nativeSendCharMods(keychar,modifiers); + nativeSendChar(keychar); + } + public static void sendMouseKeycode(int button, int modifiers, boolean isDown) { DEBUG_STRING.append("MouseKey=").append(button).append(", down=").append(isDown).append("\n"); // if (isGrabbing()) DEBUG_STRING.append("MouseGrabStrace: " + android.util.Log.getStackTraceString(new Throwable()) + "\n"); diff --git a/app_pojavlauncher/src/main/res/drawable/menu_background.xml b/app_pojavlauncher/src/main/res/drawable/menu_background.xml new file mode 100644 index 000000000..1bf066e6d --- /dev/null +++ b/app_pojavlauncher/src/main/res/drawable/menu_background.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app_pojavlauncher/src/main/res/layout/import_control_layout.xml b/app_pojavlauncher/src/main/res/layout/import_control_layout.xml new file mode 100644 index 000000000..4e5704f38 --- /dev/null +++ b/app_pojavlauncher/src/main/res/layout/import_control_layout.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + diff --git a/app_pojavlauncher/src/main/res/layout/launcher_login_v3.xml b/app_pojavlauncher/src/main/res/layout/launcher_login_v3.xml index cfdea5611..3ea7c9697 100644 --- a/app_pojavlauncher/src/main/res/layout/launcher_login_v3.xml +++ b/app_pojavlauncher/src/main/res/layout/launcher_login_v3.xml @@ -27,6 +27,11 @@ android:id="@+id/login_spinner_language" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:background="@drawable/menu_background" + android:layout_marginStart="5dp" + android:layout_marginTop="5dp" + android:gravity="center" + android:paddingVertical="3dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -47,7 +52,7 @@ app:layout_constraintWidth_max="480dp" android:layout_height="300dp" - android:background="#272727" + android:background="@drawable/menu_background" android:translationZ="-1dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" diff --git a/app_pojavlauncher/src/main/res/layout/main_with_customctrl.xml b/app_pojavlauncher/src/main/res/layout/main_with_customctrl.xml index 6fbbb4b10..a66d0bd91 100644 --- a/app_pojavlauncher/src/main/res/layout/main_with_customctrl.xml +++ b/app_pojavlauncher/src/main/res/layout/main_with_customctrl.xml @@ -53,8 +53,8 @@ android:layout_height="1dp" android:background="@android:color/darker_gray" android:ems="10" - android:imeOptions="flagNoFullscreen|flagNoExtractUi|flagNoPersonalizedLearning|actionSend" - android:inputType="textVisiblePassword" + android:imeOptions="flagNoFullscreen|flagNoExtractUi|flagNoPersonalizedLearning|actionDone" + android:inputType="textFilter|textImeMultiLine|textAutoComplete|textAutoCorrect" tools:ignore="TouchTargetSizeCheck" /> diff --git a/app_pojavlauncher/src/main/res/values/strings.xml b/app_pojavlauncher/src/main/res/values/strings.xml index 520b29f9e..b84f3f14d 100644 --- a/app_pojavlauncher/src/main/res/values/strings.xml +++ b/app_pojavlauncher/src/main/res/values/strings.xml @@ -160,6 +160,14 @@ Send custom keycode Scale up Scale down + + + Import controls + Invalid or corrupted file + import controls + File is being verified, please wait and try again… + Invalid name or file already exists + Importation done