mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-08-03 23:56:52 -04:00
fix(Pointer/Capture): Make non-opportunistic grabbing opt-in
Adds a new config option whether or not you want your mouse to be able to click your touch controls! Default is you can! Should also now let DeX users get their cursor out of the window by just moving it by default. This reverts commit 7631e3c9e70af03a294358bbfc7b809e21a1c690.
This commit is contained in:
parent
6e83225651
commit
6e6c20c450
@ -35,6 +35,7 @@ import android.provider.OpenableColumns;
|
|||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.InputDevice;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
@ -1557,4 +1558,19 @@ public final class Tools {
|
|||||||
}
|
}
|
||||||
return Integer.parseInt(iVersionArray[0] + iVersionArray[1] + iVersionArray[2]);
|
return Integer.parseInt(iVersionArray[0] + iVersionArray[1] + iVersionArray[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isPointerDeviceConnected() {
|
||||||
|
int[] deviceIds = InputDevice.getDeviceIds();
|
||||||
|
for (int id : deviceIds) {
|
||||||
|
InputDevice device = InputDevice.getDevice(id);
|
||||||
|
if (device == null) continue;
|
||||||
|
int sources = device.getSources();
|
||||||
|
if ((sources & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE
|
||||||
|
|| (sources & InputDevice.SOURCE_TOUCHPAD) == InputDevice.SOURCE_TOUCHPAD
|
||||||
|
|| (sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
package net.kdt.pojavlaunch.customcontrols.mouse;
|
package net.kdt.pojavlaunch.customcontrols.mouse;
|
||||||
|
|
||||||
|
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_MOUSE_GRAB_FORCE;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import net.kdt.pojavlaunch.GrabListener;
|
||||||
import net.kdt.pojavlaunch.MinecraftGLSurface;
|
import net.kdt.pojavlaunch.MinecraftGLSurface;
|
||||||
import net.kdt.pojavlaunch.Tools;
|
import net.kdt.pojavlaunch.Tools;
|
||||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||||
@ -15,7 +20,7 @@ import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
|||||||
import org.lwjgl.glfw.CallbackBridge;
|
import org.lwjgl.glfw.CallbackBridge;
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
public class AndroidPointerCapture implements ViewTreeObserver.OnWindowFocusChangeListener, View.OnCapturedPointerListener {
|
public class AndroidPointerCapture implements ViewTreeObserver.OnWindowFocusChangeListener, View.OnCapturedPointerListener, GrabListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
private static final float TOUCHPAD_SCROLL_THRESHOLD = 1;
|
private static final float TOUCHPAD_SCROLL_THRESHOLD = 1;
|
||||||
private final AbstractTouchpad mTouchpad;
|
private final AbstractTouchpad mTouchpad;
|
||||||
private final View mHostView;
|
private final View mHostView;
|
||||||
@ -32,13 +37,43 @@ public class AndroidPointerCapture implements ViewTreeObserver.OnWindowFocusChan
|
|||||||
this.mHostView = hostView;
|
this.mHostView = hostView;
|
||||||
hostView.setOnCapturedPointerListener(this);
|
hostView.setOnCapturedPointerListener(this);
|
||||||
hostView.getViewTreeObserver().addOnWindowFocusChangeListener(this);
|
hostView.getViewTreeObserver().addOnWindowFocusChangeListener(this);
|
||||||
|
if (!PREF_MOUSE_GRAB_FORCE)
|
||||||
|
CallbackBridge.addGrabListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableTouchpadIfNecessary() {
|
private void enableTouchpadIfNecessary() {
|
||||||
if(!mTouchpad.getDisplayState()) mTouchpad.enable(true);
|
if(!mTouchpad.getDisplayState() && !Tools.isPointerDeviceConnected()) mTouchpad.enable(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needed so it releases the cursor when inside game menu
|
||||||
|
@Override
|
||||||
|
public void onGrabState(boolean isGrabbing) {
|
||||||
|
updateCursorState(isGrabbing);
|
||||||
|
}
|
||||||
|
// It's only here so the side-dialog changes it live
|
||||||
|
@Override
|
||||||
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, @Nullable String key) {
|
||||||
|
updateCursorState(CallbackBridge.isGrabbing());
|
||||||
|
}
|
||||||
|
private void updateCursorState(boolean isGrabbing) {
|
||||||
|
if (!isGrabbing // Only capture if not in menu and user said so
|
||||||
|
&& !PREF_MOUSE_GRAB_FORCE) {
|
||||||
|
mHostView.releasePointerCapture(); // Release the capture since user said so
|
||||||
|
}
|
||||||
|
// Capture the pointer when not inside a menu
|
||||||
|
// So user doesn't have to click to get rid of cursor
|
||||||
|
handleAutomaticCapture();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleAutomaticCapture() {
|
public void handleAutomaticCapture() {
|
||||||
|
if (!CallbackBridge.isGrabbing() // Only capture if not in menu and user said so
|
||||||
|
&& !PREF_MOUSE_GRAB_FORCE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mHostView.hasPointerCapture()) {
|
||||||
|
enableTouchpadIfNecessary();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!mHostView.hasWindowFocus()) {
|
if (!mHostView.hasWindowFocus()) {
|
||||||
mHostView.requestFocus();
|
mHostView.requestFocus();
|
||||||
} else {
|
} else {
|
||||||
@ -128,6 +163,10 @@ public class AndroidPointerCapture implements ViewTreeObserver.OnWindowFocusChan
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWindowFocusChanged(boolean hasFocus) {
|
public void onWindowFocusChanged(boolean hasFocus) {
|
||||||
|
if (!CallbackBridge.isGrabbing() // Only capture if not in menu and user said so
|
||||||
|
&& !PREF_MOUSE_GRAB_FORCE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (hasFocus && Tools.isAndroid8OrHigher()) mHostView.requestPointerCapture();
|
if (hasFocus && Tools.isAndroid8OrHigher()) mHostView.requestPointerCapture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +72,8 @@ public class LauncherPreferences {
|
|||||||
public static boolean PREF_FORCE_ENABLE_TOUCHCONTROLLER = false;
|
public static boolean PREF_FORCE_ENABLE_TOUCHCONTROLLER = false;
|
||||||
public static int PREF_TOUCHCONTROLLER_VIBRATE_LENGTH = 100;
|
public static int PREF_TOUCHCONTROLLER_VIBRATE_LENGTH = 100;
|
||||||
|
|
||||||
|
public static boolean PREF_MOUSE_GRAB_FORCE = false;
|
||||||
|
|
||||||
|
|
||||||
public static void loadPreferences(Context ctx) {
|
public static void loadPreferences(Context ctx) {
|
||||||
//Required for CTRLDEF_FILE and MultiRT
|
//Required for CTRLDEF_FILE and MultiRT
|
||||||
@ -114,6 +116,7 @@ public class LauncherPreferences {
|
|||||||
PREF_VSYNC_IN_ZINK = DEFAULT_PREF.getBoolean("vsync_in_zink", true);
|
PREF_VSYNC_IN_ZINK = DEFAULT_PREF.getBoolean("vsync_in_zink", true);
|
||||||
PREF_FORCE_ENABLE_TOUCHCONTROLLER = DEFAULT_PREF.getBoolean("forceEnableTouchController", false);
|
PREF_FORCE_ENABLE_TOUCHCONTROLLER = DEFAULT_PREF.getBoolean("forceEnableTouchController", false);
|
||||||
PREF_TOUCHCONTROLLER_VIBRATE_LENGTH = DEFAULT_PREF.getInt("touchControllerVibrateLength", 100);
|
PREF_TOUCHCONTROLLER_VIBRATE_LENGTH = DEFAULT_PREF.getInt("touchControllerVibrateLength", 100);
|
||||||
|
PREF_MOUSE_GRAB_FORCE = DEFAULT_PREF.getBoolean("always_grab_mouse", false);
|
||||||
|
|
||||||
String argLwjglLibname = "-Dorg.lwjgl.opengl.libname=";
|
String argLwjglLibname = "-Dorg.lwjgl.opengl.libname=";
|
||||||
for (String arg : JREUtils.parseJavaArguments(PREF_CUSTOM_JAVA_ARGS)) {
|
for (String arg : JREUtils.parseJavaArguments(PREF_CUSTOM_JAVA_ARGS)) {
|
||||||
|
@ -7,6 +7,7 @@ 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_GYRO_SENSITIVITY;
|
||||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_LONGPRESS_TRIGGER;
|
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_MOUSESPEED;
|
||||||
|
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_MOUSE_GRAB_FORCE;
|
||||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_SCALE_FACTOR;
|
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_SCALE_FACTOR;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
@ -31,11 +32,11 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView {
|
|||||||
|
|
||||||
private SharedPreferences.Editor mEditor;
|
private SharedPreferences.Editor mEditor;
|
||||||
@SuppressLint("UseSwitchCompatOrMaterialCode")
|
@SuppressLint("UseSwitchCompatOrMaterialCode")
|
||||||
private Switch mGyroSwitch, mGyroXSwitch, mGyroYSwitch, mGestureSwitch;
|
private Switch mGyroSwitch, mGyroXSwitch, mGyroYSwitch, mGestureSwitch, mMouseGrabSwitch;
|
||||||
private CustomSeekbar mGyroSensitivityBar, mMouseSpeedBar, mGestureDelayBar, mResolutionBar;
|
private CustomSeekbar mGyroSensitivityBar, mMouseSpeedBar, mGestureDelayBar, mResolutionBar;
|
||||||
private TextView mGyroSensitivityText, mGyroSensitivityDisplayText, mMouseSpeedText, mGestureDelayText, mGestureDelayDisplayText, mResolutionText;
|
private TextView mGyroSensitivityText, mGyroSensitivityDisplayText, mMouseSpeedText, mGestureDelayText, mGestureDelayDisplayText, mResolutionText;
|
||||||
|
|
||||||
private boolean mOriginalGyroEnabled, mOriginalGyroXEnabled, mOriginalGyroYEnabled, mOriginalGestureDisabled;
|
private boolean mOriginalGyroEnabled, mOriginalGyroXEnabled, mOriginalGyroYEnabled, mOriginalGestureDisabled, mOriginalMouseGrab;
|
||||||
private float mOriginalGyroSensitivity, mOriginalMouseSpeed, mOriginalResolution;
|
private float mOriginalGyroSensitivity, mOriginalMouseSpeed, mOriginalResolution;
|
||||||
private int mOriginalGestureDelay;
|
private int mOriginalGestureDelay;
|
||||||
|
|
||||||
@ -65,6 +66,7 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView {
|
|||||||
mGyroXSwitch = mDialogContent.findViewById(R.id.checkboxGyroX);
|
mGyroXSwitch = mDialogContent.findViewById(R.id.checkboxGyroX);
|
||||||
mGyroYSwitch = mDialogContent.findViewById(R.id.checkboxGyroY);
|
mGyroYSwitch = mDialogContent.findViewById(R.id.checkboxGyroY);
|
||||||
mGestureSwitch = mDialogContent.findViewById(R.id.checkboxGesture);
|
mGestureSwitch = mDialogContent.findViewById(R.id.checkboxGesture);
|
||||||
|
mMouseGrabSwitch = mDialogContent.findViewById(R.id.always_grab_mouse_side_dialog);
|
||||||
|
|
||||||
mGyroSensitivityBar = mDialogContent.findViewById(R.id.editGyro_seekbar);
|
mGyroSensitivityBar = mDialogContent.findViewById(R.id.editGyro_seekbar);
|
||||||
mMouseSpeedBar = mDialogContent.findViewById(R.id.editMouseSpeed_seekbar);
|
mMouseSpeedBar = mDialogContent.findViewById(R.id.editMouseSpeed_seekbar);
|
||||||
@ -86,6 +88,7 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView {
|
|||||||
mOriginalGyroXEnabled = PREF_GYRO_INVERT_X;
|
mOriginalGyroXEnabled = PREF_GYRO_INVERT_X;
|
||||||
mOriginalGyroYEnabled = PREF_GYRO_INVERT_Y;
|
mOriginalGyroYEnabled = PREF_GYRO_INVERT_Y;
|
||||||
mOriginalGestureDisabled = PREF_DISABLE_GESTURES;
|
mOriginalGestureDisabled = PREF_DISABLE_GESTURES;
|
||||||
|
mOriginalMouseGrab = PREF_MOUSE_GRAB_FORCE;
|
||||||
|
|
||||||
mOriginalGyroSensitivity = PREF_GYRO_SENSITIVITY;
|
mOriginalGyroSensitivity = PREF_GYRO_SENSITIVITY;
|
||||||
mOriginalMouseSpeed = PREF_MOUSESPEED;
|
mOriginalMouseSpeed = PREF_MOUSESPEED;
|
||||||
@ -122,6 +125,10 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView {
|
|||||||
mEditor.putBoolean("disableGestures", isChecked);
|
mEditor.putBoolean("disableGestures", isChecked);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mMouseGrabSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
mEditor.putBoolean("always_grab_mouse", isChecked);
|
||||||
|
});
|
||||||
|
|
||||||
mGyroSensitivityBar.setOnSeekBarChangeListener((SimpleSeekBarListener) (seekBar, progress, fromUser) -> {
|
mGyroSensitivityBar.setOnSeekBarChangeListener((SimpleSeekBarListener) (seekBar, progress, fromUser) -> {
|
||||||
PREF_GYRO_SENSITIVITY = progress / 100f;
|
PREF_GYRO_SENSITIVITY = progress / 100f;
|
||||||
mEditor.putInt("gyroSensitivity", progress);
|
mEditor.putInt("gyroSensitivity", progress);
|
||||||
@ -156,6 +163,7 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView {
|
|||||||
setSeekTextPercent(mResolutionText, mResolutionBar.getProgress());
|
setSeekTextPercent(mResolutionText, mResolutionBar.getProgress());
|
||||||
|
|
||||||
|
|
||||||
|
updateMouseGrabVisibility();
|
||||||
updateGyroVisibility(mOriginalGyroEnabled);
|
updateGyroVisibility(mOriginalGyroEnabled);
|
||||||
updateGestureVisibility(mOriginalGestureDisabled);
|
updateGestureVisibility(mOriginalGestureDisabled);
|
||||||
}
|
}
|
||||||
@ -172,6 +180,10 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView {
|
|||||||
target.setText(target.getContext().getString(format, value));
|
target.setText(target.getContext().getString(format, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateMouseGrabVisibility(){
|
||||||
|
mMouseGrabSwitch.setVisibility(Tools.isPointerDeviceConnected()? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateGyroVisibility(boolean isEnabled) {
|
private void updateGyroVisibility(boolean isEnabled) {
|
||||||
int visibility = isEnabled ? View.VISIBLE : View.GONE;
|
int visibility = isEnabled ? View.VISIBLE : View.GONE;
|
||||||
mGyroXSwitch.setVisibility(visibility);
|
mGyroXSwitch.setVisibility(visibility);
|
||||||
@ -202,6 +214,7 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView {
|
|||||||
mGyroXSwitch.setOnCheckedChangeListener(null);
|
mGyroXSwitch.setOnCheckedChangeListener(null);
|
||||||
mGyroYSwitch.setOnCheckedChangeListener(null);
|
mGyroYSwitch.setOnCheckedChangeListener(null);
|
||||||
mGestureSwitch.setOnCheckedChangeListener(null);
|
mGestureSwitch.setOnCheckedChangeListener(null);
|
||||||
|
mMouseGrabSwitch.setOnCheckedChangeListener(null);
|
||||||
|
|
||||||
mGyroSensitivityBar.setOnSeekBarChangeListener(null);
|
mGyroSensitivityBar.setOnSeekBarChangeListener(null);
|
||||||
mMouseSpeedBar.setOnSeekBarChangeListener(null);
|
mMouseSpeedBar.setOnSeekBarChangeListener(null);
|
||||||
@ -225,6 +238,7 @@ public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView {
|
|||||||
PREF_GYRO_INVERT_X = mOriginalGyroXEnabled;
|
PREF_GYRO_INVERT_X = mOriginalGyroXEnabled;
|
||||||
PREF_GYRO_INVERT_Y = mOriginalGyroYEnabled;
|
PREF_GYRO_INVERT_Y = mOriginalGyroYEnabled;
|
||||||
PREF_DISABLE_GESTURES = mOriginalGestureDisabled;
|
PREF_DISABLE_GESTURES = mOriginalGestureDisabled;
|
||||||
|
PREF_MOUSE_GRAB_FORCE = mOriginalMouseGrab;
|
||||||
|
|
||||||
PREF_GYRO_SENSITIVITY = mOriginalGyroSensitivity;
|
PREF_GYRO_SENSITIVITY = mOriginalGyroSensitivity;
|
||||||
PREF_MOUSESPEED = mOriginalMouseSpeed;
|
PREF_MOUSESPEED = mOriginalMouseSpeed;
|
||||||
|
@ -202,4 +202,15 @@
|
|||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@id/editGestureDelay_textView" />
|
app:layout_constraintTop_toTopOf="@id/editGestureDelay_textView" />
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/always_grab_mouse_side_dialog"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/_36sdp"
|
||||||
|
android:text="@string/mcl_setting_title_grab_mouse"
|
||||||
|
|
||||||
|
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/editGestureDelay_seekbar"
|
||||||
|
tools:ignore="UseSwitchCompatOrMaterialXml" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -171,6 +171,8 @@
|
|||||||
<string name="mcl_disable_swap_hand_subtitle">Disables double tapping on the hotbar item to swap it in the second hand.</string>
|
<string name="mcl_disable_swap_hand_subtitle">Disables double tapping on the hotbar item to swap it in the second hand.</string>
|
||||||
<string name="mcl_setting_title_mousespeed">Mouse Speed</string>
|
<string name="mcl_setting_title_mousespeed">Mouse Speed</string>
|
||||||
<string name="mcl_setting_subtitle_mousespeed">Changes the speed of the virtual mouse.</string>
|
<string name="mcl_setting_subtitle_mousespeed">Changes the speed of the virtual mouse.</string>
|
||||||
|
<string name="mcl_setting_title_grab_mouse">Use virtual cursor</string>
|
||||||
|
<string name="mcl_setting_subtitle_grab_mouse">Ensures the cursor stays inside the game. Prevents mouse from clicking touch control layout.</string>
|
||||||
<string name="customctrl_passthru">Mouse pass-thru</string>
|
<string name="customctrl_passthru">Mouse pass-thru</string>
|
||||||
<string name="customctrl_swipeable">Swipeable</string>
|
<string name="customctrl_swipeable">Swipeable</string>
|
||||||
<string name="customctrl_forward_lock">Forward lock</string>
|
<string name="customctrl_forward_lock">Forward lock</string>
|
||||||
|
@ -97,6 +97,11 @@
|
|||||||
android:title="@string/preference_mouse_start_title"
|
android:title="@string/preference_mouse_start_title"
|
||||||
android:summary="@string/preference_mouse_start_description"
|
android:summary="@string/preference_mouse_start_description"
|
||||||
/>
|
/>
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="always_grab_mouse"
|
||||||
|
android:title="@string/mcl_setting_title_grab_mouse"
|
||||||
|
android:summary="@string/mcl_setting_subtitle_grab_mouse"
|
||||||
|
android:defaultValue="false"/>
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
|
Loading…
x
Reference in New Issue
Block a user