diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json index 6b59f115..d1114634 100644 --- a/.changelog/lang_zh-Hans.json +++ b/.changelog/lang_zh-Hans.json @@ -42,6 +42,7 @@ "设置页面增加 \"定时任务调度引擎\" 设置选项, 支持 AlarmManager/WorkManager/JobScheduler _[`issue #457`](http://issues.autojs6.com/457)_ _[`issue #434`](http://issues.autojs6.com/434)_ _[`issue #449`](http://issues.autojs6.com/449)_ _[`issue #388`](http://issues.autojs6.com/388)_ _[`issue #378`](http://issues.autojs6.com/378)_ _[`issue #163`](http://issues.autojs6.com/163)_ _[`issue #53`](http://issues.autojs6.com/53)_ _[`issue #21`](http://issues.autojs6.com/21)_", "设置页面增加 \"应用启动器图标\" 设置选项, 支持自适应图标/透明背景图标 _[`issue #405`](http://issues.autojs6.com/405)_", "设置页面增加 \"重启策略\" 设置选项, 用于设置主页抽屉栏重启按钮是否使用快速重启策略", + "设置页面增加 \"屏幕捕获权限申请延迟\" 设置选项, 用于避免权限申请窗口遮挡屏幕", "设置页面启动器快捷方式增加 \"插件\" 选项, 用于通过快捷方式跳转到插件中心页面", "Scrapers 工具 (run-scrapers.mjs) 用于自动更新 Gradle 构建脚本结构化数据/README 通用数据/README 模板数据等", "Gradle Wrapper 切换工具 (gradle-wrapper-switcher), 用于切换本地 Gradle 主版本 (如 Gradle 8.x 与 Gradle 9.x)" diff --git a/app/src/main/java/org/autojs/autojs/core/pref/Pref.kt b/app/src/main/java/org/autojs/autojs/core/pref/Pref.kt index 1ec619e3..a8e325b6 100644 --- a/app/src/main/java/org/autojs/autojs/core/pref/Pref.kt +++ b/app/src/main/java/org/autojs/autojs/core/pref/Pref.kt @@ -63,6 +63,18 @@ object Pref { @JvmStatic fun get(): SharedPreferences = sPref + @JvmStatic + val screenCaptureRequestDelay: Int + get() { + // Default 350ms is a practical value for most ROMs' permission dialog fade-out animation. + // zh-CN: 默认 350ms 通常可覆盖多数 ROM 授权弹窗的渐隐动画时间. + val defValue = resources.getInteger(R.integer.screen_capture_request_delay_default_value) + val minValue = resources.getInteger(R.integer.screen_capture_request_delay_min_value) + val maxValue = resources.getInteger(R.integer.screen_capture_request_delay_max_value) + val value = getInt(R.string.key_screen_capture_request_delay, defValue) + return value.coerceIn(minValue, maxValue) + } + @JvmStatic val isExtendingJsBuildInObjectsEnabled get() = getBoolean( diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/Images.java b/app/src/main/java/org/autojs/autojs/runtime/api/Images.java index fef8d265..bcba9d79 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/Images.java +++ b/app/src/main/java/org/autojs/autojs/runtime/api/Images.java @@ -12,6 +12,7 @@ import android.media.Image; import android.os.Build; import android.os.Handler; import android.os.Looper; +import android.os.SystemClock; import android.util.Base64; import android.util.Log; import android.view.Gravity; @@ -31,6 +32,7 @@ import org.autojs.autojs.core.image.capture.ScreenCapturerForegroundService; import org.autojs.autojs.core.opencv.Mat; import org.autojs.autojs.core.opencv.OpenCVHelper; import org.autojs.autojs.core.pref.Language; +import org.autojs.autojs.core.pref.Pref; import org.autojs.autojs.core.ui.inflater.util.Drawables; import org.autojs.autojs.extension.AnyExtensions; import org.autojs.autojs.pio.UncheckedIOException; @@ -101,6 +103,13 @@ public class Images { private static final String TAG = Images.class.getSimpleName(); private static volatile boolean sOpenCvInitialized; + // Gate first captures/frames until this time. + // zh-CN: 在该时间点之前, 首次截图/异步帧回调将被延迟或丢弃, 以避开授权弹窗渐隐动画. + private volatile long mScreenCaptureReadyUptimeMillis = 0L; + + private final int mScreenCaptureRequestDelayMin; + private final int mScreenCaptureRequestDelayMax; + @ScriptVariable public final RhinoColorFinder colorFinder; @@ -115,6 +124,8 @@ public class Images { public Images(Context context, ScriptRuntime scriptRuntime) { mContext = context; + mScreenCaptureRequestDelayMin = context.getResources().getInteger(R.integer.screen_capture_request_delay_min_value); + mScreenCaptureRequestDelayMax = context.getResources().getInteger(R.integer.screen_capture_request_delay_max_value); mScreenMetrics = scriptRuntime.getScreenMetrics(); mScriptRuntime = scriptRuntime; this.colorFinder = new RhinoColorFinder(mScreenMetrics); @@ -246,6 +257,16 @@ public class Images { ); mScreenCapturer = new ScreenCapturer(mContext, intent, options, handler); mScreenCapturer.setImageCaptureCallback(mOnScreenCaptureAvailableListener); + + int delayMs = Pref.getScreenCaptureRequestDelay(); + if (delayMs < mScreenCaptureRequestDelayMin) delayMs = mScreenCaptureRequestDelayMin; + if (delayMs > mScreenCaptureRequestDelayMax) delayMs = mScreenCaptureRequestDelayMax; + + mScreenCaptureReadyUptimeMillis = SystemClock.uptimeMillis() + delayMs; + + // @Caution by JetBrains AI Assistant (GPT-5.2) on Jan 19, 2025. + // ! Resolve immediately to avoid breaking ResultAdapter.wait semantics. + // ! zh-CN: 必须立即 resolve, 避免破坏 ResultAdapter.wait 的语义/线程模型. promiseAdapter.resolve(true); } catch (SecurityException ex) { promiseAdapter.reject(ex); @@ -271,6 +292,17 @@ public class Images { throw new SecurityException(mContext.getString(R.string.error_no_screen_capture_permission)); } + // Delay first capture to skip the permission dialog fade-out animation. + // zh-CN: 延迟首次取帧, 跳过授权弹窗渐隐动画. + long waitMs = mScreenCaptureReadyUptimeMillis - SystemClock.uptimeMillis(); + if (waitMs > 0) { + try { + Thread.sleep(waitMs); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + // Retry in Java side to avoid leaking transient null frames to JS. // zh-CN: 在 Java 层做重试, 避免把短暂的 null 帧暴露给 JS 层. Image capture = null; @@ -613,12 +645,28 @@ public class Images { return ImageWrapper.ofBitmap(mScriptRuntime, invertedBitmap); } + public void setImageCaptureCallback(OnScreenCaptureAvailableListener onScreenCaptureAvailableListener) { + mOnScreenCaptureAvailableListener = new ScreenCaptureAvailableHandler(mScriptRuntime, imageWrapper -> { + // Drop early frames before ready time. + // zh-CN: ready 时间点之前的帧直接丢弃, 避免把授权弹窗渐隐截入异步回调. + if (SystemClock.uptimeMillis() < mScreenCaptureReadyUptimeMillis) return; + onScreenCaptureAvailableListener.onCaptureAvailable(imageWrapper); + }); + if (mScreenCapturer != null) { + mScreenCapturer.setImageCaptureCallback(mOnScreenCaptureAvailableListener); + } + } + public void releaseScreenCapturer() { synchronized (this) { if (mScreenCapturer != null) { mScreenCapturer.release(); mScreenCapturer = null; } + // Reset gate. + // zh-CN: 重置延迟门闩. + mScreenCaptureReadyUptimeMillis = 0L; + if (mPreCapture != null) { mPreCapture.close(); mPreCapture = null; @@ -777,13 +825,6 @@ public class Images { } } - public void setImageCaptureCallback(OnScreenCaptureAvailableListener onScreenCaptureAvailableListener) { - mOnScreenCaptureAvailableListener = new ScreenCaptureAvailableHandler(mScriptRuntime, onScreenCaptureAvailableListener); - if (mScreenCapturer != null) { - mScreenCapturer.setImageCaptureCallback(mOnScreenCaptureAvailableListener); - } - } - public static void shoot(Shootable... shootableArgs) { Arrays.stream(shootableArgs).filter(Objects::nonNull).forEach(Shootable::shoot); } diff --git a/app/src/main/java/org/autojs/autojs/theme/preference/MaterialListPreference.kt b/app/src/main/java/org/autojs/autojs/theme/preference/MaterialListPreference.kt index ea89ef94..47a6060f 100644 --- a/app/src/main/java/org/autojs/autojs/theme/preference/MaterialListPreference.kt +++ b/app/src/main/java/org/autojs/autojs/theme/preference/MaterialListPreference.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.text.TextUtils import android.util.AttributeSet import com.afollestad.materialdialogs.MaterialDialog +import org.autojs.autojs.app.DialogUtils.showAdaptive import org.autojs.autojs.core.pref.Pref import org.autojs.autojs.extension.MaterialDialogExtensions.choiceWidgetThemeColor import org.autojs.autojs.extension.MaterialDialogExtensions.widgetThemeColor @@ -103,18 +104,44 @@ open class MaterialListPreference : MaterialDialogPreference { } return@itemsCallbackSingleChoice true } + val options = mutableListOf() if (defaultEntry != null) { - builder.options( - listOf( - MaterialDialog.OptionMenuItemSpec(context.getString(R.string.dialog_button_use_default)) { dialog -> - dialog.selectedIndex = itemDefaultIndex - }, - ) - ) + options += MaterialDialog.OptionMenuItemSpec(prefContext.getString(R.string.dialog_button_use_default), ::useDefaultOptionMenuItemSpecOnClickListener) } + if (longClickPrompt != null) { + options += MaterialDialog.OptionMenuItemSpec(prefContext.getString(R.string.dialog_button_details), ::detailsOptionMenuItemSpecOnClickListener) + } + builder.options(options) } } + open fun useDefaultOptionMenuItemSpecOnClickListener(dialog: MaterialDialog) { + dialog.selectedIndex = itemDefaultIndex + } + + open fun detailsOptionMenuItemSpecOnClickListener(dialog: MaterialDialog) { + MaterialDialog.Builder(prefContext) + .title(dialogTitle ?: prefContext.getString(R.string.text_details)) + .content(longClickPrompt ?: "") + .also { builder -> + longClickPromptMore?.let { longClickPromptMore -> + builder.neutralText(R.string.dialog_button_more) + builder.neutralColorRes(R.color.dialog_button_hint) + builder.onNeutral { _, _ -> + MaterialDialog.Builder(prefContext) + .title(R.string.text_details) + .content(longClickPromptMore) + .positiveText(R.string.dialog_button_dismiss) + .positiveColorRes(R.color.dialog_button_default) + .showAdaptive() + } + } + } + .positiveText(R.string.dialog_button_dismiss) + .positiveColorRes(R.color.dialog_button_default) + .showAdaptive() + } + private fun getKeyIndex(keyString: CharSequence?): Int? { return keyString?.run { mItemKeys.indexOf(this).takeIf { it != -1 } } } diff --git a/app/src/main/java/org/autojs/autojs/ui/settings/RestartStrategyPreference.kt b/app/src/main/java/org/autojs/autojs/ui/settings/RestartStrategyPreference.kt index ae244e60..24eb0bbb 100644 --- a/app/src/main/java/org/autojs/autojs/ui/settings/RestartStrategyPreference.kt +++ b/app/src/main/java/org/autojs/autojs/ui/settings/RestartStrategyPreference.kt @@ -2,7 +2,6 @@ package org.autojs.autojs.ui.settings import android.content.Context import android.util.AttributeSet -import android.view.View import com.afollestad.materialdialogs.DialogAction import com.afollestad.materialdialogs.MaterialDialog import org.autojs.autojs.theme.preference.MaterialListPreference @@ -51,14 +50,11 @@ class RestartStrategyPreference : MaterialListPreference { d.dismiss() } } - builder.options( - listOf( - MaterialDialog.OptionMenuItemSpec(context.getString(R.string.dialog_button_use_default)) { dialog -> - dialog.selectedIndex = itemDefaultIndex - dialog.configureNeutralButton() - }, - ) - ) + } + + override fun useDefaultOptionMenuItemSpecOnClickListener(dialog: MaterialDialog) { + dialog.selectedIndex = itemDefaultIndex + dialog.configureNeutralButton() } private fun MaterialDialog.Builder.configureNeutralButton() { diff --git a/app/src/main/java/org/autojs/autojs/ui/settings/ScheduledRestartSettingsDialogBuilder.kt b/app/src/main/java/org/autojs/autojs/ui/settings/ScheduledRestartSettingsDialogBuilder.kt index 49a35b49..6549271b 100644 --- a/app/src/main/java/org/autojs/autojs/ui/settings/ScheduledRestartSettingsDialogBuilder.kt +++ b/app/src/main/java/org/autojs/autojs/ui/settings/ScheduledRestartSettingsDialogBuilder.kt @@ -65,17 +65,21 @@ class ScheduledRestartSettingsDialogBuilder(context: Context) : MaterialDialog.B title(R.string.entry_restart_strategy_scheduled) customView(binding.root, false) - options(listOf(MaterialDialog.OptionMenuItemSpec(context.getString(R.string.dialog_button_use_default)) { - when (key(R.string.default_key_scheduled_restart_backend)) { - key(R.string.key_scheduled_restart_backend_alarm_manager) -> { - mOptionAlarmManager.isChecked = true - } - else -> { - mOptionWorkManager.isChecked = true - } - } - mSeekBar.progress = (context.resources.getInteger(R.integer.scheduled_restart_start_delay_default_value) - mStartDelayMinValue) / 100 - })) + options( + listOf( + MaterialDialog.OptionMenuItemSpec(context.getString(R.string.dialog_button_use_default)) { + when (key(R.string.default_key_scheduled_restart_backend)) { + key(R.string.key_scheduled_restart_backend_alarm_manager) -> { + mOptionAlarmManager.isChecked = true + } + else -> { + mOptionWorkManager.isChecked = true + } + } + mSeekBar.progress = (context.resources.getInteger(R.integer.scheduled_restart_start_delay_default_value) - mStartDelayMinValue) / 100 + }, + ), + ) negativeText(R.string.dialog_button_cancel) negativeColorRes(R.color.dialog_button_default) onNegative { d, _ -> d.dismiss() } diff --git a/app/src/main/java/org/autojs/autojs/ui/settings/ScreenCaptureRequestDelayDialogBuilder.kt b/app/src/main/java/org/autojs/autojs/ui/settings/ScreenCaptureRequestDelayDialogBuilder.kt new file mode 100644 index 00000000..a542d0c8 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/settings/ScreenCaptureRequestDelayDialogBuilder.kt @@ -0,0 +1,96 @@ +package org.autojs.autojs.ui.settings + +import android.content.Context +import android.content.DialogInterface +import android.view.LayoutInflater +import android.widget.SeekBar +import android.widget.SeekBar.OnSeekBarChangeListener +import com.afollestad.materialdialogs.MaterialDialog +import org.autojs.autojs.core.pref.Pref +import org.autojs.autojs.theme.widget.ThemeColorSeekBar +import org.autojs.autojs6.R +import org.autojs.autojs6.databinding.DialogScreenCaptureRequestDelaySettingsBinding +import kotlin.properties.Delegates + +class ScreenCaptureRequestDelayDialogBuilder(context: Context, onChangeConfirmed: () -> Unit = {}) : MaterialDialog.Builder(context), OnSeekBarChangeListener { + + private var _binding: DialogScreenCaptureRequestDelaySettingsBinding? = null + private val binding: DialogScreenCaptureRequestDelaySettingsBinding get() = _binding!! + + private var mScreenCaptureRequestDelayTitlePrefix: String + private var mScreenCaptureRequestDelayTitle: String + + private var mStartDelayMinValue by Delegates.notNull() + private var mStartDelayMaxValue by Delegates.notNull() + + private var mSeekBar: ThemeColorSeekBar + + init { + var initialRestartDelayValue = Pref.getInt(R.string.key_screen_capture_request_delay, context.resources.getInteger(R.integer.screen_capture_request_delay_default_value)) + + _binding = DialogScreenCaptureRequestDelaySettingsBinding.inflate(LayoutInflater.from(context)).also { binding -> + mScreenCaptureRequestDelayTitlePrefix = context.getString(R.string.text_delay_time) + mScreenCaptureRequestDelayTitle = "$mScreenCaptureRequestDelayTitlePrefix: ${initialRestartDelayValue}ms" + + mStartDelayMinValue = context.resources.getInteger(R.integer.screen_capture_request_delay_min_value).also { + binding.requestScreenCaptureDelayMinValue.text = "$it" + } + mStartDelayMaxValue = context.resources.getInteger(R.integer.screen_capture_request_delay_max_value).also { + binding.requestScreenCaptureDelayMaxValue.text = "$it" + } + + mSeekBar = binding.seekBar + } + + mSeekBar.setOnSeekBarChangeListener(this) + mSeekBar.max = (mStartDelayMaxValue - mStartDelayMinValue) / 50 + mSeekBar.progress = (initialRestartDelayValue - mStartDelayMinValue) / 50 + + title(R.string.text_screen_capture_request_delay) + customView(binding.root, false) + options( + listOf( + MaterialDialog.OptionMenuItemSpec(context.getString(R.string.dialog_button_use_default)) { + mSeekBar.progress = (context.resources.getInteger(R.integer.screen_capture_request_delay_default_value) - mStartDelayMinValue) / 50 + }, + MaterialDialog.OptionMenuItemSpec(context.getString(R.string.dialog_button_details)) { + MaterialDialog.Builder(context) + .title(R.string.text_screen_capture_request_delay) + .content(R.string.description_screen_capture_request_delay) + .positiveText(R.string.dialog_button_dismiss) + .positiveColorRes(R.color.dialog_button_default) + .show() + }, + ), + ) + negativeText(R.string.dialog_button_cancel) + negativeColorRes(R.color.dialog_button_default) + onNegative { d, _ -> d.dismiss() } + positiveText(R.string.dialog_button_confirm) + positiveColorRes(R.color.dialog_button_attraction) + onPositive { d, _ -> + Pref.putInt(R.string.key_screen_capture_request_delay, mStartDelayMinValue + mSeekBar.progress * 50) + onChangeConfirmed() + d.dismiss() + } + autoDismiss(false) + } + + override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { + binding.requestScreenCaptureDelayTitle.text = context.getString(R.string.text_property_colon_value_unit, mScreenCaptureRequestDelayTitlePrefix, mStartDelayMinValue + progress * 50, "ms") + } + + override fun onStartTrackingTouch(seekBar: SeekBar) { + /* Ignored. */ + } + + override fun onStopTrackingTouch(seekBar: SeekBar) { + /* Ignored. */ + } + + override fun dismissListener(listener: DialogInterface.OnDismissListener): MaterialDialog.Builder? { + _binding = null + return super.dismissListener(listener) + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/ui/settings/ScreenCaptureRequestDelayPreference.kt b/app/src/main/java/org/autojs/autojs/ui/settings/ScreenCaptureRequestDelayPreference.kt new file mode 100644 index 00000000..c5820c31 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/settings/ScreenCaptureRequestDelayPreference.kt @@ -0,0 +1,54 @@ +package org.autojs.autojs.ui.settings + +import android.content.Context +import android.util.AttributeSet +import com.afollestad.materialdialogs.MaterialDialog +import org.autojs.autojs.core.pref.Pref +import org.autojs.autojs.theme.preference.MaterialPreference +import org.autojs.autojs6.R + +/** + * Created by SuperMonster003 on Jan 19, 2026. + */ +class ScreenCaptureRequestDelayPreference : MaterialPreference { + + private var dialog: MaterialDialog? = null + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + + constructor(context: Context) : super(context) + + init { + summaryProvider = SummaryProvider { + prefContext.getString( + R.string.text_property_colon_value_unit, + prefContext.getString(R.string.text_delay_time), + Pref.screenCaptureRequestDelay, + "ms", + ) + } + } + + override fun onClick() { + run { + if (dialog?.isShowing == true) { + return@run + } + dialog = ScreenCaptureRequestDelayDialogBuilder(prefContext) { + notifyChanged() + }.show() + } + super.onClick() + } + + override fun onDetached() { + dialog?.dismiss() + dialog = null + super.onDetached() + } + +} diff --git a/app/src/main/java/org/autojs/autojs/ui/settings/WorkingDirectoryPreference.kt b/app/src/main/java/org/autojs/autojs/ui/settings/WorkingDirectoryPreference.kt index bd209954..ac15f9eb 100644 --- a/app/src/main/java/org/autojs/autojs/ui/settings/WorkingDirectoryPreference.kt +++ b/app/src/main/java/org/autojs/autojs/ui/settings/WorkingDirectoryPreference.kt @@ -17,6 +17,7 @@ import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import org.autojs.autojs.app.DialogUtils +import org.autojs.autojs.app.DialogUtils.showAdaptive import org.autojs.autojs.storage.file.FileObservable import org.autojs.autojs.theme.ThemeColorHelper import org.autojs.autojs.theme.preference.MaterialPreference @@ -48,7 +49,7 @@ class WorkingDirectoryPreference : MaterialPreference { } override fun onClick() { - WorkingDirectoryDialogBuilder().show() + WorkingDirectoryDialogBuilder().showAdaptive() super.onClick() } @@ -67,13 +68,39 @@ class WorkingDirectoryPreference : MaterialPreference { build() } - override fun build(): MaterialDialog = MaterialDialog.Builder(context) + override fun build(): MaterialDialog = MaterialDialog.Builder(prefContext) .title(R.string.text_working_dir_path) + .options( + listOf( + MaterialDialog.OptionMenuItemSpec(prefContext.getString(R.string.dialog_button_use_default)) { + val paths = WorkingDirectoryUtils.getRecommendedDefaultPaths() + when (paths.size) { + 1 -> mContentView.setText(paths.first()) + else -> MaterialDialog.Builder(prefContext) + .title(R.string.text_multiple_options) + .items(paths) + .itemsCallback { _, _, _, text -> + mContentView.setText(text) + } + .negativeText(R.string.dialog_button_back) + .showAdaptive() + } + }, + MaterialDialog.OptionMenuItemSpec(prefContext.getString(R.string.dialog_button_details)) { + MaterialDialog.Builder(prefContext) + .title(R.string.text_working_dir_path) + .content(R.string.description_change_working_dir_preference) + .positiveText(R.string.dialog_button_dismiss) + .positiveColorRes(R.color.dialog_button_default) + .showAdaptive() + }, + ) + ) .customView(R.layout.pref_working_directory, false) .neutralText(R.string.dialog_button_history) .neutralColorRes(R.color.dialog_button_hint) .onNeutral { _, _ -> - MaterialDialog.Builder(context) + MaterialDialog.Builder(prefContext) .title(R.string.text_histories) .content(R.string.text_no_histories) .items(WorkingDirectoryUtils.histories) @@ -83,7 +110,7 @@ class WorkingDirectoryPreference : MaterialPreference { } .itemsLongCallback { dHistories, _, _, text -> false.also { - MaterialDialog.Builder(context) + MaterialDialog.Builder(prefContext) .title(R.string.text_prompt) .content(R.string.text_confirm_to_delete) .negativeText(R.string.dialog_button_cancel) @@ -98,35 +125,14 @@ class WorkingDirectoryPreference : MaterialPreference { DialogUtils.toggleContentViewByItems(dHistories) } } - .show() - } - } - .neutralText(R.string.dialog_button_use_default) - .neutralColorRes(R.color.dialog_button_reset) - .onNeutral { dHistories, _ -> - val paths = WorkingDirectoryUtils.getRecommendedDefaultPaths() - if (paths.size == 1) { - mContentView.setText(paths.first()) - dHistories.dismiss() - } else { - MaterialDialog.Builder(context) - .title(R.string.text_multiple_options) - .items(paths) - .itemsCallback { _, _, _, text -> - true.also { - dHistories.dismiss() - mContentView.setText(text) - } - } - .negativeText(R.string.dialog_button_back) - .show() + .showAdaptive() } } .negativeText(R.string.dialog_button_back) .negativeColorRes(R.color.dialog_button_default) .onNegative { dHistories, _ -> dHistories.dismiss() } .autoDismiss(false) - .show() + .showAdaptive() .also { DialogUtils.toggleContentViewByItems(it) } } .negativeText(R.string.dialog_button_cancel) @@ -156,10 +162,10 @@ class WorkingDirectoryPreference : MaterialPreference { setOnClickListener { val initialDir: String? = try { File(toFullPath(mContentView.text.toString())).path - } catch (ignore: Exception) { + } catch (_: Exception) { mPrefFullPath } - FileChooserDialogBuilder(context) + FileChooserDialogBuilder(prefContext) .title(R.string.text_working_dir_path) .dir(mExtStoragePath, initialDir ?: WorkingDirectoryUtils.path) .chooseDir() @@ -203,13 +209,13 @@ class WorkingDirectoryPreference : MaterialPreference { when (mRadioGroupView.checkedRadioButtonId) { R.id.copy -> fileObservable = FileObservable.copy(srcPath, toFullPath(dstPath)) R.id.move -> fileObservable = FileObservable.move(srcPath, toFullPath(dstPath)) - else -> ViewUtils.showToast(context, R.string.error_unknown_operation, true) + else -> ViewUtils.showToast(prefContext, R.string.error_unknown_operation, true) } fileObservable?.let { showFileProgressDialog(it) } } private fun showFileProgressDialog(observable: Observable) { - val dialog = MaterialDialog.Builder(context) + val dialog = MaterialDialog.Builder(prefContext) .progress(true, 0) .progressIndeterminateStyle(true) .title(R.string.text_in_progress) @@ -235,14 +241,14 @@ class WorkingDirectoryPreference : MaterialPreference { override fun onError(e: Throwable) { e.printStackTrace() dialog.dismiss() - context.getString(R.string.text_error_copy_file, e.message).let { - ViewUtils.showToast(context, it, true) + prefContext.getString(R.string.text_error_copy_file, e.message).let { + ViewUtils.showToast(prefContext, it, true) } } override fun onComplete() { dialog.dismiss() - ViewUtils.showToast(context, R.string.text_operation_is_completed) + ViewUtils.showToast(prefContext, R.string.text_operation_is_completed) } }) } diff --git a/app/src/main/java/org/autojs/autojs/util/UpdateUtils.kt b/app/src/main/java/org/autojs/autojs/util/UpdateUtils.kt index 8e751bf3..ea324b9d 100644 --- a/app/src/main/java/org/autojs/autojs/util/UpdateUtils.kt +++ b/app/src/main/java/org/autojs/autojs/util/UpdateUtils.kt @@ -5,6 +5,7 @@ import androidx.appcompat.app.AppCompatActivity import com.afollestad.materialdialogs.DialogAction import com.afollestad.materialdialogs.MaterialDialog import org.autojs.autojs.app.DialogUtils +import org.autojs.autojs.app.DialogUtils.showAdaptive import org.autojs.autojs.core.pref.Pref import org.autojs.autojs.core.pref.Pref.isAutoCheckForUpdatesEnabled import org.autojs.autojs.core.pref.Pref.lastNoNewerUpdatesTimestamp @@ -27,6 +28,18 @@ object UpdateUtils { fun manageIgnoredUpdates(context: Context) { MaterialDialog.Builder(context) .title(R.string.text_ignored_updates) + .options( + listOf( + MaterialDialog.OptionMenuItemSpec(context.getString(R.string.dialog_button_details)) { + MaterialDialog.Builder(context) + .title(R.string.text_details) + .content(R.string.description_manage_ignored_updates_preference) + .positiveText(R.string.dialog_button_dismiss) + .positiveColorRes(R.color.dialog_button_default) + .showAdaptive() + }, + ), + ) .content(R.string.text_no_ignored_updates) .items(ignoredVersions) .itemsLongCallback { dialog, _, _, text -> @@ -74,7 +87,7 @@ object UpdateUtils { } ViewUtils.showSnack(dialogParent.view, R.string.text_all_items_cleared) } - .show() + .showAdaptive() } .positiveText(R.string.dialog_button_dismiss) .positiveColorRes(R.color.dialog_button_default) @@ -85,7 +98,7 @@ object UpdateUtils { DialogUtils.toggleContentViewByItems(it) DialogUtils.toggleActionButtonAbilityByItems(it, DialogAction.NEUTRAL) } - .show() + .showAdaptive() } private fun showRemoveIgnoredVersionPrompt(context: Context, dialogHoldingItems: MaterialDialog, dialogPromptParent: MaterialDialog?, text: CharSequence) { @@ -102,7 +115,7 @@ object UpdateUtils { DialogUtils.toggleContentViewByItems(dialogHoldingItems) dialogPromptParent?.dismiss() } - .show() + .showAdaptive() } @JvmStatic diff --git a/app/src/main/res/layout/dialog_screen_capture_request_delay_settings.xml b/app/src/main/res/layout/dialog_screen_capture_request_delay_settings.xml new file mode 100644 index 00000000..e3914847 --- /dev/null +++ b/app/src/main/res/layout/dialog_screen_capture_request_delay_settings.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 3bbc4c17..9fd45b45 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1192,5 +1192,10 @@ أعاد المكوّن الإضافي %1$s variant غير صالح: %2$s. لم يتم العثور على موارد Paddle OCR المضمّنة. يُرجى إعادة الحزم مع تفعيل Paddle OCR. تصغير + %1$s: %2$d%3$s + %1$s: %2$d %3$s + تأخير طلب إذن التقاط الشاشة + عند طلب إذن التقاط الشاشة، قد تحتوي نافذة طلب الإذن المنبثقة على حركة تلاشي عند اختفائها. إذا تم استدعاء `images.captureScreen` مباشرةً، فقد تتضمن لقطة الشاشة الناتجة محتوى نافذة طلب الإذن مما يسبب حجبًا.\n\nتضيف قيمة هذا الخيار مدة تأخير (بالمللي ثانية) قبل التقاط الشاشة مباشرةً بعد الحصول على الإذن، وذلك لتجنب مشكلة الحجب المذكورة أعلاه.\n\nينطبق هذا الخيار فقط على أول عملية التقاط بعد الحصول على الإذن؛ أما عمليات الالتقاط اللاحقة فلن تتأثر بهذه القيمة. + مدة التأخير diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index f8e68bbf..3bab15b6 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -1187,5 +1187,10 @@ %1$s plugin returned invalid variant: %2$s. No embedded Paddle OCR assets found. Please re-package with Paddle OCR enabled. Minimize + %1$s: %2$d%3$s + %1$s: %2$d %3$s + Screen capture permission request delay + When requesting screen capture permission, the permission request window may include a fade-out animation when it disappears. If `images.captureScreen` is called immediately, the captured screenshot may contain the permission request window content and be obstructed.\n\nThe current option value adds a delay (in milliseconds) before capturing the screen immediately after permission is granted, which can be used to avoid the obstruction issue described above.\n\nThis option only applies to the first screenshot after screen capture permission is granted; subsequent screenshots are not affected by this value. + Delay time diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index b49812a1..30bb6378 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1190,5 +1190,10 @@ El plugin %1$s devolvió una variante no válida: %2$s. No se encontraron recursos integrados de Paddle OCR. Vuelve a empaquetar con Paddle OCR habilitado. Minimizar + %1$s: %2$d%3$s + %1$s: %2$d %3$s + Retraso de solicitud del permiso de captura de pantalla + Al solicitar el permiso de captura de pantalla, la ventana de solicitud de permiso mostrada puede tener una animación de desvanecimiento al desaparecer. Si se llama a `images.captureScreen` inmediatamente, la captura obtenida puede incluir el contenido de dicha ventana y quedar obstruida.\n\nEl valor de esta opción añade un tiempo de espera (en milisegundos) antes de capturar la pantalla inmediatamente después de obtener el permiso, para evitar el problema de obstrucción descrito.\n\nEsta opción solo se aplica a la primera captura tras obtener el permiso; las capturas posteriores no se verán afectadas por este valor. + Tiempo de retraso diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index ba4ebe22..8909d8a3 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1190,5 +1190,10 @@ Le plugin %1$s a renvoyé une variante invalide: %2$s. Aucune ressource Paddle OCR intégrée n\'a été trouvée. Veuillez reconditionner avec Paddle OCR activé. Réduire + %1$s : %2$d%3$s + %1$s : %2$d %3$s + Délai de demande d\'autorisation de capture d\'écran + Lors de la demande d\'autorisation de capture d\'écran, la fenêtre de demande affichée peut comporter une animation de fondu lors de sa disparition. Si `images.captureScreen` est appelé immédiatement, la capture obtenue peut contenir le contenu de cette fenêtre et être partiellement masquée.\n\nLa valeur de cette option ajoute un délai (en millisecondes) avant d\'effectuer la capture juste après l\'obtention de l\'autorisation, afin d\'éviter le problème de masquage ci-dessus.\n\nCette option ne s\'applique qu\'à la première capture après l\'obtention de l\'autorisation ; les captures suivantes ne seront plus affectées par cette valeur. + Délai diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 9222af04..9b12a14e 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -347,7 +347,7 @@ リモートプラグインの URL を入力してください. \n例: \"https://example.com/plugin.apk\" 最終使用時: %1$s \"Blob\" スレッドが失敗 - \"Blob\" スレッドが成功、オフラインキャッシュを書き込み + \"Blob\" スレッドが成功, オフラインキャッシュを書き込み データ読み込み完了 初期コンテンツを選択 新しい項目を挿入中 @@ -362,7 +362,7 @@ 日付内容を上書き中 更新履歴を上書き中 \"Raw\" スレッドが失敗 - \"Raw\" スレッドが成功、オフラインキャッシュを書き込み + \"Raw\" スレッドが成功, オフラインキャッシュを書き込み \"Blob\" 予備リクエストスレッドを開始 \"Raw\" リクエストスレッドを開始 アルバム @@ -1191,5 +1191,10 @@ %1$s プラグインが無効な variant を返しました: %2$s. 埋め込みの Paddle OCR assets が見つかりません. Paddle OCR を有効にして再パッケージしてください. 最小化 + %1$s: %2$d%3$s + %1$s: %2$d %3$s + 画面キャプチャ権限リクエスト遅延 + 画面キャプチャ権限を要求する際, 表示される権限リクエスト画面が閉じるときにフェード等のアニメーションが発生する場合があります. この直後に `images.captureScreen` を呼び出すと, 取得したスクリーンショットに権限リクエスト画面の内容が写り込み, 遮蔽が発生することがあります. \n\nこの設定値は, 権限取得直後に最初のスクリーンショットを取得する前に遅延時間(ミリ秒)を追加し, 上記の遮蔽問題を回避するために使用します. \n\nこの設定は権限取得後の最初のスクリーンショットにのみ適用され, 以降のスクリーンショットには影響しません. + 遅延時間 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 9e4731c7..368bb7f4 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -1192,5 +1192,10 @@ %1$s 플러그인이 잘못된 variant를 반환했습니다: %2$s. 내장된 Paddle OCR assets를 찾을 수 없습니다. Paddle OCR을 활성화하여 다시 패키징해 주세요. 최소화 + %1$s: %2$d%3$s + %1$s: %2$d %3$s + 화면 캡처 권한 요청 지연 + 화면 캡처 권한을 요청할 때 표시되는 권한 요청 창은 사라질 때 페이드 등의 전환 애니메이션이 있을 수 있습니다. 이때 `images.captureScreen` 메서드를 즉시 호출하면, 얻은 스크린샷에 권한 요청 창의 내용이 포함되어 화면이 가려질 수 있습니다.\n\n이 설정 값은 권한을 획득한 직후 첫 스크린샷을 캡처하기 전에 지연 시간(밀리초)을 추가하여, 위와 같은 가림 문제를 방지하는 데 사용할 수 있습니다.\n\n이 설정은 권한 획득 후 첫 번째 캡처에만 적용되며, 이후 캡처에는 더 이상 영향을 주지 않습니다. + 지연 시간 diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index e54cf590..9d868d9f 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1190,5 +1190,10 @@ Плагин %1$s вернул недопустимый variant: %2$s. Не найдены встроенные ресурсы Paddle OCR. Перепакуйте приложение с включённым Paddle OCR. Свернуть + %1$s: %2$d%3$s + %1$s: %2$d %3$s + Задержка запроса разрешения на захват экрана + При запросе разрешения на захват экрана отображаемое окно запроса может иметь анимацию затухания при исчезновении. Если сразу вызвать `images.captureScreen`, полученный снимок может содержать содержимое этого окна и быть частично перекрыт.\n\nЗначение этой опции добавляет задержку (в миллисекундах) перед выполнением снимка экрана сразу после получения разрешения, что позволяет избежать описанной выше проблемы перекрытия.\n\nЭта опция применяется только к первому снимку после получения разрешения; последующие снимки не будут зависеть от этого значения. + Время задержки diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 66862b5d..7d2ca5c5 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -1188,5 +1188,10 @@ %1$s 插件返回的 variant 無效: %2$s. 未找到內置 Paddle OCR 資源, 請在打包時勾選並注入 Paddle OCR 後重試. 最小化 + %1$s: %2$d%3$s + %1$s: %2$d %3$s + 屏幕捕獲權限申請延遲 + 申請屏幕捕獲權限時, 彈出的權限申請窗口消失時可能存在漸變動畫, 此時如果立即調用 `images.captureScreen` 方法, 獲取的屏幕截圖中會出現權限申請的窗口內容而造成遮擋.\n\n當前設置選項值用於在截圖權限申請後立即獲取屏幕截圖前增加一個延遲時間 (單位為毫秒), 可用於避免上述遮擋問題.\n\n當前設置選項僅適用於獲取截圖權限後的首次截圖操作, 後續截圖操作不再受此設置值影響. + 延遲時間 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index d11f06cb..924d45e1 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1188,5 +1188,10 @@ %1$s 外掛返回的 variant 無效: %2$s. 未找到內建 Paddle OCR 資源, 請在打包時勾選並注入 Paddle OCR 後重試. 最小化 + %1$s: %2$d%3$s + %1$s: %2$d %3$s + 螢幕捕獲許可權申請延遲 + 申請螢幕捕獲許可權時, 彈出的許可權申請視窗消失時可能存在漸變動畫, 此時如果立即呼叫 `images.captureScreen` 方法, 獲取的螢幕截圖中會出現許可權申請的視窗內容而造成遮擋.\n\n當前設定選項值用於在截圖許可權申請後立即獲取螢幕截圖前增加一個延遲時間 (單位為毫秒), 可用於避免上述遮擋問題.\n\n當前設定選項僅適用於獲取截圖許可權後的首次截圖操作, 後續截圖操作不再受此設定值影響. + 延遲時間 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index c8c07a6f..7ee9c2be 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -1187,6 +1187,11 @@ %1$s 插件返回的 info 为空. %1$s 插件返回的 variant 无效: %2$s. 未找到内置 Paddle OCR 资源, 请在打包时勾选并注入 Paddle OCR 后重试. - Minimize + 最小化 + %1$s: %2$d%3$s + %1$s: %2$d %3$s + 屏幕捕获权限申请延迟 + 申请屏幕捕获权限时, 弹出的权限申请窗口消失时可能存在渐变动画, 此时如果立即调用 `images.captureScreen` 方法, 获取的屏幕截图中会出现权限申请的窗口内容而造成遮挡.\n\n当前设置选项值用于在截图权限申请后立即获取屏幕截图前增加一个延迟时间 (单位为毫秒), 可用于避免上述遮挡问题.\n\n当前设置选项仅适用于获取截图权限后的首次截图操作, 后续截图操作不再受此设置值影响. + 延迟时间 diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml index dbde0765..ae6e80c4 100644 --- a/app/src/main/res/values/integers.xml +++ b/app/src/main/res/values/integers.xml @@ -35,6 +35,10 @@ 3000 300 + 0 + 2000 + 350 + 1 1 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0adc30c8..04236cdc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -157,6 +157,7 @@ key_$_scheduled_restart_backend_alarm_manager key_$_scheduled_restart_backend_work_manager key_$_scheduled_restart_delay + key_$_screen_capture_request_delay_ms key_$_scripts_after_app_restart key_$_server_address key_$_server_socket_normally_closed @@ -1445,5 +1446,10 @@ %1$s plugin returned invalid variant: %2$s. No embedded Paddle OCR assets found. Please re-package with Paddle OCR enabled. Minimize + %1$s: %2$d%3$s + %1$s: %2$d %3$s + Screen capture permission request delay + When requesting screen capture permission, the permission request window may include a fade-out animation when it disappears. If `images.captureScreen` is called immediately, the captured screenshot may contain the permission request window content and be obstructed.\n\nThe current option value adds a delay (in milliseconds) before capturing the screen immediately after permission is granted, which can be used to avoid the obstruction issue described above.\n\nThis option only applies to the first screenshot after screen capture permission is granted; subsequent screenshots are not affected by this value. + Delay time diff --git a/app/src/main/res/xml/fragment_preferences.xml b/app/src/main/res/xml/fragment_preferences.xml index a0c9296e..29956c41 100644 --- a/app/src/main/res/xml/fragment_preferences.xml +++ b/app/src/main/res/xml/fragment_preferences.xml @@ -187,6 +187,12 @@ app:itemDefaultKey="@string/default_key_root_mode" app:longClickPrompt="@string/description_root_mode_preference" /> + +