diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json index 66be77e6..a6bb8642 100644 --- a/.changelog/lang_zh-Hans.json +++ b/.changelog/lang_zh-Hans.json @@ -8,7 +8,8 @@ "console.setContentTextColor 方法导致日志字体颜色丢失默认值的问题 _[`issue #346`](http://issues.autojs6.com/346)_" ], "improvement": [ - "打包单文件时自动读取并勾选已安装应用的声明权限 _[`issue #362`](http://issues.autojs6.com/362)_" + "打包单文件时自动读取并勾选已安装应用的声明权限 _[`issue #362`](http://issues.autojs6.com/362)_", + "使用异步加载方式一定程度提升文件管理器列表滑动流畅性" ], "dependency": [ "升级 Gradle 版本 8.14-rc-1 -> 8.14-rc-2" diff --git a/.readme/README-zh-Hans.md b/.readme/README-zh-Hans.md index b5bb921d..3c7563a2 100644 --- a/.readme/README-zh-Hans.md +++ b/.readme/README-zh-Hans.md @@ -163,6 +163,7 @@ AutoJs6 在 Auto.js 最终项目的基础上, 于 `2021/12/01` 进行二次开 * `修复` 部分设备代码编辑器空行显示方框字符的问题 * `修复` console.setContentTextColor 方法导致日志字体颜色丢失默认值的问题 _[`issue #346`](http://issues.autojs6.com/346)_ * `优化` 打包单文件时自动读取并勾选已安装应用的声明权限 _[`issue #362`](http://issues.autojs6.com/362)_ +* `优化` 使用异步加载方式一定程度提升文件管理器列表滑动流畅性 * `优化` 部分依赖或本地库版本调整 _[`CHANGELOG.md`](http://project.autojs6.com/blob/master/app/src/main/assets-app/doc/CHANGELOG.md#v663)_ # v6.6.2 diff --git a/README.md b/README.md index b5bb921d..3c7563a2 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,7 @@ AutoJs6 在 Auto.js 最终项目的基础上, 于 `2021/12/01` 进行二次开 * `修复` 部分设备代码编辑器空行显示方框字符的问题 * `修复` console.setContentTextColor 方法导致日志字体颜色丢失默认值的问题 _[`issue #346`](http://issues.autojs6.com/346)_ * `优化` 打包单文件时自动读取并勾选已安装应用的声明权限 _[`issue #362`](http://issues.autojs6.com/362)_ +* `优化` 使用异步加载方式一定程度提升文件管理器列表滑动流畅性 * `优化` 部分依赖或本地库版本调整 _[`CHANGELOG.md`](http://project.autojs6.com/blob/master/app/src/main/assets-app/doc/CHANGELOG.md#v663)_ # v6.6.2 diff --git a/app/src/main/assets-app/doc/CHANGELOG-zh-Hans.md b/app/src/main/assets-app/doc/CHANGELOG-zh-Hans.md index 10bc0c53..1fc990a9 100644 --- a/app/src/main/assets-app/doc/CHANGELOG-zh-Hans.md +++ b/app/src/main/assets-app/doc/CHANGELOG-zh-Hans.md @@ -12,6 +12,7 @@ * `修复` 部分设备代码编辑器空行显示方框字符的问题 * `修复` console.setContentTextColor 方法导致日志字体颜色丢失默认值的问题 _[`issue #346`](http://issues.autojs6.com/346)_ * `优化` 打包单文件时自动读取并勾选已安装应用的声明权限 _[`issue #362`](http://issues.autojs6.com/362)_ +* `优化` 使用异步加载方式一定程度提升文件管理器列表滑动流畅性 * `依赖` 升级 Gradle 版本 8.14-rc-1 -> 8.14-rc-2 # v6.6.2 diff --git a/app/src/main/assets-app/doc/CHANGELOG.md b/app/src/main/assets-app/doc/CHANGELOG.md index 10bc0c53..1fc990a9 100644 --- a/app/src/main/assets-app/doc/CHANGELOG.md +++ b/app/src/main/assets-app/doc/CHANGELOG.md @@ -12,6 +12,7 @@ * `修复` 部分设备代码编辑器空行显示方框字符的问题 * `修复` console.setContentTextColor 方法导致日志字体颜色丢失默认值的问题 _[`issue #346`](http://issues.autojs6.com/346)_ * `优化` 打包单文件时自动读取并勾选已安装应用的声明权限 _[`issue #362`](http://issues.autojs6.com/362)_ +* `优化` 使用异步加载方式一定程度提升文件管理器列表滑动流畅性 * `依赖` 升级 Gradle 版本 8.14-rc-1 -> 8.14-rc-2 # v6.6.2 diff --git a/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerView.kt b/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerView.kt index 33da624a..45992cd7 100644 --- a/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerView.kt +++ b/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerView.kt @@ -10,13 +10,13 @@ import android.view.View import android.view.ViewGroup import android.widget.PopupMenu import android.widget.TextView -import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSmoothScroller import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import io.reactivex.Completable import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -132,32 +132,32 @@ open class ExplorerView : ThemeColorSwipeRefreshLayout, SwipeRefreshLayout.OnRef } private fun init() { - Log.d(LOG_TAG, "item bg = " + Integer.toHexString(ContextCompat.getColor(context, R.color.item_background))) ExplorerViewBinding.inflate(LayoutInflater.from(context), this, true).also { binding -> this.binding = binding - explorerItemListView = initExplorerItemListView(binding.explorerItemList) - mProjectToolbar = binding.projectToolbar + binding.explorerItemList.also { + this.explorerItemListView = it + it.adapter = mExplorerAdapter + it.setHasFixedSize(true) + it.layoutManager = WrapContentGridLayoutManger(this.context, 2).also { manager -> + manager.setDebugInfo(ExplorerView::class.java.simpleName) + manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int) = when { + position > positionOfCategoryDir && position < positionOfCategoryFile() -> { + mDirectorySpanSize // For directories + } + else -> 2 // For files and category + } + } + } + } + binding.projectToolbar.also { + mProjectToolbar = it + } } restoreSortConfig() setOnRefreshListener(this) } - private fun initExplorerItemListView(explorerItemListView: RecyclerView) = explorerItemListView.also { - it.adapter = mExplorerAdapter - it.setItemViewCacheSize(4) - it.layoutManager = WrapContentGridLayoutManger(context, 2).also { manager -> - manager.setDebugInfo("ExplorerView") - manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { - override fun getSpanSize(position: Int) = when { - position > positionOfCategoryDir && position < positionOfCategoryFile() -> { - mDirectorySpanSize // For directories - } - else -> 2 // For files and category - } - } - } - } - @Subscribe fun onExplorerChange(event: ExplorerChangeEvent) { Log.d(LOG_TAG, "on explorer change: $event") @@ -589,6 +589,11 @@ open class ExplorerView : ThemeColorSwipeRefreshLayout, SwipeRefreshLayout.OnRef } } + override fun onViewRecycled(holder: BindableViewHolder) { + super.onViewRecycled(holder) + holder.onViewRecycled() + } + override fun getItemViewType(position: Int): Int { val positionOfCategoryFile = positionOfCategoryFile() return when { @@ -639,15 +644,19 @@ open class ExplorerView : ThemeColorSwipeRefreshLayout, SwipeRefreshLayout.OnRef private val globalAppContext by lazy { GlobalAppContext.get() } + private val mFirstChar: FirstCharView + private val mName: TextView private val mFileDate: TextView private val mFileSize: TextView - private val mOptions: View + + private val mActionIconContainer: View private val mInstall: View private val mRun: View private val mEdit: View private val mInfo: View - private val mFirstChar: FirstCharView + + private val mOptions: View private lateinit var mExplorerItem: ExplorerItem @@ -655,25 +664,20 @@ open class ExplorerView : ThemeColorSwipeRefreshLayout, SwipeRefreshLayout.OnRef val explorerFileBinding = ExplorerFileBinding.bind(itemView) val firstCharIconBinding = ExplorerFirstCharIconBinding.bind(itemView) + mFirstChar = firstCharIconBinding.firstChar + mName = explorerFileBinding.name mFileDate = explorerFileBinding.scriptFileDate mFileSize = explorerFileBinding.scriptFileSize - mFirstChar = firstCharIconBinding.firstChar - mRun = explorerFileBinding.run - mRun.setOnClickListener { withItemSelected { run() } } + mActionIconContainer = explorerFileBinding.actionIconContainer - mEdit = explorerFileBinding.edit - mEdit.setOnClickListener { withItemSelected { edit() } } + mRun = explorerFileBinding.run.apply { setOnClickListener { withItemSelected { run() } } } + mEdit = explorerFileBinding.edit.apply { setOnClickListener { withItemSelected { edit() } } } + mInfo = explorerFileBinding.info.apply { setOnClickListener { withItemSelected { showInfo() } } } + mInstall = explorerFileBinding.install.apply { setOnClickListener { withItemSelected { install() } } } - mInfo = explorerFileBinding.info - mInfo.setOnClickListener { withItemSelected { showInfo() } } - - mInstall = explorerFileBinding.install - mInstall.setOnClickListener { withItemSelected { install() } } - - mOptions = explorerFileBinding.more - mOptions.setOnClickListener { withItemSelected { showOptionsMenu() } } + mOptions = explorerFileBinding.more.apply { setOnClickListener { withItemSelected { showOptionsMenu() } } } explorerFileBinding.item.setOnClickListener { withItemSelected { onItemClick() } } } @@ -683,54 +687,117 @@ open class ExplorerView : ThemeColorSwipeRefreshLayout, SwipeRefreshLayout.OnRef if (item !is ExplorerItem) return mExplorerItem = item - mName.text = ExplorerViewHelper.getDisplayName(context, item) - mFileDate.text = PFile.getFullDateString(item.lastModified()) - mFileSize.text = PFiles.getHumanReadableSize(item.size) - setFirstChar(item) - updateVisibility(mEdit, item.isTextEditable || item.isExternalEditable) - updateVisibility(mRun, item.isExecutable || item.isMediaPlayable) - updateVisibility(mInstall, item.isInstallable) - updateVisibility(mInfo, item.isInstallable || item.isMediaMenu || item.isMediaPlayable) + setTextWith(mName) { ExplorerViewHelper.getDisplayName(context, item) } + setTextWith(mFileDate) { PFile.getFullDateString(item.lastModified()) } + setTextWith(mFileSize) { PFiles.getHumanReadableSize(item.size) } - when /* listOf(mEdit, mRun, mInstall, mInfo).filter { it.isVisible }.size > 2 */ { - listOf(mEdit, mRun, mInfo).all { it.isVisible } -> { - mInfo.isVisible = false - } - listOf(mEdit, mInstall, mInfo).all { it.isVisible } -> { - mInfo.isVisible = false - } - // listOf(mEdit, mRun, mInstall).all { it.isVisible } -> { /* Should not happen. */ } - // listOf(mRun, mInstall, mInfo).all { it.isVisible } -> { /* Should not happen. */ } - // listOf(mEdit, mRun, mInstall, mInfo).all { it.isVisible } -> { /* Should not happen. */ } + Observable.fromCallable { + val shouldEditShow = item.isTextEditable || item.isExternalEditable + val shouldRunShow = item.isExecutable || item.isMediaPlayable + val shouldInstallShow = item.isInstallable + + val alreadyHasTwoImportantIcons = shouldEditShow && (shouldRunShow || shouldInstallShow) + val shouldInfoShow = !alreadyHasTwoImportantIcons && (item.isInstallable || item.isMediaMenu || item.isMediaPlayable) + + listOf(shouldEditShow, shouldRunShow, shouldInstallShow, shouldInfoShow) } + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { list -> + val (shouldEditShow, shouldRunShow, shouldInstallShow, shouldInfoShow) = list + if (shouldEditShow || shouldRunShow || shouldInstallShow || shouldInfoShow) { + setVisibilityIf(mEdit, shouldEditShow) + setVisibilityIf(mRun, shouldRunShow) + setVisibilityIf(mInstall, shouldInstallShow) + setVisibilityIf(mInfo, shouldInfoShow) + setVisibilityIf(mActionIconContainer, true) + } else { + setVisibilityIf(mActionIconContainer, false) + } + } } - private fun updateVisibility(view: View, visible: Boolean) { - val visibility = if (visible) View.VISIBLE else View.GONE + private fun setTextWith(textView: TextView, callable: () -> CharSequence) { + Observable.fromCallable(callable) + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { text -> textView.text = text } + } + + private fun setVisibilityWith(view: View, callable: () -> Boolean) { + Observable.fromCallable(callable) + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { isVisible -> + if (view.isVisible != isVisible) { + view.isVisible = isVisible + } + } + } + + private fun setVisibilityIf(view: View, visibility: Int) { if (view.visibility != visibility) { view.visibility = visibility } } + private fun setVisibilityIf(view: View, visible: Boolean) { + setVisibilityIf(view, if (visible) VISIBLE else GONE) + } + private fun setFirstChar(item: ExplorerItem) { - mFirstChar.setIcon(ExplorerViewHelper.getIcon(item)) - when (item.type) { - FileUtils.TYPE.JAVASCRIPT, FileUtils.TYPE.AUTO -> { - val themeColorForContrast = ColorUtils.adjustColorForContrast(context.getColor(R.color.item_background_dark), ThemeColorManagerCompat.getColorPrimary(), 1.15) - mFirstChar - .setIconTextColorByThemeColorLuminance() - .setStrokeColor(themeColorForContrast) - .setFillColor(themeColorForContrast) + Observable.fromCallable { ExplorerViewHelper.getIcon(item) } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { icon -> + mFirstChar.setIcon(icon) + + val actions = when (item.type) { + FileUtils.TYPE.JAVASCRIPT, FileUtils.TYPE.AUTO -> { + Observable.fromCallable { + ColorUtils.adjustColorForContrast( + context.getColor(R.color.item_background_dark), + ThemeColorManagerCompat.getColorPrimary(), + 1.15 + ) + }.map { themeColorForContrast -> + listOf( + { mFirstChar.setIconTextColorByThemeColorLuminance() }, + { mFirstChar.setStrokeColor(themeColorForContrast) }, + { mFirstChar.setFillColor(themeColorForContrast) }, + { mFirstChar.visibility = VISIBLE }, + ) + } + } + else -> Observable.just( + listOf( + { mFirstChar.setIconTextColorDayNight() }, + { mFirstChar.setStrokeColorDayNight() }, + { mFirstChar.setFillTransparent() }, + { mFirstChar.visibility = VISIBLE }, + ) + ) + } + + actions.subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .flatMapCompletable { list -> + Observable.fromIterable(list) + .flatMapCompletable { action -> Completable.fromAction { action.invoke() } } + } + .subscribe() } - else -> { - mFirstChar - .setIconTextColorDayNight() - .setStrokeColorDayNight() - .setFillTransparent() - } - } + } + + override fun onViewRecycled() { + super.onViewRecycled() + mName.text = null + mFileDate.text = null + mFileSize.text = null + setVisibilityIf(mFirstChar, INVISIBLE) + setVisibilityIf(mActionIconContainer, false) } private fun onItemClick() { @@ -848,7 +915,7 @@ open class ExplorerView : ThemeColorSwipeRefreshLayout, SwipeRefreshLayout.OnRef mName.text = ExplorerViewHelper.getDisplayName(context, data) mDirDate.text = PFile.getFullDateString(data.lastModified()) mIcon.setImageResource(ExplorerViewHelper.getIconRes(data)) - mOptions.visibility = if (data is ExplorerSamplePage) View.GONE else View.VISIBLE + mOptions.visibility = if (data is ExplorerSamplePage) GONE else VISIBLE mExplorerPage = data } @@ -930,14 +997,14 @@ open class ExplorerView : ThemeColorSwipeRefreshLayout, SwipeRefreshLayout.OnRef val currentSortType = if (mIsDir) explorerItemManager.dirSortType else explorerItemManager.fileSortType when (currentSortType) { - ExplorerItemManager.SORT_TYPE_DATE -> popupMenu.menu.findItem(R.id.action_sort_by_date).setChecked(true) - ExplorerItemManager.SORT_TYPE_SIZE -> popupMenu.menu.findItem(R.id.action_sort_by_size).setChecked(true) - ExplorerItemManager.SORT_TYPE_TYPE -> popupMenu.menu.findItem(R.id.action_sort_by_type).setChecked(true) - else -> popupMenu.menu.findItem(R.id.action_sort_by_name).setChecked(true) + ExplorerItemManager.SORT_TYPE_DATE -> popupMenu.menu.findItem(R.id.action_sort_by_date).isChecked = true + ExplorerItemManager.SORT_TYPE_SIZE -> popupMenu.menu.findItem(R.id.action_sort_by_size).isChecked = true + ExplorerItemManager.SORT_TYPE_TYPE -> popupMenu.menu.findItem(R.id.action_sort_by_type).isChecked = true + else -> popupMenu.menu.findItem(R.id.action_sort_by_name).isChecked = true } popupMenu.setOnMenuItemClickListener { item: MenuItem -> - item.setChecked(true) + item.isChecked = true when (item.itemId) { R.id.action_sort_by_name -> { sort(ExplorerItemManager.SORT_TYPE_NAME, isDirSortMenuShowing, true) @@ -975,9 +1042,9 @@ open class ExplorerView : ThemeColorSwipeRefreshLayout, SwipeRefreshLayout.OnRef binding.title.setText(if (isDirCategory) R.string.text_directory else R.string.text_file) mIsDir = isDirCategory if (isDirCategory && canGoUp()) { - binding.goUp.visibility = View.VISIBLE + binding.goUp.visibility = VISIBLE } else { - binding.goUp.visibility = View.GONE + binding.goUp.visibility = GONE } if (isDirCategory) { binding.arrowIcon.rotation = (if (currentPageState.dirsCollapsed) -90 else 0).toFloat() diff --git a/app/src/main/java/org/autojs/autojs/ui/widget/BindableViewHolder.kt b/app/src/main/java/org/autojs/autojs/ui/widget/BindableViewHolder.kt index 09e38fa2..9fe5cf90 100644 --- a/app/src/main/java/org/autojs/autojs/ui/widget/BindableViewHolder.kt +++ b/app/src/main/java/org/autojs/autojs/ui/widget/BindableViewHolder.kt @@ -11,4 +11,8 @@ abstract class BindableViewHolder(itemView: View) : RecyclerView.ViewH abstract fun bind(data: DataType, position: Int) + open fun onViewRecycled() { + /* Nothing to do by default. */ + } + } diff --git a/app/src/main/res/drawable/ic_edit_explorer_btn.png b/app/src/main/res/drawable/ic_edit_explorer_btn.png new file mode 100644 index 00000000..6d3963ce Binary files /dev/null and b/app/src/main/res/drawable/ic_edit_explorer_btn.png differ diff --git a/app/src/main/res/drawable/ic_info_explorer_btn.png b/app/src/main/res/drawable/ic_info_explorer_btn.png new file mode 100644 index 00000000..d89eba1b Binary files /dev/null and b/app/src/main/res/drawable/ic_info_explorer_btn.png differ diff --git a/app/src/main/res/drawable/ic_install_explorer_btn.png b/app/src/main/res/drawable/ic_install_explorer_btn.png new file mode 100644 index 00000000..f141242e Binary files /dev/null and b/app/src/main/res/drawable/ic_install_explorer_btn.png differ diff --git a/app/src/main/res/drawable/ic_more_explorer_btn.png b/app/src/main/res/drawable/ic_more_explorer_btn.png new file mode 100644 index 00000000..9006d388 Binary files /dev/null and b/app/src/main/res/drawable/ic_more_explorer_btn.png differ diff --git a/app/src/main/res/drawable/ic_run_explorer_btn.png b/app/src/main/res/drawable/ic_run_explorer_btn.png new file mode 100644 index 00000000..e30b8ed0 Binary files /dev/null and b/app/src/main/res/drawable/ic_run_explorer_btn.png differ diff --git a/app/src/main/res/layout/explorer_file.xml b/app/src/main/res/layout/explorer_file.xml index 315aed6e..ec5fd3d6 100644 --- a/app/src/main/res/layout/explorer_file.xml +++ b/app/src/main/res/layout/explorer_file.xml @@ -23,13 +23,14 @@ android:foreground="?selectableItemBackground" android:orientation="horizontal" android:paddingVertical="9dp" - android:paddingStart="8dp"> + android:paddingStart="8dp" + android:paddingEnd="0dp"> + android:layout_gravity="center_vertical" /> + android:contentDescription="@string/text_install" + android:focusable="true" + android:src="@drawable/ic_install_explorer_btn" + android:visibility="gone" + app:tint="#A9AAAB" + tools:visibility="visible" /> + android:contentDescription="@string/text_run" + android:focusable="true" + android:src="@drawable/ic_run_explorer_btn" + android:visibility="gone" + app:tint="#A9AAAB" + tools:visibility="visible" /> + android:contentDescription="@string/text_edit" + android:focusable="true" + android:src="@drawable/ic_edit_explorer_btn" + android:visibility="gone" + app:tint="#A9AAAB" + tools:visibility="visible" /> - - + app:tint="#A9AAAB" + tools:visibility="visible" /> + +