diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json index d2ed6508..20055160 100644 --- a/.changelog/lang_zh-Hans.json +++ b/.changelog/lang_zh-Hans.json @@ -1,7 +1,7 @@ { "$data": { "v6.6.3": { - "released_date": "2025/05/02", + "released_date": "2025/05/03", "feature": [ "版本历史功能, 可查看发行版本历史更新记录 (多语言) 与统计数据", "timers.keepAlive 方法 (已全局化), 用于保持脚本活跃状态", diff --git a/app/src/main/java/org/autojs/autojs/theme/ThemeColorHelper.java b/app/src/main/java/org/autojs/autojs/theme/ThemeColorHelper.java index def2a5a6..fb0e177d 100644 --- a/app/src/main/java/org/autojs/autojs/theme/ThemeColorHelper.java +++ b/app/src/main/java/org/autojs/autojs/theme/ThemeColorHelper.java @@ -1,6 +1,7 @@ package org.autojs.autojs.theme; import android.app.Activity; +import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; @@ -13,6 +14,7 @@ import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.AbsListView; +import android.widget.CheckBox; import android.widget.Switch; import androidx.appcompat.widget.SwitchCompat; import androidx.core.graphics.drawable.DrawableCompat; @@ -71,6 +73,16 @@ public class ThemeColorHelper { setTrackDrawableTintList(switchCompat.getTrackDrawable(), adjustedTrackColor, true); } + public static void setColorPrimary(CheckBox checkBox, int color, boolean contrastMatters) { + int checkedColor = contrastMatters + ? ColorUtils.adjustColorForContrast(checkBox.getContext().getColor(R.color.window_background), color, 3.6) + : color; + int uncheckedColor = contrastMatters + ? ColorUtils.adjustColorForContrast(checkBox.getContext().getColor(R.color.window_background), color, 2.3) + : color; + DrawableCompat.setTintList(DrawableCompat.wrap(checkBox.getButtonDrawable()), new ColorStateList(SWITCH_STATES, new int[]{checkedColor, uncheckedColor})); + } + public static void setColorPrimary(Switch sw, int color) { setColorPrimary(sw, color, false); } @@ -96,7 +108,6 @@ public class ThemeColorHelper { unselectedColor, }; DrawableCompat.setTintList(DrawableCompat.wrap(drawable), new ColorStateList(SWITCH_STATES, thumbColors)); - } private static void setTrackDrawableTintList(Drawable drawable, int color, boolean isCompat) { @@ -111,6 +122,17 @@ public class ThemeColorHelper { private static int makeAlpha(int alpha, int color) { return (color & 0xffffff) | (alpha << 24); } + + public static ColorStateList getThemeColorStateList(Context context) { + return getColorPrimaryStateList(context, ThemeColorManager.getColorPrimary()); + } + + public static ColorStateList getColorPrimaryStateList(Context context, int color) { + int background = context.getColor(R.color.window_background); + int checkedColor = ColorUtils.adjustColorForContrast(background, color, 3.6); + int uncheckedColor = ColorUtils.adjustColorForContrast(background, color, 2.3); + return new ColorStateList(SWITCH_STATES, new int[]{checkedColor, uncheckedColor}); + } public static void setStatusBarColor(Activity activity, int color) { Window window = activity.getWindow(); diff --git a/app/src/main/java/org/autojs/autojs/theme/app/ColorLibraryViewHolder.kt b/app/src/main/java/org/autojs/autojs/theme/app/ColorLibraryViewHolder.kt index 9849f07b..44127333 100644 --- a/app/src/main/java/org/autojs/autojs/theme/app/ColorLibraryViewHolder.kt +++ b/app/src/main/java/org/autojs/autojs/theme/app/ColorLibraryViewHolder.kt @@ -31,7 +31,7 @@ class ColorLibraryViewHolder(itemViewBinding: MtColorLibrariesRecyclerViewItemBi } else -> { val size = library.colors.size - resources.getQuantityString(R.plurals.text_items_total_sum, size, size) + resources.getQuantityString(R.plurals.text_items_total_sum_with_colon, size, size) } } diff --git a/app/src/main/java/org/autojs/autojs/ui/settings/AboutActivity.kt b/app/src/main/java/org/autojs/autojs/ui/settings/AboutActivity.kt index 6e0e3831..8aa1eef8 100644 --- a/app/src/main/java/org/autojs/autojs/ui/settings/AboutActivity.kt +++ b/app/src/main/java/org/autojs/autojs/ui/settings/AboutActivity.kt @@ -22,6 +22,7 @@ import org.autojs.autojs6.BuildConfig import org.autojs.autojs6.R import org.autojs.autojs6.databinding.ActivityAboutBinding import org.autojs.autojs6.databinding.ActivityAboutFunctionButtonsBinding +import androidx.core.net.toUri /** * Created by Stardust on Feb 2, 2017. @@ -176,13 +177,13 @@ open class AboutActivity : BaseActivity() { .positiveText(R.string.dialog_button_continue) .onNegative { d, _ -> d.dismiss() } .onPositive { _, _ -> launchGithubIssuesPage() } - .cancelable(false) + .positiveColorRes(R.color.dialog_button_attraction) .show() ?: launchGithubIssuesPage() } private fun launchGithubIssuesPage() { Intent(Intent.ACTION_VIEW) - .setData(Uri.parse(getString(R.string.url_github_autojs6_issues))) + .setData(getString(R.string.url_github_autojs6_issues).toUri()) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .let { startActivity(it) } } diff --git a/app/src/main/java/org/autojs/autojs/ui/settings/DisplayVersionHistoriesActivity.kt b/app/src/main/java/org/autojs/autojs/ui/settings/DisplayVersionHistoriesActivity.kt index 73ebb4c4..c702a76d 100644 --- a/app/src/main/java/org/autojs/autojs/ui/settings/DisplayVersionHistoriesActivity.kt +++ b/app/src/main/java/org/autojs/autojs/ui/settings/DisplayVersionHistoriesActivity.kt @@ -1,24 +1,40 @@ +@file:Suppress("EnumValuesSoftDeprecate") + package org.autojs.autojs.ui.settings import android.content.Context import android.content.Intent import android.os.Bundle +import android.text.util.Linkify import android.view.Menu import android.view.MenuItem +import android.view.View +import android.widget.ScrollView import androidx.core.view.isVisible +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.afollestad.materialdialogs.MaterialDialog import io.noties.markwon.Markwon import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.autojs.autojs.core.pref.Language import org.autojs.autojs.core.pref.Language.Companion.getPrefLanguageOrNull +import org.autojs.autojs.theme.ThemeColorHelper import org.autojs.autojs.theme.widget.ThemeColorToolbar import org.autojs.autojs.ui.BaseActivity +import org.autojs.autojs.ui.settings.VersionHistoryRepository.Companion.Category +import org.autojs.autojs.ui.settings.VersionHistoryRepository.Companion.DEFAULT_FILTER +import org.autojs.autojs.ui.settings.VersionHistoryRepository.Companion.DEFAULT_VERSION_NAME +import org.autojs.autojs.util.ProcessLogger import org.autojs.autojs.util.ViewUtils import org.autojs.autojs.util.ViewUtils.setMenuIconsColorByThemeColorLuminance import org.autojs.autojs.util.ViewUtils.setNavigationIconColorByThemeColorLuminance @@ -34,6 +50,9 @@ class DisplayVersionHistoriesActivity : BaseActivity() { private lateinit var mAdapter: VersionHistoryAdapter private lateinit var mToolbar: ThemeColorToolbar + private var mAllDataHandled = false + private val mSelectedCategories = MutableStateFlow>(DEFAULT_FILTER) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -63,14 +82,48 @@ class DisplayVersionHistoriesActivity : BaseActivity() { val urlSuffix = "app/src/main/assets-app/doc/CHANGELOG-$languageTag.md" val urlRaw = "https://raw.githubusercontent.com/SuperMonster003/AutoJs6/master/$urlSuffix" val urlBlob = "https://github.com/SuperMonster003/AutoJs6/blob/master/$urlSuffix" + + val localItems = withContext(Dispatchers.IO) { + VersionHistoryRepository.readBestLocalSample( + context = this@DisplayVersionHistoriesActivity, + languageTag = languageTag, + ) + } + if (localItems.isNotEmpty()) { + ProcessLogger.i("${getString(R.string.logger_ver_history_load_local_data)} (${resources.getQuantityString(R.plurals.text_items_total_sum, localItems.size, localItems.size).lowercase(Language.getPrefLanguage().locale)})") + hideLoadingTextContainerIfNeeded() + mAdapter.submit(localItems.toMutableList()) + } else { + ProcessLogger.i(getString(R.string.logger_ver_history_local_data_empty)) + } + + val localItemLatestVersion = localItems.firstOrNull()?.version ?: DEFAULT_VERSION_NAME + var onlineItemLatestVersion: String? = null + var shouldContinueCollect = true + repo.loadVersionHistoriesFlow( - activity = this@DisplayVersionHistoriesActivity, + context = this@DisplayVersionHistoriesActivity, + languageTag = languageTag, urlRaw = urlRaw, urlBlob = urlBlob - ).collectLatest { item -> - mAdapter.add(item) + ).collectLatest { onlineItem -> hideLoadingTextContainerIfNeeded() + if (!shouldContinueCollect) { + return@collectLatest + } + if (onlineItemLatestVersion == null) { + onlineItemLatestVersion = onlineItem.version + if (VersionHistoryRepository.compareVersion(localItemLatestVersion, onlineItemLatestVersion) > 0) { + shouldContinueCollect = false + ProcessLogger.i(getString(R.string.logger_ver_history_local_data_newer_stop_online)) + return@collectLatest + } + } + mAdapter.addOrUpdate(onlineItem) } + + ProcessLogger.i(getString(R.string.logger_ver_history_data_loaded)) + mAllDataHandled = true } setToolbarAsBack(R.string.text_version_histories) @@ -83,6 +136,11 @@ class DisplayVersionHistoriesActivity : BaseActivity() { } } + override fun onDestroy() { + super.onDestroy() + ProcessLogger.clear() + } + override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.menu_display_version_histories, menu) setUpToolbarColors() @@ -93,10 +151,95 @@ class DisplayVersionHistoriesActivity : BaseActivity() { when (item.itemId) { R.id.action_expand_all -> mAdapter.expandAll() R.id.action_collapse_all -> mAdapter.collapseAll() + R.id.action_filter_category -> showCategoryFilterDialog() + R.id.action_show_logs -> showProcessLogs() } return super.onOptionsItemSelected(item) } + private fun showCategoryFilterDialog() { + if (!mAllDataHandled) { + val dialog = MaterialDialog.Builder(this) + .title(R.string.text_please_wait) + .content(R.string.text_waiting_for_all_data_processing_to_complete) + .positiveText(R.string.dialog_button_cancel) + .positiveColorRes(R.color.dialog_button_default) + .show() + + lifecycleScope.launch { + while (!mAllDataHandled && dialog.isShowing) { + delay(100) + } + dialog.dismiss() + if (mAllDataHandled && !isFinishing) { + showCategoryFilterDialog() + } + } + return + } + val categories = Category.values() + val totalItems = categories.map { getString(it.labelRes) } + val checkedItemsIndices = categories.indices.filter { categories[it] in mSelectedCategories.value }.toTypedArray() + MaterialDialog.Builder(this) + .title(R.string.text_category_filter) + .positiveText(R.string.dialog_button_confirm) + .positiveColorRes(R.color.dialog_button_attraction) + .onPositive { dialog, _ -> dialog.dismiss() } + .negativeText(R.string.dialog_button_cancel) + .neutralColorRes(R.color.dialog_button_default) + .onNegative { dialog, _ -> dialog.dismiss() } + .neutralText(R.string.dialog_button_use_default) + .neutralColorRes(R.color.dialog_button_hint) + .choiceWidgetColor(ThemeColorHelper.getThemeColorStateList(this)) + .onNeutral { dialog, _ -> + dialog.setSelectedIndices(categories.indices.filter { categories[it] in DEFAULT_FILTER }.toTypedArray()) + } + .autoDismiss(false) + .items(totalItems) + .itemsCallbackMultiChoice(checkedItemsIndices) { dialog, which, text -> + lifecycleScope.launch { + mSelectedCategories.value = which.map { categories[it] }.toSet() + repeatOnLifecycle(Lifecycle.State.STARTED) { + mSelectedCategories.collect { newFilter -> + mAdapter.updateFilter(newFilter) + } + } + } + true + } + .show() + } + + private fun showProcessLogs() { + val dialog = MaterialDialog.Builder(this) + .title(R.string.text_process_log) + .content(ProcessLogger.dump()) + .positiveText(R.string.dialog_button_dismiss) + .show() + + val tv = dialog.contentView?.apply { + autoLinkMask = Linkify.WEB_URLS + text = text + } ?: return + + val job = lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + ProcessLogger.flow.collect { newLine -> + if (!dialog.isShowing) { + this@repeatOnLifecycle.cancel() + return@collect + } + tv.append(newLine) + + (tv.parent as? ScrollView)?.post { + (tv.parent as ScrollView).fullScroll(View.FOCUS_DOWN) + } + } + } + } + dialog.setOnDismissListener { job.cancel() } + } + private fun setUpToolbarColors() { mToolbar.setMenuIconsColorByThemeColorLuminance(this) mToolbar.setNavigationIconColorByThemeColorLuminance(this) diff --git a/app/src/main/java/org/autojs/autojs/ui/settings/ManageIgnoredUpdatesPreference.kt b/app/src/main/java/org/autojs/autojs/ui/settings/ManageIgnoredUpdatesPreference.kt index b187d312..e303bd46 100644 --- a/app/src/main/java/org/autojs/autojs/ui/settings/ManageIgnoredUpdatesPreference.kt +++ b/app/src/main/java/org/autojs/autojs/ui/settings/ManageIgnoredUpdatesPreference.kt @@ -26,7 +26,7 @@ class ManageIgnoredUpdatesPreference : MaterialPreference, SharedPreferences.OnS Pref.registerOnSharedPreferenceChangeListener(this) summaryProvider = SummaryProvider { Pref.getLinkedHashSet(R.string.key_ignored_updates).let { - prefContext.resources.getQuantityString(R.plurals.text_items_total_sum, it.size, it.size) + prefContext.resources.getQuantityString(R.plurals.text_items_total_sum_with_colon, it.size, it.size) } } } diff --git a/app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoryAdapter.kt b/app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoryAdapter.kt index 0f8a9512..2fa0bd0e 100644 --- a/app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoryAdapter.kt +++ b/app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoryAdapter.kt @@ -2,75 +2,138 @@ package org.autojs.autojs.ui.settings import android.annotation.SuppressLint import android.content.Context +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import io.noties.markwon.Markwon import org.autojs.autojs.ui.settings.VersionHistoryAdapter.VersionHistoryViewHolder +import org.autojs.autojs.ui.settings.VersionHistoryRepository.Companion.Category +import org.autojs.autojs.ui.settings.VersionHistoryRepository.Companion.DEFAULT_FILTER +import org.autojs.autojs.ui.settings.VersionHistoryRepository.Companion.compareVersion +import org.autojs.autojs.util.ProcessLogger import org.autojs.autojs6.R import org.autojs.autojs6.databinding.ItemVersionHistoryBinding +@SuppressLint("NotifyDataSetChanged") class VersionHistoryAdapter(private val context: Context, private val markwon: Markwon) : RecyclerView.Adapter() { - private val data = mutableListOf() + private val mData = mutableListOf() + private var mLayoutManager: RecyclerView.LayoutManager? = null + private val mCategoryFilter: MutableSet = DEFAULT_FILTER.clone() - @SuppressLint("NotifyDataSetChanged") - fun submit(list: List) { - data.clear() - data += list + fun submit(list: MutableList) { + mData.clear() + mData += list notifyDataSetChanged() } fun add(item: VersionHistoryItem) { - data += item - notifyItemInserted(data.lastIndex) + mData += item + notifyItemInserted(mData.lastIndex) } fun addAll(list: List) { - val start = data.size - data += list + val start = mData.size + mData += list notifyItemRangeInserted(start, list.size) } + fun updateFilter(filter: Set) { + mCategoryFilter.clear() + mCategoryFilter.addAll(filter) + notifyDataSetChanged() + } + + fun addOrUpdate(item: VersionHistoryItem) { + when (val idx = mData.indexOfFirst { compareVersion(it.version, item.version) == 0 }) { + -1 -> { + ProcessLogger.i("${item.version}: ${context.getString(R.string.logger_ver_history_insert_new_entries)}") + val insertPos = mData.indexOfFirst { compareVersion(it.version, item.version) < 0 } + val realPos = if (insertPos == -1) mData.size else insertPos + mData.add(realPos, item) + notifyItemInserted(realPos) + mLayoutManager?.scrollToPosition(0) + } + else -> { + fun sameDate(local: VersionHistoryItem) = local.date == item.date + fun sameLines(local: VersionHistoryItem) = local.lines.joinToString(",") { it.trim() } == item.lines.joinToString(",") { it.trim() } + val local = mData[idx] + if (sameDate(local) && sameLines(local)) { + ProcessLogger.i("${item.version}: ${context.getString(R.string.logger_ver_history_no_processing_needed)}") + } else { + if (!sameDate(local)) { + ProcessLogger.i("${item.version}: ${context.getString(R.string.logger_ver_history_overwrite_date)}") + Log.d(TAG, "item date: ${item.date}") + Log.d(TAG, "local data: ${local.date}") + } + if (!sameLines(local)) { + ProcessLogger.i("${item.version}: ${context.getString(R.string.logger_ver_history_overwrite_update_record)}") + Log.d(TAG, "item lines: ${item.lines.joinToString(",")}") + Log.d(TAG, "local lines: ${local.lines.joinToString(",")}") + } + mData[idx] = item + notifyItemChanged(idx) + } + } + } + } + + override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { + super.onAttachedToRecyclerView(recyclerView) + mLayoutManager = recyclerView.layoutManager + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VersionHistoryViewHolder { val binding = ItemVersionHistoryBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return VersionHistoryViewHolder(binding) + return VersionHistoryViewHolder(parent.context, binding) } - override fun getItemCount() = data.size + override fun getItemCount() = mData.size override fun onBindViewHolder(holder: VersionHistoryViewHolder, position: Int) { - holder.bind(context, data[position], animate = false) + holder.bind(mData[position], animate = false) } override fun onBindViewHolder(holder: VersionHistoryViewHolder, position: Int, payloads: MutableList) { val animate = payloads.contains(PAYLOAD_EXPAND_STATE_CHANGED) - holder.bind(context, data[position], animate) + holder.bind(mData[position], animate) } fun expandAll() { - data.forEach { it.expanded = true } + mData.forEach { it.expanded = true } notifyItemRangeChanged(0, itemCount, PAYLOAD_EXPAND_STATE_CHANGED) } fun collapseAll() { - data.forEach { it.expanded = false } + mData.forEach { it.expanded = false } notifyItemRangeChanged(0, itemCount, PAYLOAD_EXPAND_STATE_CHANGED) } - inner class VersionHistoryViewHolder(binding: ItemVersionHistoryBinding) : RecyclerView.ViewHolder(binding.root) { + inner class VersionHistoryViewHolder(context: Context, binding: ItemVersionHistoryBinding) : RecyclerView.ViewHolder(binding.root) { private val chevron = binding.chevron + private val tvTitle = binding.tvTitle private val tvDate = binding.tvDate - private val tvBody = binding.tvBody + private val tvLines = binding.tvLines - private val tvFeature = binding.tvFeatureCount - private val tvFix = binding.tvFixCount - private val tvImprovement = binding.tvImprovementCount + private val tvFeatureCount = binding.tvFeatureCount + private val tvFixCount = binding.tvFixCount + private val tvImprovementCount = binding.tvImprovementCount - fun bind(context: Context, item: VersionHistoryItem, animate: Boolean) { + private val tvFeatureCountContainer = binding.tvFeatureCountContainer + private val tvFixCountContainer = binding.tvFixCountContainer + private val tvImprovementCountContainer = binding.tvImprovementCountContainer + + private val changelogLabelHint = "`${context.getString(R.string.changelog_label_hint)}`" + private val changelogLabelFeature = "`${context.getString(R.string.changelog_label_feature)}`" + private val changelogLabelFix = "`${context.getString(R.string.changelog_label_fix)}`" + private val changelogLabelImprovement = "`${context.getString(R.string.changelog_label_improvement)}`" + private val changelogLabelDependency = "`${context.getString(R.string.changelog_label_dependency)}`" + + fun bind(item: VersionHistoryItem, animate: Boolean) { val rotationAngle = if (item.expanded) 180f else 0f chevron.animate().cancel() when (animate) { @@ -78,33 +141,64 @@ class VersionHistoryAdapter(private val context: Context, private val markwon: M else -> chevron.rotation = rotationAngle } - item.lines.count { it.contains("`${context.getString(R.string.changelog_label_feature)}`") }.let { - tvFeature.text = "$it" - } - item.lines.count { it.contains("`${context.getString(R.string.changelog_label_fix)}`") }.let { - tvFix.text = "$it" - } - item.lines.count { it.contains("`${context.getString(R.string.changelog_label_improvement)}`") }.let { - tvImprovement.text = "$it" - } - tvTitle.text = item.version tvDate.text = item.date - tvBody.run { - markwon.setMarkdown(this, item.lines.joinToString("\n")) + tvLines.run { + markwon.setMarkdown(this, applyCategoryFilter(item.lines).joinToString("\n")) visibility = if (item.expanded) View.VISIBLE else View.GONE } + when { + mCategoryFilter.contains(Category.FEATURE) -> { + tvFeatureCount.text = item.lines.count { it.contains(changelogLabelFeature) }.toString() + tvFeatureCountContainer.visibility = View.VISIBLE + } + else -> { + tvFeatureCountContainer.visibility = View.GONE + } + } + when { + mCategoryFilter.contains(Category.FIX) -> { + tvFixCount.text = item.lines.count { it.contains(changelogLabelFix) }.toString() + tvFixCountContainer.visibility = View.VISIBLE + } + else -> { + tvFixCountContainer.visibility = View.GONE + } + } + when { + mCategoryFilter.contains(Category.IMPROVEMENT) -> { + tvImprovementCount.text = item.lines.count { it.contains(changelogLabelImprovement) }.toString() + tvImprovementCountContainer.visibility = View.VISIBLE + } + else -> { + tvImprovementCountContainer.visibility = View.GONE + } + } + itemView.setOnClickListener { item.expanded = !item.expanded notifyItemChanged(absoluteAdapterPosition, PAYLOAD_EXPAND_STATE_CHANGED) } } + private fun applyCategoryFilter(lines: List) = lines.filter { + when { + it.contains(changelogLabelHint) && !mCategoryFilter.contains(Category.HINT) -> false + it.contains(changelogLabelFeature) && !mCategoryFilter.contains(Category.FEATURE) -> false + it.contains(changelogLabelFix) && !mCategoryFilter.contains(Category.FIX) -> false + it.contains(changelogLabelImprovement) && !mCategoryFilter.contains(Category.IMPROVEMENT) -> false + it.contains(changelogLabelDependency) && !mCategoryFilter.contains(Category.DEPENDENCY) -> false + else -> true + } + } + } companion object { + private val TAG = VersionHistoryAdapter::class.java.simpleName + private const val PAYLOAD_EXPAND_STATE_CHANGED = 1 } diff --git a/app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoriesPreference.kt b/app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoryPreference.kt similarity index 92% rename from app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoriesPreference.kt rename to app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoryPreference.kt index 09dbb8d5..9109b652 100644 --- a/app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoriesPreference.kt +++ b/app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoryPreference.kt @@ -7,7 +7,7 @@ import org.autojs.autojs.theme.preference.MaterialPreference /** * Created by SuperMonster003 on May 1, 2025. */ -class VersionHistoriesPreference : MaterialPreference { +class VersionHistoryPreference : MaterialPreference { constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) diff --git a/app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoryRepository.kt b/app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoryRepository.kt index 5718a09e..6072ade3 100644 --- a/app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoryRepository.kt +++ b/app/src/main/java/org/autojs/autojs/ui/settings/VersionHistoryRepository.kt @@ -1,67 +1,75 @@ package org.autojs.autojs.ui.settings -import android.widget.TextView -import com.afollestad.materialdialogs.MaterialDialog +import android.content.Context +import androidx.annotation.StringRes import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow -import kotlinx.coroutines.launch +import kotlinx.coroutines.flow.toList import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import okhttp3.ResponseBody -import okio.BufferedSource +import org.autojs.autojs.util.ProcessLogger import org.autojs.autojs.util.TextUtils -import org.autojs.autojs6.R import org.jsoup.Jsoup +import java.io.File +import org.autojs.autojs6.R +import java.util.EnumSet class VersionHistoryRepository { private val mOkHttpClient by lazy { OkHttpClient() } - fun loadVersionHistoriesFlow(activity: DisplayVersionHistoriesActivity, urlRaw: String, urlBlob: String): Flow = channelFlow { - var tRaw: Throwable? = null - var tBlob: Throwable? = null - - val loadingTextView: TextView? = activity.findViewById(R.id.loading_text) - val loadingSecondTextView: TextView? = activity.findViewById(R.id.loading_second_text) - + fun loadVersionHistoriesFlow( + context: Context, + languageTag: String, + urlRaw: String, + urlBlob: String, + ): Flow = channelFlow { runCatching { - loadingTextView?.visibility = TextView.VISIBLE - return@channelFlow fetchStreamFlow(urlRaw).collect { send(it) } + ProcessLogger.i(context.getString(R.string.logger_ver_history_start_raw_thread)) + ProcessLogger.i("URL: $urlRaw") + val markdown = fetchStreamString(urlRaw) + ProcessLogger.i(context.getString(R.string.logger_ver_history_raw_thread_success)) + writeCacheMarkdown(context, languageTag, markdown) + return@channelFlow parseMarkdownFlow(markdown).collect { send(it) } }.onFailure { eRaw -> - tRaw = eRaw.apply { printStackTrace() } - } - - runCatching { - loadingSecondTextView?.visibility = TextView.VISIBLE - return@channelFlow fetchStreamString(urlBlob).let { html -> - parseHtmlFlow(html).collect { send(it) } + eRaw.apply { + printStackTrace() + ProcessLogger.i("${context.getString(R.string.logger_ver_history_raw_thread_failure)}: $message") } - }.onFailure { eBlob -> - tBlob = eBlob.apply { printStackTrace() } } + runCatching { + ProcessLogger.i(context.getString(R.string.logger_ver_history_start_blob_thread)) - loadingTextView?.visibility = TextView.GONE - loadingSecondTextView?.visibility = TextView.GONE + var niceUrlBlob = urlBlob + val hasQuery = urlBlob.contains('?') + val queryPrefix = if (hasQuery) "&" else "?" - showErrorDialog(activity, tRaw, tBlob) - } - - private fun parseHtmlFlow(html: String) = channelFlow { - val doc = Jsoup.parse(html) - doc.select("div.markdown-heading:has(> h1.heading-element:first-child)").forEach { h1Container -> - val h1 = h1Container.selectFirst("> h1.heading-element:first-child") ?: return@forEach - val version = h1.text() - val date = h1Container.nextElementSibling()?.selectFirst("> h6.heading-element:first-child")?.text() ?: "" - val ul = h1Container.nextElementSibling()?.nextElementSibling() - - if (ul != null && ul.tagName() == "ul") { - val lines = ul.select("> li").map { li -> - TextUtils.htmlToMarkdown(li.outerHtml()).trim() + if (!urlBlob.contains("plain=")) { + niceUrlBlob += "${queryPrefix}plain=1" + if (!urlBlob.contains("raw=")) { + niceUrlBlob += "&raw=true" } - send(VersionHistoryItem(version, date, lines)) + } else if (!urlBlob.contains("raw=")) { + niceUrlBlob += "${queryPrefix}raw=true" + } + + ProcessLogger.i("URL: $niceUrlBlob") + + // val html = fetchStreamString(niceUrlBlob) + // return@channelFlow parseHtmlFlow(html).collect { send(it) } + + val markdown = fetchStreamString(niceUrlBlob) + ProcessLogger.i(context.getString(R.string.logger_ver_history_blob_thread_success)) + writeCacheMarkdown(context, languageTag, markdown) + return@channelFlow parseMarkdownFlow(markdown).collect { send(it) } + }.onFailure { eBlob -> + eBlob.apply { + printStackTrace() + ProcessLogger.i("${context.getString(R.string.logger_ver_history_blob_thread_failure)}: $message") } } } @@ -74,69 +82,148 @@ class VersionHistoryRepository { } } - private fun fetchStreamFlow(url: String): Flow = channelFlow { - launch(Dispatchers.IO) { - mOkHttpClient.newCall(Request.Builder().url(url).build()).execute().use { response -> - val src: BufferedSource = ensureSuccessfulResponse(response, url).source() - val regexVersion = Regex("""^#\s+v[\d.]+\S*""") + companion object { - var curTitle = "" - var curDate = "" - val bodyLines = mutableListOf() + const val DEFAULT_VERSION_NAME = "0.0.0" - fun flush() { - if (curTitle.isBlank()) return + private val regexValidMarkdownVersion = Regex("""^#\s+v[\d.]+\S*""") + + enum class Category(@StringRes val labelRes: Int) { + HINT(R.string.changelog_label_hint), + FEATURE(R.string.changelog_label_feature), + FIX(R.string.changelog_label_fix), + IMPROVEMENT(R.string.changelog_label_improvement), + DEPENDENCY(R.string.changelog_label_dependency); + } + + /* 默认显示: 除 HINT 及 DEPENDENCY 以外的全部类别. */ + val DEFAULT_FILTER: EnumSet = + EnumSet.complementOf(EnumSet.of(Category.HINT, Category.DEPENDENCY)) + + private fun String.toChangelogMarkdownFile(): String { + return "CHANGELOG-$this.md" + } + + private fun String.toChangelogMarkdownCacheFile(): String { + return "CHANGELOG-$this.cache.md" + } + + suspend fun readBestLocalSample(context: Context, languageTag: String): List { + val assetStr = runCatching { + context.assets.open("doc/${languageTag.toChangelogMarkdownFile()}").bufferedReader().use { it.readText() } + }.getOrNull() + + val cacheStr = runCatching { + File(context.filesDir, languageTag.toChangelogMarkdownCacheFile()).readText() + }.getOrNull() + + assetStr ?: cacheStr ?: return emptyList() + + fun String?.parseFirstVersion() = this + ?.lineSequence() + ?.firstOrNull { regexValidMarkdownVersion.matches(it.trim()) } + ?.removePrefix("#")?.trim() + ?.removePrefix("v")?.trim() + ?: DEFAULT_VERSION_NAME + + val assetLatestVer = assetStr.parseFirstVersion() + val cacheLatestVer = cacheStr.parseFirstVersion() + + val useCache = compareVersion(cacheLatestVer, assetLatestVer) >= 0 + val chosenMarkdown = if (useCache && !cacheStr.isNullOrBlank()) cacheStr else assetStr + + ProcessLogger.i("${context.getString(R.string.logger_ver_history_local_asset_latest)}: $assetLatestVer") + ProcessLogger.i("${context.getString(R.string.logger_ver_history_offline_cache_latest)}: $cacheLatestVer") + ProcessLogger.i( + context.getString(R.string.logger_ver_history_initial_content_chosen) + ": ${ + when (useCache) { + true -> context.getString(R.string.logger_ver_history_offline_cache_file) + else -> context.getString(R.string.logger_ver_history_local_asset_file) + } + }" + ) + + return parseMarkdownFlow(chosenMarkdown ?: "").toList() + } + + private fun ensureSuccessfulResponse(response: Response, url: String): ResponseBody { + require(response.isSuccessful && response.body != null) { + buildString { + append("URL: $url\nCode: ${response.code}") + val content = response.body?.string() + if (!content.isNullOrBlank() && content.length <= 200) { + append("\nBody: $content") + } + } + } + return response.body!! + } + + private fun writeCacheMarkdown(context: Context, languageTag: String, markdown: String) { + File(context.filesDir, languageTag.toChangelogMarkdownCacheFile()).writeText(markdown) + } + + private fun parseMarkdownFlow(md: String): Flow = channelFlow { + var curTitle = "" + var curDate = "" + val bodyLines = mutableListOf() + + fun flush(isClose: Boolean = false) { + if (curTitle.isNotBlank()) { trySend(VersionHistoryItem(curTitle, curDate, bodyLines.toList())) bodyLines.clear() } + if (isClose) close() + } - @Suppress("AssignedValueIsNeverRead") - while (true) { - val line = src.readUtf8Line() ?: break - when { - regexVersion.matches(line.trim()) -> { - flush() - curTitle = line.trim().removePrefix("#").trim() - } - line.trim().startsWith("######") -> { - curDate = line.trim().removePrefix("######").trim() - } - line.trim().startsWith("*") && !line.trim().matches(Regex("\\*+")) -> { - bodyLines += line.trim() - } + @Suppress("AssignedValueIsNeverRead") + md.lineSequence().forEach { line -> + when { + regexValidMarkdownVersion.matches(line.trim()) -> { + flush() + curTitle = line.trim().removePrefix("#").trim() + } + line.trim().startsWith("######") -> { + curDate = line.trim().removePrefix("######").trim() + } + line.trim().startsWith("*") && !line.trim().matches(Regex("\\*+")) -> { + bodyLines += line.trim() } } - flush() } - close() + flush(true) } - } - private fun ensureSuccessfulResponse(response: Response, url: String): ResponseBody { - require(response.isSuccessful && response.body != null) { - buildString { - append("URL: $url\nCode: ${response.code}") - val content = response.body?.string() - if (!content.isNullOrBlank() && content.length <= 200) { - append("\nBody: $content") + private fun parseHtmlFlow(html: String) = channelFlow { + val doc = Jsoup.parse(html) + doc.select("div.markdown-heading").filter { it.children().first()?.tagName() == "h1" }.forEach { h1Container -> + val h1 = h1Container.getElementsByTag("h1").first() ?: return@forEach + val version = h1.text() + val nextSibling = h1Container.nextElementSibling() + val date = nextSibling?.getElementsByTag("h6")?.first()?.text() ?: "" + val ul = nextSibling?.nextElementSibling() + + if (ul?.tagName() == "ul") { + val lines = ul.getElementsByTag("li").map { li -> + TextUtils.htmlToMarkdown(li.outerHtml()).trim() + } + send(VersionHistoryItem(version, date, lines)) } } } - return response.body!! - } - private fun showErrorDialog(activity: DisplayVersionHistoriesActivity, tRaw: Throwable?, tBlob: Throwable?) { - val message: String? = buildString { - if (tRaw != null) append("Error message of raw:\n\n${tRaw.message}\n\n") - if (tBlob != null) append("Error message of blob:\n\n${tBlob.message}\n\n") - }.takeIf(String::isNotBlank)?.trim() + fun compareVersion(v1: String, v2: String): Int { + val a1 = v1.trimStart('v').split(".") + val a2 = v2.trimStart('v').split(".") + val max = maxOf(a1.size, a2.size) + for (i in 0 until max) { + val n1 = a1.getOrNull(i)?.toIntOrNull() ?: 0 + val n2 = a2.getOrNull(i)?.toIntOrNull() ?: 0 + if (n1 != n2) return n1.compareTo(n2) + } + return 0 + } - MaterialDialog.Builder(activity) - .title(R.string.error_failed_to_retrieve_version_histories) - .apply { message?.let(::content) } - .positiveText(R.string.dialog_button_dismiss) - .dismissListener { activity.finish() } - .show() } } \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/util/ProcessLogger.kt b/app/src/main/java/org/autojs/autojs/util/ProcessLogger.kt new file mode 100644 index 00000000..c01fea62 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/util/ProcessLogger.kt @@ -0,0 +1,34 @@ +package org.autojs.autojs.util + +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import java.text.SimpleDateFormat +import java.util.* + +object ProcessLogger { + + private val sdf + get() = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()) + + private val buffer = StringBuilder() + + /* 用 SharedFlow 推送 "增量行". */ + private val _flow = MutableSharedFlow(extraBufferCapacity = 64) + val flow: SharedFlow = _flow.asSharedFlow() + + @Synchronized + fun dump(): String = buffer.toString() + + @Synchronized + fun clear() = buffer.clear() + + @Synchronized + fun i(msg: String) { + val suffix = if (buffer.isNotEmpty()) "\n" else "" + val line = "$suffix${sdf.format(Date())}: $msg" + buffer.append(line) + _flow.tryEmit(line) + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_display_version_histories.xml b/app/src/main/res/layout/activity_display_version_histories.xml index f91af999..2dbae186 100644 --- a/app/src/main/res/layout/activity_display_version_histories.xml +++ b/app/src/main/res/layout/activity_display_version_histories.xml @@ -33,14 +33,6 @@ android:gravity="center" android:orientation="vertical"> - - - - + android:textSize="16sp" /> diff --git a/app/src/main/res/layout/item_version_history.xml b/app/src/main/res/layout/item_version_history.xml index e2843e78..1107e095 100644 --- a/app/src/main/res/layout/item_version_history.xml +++ b/app/src/main/res/layout/item_version_history.xml @@ -77,6 +77,7 @@ android:gravity="end"> @@ -111,6 +112,7 @@ @@ -145,6 +147,7 @@ @@ -183,7 +186,7 @@ + tools:text="@plurals/text_items_total_sum_with_colon" /> diff --git a/app/src/main/res/layout/mt_color_library_recycler_view_item.xml b/app/src/main/res/layout/mt_color_library_recycler_view_item.xml index 5d111057..7d8efdbc 100644 --- a/app/src/main/res/layout/mt_color_library_recycler_view_item.xml +++ b/app/src/main/res/layout/mt_color_library_recycler_view_item.xml @@ -55,7 +55,7 @@ android:maxLines="1" android:textColor="@color/day_night_alpha_60" android:textSize="12sp" - tools:text="@plurals/text_items_total_sum" /> + tools:text="@plurals/text_items_total_sum_with_colon" /> + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ar/plurals.xml b/app/src/main/res/values-ar/plurals.xml index 36ca00d6..c531e45d 100644 --- a/app/src/main/res/values-ar/plurals.xml +++ b/app/src/main/res/values-ar/plurals.xml @@ -1,21 +1,63 @@ + - المجموع: لا شيء + المجموع لا عناصر + المجموع عنصر واحد + المجموع عنصران + المجموع %d عناصر + المجموع %d عنصراً + المجموع %d عنصر + + + + + المجموع: لا عناصر المجموع: عنصر واحد المجموع: عنصران المجموع: %d عناصر - المجموع: %d عنصرًا - المجموع: %d عناصر + المجموع: %d عنصراً + المجموع: %d عنصر + - لم يتم إيقاف أي برنامج نصي - تم إيقاف %d برنامج نصي - تم إيقاف برنامجين نصيين - تم إيقاف %d نصوص - تم إيقاف %d نصوص - تم إيقاف %d نصوص + لم يُوقَف أي سكربت + تم إيقاف سكربت واحد + تم إيقاف سكربتين + تم إيقاف %d سكربتات + تم إيقاف %d سكربتاً + تم إيقاف %d سكربت + + + + + لا تقبل الدالة أي مُعامل + تقبل الدالة مُعاملاً واحداً فقط + تقبل الدالة مُعاملين فقط + تقبل الدالة %d مُعاملات فقط + تقبل الدالة %d مُعاملاً فقط + تقبل الدالة %d مُعامل فقط + + + + + لا تقبل الدالة أي مُعامل + تقبل الدالة مُعاملاً واحداً كحد أقصى + تقبل الدالة مُعاملين كحد أقصى + تقبل الدالة %d مُعاملات كحد أقصى + تقبل الدالة %d مُعاملاً كحد أقصى + تقبل الدالة %d مُعامل كحد أقصى + + + + + لا تعمل الدالة دون مُعاملات + تقبل الدالة مُعاملاً واحداً على الأقل + تقبل الدالة مُعاملين على الأقل + تقبل الدالة %d مُعاملات على الأقل + تقبل الدالة %d مُعاملاً على الأقل + تقبل الدالة %d مُعامل على الأقل \ 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 e0d6b48d..834a6e42 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -279,6 +279,25 @@ تأخير قبل الحلقة 0 للحلقة اللانهائية آخر استخدام: %1$s + فشل خيط \"blob"\ + نجح خيط \"blob"\ وجرى حفظ التخزين المؤقت دون اتصال + تم تحميل البيانات + تم اختيار المحتوى الابتدائي + جارٍ إدراج عناصر جديدة + جارٍ تحميل البيانات المحلية + ملف الأصول المحلي + ملف الأصول المحلي هو أحدث إصدار + البيانات المحلية فارغة + البيانات المحلية أحدث، إيقاف تحليل البيانات المتصلة + لا حاجة للمعالجة + ملف التخزين المؤقت دون اتصال + ملف التخزين المؤقت دون اتصال هو أحدث إصدار + جارٍ استبدال محتوى التاريخ + جارٍ استبدال سجل التحديث + فشل خيط \"raw"\ + نجح خيط \"raw"\ وجرى حفظ التخزين المؤقت دون اتصال + بدء خيط طلب احتياطي \"blob"\ + بدء خيط طلب \"raw"\ الألبوم نسبة العرض إلى الارتفاع الصوت @@ -422,6 +441,7 @@ مقسم الشاشة النظام نوع غير معروف + فلتر الفئة تغيير دليل العمل تعديلات على إصدارات بعض التبعيات أو المكتبات المحلية تحقق من وجود تحديثات @@ -818,6 +838,7 @@ اختر من فضلك اختر نصًا اسم الإدخال + الرجاء الانتظار... موقع المؤشر فشل تبديل \"موقع المؤشر\".\nالوصول إلى الجذر مطلوب. نشر الإخطارات @@ -829,6 +850,7 @@ اضغط مرة أخرى للخروج لإغلاق، اضغط على \"رجوع\" أو \"خفض الصوت\" معاينة + سجل العملية يعالج مشروع موقع المشروع @@ -1006,6 +1028,7 @@ عرض عرض المستندات إصدار امتداد VSCode لا يفي بالمتطلبات + بانتظار اكتمال معالجة جميع البيانات... مهمة أسبوعية يجب اختيار يوم واحد على الأقل مسار دليل العمل diff --git a/app/src/main/res/values-en/plurals.xml b/app/src/main/res/values-en/plurals.xml index b7c58805..bfb0b0ae 100644 --- a/app/src/main/res/values-en/plurals.xml +++ b/app/src/main/res/values-en/plurals.xml @@ -1,6 +1,11 @@ + Total %d item + Total %d items + + + Total: %d item Total: %d items diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 5346701e..26e858c6 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -274,6 +274,25 @@ Delay before loop 0 for infinite loop Latest used: %1$s + \"Blob\" thread request failed + \"Blob\" thread request successful, writing offline cache + Data loaded + Chosen initial content + Inserting new entries + Loading local data + Local asset file + Local asset file is latest version + Local data is empty + Local data is newer, stop analyzing online data + No processing needed + Offline cache file + Offline cache file is latest version + Overwriting date content + Overwriting update record + \"Raw\" thread request failed + \"Raw\" thread request successful, writing offline cache + Starting \"blob\" fallback request thread + Starting \"raw\" request thread Album Aspect ratio Audio @@ -417,6 +436,7 @@ Split Screen Divider System Unknown type + Category filter Change working directory Some dependency or local library version adjustments Check for updates @@ -813,6 +833,7 @@ Please choose Choose a script Input name + Please wait... Pointer location Toggle \"pointer location\" failed.\nRoot access is required. Post notifications @@ -824,6 +845,7 @@ Press again to exit To close, press \"Back\" or \"Volume Down\" Preview + Process log Processing Project Project location @@ -1001,6 +1023,7 @@ View View documents The version of the VSCode extension does not meet the requirements + Waiting for all data processing to complete... Weekly task At least one day must be selected Working directory path diff --git a/app/src/main/res/values-es/plurals.xml b/app/src/main/res/values-es/plurals.xml index 33791c53..8e964be4 100644 --- a/app/src/main/res/values-es/plurals.xml +++ b/app/src/main/res/values-es/plurals.xml @@ -1,9 +1,15 @@ - Total: %d artículo - Total: %d artículos - Total: %d artículos + Total %d elemento + Total %d elementos + Total %d elementos + + + + Total: %d elemento + Total: %d elementos + Total: %d elementos @@ -12,4 +18,22 @@ %d scripts detenidos + + El método solo acepta %d argumento + El método solo acepta %d argumentos + El método solo acepta %d argumentos + + + + El método solo acepta como máximo %d argumento + El método solo acepta como máximo %d argumentos + El método solo acepta como máximo %d argumentos + + + + El método solo acepta al menos %d argumento + El método solo acepta al menos %d argumentos + El método solo acepta al menos %d argumentos + + \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3e8fe2fd..57c96741 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -277,6 +277,25 @@ Retraso antes del bucle 0 para bucle infinito Último uso: %1$s + Hilo \"blob\" fallido + Hilo \"blob\" exitoso, escribiendo caché sin conexión + Datos cargados + Contenido inicial seleccionado + Insertando nuevas entradas + Cargando datos locales + Archivo de recursos local + El archivo de recursos local está en la última versión + Los datos locales están vacíos + Los datos locales son más recientes, se detiene el análisis en línea + No se necesita procesamiento + Archivo de caché sin conexión + El archivo de caché sin conexión está en la última versión + Sobrescribiendo contenido de fecha + Sobrescribiendo registro de actualización + Hilo \"raw\" fallido + Hilo \"raw\" exitoso, escribiendo caché sin conexión + Iniciando hilo de solicitud alterna \"blob\" + Iniciando hilo de solicitud \"raw\" Álbum Relación de aspecto Audio @@ -420,6 +439,7 @@ Divisor de pantalla dividida Sistema Tipo desconocido + Filtro de categoría Cambiar el directorio de trabajo Ajustes en algunas dependencias o versiones de bibliotecas locales Comprobar si hay actualizaciones @@ -816,6 +836,7 @@ Elija Elija un script Nombre de entrada + Por favor, espere... Ubicación del puntero Falló la conmutación de la \"ubicación del puntero\".\nSe requiere acceso a la raíz. Notificaciones postales @@ -827,6 +848,7 @@ Pulse de nuevo para salir Para cerrar, presiona \"Atrás\" o \"Bajar volumen\" Vista previa + Registro de proceso Procesando Proyecto Ubicación del proyecto @@ -1004,6 +1026,7 @@ Ver Ver documentos La versión de la extensión VSCode no cumple los requisitos + Esperando a que se complete el procesamiento de todos los datos... Tarea semanal Debe seleccionarse al menos un día Ruta del directorio de trabajo diff --git a/app/src/main/res/values-fr/plurals.xml b/app/src/main/res/values-fr/plurals.xml index 7bd33dbd..7617c148 100644 --- a/app/src/main/res/values-fr/plurals.xml +++ b/app/src/main/res/values-fr/plurals.xml @@ -1,9 +1,15 @@ - Total: %d article - Total: %d articles - Total: %d articles + Total %d élément + Total %d éléments + Total %d éléments + + + + Total : %d élément + Total : %d éléments + Total : %d éléments @@ -12,4 +18,22 @@ %d scripts arrêtés + + La méthode n’accepte que %d argument + La méthode n’accepte que %d arguments + La méthode n’accepte que %d arguments + + + + La méthode n’accepte pas plus de %d argument + La méthode n’accepte pas plus de %d arguments + La méthode n’accepte pas plus de %d arguments + + + + La méthode n’accepte pas moins de %d argument + La méthode n’accepte pas moins de %d arguments + La méthode n’accepte pas moins de %d arguments + + \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index be89d646..28f9fdad 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -277,6 +277,25 @@ Délai avant boucle 0 pour une boucle infinie Dernière utilisation : %1$s + Échec du thread \"blob\" + Thread \"blob\" réussi, écriture du cache hors ligne + Données chargées + Contenu initial choisi + Insertion de nouvelles entrées + Chargement des données locales + Fichier d\'actifs local + Le fichier d\'actifs local est à jour + Les données locales sont vides + Les données locales sont plus récentes, arrêt de l\'analyse en ligne + Aucun traitement nécessaire + Fichier de cache hors ligne + Le fichier de cache hors ligne est à jour + Remplacement du contenu de date + Remplacement de l\'enregistrement de mise à jour + Échec du thread \"raw\" + Thread \"raw\" réussi, écriture du cache hors ligne + Démarrage du thread de requête de secours \"blob\" + Démarrage du thread de requête \"raw\" Album Ratio d\'aspect Audio @@ -420,6 +439,7 @@ Séparateur d\'écran partagé Système Type inconnu + Filtre de catégorie Changer de répertoire de travail Certaines dépendances ou versions de bibliothèques locales ont été ajustées Vérifier les mises à jour @@ -816,6 +836,7 @@ Veuillez choisir Choisissez un script Nom de l\'entrée + Veuillez patienter... L\'emplacement du pointeur Toggle \"pointer location\" failed.\nL\'accès à la racine est nécessaire. Notifications postales @@ -827,6 +848,7 @@ Appuyez à nouveau pour quitter Pour fermer, appuyez sur \"Retour\" ou \"Volume bas\" Préview + Journal de processus Traitement Projet Localisation du projet @@ -1004,6 +1026,7 @@ Voir Voir les documents La version de l\'extension VSCode ne répond pas aux exigences + En attente de la fin du traitement de toutes les données... Tâche hebdomadaire Au moins un jour doit être sélectionné Chemin du répertoire de travail. diff --git a/app/src/main/res/values-ja/plurals.xml b/app/src/main/res/values-ja/plurals.xml index 30dc4c15..7a3b6eb4 100644 --- a/app/src/main/res/values-ja/plurals.xml +++ b/app/src/main/res/values-ja/plurals.xml @@ -1,11 +1,27 @@ + 合計 %d 件 + + + 合計: %d 件 - %d つのスクリプトが停止している + %d 個のスクリプトを停止しました + + + + メソッドは %d 個の引数のみ受け付けます + + + + メソッドが受け付ける引数は最大 %d 個までです + + + + メソッドが受け付ける引数は最小 %d 個からです \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 52e4ca68..83433c00 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -278,6 +278,25 @@ ループ前の遅延時間 無限ループの場合は 0 最終使用時: %1$s + \"Blob\" スレッドが失敗 + \"Blob\" スレッドが成功、オフラインキャッシュを書き込み + データ読み込み完了 + 初期コンテンツを選択 + 新しい項目を挿入中 + ローカルデータを読み込み中 + ローカルアセットファイル + ローカルアセットファイルは最新です + ローカルデータが空です + ローカルデータの方が新しいためオンライン解析を停止 + 処理は不要 + オフラインキャッシュファイル + オフラインキャッシュファイルは最新です + 日付内容を上書き中 + 更新履歴を上書き中 + \"Raw\" スレッドが失敗 + \"Raw\" スレッドが成功、オフラインキャッシュを書き込み + \"Blob\" 予備リクエストスレッドを開始 + \"Raw\" リクエストスレッドを開始 アルバム アスペクト比 オーディオ @@ -421,6 +440,7 @@ 画面区切り システム 不明なタイプ + カテゴリフィルター 作業ディレクトリの変更 一部の依存関係またはローカルライブラリのバージョン調整 アップデートの確認 @@ -817,6 +837,7 @@ 選択してください スクリプトを選択してください 入力名 + しばらくお待ちください... ポインターの位置 トグル「ポインターの位置」に失敗しました.\nroot 権限が必要です ゆうびんけいほう @@ -828,6 +849,7 @@ もう一度押すと終了します 閉じるには \"戻る\" または \"音量を下げる\" を押してください プレビュー + プロセスログ 処理中 プロジェクト プロジェクトの場所 @@ -1005,6 +1027,7 @@ 表示 ドキュメントを見る VSCode 拡張機能のバージョンが要件を満たしていない + すべてのデータ処理が完了するのを待機しています... 週次タスク 最低 1 日選択する必要があります 作業ディレクトリのパス diff --git a/app/src/main/res/values-ko/plurals.xml b/app/src/main/res/values-ko/plurals.xml index c79e9283..c4a773b9 100644 --- a/app/src/main/res/values-ko/plurals.xml +++ b/app/src/main/res/values-ko/plurals.xml @@ -1,11 +1,27 @@ - 총: %d 개 항목 + 총 %d개 항목 + + + + 총: %d개 항목 - %d 개의 스크립트가 중지되었습니다 + %d개의 스크립트가 중지되었습니다 + + + + 메서드는 %d개의 인자만 허용합니다 + + + + 메서드는 최대 %d개의 인자만 허용합니다 + + + + 메서드는 최소 %d개의 인자를 허용합니다 \ No newline at end of file diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index b2260a89..c49fd166 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -279,6 +279,25 @@ 루프 전 지연 무한 루프의 경우 0 최종 사용: %1$s + \"Blob\" 스레드 요청 실패 + \"Blob\" 스레드 요청 성공, 오프라인 캐시 기록 + 데이터 로드 완료 + 초기 콘텐츠 선택됨 + 새 항목 삽입 중 + 로컬 데이터 불러오는 중 + 로컬 에셋 파일 + 로컬 에셋 파일이 최신 버전임 + 로컬 데이터가 비어 있음 + 로컬 데이터가 최신이므로 온라인 분석 중지 + 처리 필요 없음 + 오프라인 캐시 파일 + 오프라인 캐시 파일이 최신 버전임 + 날짜 내용을 덮어쓰는 중 + 업데이트 기록을 덮어쓰는 중 + \"Raw\" 스레드 요청 실패 + \"Raw\" 스레드 요청 성공, 오프라인 캐시 기록 + \"Blob\" 대체 요청 스레드 시작 + \"Raw\" 요청 스레드 시작 앨범 종횡비 오디오 @@ -422,6 +441,7 @@ 화면 분할 시스템 알 수 없음 + 카테고리 필터 작업 디렉토리를 변경하십시오 일부 의존성 또는 로컬 라이브러리 버전 조정 업데이트 확인 @@ -818,6 +838,7 @@ 선택하십시오 스크립트를 선택하십시오 입력 이름 + 잠시만 기다려주세요... 포인터 위치 \"포인터 위치\"토글이 실패했습니다.\n루트 액세스가 필요합니다. 게시물 알림 @@ -829,6 +850,7 @@ 종료하려면 다시 누릅니다 창을 닫으려면 \"뒤로\" 또는 \"볼륨 감소\" 버튼을 누르세요 시사 + 프로세스 로그 처리 프로젝트 프로젝트 위치 @@ -1006,6 +1028,7 @@ 보기 문서를 봅니다 VSCode 확장 버전이 요구 사항을 충족하지 않습니다 + 모든 데이터 처리가 완료될 때까지 대기 중입니다... 주간 과제 최소한 하루를 선택해야합니다 작업 디렉토리 경로 diff --git a/app/src/main/res/values-ru/plurals.xml b/app/src/main/res/values-ru/plurals.xml index a201d0b4..f8167e9a 100644 --- a/app/src/main/res/values-ru/plurals.xml +++ b/app/src/main/res/values-ru/plurals.xml @@ -1,17 +1,47 @@ + + - Всего: %d элемент - Всего: %d элемента - Всего: %d элементов - Всего: %d элементов + Итого %d элемент + Итого %d элемента + Итого %d элементов + Итого %d элемента + + + + Итого: %d элемент + Итого: %d элемента + Итого: %d элементов + Итого: %d элемента %d скрипт остановлен - %d скрипта остановлены - %d скриптов остановлены - %d скриптов остановлены + %d скрипта остановлено + %d скриптов остановлено + %d скрипта остановлено + + + + Метод принимает только %d аргумент + Метод принимает только %d аргумента + Метод принимает только %d аргументов + Метод принимает только %d аргумента + + + + Метод принимает не более %d аргумента + Метод принимает не более %d аргументов + Метод принимает не более %d аргументов + Метод принимает не более %d аргумента + + + + Метод принимает не менее %d аргумента + Метод принимает не менее %d аргументов + Метод принимает не менее %d аргументов + Метод принимает не менее %d аргумента \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 2ad22c12..ce13b2dc 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -277,6 +277,25 @@ Задержка перед циклом 0 для бесконечного цикла Последнее использование: %1$s + Поток \"blob\" неудачен + Поток \"blob\" успешен, запись офлайн-кеша + Данные загружены + Выбран исходный контент + Вставка новых записей + Загрузка локальных данных + Локальный файл ресурсов + Локальный файл ресурсов актуален + Локальные данные пусты + Локальные данные новее, анализ онлайн-данных остановлен + Обработка не требуется + Файл офлайн-кеша + Файл офлайн-кеша актуален + Перезапись содержимого даты + Перезапись записи обновления + Поток \"raw\" неудачен + Поток \"raw\" успешен, запись офлайн-кеша + Запуск резервного потока \"blob\" + Запуск потока запроса \"raw\" Альбом Соотношение сторон Аудио @@ -420,6 +439,7 @@ Разделитель экрана Система Неизвестный тип + Фильтр категории Изменить рабочий каталог Изменения версий некоторых зависимостей или локальных библиотек Проверка наличия обновлений @@ -816,6 +836,7 @@ Пожалуйста, выберите Выберите сценарий Имя ввода + Пожалуйста, подождите... Расположение указателя Переключение \"Расположение указателя\" не удалось.\nТребуется корневой доступ. почтовые уведомления @@ -827,6 +848,7 @@ Нажмите еще раз, чтобы выйти Чтобы закрыть, нажмите \"Назад\" или \"Уменьшение громкости\" Предварительный просмотр + Журнал процесса Обработка Проект Расположение проекта @@ -1004,6 +1026,7 @@ Просмотр Просмотр документов Версия расширения VSCode не соответствует требованиям + Ожидание завершения обработки всех данных... Недельное задание Должен быть выбран хотя бы один день Путь к рабочему каталогу diff --git a/app/src/main/res/values-zh-rHK/plurals.xml b/app/src/main/res/values-zh-rHK/plurals.xml index 2457d14c..79e18fc2 100644 --- a/app/src/main/res/values-zh-rHK/plurals.xml +++ b/app/src/main/res/values-zh-rHK/plurals.xml @@ -1,6 +1,10 @@ + 共計 %d 項 + + + 共計: %d 項 @@ -8,4 +12,16 @@ 已停止運行 %d 個腳本 + + 方法僅可接受 %d 個參數 + + + + 方法僅可接受不多於 %d 個參數 + + + + 方法僅可接受不少於 %d 個參數 + + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index bf6da654..c84fcc1c 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -275,6 +275,25 @@ 開始循環前的延遲 0 表示無限循環 最近使用: %1$s + 線程 "blob" 請求失敗 + 線程 "blob" 請求成功, 寫入離線緩存 + 數據加載完畢 + 初始內容選用 + 插入新條目 + 加載本地數據 + 本地資產文件 + 本地資產文件最新版本 + 本地數據為空 + 本地數據較新, 停止繼續分析在線數據 + 無需處理 + 離線緩存文件 + 離線緩存文件最新版本 + 覆寫日期內容 + 覆寫更新記錄 + 線程 "raw" 請求失敗 + 線程 "raw" 請求成功, 寫入離線緩存 + 啓動 "blob" 備用請求線程 + 啓動 "raw" 請求線程 專輯 縱橫比 音頻 @@ -418,6 +437,7 @@ 分割屏幕分隔線 系統 未知類型 + 類別篩選 更改工作路徑 部分依賴或本地庫版本調整 檢查更新 @@ -814,6 +834,7 @@ 請選擇 請選擇腳本 請輸入名稱 + 請稍候... 指針位置 切換 \"指針位置\" 顯示狀態失敗\n可能缺少 root 權限 發佈通知權限 @@ -825,6 +846,7 @@ 再按一次退出應用 如需關閉窗口, 可按 \"返回鍵\" 或 \"音量減鍵\" 預覽 + 流程日誌 處理中 項目 項目位置 @@ -1002,6 +1024,7 @@ 查看 查看文檔 VSCode 插件版本不符合要求 + 正在等待全部數據處理完畢... 每週任務 需至少選擇一天 工作路徑 diff --git a/app/src/main/res/values-zh-rTW/plurals.xml b/app/src/main/res/values-zh-rTW/plurals.xml index d0dc2b5b..f7896541 100644 --- a/app/src/main/res/values-zh-rTW/plurals.xml +++ b/app/src/main/res/values-zh-rTW/plurals.xml @@ -1,6 +1,10 @@ + 共計 %d 項 + + + 共計: %d 項 @@ -8,4 +12,16 @@ 已停止執行 %d 個指令碼 + + 方法僅可接受 %d 個引數 + + + + 方法僅可接受不多於 %d 個引數 + + + + 方法僅可接受不少於 %d 個引數 + + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index a9a849ab..ded504c9 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -275,6 +275,25 @@ 開始迴圈前的延遲 0 表示無限迴圈 最近使用: %1$s + 執行緒 "blob" 請求失敗 + 執行緒 "blob" 請求成功, 寫入離線快取 + 資料載入完畢 + 初始內容選用 + 插入新條目 + 載入本地資料 + 本地資產檔案 + 本地資產檔案最新版本 + 本地資料為空 + 本地資料較新, 停止繼續分析線上資料 + 無需處理 + 離線快取檔案 + 離線快取檔案最新版本 + 覆寫日期內容 + 覆寫更新記錄 + 執行緒 "raw" 請求失敗 + 執行緒 "raw" 請求成功, 寫入離線快取 + 啟動 "blob" 備用請求執行緒 + 啟動 "raw" 請求執行緒 專輯 縱橫比 音訊 @@ -418,6 +437,7 @@ 分割螢幕分隔線 系統 未知型別 + 類別篩選 更改工作路徑 部分依賴或本地庫版本調整 檢查更新 @@ -814,6 +834,7 @@ 請選擇 請選擇指令碼 請輸入名稱 + 請稍候... 指標位置 切換 \"指標位置\" 顯示狀態失敗\n可能缺少 root 許可權 釋出通知許可權 @@ -825,6 +846,7 @@ 再按一次退出應用 如需關閉視窗, 可按 \"返回鍵\" 或 \"音量減鍵\" 預覽 + 流程日誌 處理中 專案 專案位置 @@ -1002,6 +1024,7 @@ 檢視 檢視文件 VSCode 外掛版本不符合要求 + 正在等待全部資料處理完畢... 每週任務 需至少選擇一天 工作路徑 diff --git a/app/src/main/res/values-zh/plurals.xml b/app/src/main/res/values-zh/plurals.xml index 6b3bb866..18d7b6f1 100644 --- a/app/src/main/res/values-zh/plurals.xml +++ b/app/src/main/res/values-zh/plurals.xml @@ -1,6 +1,10 @@ + 共计 %d 项 + + + 共计: %d 项 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 4b84eb40..d6b12894 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -275,6 +275,25 @@ 开始循环前的延迟 0 表示无限循环 最近使用: %1$s + 线程 \"blob\" 请求失败 + 线程 \"blob\" 请求成功, 写入离线缓存 + 数据加载完毕 + 初始内容选用 + 插入新条目 + 加载本地数据 + 本地资产文件 + 本地资产文件最新版本 + 本地数据为空 + 本地数据较新, 停止继续分析在线数据 + 无需处理 + 离线缓存文件 + 离线缓存文件最新版本 + 覆写日期内容 + 覆写更新记录 + 线程 \"raw\" 请求失败 + 线程 \"raw\" 请求成功, 写入离线缓存 + 启动 \"blob\" 备用请求线程 + 启动 \"raw\" 请求线程 专辑 纵横比 音频 @@ -418,6 +437,7 @@ 分割屏幕分隔线 系统 未知类型 + 类别筛选 更改工作路径 部分依赖或本地库版本调整 检查更新 @@ -814,6 +834,7 @@ 请选择 请选择脚本 请输入名称 + 请稍候... 指针位置 切换 \"指针位置\" 显示状态失败\n可能缺少 root 权限 发布通知权限 @@ -825,6 +846,7 @@ 再按一次退出应用 如需关闭窗口, 可按 \"返回键\" 或 \"音量减键\" 预览 + 流程日志 处理中 项目 项目位置 @@ -1002,6 +1024,7 @@ 查看 查看文档 VSCode 插件版本不符合要求 + 正在等待全部数据处理完毕... 每周任务 需至少选择一天 工作路径 diff --git a/app/src/main/res/values/plurals.xml b/app/src/main/res/values/plurals.xml index b7c58805..bfb0b0ae 100644 --- a/app/src/main/res/values/plurals.xml +++ b/app/src/main/res/values/plurals.xml @@ -1,6 +1,11 @@ + Total %d item + Total %d items + + + Total: %d item Total: %d items diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3dc2853c..bf080748 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -504,6 +504,25 @@ Delay before loop 0 for infinite loop Latest used: %1$s + \"Blob\" thread request failed + \"Blob\" thread request successful, writing offline cache + Data loaded + Chosen initial content + Inserting new entries + Loading local data + Local asset file + Local asset file is latest version + Local data is empty + Local data is newer, stop analyzing online data + No processing needed + Offline cache file + Offline cache file is latest version + Overwriting date content + Overwriting update record + \"Raw\" thread request failed + \"Raw\" thread request successful, writing offline cache + Starting \"blob\" fallback request thread + Starting \"raw\" request thread Album Aspect ratio Audio @@ -647,6 +666,7 @@ Split Screen Divider System Unknown type + Category filter Change working directory Some dependency or local library version adjustments Check for updates @@ -1043,6 +1063,7 @@ Please choose Choose a script Input name + Please wait... Pointer location Toggle \"pointer location\" failed.\nRoot access is required. Post notifications @@ -1054,6 +1075,7 @@ Press again to exit To close, press \"Back\" or \"Volume Down\" Preview + Process log Processing Project Project location @@ -1231,6 +1253,7 @@ View View documents The version of the VSCode extension does not meet the requirements + Waiting for all data processing to complete... Weekly task At least one day must be selected Working directory path diff --git a/app/src/main/res/xml/fragment_preferences.xml b/app/src/main/res/xml/fragment_preferences.xml index fd76ec80..a26b8780 100644 --- a/app/src/main/res/xml/fragment_preferences.xml +++ b/app/src/main/res/xml/fragment_preferences.xml @@ -221,7 +221,7 @@ app:title="@string/text_manage_ignored_updates" app:longClickPrompt="@string/description_manage_ignored_updates_preference" /> -