Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app_pojavlauncher/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|keyboard|navigation|uiMode"
android:launchMode="singleTop"
android:process=":game"
android:windowSoftInputMode="adjustResize"
android:screenOrientation="sensorLandscape" />

<provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import android.content.ServiceConnection;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Build;
Expand All @@ -32,15 +33,22 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.Toast;

import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsAnimationCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.drawerlayout.widget.DrawerLayout;

import com.kdt.LoggerView;
Expand Down Expand Up @@ -75,9 +83,10 @@

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Objects;

public class MainActivity extends BaseActivity implements ControlButtonMenuListener, EditorExitable, ServiceConnection {
public class MainActivity extends BaseActivity implements ControlButtonMenuListener, EditorExitable, ServiceConnection, TouchControllerInputView.InputAreaRectListener {
public static volatile ClipboardManager GLOBAL_CLIPBOARD;
public static final String TAG = "MainActivity";
public static final String INTENT_MINECRAFT_VERSION = "intent_version";
Expand All @@ -96,6 +105,12 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
private GyroControl mGyroControl = null;
private ControlLayout mControlLayout;
private HotbarView mHotbarView;
private FrameLayout contentFrame;

@Nullable
private RectF inputAreaRect;
private int imeHeight;
private boolean hasOngoingImeAnimation;

MinecraftProfile minecraftProfile;

Expand All @@ -110,6 +125,8 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);

if (LauncherPreferences.PREF_GAMEPAD_SDL_PASSTHRU) {
// TODO: Use lower level HID capture that needs a dialogue box from the user for the
// app to fully take focus of the input devices. Might cause issues with older android
Expand Down Expand Up @@ -185,6 +202,39 @@ public void onCreate(Bundle savedInstanceState) {
MCOptionUtils.addMCOptionListener(optionListener);
mControlLayout.setModifiable(false);

// Listen to IME insets animation
ViewCompat.setWindowInsetsAnimationCallback(contentFrame, new WindowInsetsAnimationCompat.Callback(WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP) {
@Override
public void onPrepare(@NonNull WindowInsetsAnimationCompat animation) {
if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) {
hasOngoingImeAnimation = true;
}
}

@NonNull
@Override
public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets, @NonNull List<WindowInsetsAnimationCompat> runningAnimations) {
imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
refreshImeTranslation();
return insets;
}

@Override
public void onEnd(@NonNull WindowInsetsAnimationCompat animation) {
if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) {
hasOngoingImeAnimation = false;
}
}
});
ViewCompat.setOnApplyWindowInsetsListener(contentFrame, (v, insets) -> {
// Only refresh translation if IME change insets itself, not the animation
if (!hasOngoingImeAnimation) {
imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
refreshImeTranslation();
}
return insets;
});

