From 9d80a79cda52ab0cbc1d6b26c84711ed3487cf83 Mon Sep 17 00:00:00 2001 From: Roman Musin Date: Tue, 8 Jun 2021 23:39:20 +0100 Subject: [PATCH] Android: Resolves #4216: Focus note editor where tapped instead of scrolling to the end (#4998) --- .../net/cozic/joplin/MainApplication.java | 2 + .../joplin/textinput/TextInputPackage.java | 63 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 packages/app-mobile/android/app/src/main/java/net/cozic/joplin/textinput/TextInputPackage.java diff --git a/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/MainApplication.java b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/MainApplication.java index f4792fe3e6..3ebccf791e 100644 --- a/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/MainApplication.java +++ b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/MainApplication.java @@ -16,6 +16,7 @@ import com.facebook.soloader.SoLoader; import net.cozic.joplin.share.SharePackage; import net.cozic.joplin.ssl.SslPackage; +import net.cozic.joplin.textinput.TextInputPackage; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -44,6 +45,7 @@ public class MainApplication extends Application implements ReactApplication { // Packages that cannot be autolinked yet can be added manually here, for example: packages.add(new SharePackage()); packages.add(new SslPackage()); + packages.add(new TextInputPackage()); return packages; } diff --git a/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/textinput/TextInputPackage.java b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/textinput/TextInputPackage.java new file mode 100644 index 0000000000..2a11c295d2 --- /dev/null +++ b/packages/app-mobile/android/app/src/main/java/net/cozic/joplin/textinput/TextInputPackage.java @@ -0,0 +1,63 @@ +package net.cozic.joplin.textinput; + +import android.text.Selection; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.uimanager.ViewManager; +import com.facebook.react.views.textinput.ReactEditText; +import com.facebook.react.views.textinput.ReactTextInputManager; + +import java.util.Collections; +import java.util.List; + +/** + * This class provides a workaround for + * https://github.com/facebook/react-native/issues/29911 + * + * The reason the editor is scrolled seems to be due to this block in + *
android.widget.Editor#onFocusChanged:
+ * + *
+ *                 // The DecorView does not have focus when the 'Done' ExtractEditText button is
+ *                 // pressed. Since it is the ViewAncestor's mView, it requests focus before
+ *                 // ExtractEditText clears focus, which gives focus to the ExtractEditText.
+ *                 // This special case ensure that we keep current selection in that case.
+ *                 // It would be better to know why the DecorView does not have focus at that time.
+ *                 if (((mTextView.isInExtractedMode()) || mSelectionMoved)
+ *                         && selStart >= 0 && selEnd >= 0) {
+ *                      Selection.setSelection((Spannable)mTextView.getText(),selStart,selEnd);
+ *                 }
+ * 
+ * When using native Android TextView mSelectionMoved is false so this block is skipped, + * with RN however it's true and this is where the scrolling comes from. + * + * The below workaround resets the selection before a focus event is passed on to the native component. + * This way when the above condition is reached
selStart == selEnd == -1
and no scrolling + * happens. + */ +public class TextInputPackage implements com.facebook.react.ReactPackage { + @NonNull + @Override + public List createNativeModules(@NonNull ReactApplicationContext reactContext) { + return Collections.emptyList(); + } + + @NonNull + @Override + public List createViewManagers(@NonNull ReactApplicationContext reactContext) { + return Collections.singletonList(new ReactTextInputManager() { + @Override + public void receiveCommand(ReactEditText reactEditText, String commandId, @Nullable ReadableArray args) { + if ("focus".equals(commandId) || "focusTextInput".equals(commandId)) { + Selection.removeSelection(reactEditText.getText()); + } + super.receiveCommand(reactEditText, commandId, args); + } + }); + } +}