6.7.0 - Alpha19 - 修复 Android 15+ 代码编辑器点击 fx 按钮无法显示模块函数快捷面板的问题 (试修)

This commit is contained in:
SuperMonster003
2026-02-12 17:49:00 +08:00
parent 8a8d102e3f
commit 0cd4435d2f
5 changed files with 162 additions and 79 deletions

View File

@@ -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)_",
"崩溃报告页面复制详细信息功能失效的问题",

View File

@@ -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 {

View File

@@ -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<String> {
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<String>) {
private fun streamDecodeAndEmit(input: BufferedInputStream, emitter: ObservableEmitter<String>) {
// 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<Theme?> ->
.subscribe { themes: List<Theme> ->
editor.setProgress(false)
selectEditorTheme(themes)
}
@@ -1841,18 +1847,15 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag
editor.lastTextSize = value
}
private fun selectEditorTheme(themes: List<Theme?>) {
var i = themes.indexOf(mEditorTheme)
if (i < 0) {
i = 0
}
private fun selectEditorTheme(themes: List<Theme>) {
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)
}

View File

@@ -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.
* <a href="https://github.com/dss886/Android-FunctionsInputDetector">Android-FunctionsInputDetector</a>
* 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<Activity> 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);

View File

@@ -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">
<FrameLayout
android:id="@+id/toolbar_menu"
@@ -50,38 +50,53 @@
android:layout_height="0dp"
android:layout_weight="1" />
<RelativeLayout
<FrameLayout
android:id="@+id/input_method_enhance_bar"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_height="wrap_content"
android:background="#77f2f3f7">
<org.autojs.autojs.ui.edit.completion.CodeCompletionBar
android:id="@+id/symbol_bar"
<LinearLayout
android:id="@+id/input_method_enhance_bar_content"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_alignParentBottom="true" />
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/functions"
android:layout_width="40dp"
android:layout_height="35dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/text_function"
android:padding="6dp"
android:src="@drawable/ic_ali_fx"
app:tint="#222329" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/code_completion_row"
android:layout_width="match_parent"
android:layout_height="35dp">
<org.autojs.autojs.ui.edit.completion.CodeCompletionBar
android:id="@+id/code_completion_bar"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_above="@+id/symbol_bar"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_toEndOf="@+id/functions" />
<ImageView
android:id="@+id/functions"
android:layout_width="40dp"
android:layout_height="35dp"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/text_function"
android:padding="6dp"
android:src="@drawable/ic_ali_fx"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="#222329" />
<org.autojs.autojs.ui.edit.completion.CodeCompletionBar
android:id="@+id/code_completion_bar"
android:layout_width="0dp"
android:layout_height="35dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/functions"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<org.autojs.autojs.ui.edit.completion.CodeCompletionBar
android:id="@+id/symbol_bar"
android:layout_width="match_parent"
android:layout_height="35dp" />
</LinearLayout>
<FrameLayout
android:id="@+id/loading_bar_container"
@@ -89,9 +104,10 @@
android:layout_height="match_parent"
android:clickable="false"
android:focusable="false"
android:minHeight="35dp"
android:visibility="gone"
tools:visibility="visible"
tools:background="#A0000000">
tools:background="#A0000000"
tools:visibility="visible">
<TextView
android:id="@+id/loading_bar_text"
@@ -106,7 +122,7 @@
</FrameLayout>
</RelativeLayout>
</FrameLayout>
<org.autojs.autojs.ui.edit.keyboard.FunctionsKeyboardView
android:id="@+id/functions_keyboard"