// Set the activity for the executor. Must do this here, or else Tools.showErrorRemote() may not
// execute the correct method
ContextExecutor.setActivity(this);
Expand All @@ -209,7 +259,7 @@ protected void initLayout(int resId) {
GLOBAL_CLIPBOARD = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
touchCharInput.setCharacterSender(new LwjglCharSender());

touchControllerInputView.setSize(minecraftGLView.getWidth(), minecraftGLView.getHeight());
touchControllerInputView.setInputAreaRectListener(this);

if(minecraftProfile.pojavRendererName != null) {
Log.i("RdrDebug","__P_renderer="+minecraftProfile.pojavRendererName);
Expand Down Expand Up @@ -260,6 +310,9 @@ protected void initLayout(int resId) {
touchpad.post(() -> touchpad.switchState());
}

// At this time, correct size is known
touchControllerInputView.setSize(minecraftGLView.getWidth(), minecraftGLView.getHeight());

runCraft(finalVersion, mVersionInfo);
}catch (Throwable e){
Tools.showErrorRemote(e);
Expand Down Expand Up @@ -314,6 +367,7 @@ private void bindValues(){
touchControllerInputView = findViewById(R.id.touch_controller_input);
mDrawerPullButton = findViewById(R.id.drawer_button);
mHotbarView = findViewById(R.id.hotbar_view);
contentFrame = findViewById(R.id.content_frame);
}

@Override
Expand Down Expand Up @@ -509,6 +563,15 @@ public void onResolutionChanged() {
mHotbarView.onResolutionChanged();
}

@Override
public void onKeyboardPanningChanged() {
if (LauncherPreferences.PREF_KEYBOARD_PANNING) {
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My commit may still conflict with your logic as it uses pan rather than resize. I'll test and fix this once you're done with your part. Just leaving a note here so it isn't forgotten.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use android:windowSoftInputMode="adjustResize" for all cases, so there is no need to use setSoftInputMode here.

The reason why I do this is you can use window insets animation only when softInputMode is set to adjustResize.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SOFT_INPUT_ADJUST_PAN doesn't provide seamless animation either. The best way to handle it is using WindowInsets API.

@alexytomi alexytomi Jun 9, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I wasn't familiar with this, you're doing a far better job at it than me, thanks!

I used setSoftInputMode to be able to toggle the animation on and off in case anyone wanted the old behavior.

} else {
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
}

@Override
public void onGyroStateChanged() {
mGyroControl.updateOrientation();
Expand Down Expand Up @@ -701,4 +764,33 @@ public void onTrimMemory(int level) {
public void onBackPressed() {
super.onBackPressed();
}

@Override
public void updateInputAreaRect(@Nullable RectF rect) {
inputAreaRect = rect;
refreshImeTranslation();
}

private void refreshImeTranslation() {
if (imeHeight == 0) {
// Early exit
contentFrame.setTranslationY(0);
return;
}

int inputAreaBottom;
if (inputAreaRect != null) {
inputAreaBottom = (int) inputAreaRect.bottom;
} else if (LauncherPreferences.PREF_KEYBOARD_PANNING) {
inputAreaBottom = contentFrame.getHeight();
} else {
contentFrame.setTranslationY(0);
return;
}

int bottomDistance = contentFrame.getHeight() - inputAreaBottom;
int bottomPadding = Math.max(imeHeight - bottomDistance, 0);

contentFrame.setTranslationY(-bottomPadding);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,11 @@ public boolean getDisplayState() {

@Override
public void applyMotionVector(float x, float y) {
mMouseX = Math.max(0, Math.min(currentDisplayMetrics.widthPixels, mMouseX + x * LauncherPreferences.PREF_MOUSESPEED));
mMouseY = Math.max(0, Math.min(currentDisplayMetrics.heightPixels, mMouseY + y * LauncherPreferences.PREF_MOUSESPEED));
updateMousePosition();
if (mDisplayState) { // Make sure no motion leaks through when disabling a moving cursor
mMouseX = Math.max(0, Math.min(currentDisplayMetrics.widthPixels, mMouseX + x * LauncherPreferences.PREF_MOUSESPEED));
mMouseY = Math.max(0, Math.min(currentDisplayMetrics.heightPixels, mMouseY + y * LauncherPreferences.PREF_MOUSESPEED));
updateMousePosition();
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public class LauncherPreferences {
public static int PREF_TOUCHCONTROLLER_VIBRATE_LENGTH = 100;

public static boolean PREF_MOUSE_GRAB_FORCE = false;
public static boolean PREF_KEYBOARD_PANNING = true;


public static void loadPreferences(Context ctx) {
Expand Down Expand Up @@ -121,6 +122,7 @@ public static void loadPreferences(Context ctx) {
PREF_FORCE_ENABLE_TOUCHCONTROLLER = DEFAULT_PREF.getBoolean("forceEnableTouchController", false);
PREF_TOUCHCONTROLLER_VIBRATE_LENGTH = DEFAULT_PREF.getInt("touchControllerVibrateLength", 100);
PREF_MOUSE_GRAB_FORCE = DEFAULT_PREF.getBoolean("always_grab_mouse", false);
PREF_KEYBOARD_PANNING = DEFAULT_PREF.getBoolean("keyboardPanning", true);

String argLwjglLibname = "-Dorg.lwjgl.opengl.libname=";
for (String arg : JREUtils.parseJavaArguments(PREF_CUSTOM_JAVA_ARGS)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_GYRO_INVERT_X;
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_GYRO_INVERT_Y;
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_GYRO_SENSITIVITY;
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_KEYBOARD_PANNING;
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_LONGPRESS_TRIGGER;
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_MOUSESPEED;
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_MOUSE_GRAB_FORCE;
Expand Down Expand Up @@ -32,11 +33,11 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView {

private SharedPreferences.Editor mEditor;
@SuppressLint("UseSwitchCompatOrMaterialCode")
private Switch mGyroSwitch, mGyroXSwitch, mGyroYSwitch, mGestureSwitch, mMouseGrabSwitch;
private Switch mGyroSwitch, mGyroXSwitch, mGyroYSwitch, mGestureSwitch, mMouseGrabSwitch, mKeyboardPanningSwitch;
private CustomSeekbar mGyroSensitivityBar, mMouseSpeedBar, mGestureDelayBar, mResolutionBar;
private TextView mGyroSensitivityText, mGyroSensitivityDisplayText, mMouseSpeedText, mGestureDelayText, mGestureDelayDisplayText, mResolutionText;

private boolean mOriginalGyroEnabled, mOriginalGyroXEnabled, mOriginalGyroYEnabled, mOriginalGestureDisabled, mOriginalMouseGrab;
private boolean mOriginalGyroEnabled, mOriginalGyroXEnabled, mOriginalGyroYEnabled, mOriginalGestureDisabled, mOriginalMouseGrab, mOriginalKeyboardPanning;
private float mOriginalGyroSensitivity, mOriginalMouseSpeed, mOriginalResolution;
private int mOriginalGestureDelay;

Expand Down Expand Up @@ -67,6 +68,7 @@ private void bindLayout() {
mGyroYSwitch = mDialogContent.findViewById(R.id.checkboxGyroY);
mGestureSwitch = mDialogContent.findViewById(R.id.checkboxGesture);
mMouseGrabSwitch = mDialogContent.findViewById(R.id.always_grab_mouse_side_dialog);
mKeyboardPanningSwitch = mDialogContent.findViewById(R.id.checkboxKeyboardPanning);

mGyroSensitivityBar = mDialogContent.findViewById(R.id.editGyro_seekbar);
mMouseSpeedBar = mDialogContent.findViewById(R.id.editMouseSpeed_seekbar);
Expand All @@ -89,6 +91,7 @@ private void setupListeners() {
mOriginalGyroYEnabled = PREF_GYRO_INVERT_Y;
mOriginalGestureDisabled = PREF_DISABLE_GESTURES;
mOriginalMouseGrab = PREF_MOUSE_GRAB_FORCE;
mOriginalKeyboardPanning = PREF_KEYBOARD_PANNING;

mOriginalGyroSensitivity = PREF_GYRO_SENSITIVITY;
mOriginalMouseSpeed = PREF_MOUSESPEED;
Expand All @@ -100,6 +103,7 @@ private void setupListeners() {
mGyroYSwitch.setChecked(mOriginalGyroYEnabled);
mGestureSwitch.setChecked(mOriginalGestureDisabled);
mMouseGrabSwitch.setChecked(mOriginalMouseGrab);
mKeyboardPanningSwitch.setChecked(mOriginalKeyboardPanning);

mGyroSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
PREF_ENABLE_GYRO = isChecked;
Expand Down Expand Up @@ -131,6 +135,12 @@ private void setupListeners() {
mEditor.putBoolean("always_grab_mouse", isChecked);
});

mKeyboardPanningSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
PREF_KEYBOARD_PANNING = isChecked;
onKeyboardPanningChanged();
mEditor.putBoolean("keyboardPanning", isChecked);
});

mGyroSensitivityBar.setOnSeekBarChangeListener((SimpleSeekBarListener) (seekBar, progress, fromUser) -> {
PREF_GYRO_SENSITIVITY = progress / 100f;
mEditor.putInt("gyroSensitivity", progress);
Expand Down Expand Up @@ -217,6 +227,7 @@ private void removeListeners() {
mGyroYSwitch.setOnCheckedChangeListener(null);
mGestureSwitch.setOnCheckedChangeListener(null);
mMouseGrabSwitch.setOnCheckedChangeListener(null);
mKeyboardPanningSwitch.setOnCheckedChangeListener(null);

mGyroSensitivityBar.setOnSeekBarChangeListener(null);
mMouseSpeedBar.setOnSeekBarChangeListener(null);
Expand All @@ -241,6 +252,7 @@ public void cancel() {
PREF_GYRO_INVERT_Y = mOriginalGyroYEnabled;
PREF_DISABLE_GESTURES = mOriginalGestureDisabled;
PREF_MOUSE_GRAB_FORCE = mOriginalMouseGrab;
PREF_KEYBOARD_PANNING = mOriginalKeyboardPanning;

PREF_GYRO_SENSITIVITY = mOriginalGyroSensitivity;
PREF_MOUSESPEED = mOriginalMouseSpeed;
Expand All @@ -249,6 +261,7 @@ public void cancel() {

onGyroStateChanged();
onResolutionChanged();
onKeyboardPanningChanged();
}

disappear(true);
Expand All @@ -257,6 +270,9 @@ public void cancel() {
/** Called when the resolution is changed. Use {@link LauncherPreferences#PREF_SCALE_FACTOR} */
public abstract void onResolutionChanged();

/** Called when the keyboard panning state is changed. Use {@link LauncherPreferences#PREF_KEYBOARD_PANNING} */
public abstract void onKeyboardPanningChanged();

/** Called when the gyro state is changed.
* Use {@link LauncherPreferences#PREF_ENABLE_GYRO}
* Use {@link LauncherPreferences#PREF_GYRO_INVERT_X}
Expand Down
Loading