From 96b12d616512435c2c38c157198c92db9cfaef7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Brey?= Date: Mon, 31 Oct 2022 15:55:35 +0100 Subject: [PATCH] Fix keyboard opening on dialogs causing squished dialogs in Material3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the proper InputMethodManager service to allow dialogs to correctly detect keyboard opening, and adjust margins properly Signed-off-by: Álvaro Brey --- ...seRichDocumentsTemplateDialogFragment.java | 21 ++++----- .../ui/dialog/ChooseTemplateDialogFragment.kt | 16 ++++--- .../ui/dialog/CreateFolderDialogFragment.java | 25 +++++----- .../ui/dialog/MultipleAccountsDialog.java | 12 +---- .../android/ui/dialog/NoteDialogFragment.java | 21 ++++----- .../ui/dialog/RenameFileDialogFragment.java | 20 ++++---- .../RenamePublicShareDialogFragment.java | 20 ++++---- .../dialog/SharePasswordDialogFragment.java | 20 ++++---- .../owncloud/android/utils/KeyboardUtils.kt | 47 +++++++++++++++++++ 9 files changed, 116 insertions(+), 86 deletions(-) create mode 100644 app/src/main/java/com/owncloud/android/utils/KeyboardUtils.kt diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ChooseRichDocumentsTemplateDialogFragment.java b/app/src/main/java/com/owncloud/android/ui/dialog/ChooseRichDocumentsTemplateDialogFragment.java index 8bb336bf4558..f78292c4ff57 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/ChooseRichDocumentsTemplateDialogFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ChooseRichDocumentsTemplateDialogFragment.java @@ -31,8 +31,6 @@ import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; -import android.view.Window; -import android.view.WindowManager.LayoutParams; import android.widget.Button; import com.google.android.material.dialog.MaterialAlertDialogBuilder; @@ -59,6 +57,7 @@ import com.owncloud.android.ui.adapter.RichDocumentsTemplateAdapter; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.KeyboardUtils; import com.owncloud.android.utils.NextcloudServer; import com.owncloud.android.utils.theme.ViewThemeUtils; @@ -90,6 +89,7 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im @Inject ClientFactory clientFactory; @Inject ViewThemeUtils viewThemeUtils; @Inject FileDataStorageManager fileDataStorageManager; + @Inject KeyboardUtils keyboardUtils; private RichDocumentsTemplateAdapter adapter; private OCFile parentFolder; private OwnCloudClient client; @@ -128,6 +128,12 @@ public void onStart() { checkEnablingCreateButton(); } + @Override + public void onResume() { + super.onResume(); + keyboardUtils.showKeyboardForEditText(binding.filename); + } + @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { @@ -160,7 +166,6 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { binding = ChooseTemplateBinding.inflate(inflater, null, false); View view = binding.getRoot(); - binding.filename.requestFocus(); viewThemeUtils.material.colorTextInputLayout(binding.filenameContainer); Type type = Type.valueOf(arguments.getString(ARG_TYPE)); @@ -219,15 +224,7 @@ public void afterTextChanged(Editable s) { viewThemeUtils.dialog.colorMaterialAlertDialogBackground(activity, builder); - Dialog dialog = builder.create(); - - Window window = dialog.getWindow(); - - if (window != null) { - window.setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); - } - - return dialog; + return builder.create(); } private int getTitle(Type type) { diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt index 9304d70bcb3f..8637c6b9d10b 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt @@ -30,7 +30,6 @@ import android.os.Bundle import android.text.Editable import android.text.TextWatcher import android.view.View -import android.view.WindowManager import android.widget.Button import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment @@ -59,6 +58,7 @@ import com.owncloud.android.ui.activity.TextEditorWebView import com.owncloud.android.ui.adapter.TemplateAdapter import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.FileStorageUtils +import com.owncloud.android.utils.KeyboardUtils import com.owncloud.android.utils.theme.ViewThemeUtils import java.lang.ref.WeakReference import javax.inject.Inject @@ -82,6 +82,9 @@ class ChooseTemplateDialogFragment : DialogFragment(), View.OnClickListener, Tem @Inject lateinit var viewThemeUtils: ViewThemeUtils + @Inject + lateinit var keyboardUtils: KeyboardUtils + private var adapter: TemplateAdapter? = null private var parentFolder: OCFile? = null private var title: String? = null @@ -112,6 +115,11 @@ class ChooseTemplateDialogFragment : DialogFragment(), View.OnClickListener, Tem checkEnablingCreateButton() } + override fun onResume() { + super.onResume() + keyboardUtils.showKeyboardForEditText(binding.filename) + } + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val arguments = arguments ?: throw IllegalArgumentException("Arguments may not be null") val activity = activity ?: throw IllegalArgumentException("Activity may not be null") @@ -131,7 +139,6 @@ class ChooseTemplateDialogFragment : DialogFragment(), View.OnClickListener, Tem _binding = ChooseTemplateBinding.inflate(inflater, null, false) val view: View = binding.root - binding.filename.requestFocus() viewThemeUtils.material.colorTextInputLayout( binding.filenameContainer ) @@ -171,10 +178,7 @@ class ChooseTemplateDialogFragment : DialogFragment(), View.OnClickListener, Tem viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.list.context, builder) - val dialog: Dialog = builder.create() - val window = dialog.window - window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE) - return dialog + return builder.create() } @Suppress("TooGenericExceptionCaught") // legacy code diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.java b/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.java index e095cfac98f7..d2783447d031 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.java @@ -28,8 +28,6 @@ import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; -import android.view.Window; -import android.view.WindowManager.LayoutParams; import android.widget.Button; import android.widget.TextView; @@ -43,6 +41,7 @@ import com.owncloud.android.lib.resources.files.FileUtils; import com.owncloud.android.ui.activity.ComponentsGetter; import com.owncloud.android.utils.DisplayUtils; +import com.owncloud.android.utils.KeyboardUtils; import com.owncloud.android.utils.theme.ViewThemeUtils; import java.util.List; @@ -68,11 +67,15 @@ public class CreateFolderDialogFragment @Inject FileDataStorageManager fileDataStorageManager; @Inject ViewThemeUtils viewThemeUtils; + @Inject KeyboardUtils keyboardUtils; private OCFile mParentFolder; private Button positiveButton; + + private EditBoxDialogBinding binding; + /** * Public factory method to create new CreateFolderDialogFragment instances. * @@ -102,6 +105,12 @@ public void onStart() { } } + @Override + public void onResume() { + super.onResume(); + keyboardUtils.showKeyboardForEditText(binding.userInput); + } + @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { @@ -109,12 +118,11 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { // Inflate the layout for the dialog LayoutInflater inflater = requireActivity().getLayoutInflater(); - EditBoxDialogBinding binding = EditBoxDialogBinding.inflate(inflater, null, false); + binding = EditBoxDialogBinding.inflate(inflater, null, false); View view = binding.getRoot(); // Setup layout binding.userInput.setText(""); - binding.userInput.requestFocus(); viewThemeUtils.material.colorTextInputLayout(binding.userInputContainer); OCFile parentFolder = requireArguments().getParcelable(ARG_PARENT_FOLDER); @@ -175,14 +183,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.userInputContainer.getContext(), builder); - AlertDialog d = builder.create(); - - Window window = d.getWindow(); - if (window != null) { - window.setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); - } - - return d; + return builder.create(); } @Override diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/MultipleAccountsDialog.java b/app/src/main/java/com/owncloud/android/ui/dialog/MultipleAccountsDialog.java index e985db2a422f..350dcf109b24 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/MultipleAccountsDialog.java +++ b/app/src/main/java/com/owncloud/android/ui/dialog/MultipleAccountsDialog.java @@ -32,8 +32,6 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; -import android.view.Window; -import android.view.WindowManager; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.nextcloud.client.account.User; @@ -92,15 +90,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.getRoot().getContext(), builder); - Dialog dialog = builder.create(); - - Window window = dialog.getWindow(); - - if (window != null) { - window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); - } - - return dialog; + return builder.create(); } /** diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/NoteDialogFragment.java b/app/src/main/java/com/owncloud/android/ui/dialog/NoteDialogFragment.java index 5ae7cf21413e..d1cc28b8896b 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/NoteDialogFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/dialog/NoteDialogFragment.java @@ -26,8 +26,6 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; -import android.view.Window; -import android.view.WindowManager.LayoutParams; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.nextcloud.client.di.Injectable; @@ -36,6 +34,7 @@ import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.ui.activity.ComponentsGetter; import com.owncloud.android.utils.DisplayUtils; +import com.owncloud.android.utils.KeyboardUtils; import com.owncloud.android.utils.theme.ViewThemeUtils; import javax.inject.Inject; @@ -53,6 +52,7 @@ public class NoteDialogFragment extends DialogFragment implements DialogInterfac private static final String ARG_SHARE = "SHARE"; @Inject ViewThemeUtils viewThemeUtils; + @Inject KeyboardUtils keyboardUtils; private OCShare share; private NoteDialogBinding binding; @@ -87,6 +87,12 @@ public void onStart() { alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL)); } + @Override + public void onResume() { + super.onResume(); + keyboardUtils.showKeyboardForEditText(binding.noteText); + } + @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { @@ -97,7 +103,6 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { // Setup layout binding.noteText.setText(share.getNote()); - binding.noteText.requestFocus(); viewThemeUtils.material.colorTextInputLayout(binding.noteContainer); // Build the dialog @@ -109,15 +114,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.noteContainer.getContext(), builder); - Dialog dialog = builder.create(); - - Window window = dialog.getWindow(); - - if (window != null) { - window.setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); - } - - return dialog; + return builder.create(); } @Override diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/RenameFileDialogFragment.java b/app/src/main/java/com/owncloud/android/ui/dialog/RenameFileDialogFragment.java index e56d58cb5ae4..a2bef06a0771 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/RenameFileDialogFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/dialog/RenameFileDialogFragment.java @@ -34,8 +34,6 @@ import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; -import android.view.Window; -import android.view.WindowManager.LayoutParams; import android.widget.Button; import com.google.android.material.dialog.MaterialAlertDialogBuilder; @@ -48,6 +46,7 @@ import com.owncloud.android.lib.resources.files.FileUtils; import com.owncloud.android.ui.activity.ComponentsGetter; import com.owncloud.android.utils.DisplayUtils; +import com.owncloud.android.utils.KeyboardUtils; import com.owncloud.android.utils.theme.ViewThemeUtils; import java.util.List; @@ -73,6 +72,7 @@ public class RenameFileDialogFragment @Inject ViewThemeUtils viewThemeUtils; @Inject FileDataStorageManager fileDataStorageManager; + @Inject KeyboardUtils keyboardUtils; private EditBoxDialogBinding binding; private OCFile mTargetFile; @@ -107,6 +107,12 @@ public void onStart() { } } + @Override + public void onResume() { + super.onResume(); + keyboardUtils.showKeyboardForEditText(binding.userInput); + } + @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { @@ -124,7 +130,6 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { int extensionStart = mTargetFile.isFolder() ? -1 : currentName.lastIndexOf('.'); int selectionEnd = extensionStart >= 0 ? extensionStart : currentName.length(); binding.userInput.setSelection(0, selectionEnd); - binding.userInput.requestFocus(); OCFile parentFolder = requireArguments().getParcelable(ARG_PARENT_FOLDER); List folderContent = fileDataStorageManager.getFolderContent(parentFolder, false); @@ -181,14 +186,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.userInputContainer.getContext(), builder); - Dialog d = builder.create(); - - Window window = d.getWindow(); - if (window != null) { - window.setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); - } - - return d; + return builder.create(); } diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/RenamePublicShareDialogFragment.java b/app/src/main/java/com/owncloud/android/ui/dialog/RenamePublicShareDialogFragment.java index a85491ffc1bf..340938bb2b4a 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/RenamePublicShareDialogFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/dialog/RenamePublicShareDialogFragment.java @@ -27,8 +27,6 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; -import android.view.Window; -import android.view.WindowManager.LayoutParams; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.nextcloud.client.di.Injectable; @@ -37,6 +35,7 @@ import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.ui.activity.ComponentsGetter; import com.owncloud.android.utils.DisplayUtils; +import com.owncloud.android.utils.KeyboardUtils; import com.owncloud.android.utils.theme.ViewThemeUtils; import javax.inject.Inject; @@ -56,6 +55,7 @@ public class RenamePublicShareDialogFragment public static final String RENAME_PUBLIC_SHARE_FRAGMENT = "RENAME_PUBLIC_SHARE_FRAGMENT"; @Inject ViewThemeUtils viewThemeUtils; + @Inject KeyboardUtils keyboardUtils; private EditBoxDialogBinding binding; private OCShare publicShare; @@ -80,6 +80,12 @@ public void onStart() { } } + @Override + public void onResume() { + super.onResume(); + keyboardUtils.showKeyboardForEditText(binding.userInput); + } + @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { @@ -93,7 +99,6 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { // Setup layout viewThemeUtils.material.colorTextInputLayout(binding.userInputContainer); binding.userInput.setText(publicShare.getLabel()); - binding.userInput.requestFocus(); // Build the dialog MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(view.getContext()); @@ -104,14 +109,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.userInput.getContext(), builder); - Dialog dialog = builder.create(); - - Window window = dialog.getWindow(); - if (window != null) { - window.setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); - } - - return dialog; + return builder.create(); } @Override diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java b/app/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java index 0c51a1b327f4..4d85e423932c 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java @@ -26,8 +26,6 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; -import android.view.Window; -import android.view.WindowManager; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.nextcloud.client.di.Injectable; @@ -37,6 +35,7 @@ import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.utils.DisplayUtils; +import com.owncloud.android.utils.KeyboardUtils; import com.owncloud.android.utils.theme.ViewThemeUtils; import javax.inject.Inject; @@ -59,6 +58,7 @@ public class SharePasswordDialogFragment extends DialogFragment implements Dialo public static final String PASSWORD_FRAGMENT = "PASSWORD_FRAGMENT"; @Inject ViewThemeUtils viewThemeUtils; + @Inject KeyboardUtils keyboardUtils; private PasswordDialogBinding binding; private OCFile file; @@ -96,6 +96,12 @@ public void onStart() { } } + @Override + public void onResume() { + super.onResume(); + keyboardUtils.showKeyboardForEditText(binding.sharePassword); + } + /** * Public factory method to create new SharePasswordDialogFragment instances. * @@ -159,7 +165,6 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { // Setup layout binding.sharePassword.setText(""); viewThemeUtils.material.colorTextInputLayout(binding.sharePasswordContainer); - binding.sharePassword.requestFocus(); int negativeButtonCaption; int title; @@ -182,14 +187,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { viewThemeUtils.dialog.colorMaterialAlertDialogBackground(view.getContext(), builder); - Dialog d = builder.create(); - - Window window = d.getWindow(); - if (window != null) { - window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); - } - - return d; + return builder.create(); } @Override diff --git a/app/src/main/java/com/owncloud/android/utils/KeyboardUtils.kt b/app/src/main/java/com/owncloud/android/utils/KeyboardUtils.kt new file mode 100644 index 000000000000..2d0b922a0bf3 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/utils/KeyboardUtils.kt @@ -0,0 +1,47 @@ +/* + * Nextcloud Android client application + * + * @author Álvaro Brey + * Copyright (C) 2022 Álvaro Brey + * Copyright (C) 2022 Nextcloud GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see . + * + */ + +package com.owncloud.android.utils + +import android.content.Context +import android.view.inputmethod.InputMethodManager +import android.widget.EditText +import javax.inject.Inject + +class KeyboardUtils @Inject constructor() { + + fun showKeyboardForEditText(editText: EditText) { + editText.requestFocus() + // needs 100ms delay to account for focus animations + editText.postDelayed({ + val context = editText.context + if (context != null) { + val inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + inputMethodManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT) + } + }, SHOW_INPUT_DELAY_MILLIS) + } + + companion object { + private const val SHOW_INPUT_DELAY_MILLIS = 100L + } +}