diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json index 396e4b0c..e9016aa6 100644 --- a/.changelog/lang_zh-Hans.json +++ b/.changelog/lang_zh-Hans.json @@ -103,6 +103,7 @@ "代码编辑器在只读模式下点击标题区域及部分菜单项导致应用崩溃的问题", "代码编辑器加载大文件可能导致应用崩溃的问题 (试修)", "代码编辑器自动打开新建文件时功能按钮状态初始化异常", + "Android 15+ 代码编辑器点击 fx 按钮无法显示模块函数快捷面板的问题 (试修)", "ErrorDialogActivity 可能无法正常启动或短时间自动消失的问题 _[`issue #479`](http://issues.autojs6.com/479)_ _[`issue #471`](http://issues.autojs6.com/471)_ _[`issue #414`](http://issues.autojs6.com/414)_ _[`issue #340`](http://issues.autojs6.com/340#issuecomment-2973485826)_", "Canvas 构造函数可接受的参数类型错误 _[`issue #402`](http://issues.autojs6.com/402)_", "崩溃报告页面复制详细信息功能失效的问题", diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.kt b/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.kt index 9a3f8fcc..5955565d 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.kt +++ b/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.kt @@ -34,6 +34,7 @@ import org.autojs.autojs.theme.widget.ThemeColorToolbar import org.autojs.autojs.ui.BaseActivity import org.autojs.autojs.ui.main.MainActivity import org.autojs.autojs.ui.main.scripts.EditableFileInfoDialogManager +import org.autojs.autojs.util.DialogUtils import org.autojs.autojs.util.IntentUtils.startSafely import org.autojs.autojs.util.Observers import org.autojs.autojs.util.ViewUtils.onceGlobalLayout @@ -48,6 +49,7 @@ import java.io.IOException /** * Created by Stardust on Jan 29, 2017. * Modified by SuperMonster003 as of Jan 21, 2023. + * Modified by JetBrains AI Assistant (GPT-5.2) as of Feb 8, 2026. */ open class EditActivity : BaseActivity(), DelegateHost, PermissionRequestProxyActivity { @@ -304,20 +306,24 @@ open class EditActivity : BaseActivity(), DelegateHost, PermissionRequestProxyAc } private fun showExitConfirmDialog() { - MaterialDialog.Builder(this) - .title(R.string.text_prompt) - .content(R.string.edit_exit_without_save_warn) - .neutralText(R.string.dialog_button_back) - .negativeText(R.string.text_exit_directly) - .negativeColorRes(R.color.dialog_button_caution) - .positiveText(R.string.text_save_and_exit) - .positiveColorRes(R.color.dialog_button_warn) - .onNegative { _, _ -> finishAndRemoveFromRecents() } - .onPositive { _, _ -> - mEditorView.saveFile() - finishAndRemoveFromRecents() - } - .show() + DialogUtils.buildAndShowAdaptive { + MaterialDialog.Builder(this) + .title(R.string.text_prompt) + .content(R.string.edit_exit_without_save_warn) + .neutralText(R.string.dialog_button_back) + .negativeText(R.string.text_exit_directly) + .negativeColorRes(R.color.dialog_button_caution) + .onNegative { _, _ -> + finishAndRemoveFromRecents() + } + .positiveText(R.string.text_save_and_exit) + .positiveColorRes(R.color.dialog_button_warn) + .onPositive { _, _ -> + mEditorView.saveFile() + finishAndRemoveFromRecents() + } + .build() + } } override fun onDestroy() { @@ -363,6 +369,11 @@ open class EditActivity : BaseActivity(), DelegateHost, PermissionRequestProxyAc null } + override fun onResume() { + super.onResume() + runCatching { mEditorView.refreshSymbolsBar() } + } + override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) savedInstanceState.getString("text")?.let { diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.kt b/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.kt index 492a7510..3d9786a5 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.kt +++ b/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.kt @@ -28,12 +28,14 @@ import android.widget.TextView import androidx.core.view.GravityCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.FragmentActivity import com.afollestad.materialdialogs.MaterialDialog import com.google.android.material.snackbar.Snackbar import io.reactivex.Observable +import io.reactivex.ObservableEmitter import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers @@ -46,7 +48,6 @@ import org.autojs.autojs.event.BackPressedHandler.HostActivity import org.autojs.autojs.execution.ScriptExecution import org.autojs.autojs.model.autocomplete.AutoCompletion import org.autojs.autojs.model.autocomplete.CodeCompletions -import org.autojs.autojs.model.autocomplete.Symbols import org.autojs.autojs.model.indices.Module import org.autojs.autojs.model.indices.Property import org.autojs.autojs.model.script.Scripts.ACTION_ON_EXECUTION_FINISHED @@ -75,6 +76,7 @@ import org.autojs.autojs.ui.edit.editor.LayoutHelper import org.autojs.autojs.ui.edit.keyboard.FunctionsKeyboardHelper import org.autojs.autojs.ui.edit.keyboard.FunctionsKeyboardView import org.autojs.autojs.ui.edit.keyboard.FunctionsKeyboardView.ClickCallback +import org.autojs.autojs.ui.edit.keyboard.SymbolsConfigStore import org.autojs.autojs.ui.edit.theme.Theme import org.autojs.autojs.ui.edit.theme.Themes import org.autojs.autojs.ui.edit.toolbar.DebugToolbarFragment @@ -546,12 +548,20 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag true -> { mShowFunctionsButton.visibility = VISIBLE mCodeCompletionBar.visibility = VISIBLE - mSymbolBar.visibility = VISIBLE } else -> { mShowFunctionsButton.visibility = INVISIBLE mCodeCompletionBar.visibility = INVISIBLE - mSymbolBar.visibility = INVISIBLE + } + } + if (!mSymbolBar.isGone) { + when (interactive) { + true -> { + mSymbolBar.visibility = VISIBLE + } + else -> { + mSymbolBar.visibility = INVISIBLE + } } } } @@ -581,7 +591,7 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag } private fun loadUri(uri: Uri): Observable { - val streamThresholdBytes = 1024L * 1024L + val streamThresholdBytes = MAX_HIGHLIGHT_CHARS val sizeOrNull = runCatching { queryContentLengthOrNull(uri) }.getOrNull() if (sizeOrNull != null && sizeOrNull >= streamThresholdBytes) { @@ -672,15 +682,6 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag (context as? HostActivity)?.backPressedObserver?.unregisterHandler(mFunctionsKeyboardHelper) } - // Expose current/last search session for reopening find/replace dialog. - // zh-CN: 对外暴露当前/上一次搜索会话信息, 用于再次打开查找/替换对话框时恢复状态. - fun getCurrentSearchQueryOrNull(): String? = mCurrentSearchQuery - fun getCurrentSearchUsingRegex(): Boolean = mCurrentSearchUsingRegex - fun isInSearchMode(): Boolean = mInSearchMode - - fun getLastSearchQueryOrNull(): String? = mLastSearchQuery - fun getLastSearchUsingRegex(): Boolean = mLastSearchUsingRegex - fun getPreferredSearchQueryForDialogOrNull(): String? = (mCurrentSearchQuery ?: mLastSearchQuery) @@ -951,7 +952,7 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag choreographer.postFrameCallback(callback) } - private fun streamDecodeAndEmit(input: BufferedInputStream, emitter: io.reactivex.ObservableEmitter) { + private fun streamDecodeAndEmit(input: BufferedInputStream, emitter: ObservableEmitter) { // Skip BOM bytes by consuming from raw stream before decoding. // zh-CN: 在解码前先从原始字节流中消费 BOM 字节. val bomBytes = StringUtils.bomBytes(mCurrentCharset) @@ -1002,8 +1003,8 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag return } - val progressiveThreshold = 1024 * 1024 - if (text.length >= progressiveThreshold) { + val progressiveThreshold = MAX_HIGHLIGHT_CHARS + if (text.length > progressiveThreshold) { mLargeFileMode = true setInitialTextProgressively(text) return @@ -1041,7 +1042,6 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag setMenuItemStatus(R.id.redo, false) setMenuItemStatus(R.id.save, false) } - mLargeFileMode -> { // Large file: keep undo/redo disabled; save is enabled only when text changed by explicit edits. // zh-CN: 大文件: 撤销/重做保持禁用; 保存仅在明确编辑导致文本变化时启用. @@ -1050,7 +1050,6 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag setMenuItemStatus(R.id.redo, false) setMenuItemStatus(R.id.save, editor.isTextChanged) } - else -> { // Normal mode. // zh-CN: 普通模式. @@ -1204,7 +1203,7 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag private fun setUpInputMethodEnhancedBar() { mSymbolBar.let { bar -> bar.setOnHintClickListener(this) - bar.codeCompletions = Symbols.getSymbols() + refreshSymbolsBar() } mCodeCompletionBar.let { bar -> bar.setOnHintClickListener(this) @@ -1215,6 +1214,13 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag } } + fun refreshSymbolsBar() { + SymbolsConfigStore.ensureDefaultProfileExists(context) + val symbols = SymbolsConfigStore.getEnabledSymbolsForActiveProfile(context) + mSymbolBar.isVisible = symbols.isNotEmpty() + mSymbolBar.codeCompletions = CodeCompletions.just(symbols) + } + private fun setUpEditor() { editor.let { editor -> editor.codeEditText.let { editText -> @@ -1820,7 +1826,7 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag Themes.getAllThemes(context) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe { themes: List -> + .subscribe { themes: List -> editor.setProgress(false) selectEditorTheme(themes) } @@ -1841,18 +1847,15 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag editor.lastTextSize = value } - private fun selectEditorTheme(themes: List) { - var i = themes.indexOf(mEditorTheme) - if (i < 0) { - i = 0 - } + private fun selectEditorTheme(themes: List) { + val i = themes.indexOf(mEditorTheme).coerceAtLeast(0) DialogUtils.buildAndShowAdaptive { MaterialDialog.Builder(context) .title(R.string.text_editor_theme) .items(themes) .choiceWidgetThemeColor() .itemsCallbackSingleChoice(i) { _, _, which, _ -> - themes[which]?.let { + themes[which].let { setTheme(it) Themes.setCurrent(it.name) } diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/keyboard/FunctionsKeyboardHelper.java b/app/src/main/java/org/autojs/autojs/ui/edit/keyboard/FunctionsKeyboardHelper.java index 98ee0abd..f50b1144 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/keyboard/FunctionsKeyboardHelper.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/keyboard/FunctionsKeyboardHelper.java @@ -11,7 +11,6 @@ import android.view.View; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.LinearLayout; - import org.autojs.autojs.event.BackPressedHandler; import java.lang.ref.WeakReference; @@ -19,14 +18,21 @@ import java.lang.ref.WeakReference; /** * Created by Stardust on Dec 9, 2017. * Android-FunctionsInputDetector + * Modified by JetBrains AI Assistant (GPT-5.2) as of Feb 11, 2026. */ public class FunctionsKeyboardHelper implements BackPressedHandler { private static final String SHARE_PREFERENCE_NAME = "FunctionsKeyboardHelper"; private static final String SHARE_PREFERENCE_SOFT_INPUT_HEIGHT = "soft_input_height"; + + // Minimum IME height threshold (dp) to filter out navigation bar / gesture insets. + // zh-CN: IME 高度的最小阈值 (dp), 用于过滤导航栏/手势区域等非 IME 的小高度. + private static final int MIN_IME_HEIGHT_DP = 80; + private final WeakReference mActivityRef; private final InputMethodManager mInputManager; private final SharedPreferences mPreferences; + private View mFunctionsLayout; private View mEditView; private View mContentView; @@ -78,7 +84,7 @@ public class FunctionsKeyboardHelper implements BackPressedHandler { showFunctionsLayout(); unlockContentHeightDelayed(); } else { - showFunctionsLayout(); // 两者都没显示, 直接显示表情布局 + showFunctionsLayout(); } } }); @@ -99,17 +105,28 @@ public class FunctionsKeyboardHelper implements BackPressedHandler { private void showFunctionsLayout() { int softInputHeight = getSupportSoftInputHeight(); - if (softInputHeight == 0) { - softInputHeight = mPreferences.getInt(SHARE_PREFERENCE_SOFT_INPUT_HEIGHT, 400); + if (softInputHeight <= 0) { + softInputHeight = getKeyBoardHeight(); } hideSoftInput(); mFunctionsLayout.getLayoutParams().height = softInputHeight; + + // Ensure layout params update is applied immediately. + // zh-CN: 确保布局参数的更新可以立即生效. + mFunctionsLayout.requestLayout(); + mFunctionsLayout.setVisibility(View.VISIBLE); } public void hideFunctionsLayout(boolean showSoftInput) { if (mFunctionsLayout.isShown()) { mFunctionsLayout.setVisibility(View.GONE); + + // Reset height so the next show() won't inherit an old value unexpectedly. + // zh-CN: 重置高度, 避免下次 show() 意外继承旧高度. + mFunctionsLayout.getLayoutParams().height = 0; + mFunctionsLayout.requestLayout(); + if (showSoftInput) { showSoftInput(); } @@ -120,10 +137,25 @@ public class FunctionsKeyboardHelper implements BackPressedHandler { LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mContentView.getLayoutParams(); params.height = mContentView.getHeight(); params.weight = 0.0F; + + // Apply params so the lock really takes effect. + // zh-CN: 应用参数, 让锁定高度真正生效. + mContentView.setLayoutParams(params); + mContentView.requestLayout(); } private void unlockContentHeightDelayed() { - mEditView.postDelayed(() -> ((LinearLayout.LayoutParams) mContentView.getLayoutParams()).weight = 1.0F, 200L); + mEditView.postDelayed(() -> { + LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mContentView.getLayoutParams(); + + // Restore "0dp + weight=1" so resize works correctly again. + // zh-CN: 恢复为 "0dp + weight=1", 让 resize 行为回到正常状态. + params.height = 0; + params.weight = 1.0F; + + mContentView.setLayoutParams(params); + mContentView.requestLayout(); + }, 200L); } private void showSoftInput() { @@ -136,22 +168,42 @@ public class FunctionsKeyboardHelper implements BackPressedHandler { } private boolean isSoftInputShown() { - return getSupportSoftInputHeight() != 0; + // Soft input is considered shown only when the computed height is above a minimum threshold. + // zh-CN: 仅当计算出的高度超过最小阈值时, 才认为软键盘处于显示状态. + return getSupportSoftInputHeight() > 0; } private int getSupportSoftInputHeight() { Rect r = new Rect(); - mActivityRef.get().getWindow().getDecorView().getWindowVisibleDisplayFrame(r); - int screenHeight = mActivityRef.get().getWindow().getDecorView().getRootView().getHeight(); + View decorView = mActivityRef.get().getWindow().getDecorView(); + decorView.getWindowVisibleDisplayFrame(r); + int screenHeight = decorView.getRootView().getHeight(); int softInputHeight = screenHeight - r.bottom; // When SDK Level >= 20 (Android L), the softInputHeight will contain the height of softButtonsBar (if has) softInputHeight = softInputHeight - getSoftKeyButtonsHeight(); - if (softInputHeight > 0) { + + // Filter out small non-IME insets (e.g., gesture navigation bar height on Android 15+). + // zh-CN: 过滤较小的非 IME inset (例如 Android 15+ 上的手势导航栏高度). + final int minImeHeightPx = dpToPx(MIN_IME_HEIGHT_DP); + if (softInputHeight > 0 && softInputHeight < minImeHeightPx) { + return 0; + } + + if (softInputHeight >= minImeHeightPx) { mPreferences.edit().putInt(SHARE_PREFERENCE_SOFT_INPUT_HEIGHT, softInputHeight).apply(); } return softInputHeight; } + private int dpToPx(int dp) { + final Activity activity = mActivityRef.get(); + if (activity == null) { + return dp; + } + final float density = activity.getResources().getDisplayMetrics().density; + return Math.round(dp * density); + } + private int getSoftKeyButtonsHeight() { DisplayMetrics metrics = new DisplayMetrics(); mActivityRef.get().getWindowManager().getDefaultDisplay().getMetrics(metrics); diff --git a/app/src/main/res/layout/editor_view.xml b/app/src/main/res/layout/editor_view.xml index a7a2422d..42b610db 100644 --- a/app/src/main/res/layout/editor_view.xml +++ b/app/src/main/res/layout/editor_view.xml @@ -21,8 +21,8 @@ app:contentInsetEndWithActions="2dp" app:contentInsetStartWithNavigation="0dp" app:popupTheme="@style/PopupMenuTheme" - tools:title="@string/app_name" - app:titleTextAppearance="@style/TextAppearanceEditorTitle"> + app:titleTextAppearance="@style/TextAppearanceEditorTitle" + tools:title="@string/app_name"> - - + android:layout_height="wrap_content" + android:orientation="vertical"> - + - + + + + + + + + + + tools:background="#A0000000" + tools:visibility="visible"> - +