From 361b21f14ebe9c42d61c09c705d4b586a9155e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=BB=E5=A4=A9=E5=A4=A9?= Date: Wed, 24 Jan 2024 03:29:32 +0800 Subject: [PATCH 1/4] feat: external sticker cache --- .../messenger/MediaDataController.java | 3 + .../config/cell/ConfigCellButton.java | 60 +++++++++ .../NekoExperimentalSettingsActivity.java | 36 +++++- .../kotlin/xyz/nextalone/nagram/NaConfig.kt | 10 ++ .../helper/ExternalStickerCacheHelper.kt | 120 ++++++++++++++++++ .../src/main/res/values-zh-rCN/strings_na.xml | 2 + .../src/main/res/values/strings_na.xml | 2 + 7 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellButton.java create mode 100644 TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/helper/ExternalStickerCacheHelper.kt diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java index ba4be1fed6..3509031f6f 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java @@ -99,6 +99,7 @@ import tw.nekomimi.nekogram.NekoConfig; import tw.nekomimi.nekogram.ui.PinnedStickerHelper; +import xyz.nextalone.nagram.helper.ExternalStickerCacheHelper; @SuppressWarnings("unchecked") public class MediaDataController extends BaseController { @@ -2940,6 +2941,8 @@ private void processLoadedStickers(int type, ArrayList loadDate[type] = date); putStickersToCache(type, null, date, 0); diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellButton.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellButton.java new file mode 100644 index 0000000000..903f58c724 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellButton.java @@ -0,0 +1,60 @@ +package tw.nekomimi.nekogram.config.cell; + +import androidx.recyclerview.widget.RecyclerView; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Cells.TextCell; +import tw.nekomimi.nekogram.config.CellGroup; + +import java.util.function.Consumer; + +public class ConfigCellButton extends AbstractConfigCell { + private final Consumer onClick; + private final String title; + private String subtitle = null; + private final String subtitleFallback; + private boolean enabled = true; + public TextCell cell; + + public ConfigCellButton(String title, String subtitleFallback, Consumer onClick) { + this.title = title; + this.subtitleFallback = subtitleFallback; + this.onClick = onClick; + } + + public ConfigCellButton(String title, Consumer onClick) { + this(title, null, onClick); + } + + public int getType() { + return CellGroup.ITEM_TYPE_TEXT; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + if (this.cell != null) + this.cell.setEnabled(this.enabled); + } + + public void setSubtitle(String subtitle) { + if (subtitle == null || subtitle.isBlank()) return; + this.subtitle = subtitle; + cell.setSubtitle(subtitle); + } + + public void onBindViewHolder(RecyclerView.ViewHolder holder) { + TextCell cell = (TextCell) holder.itemView; + this.cell = cell; + cell.setText(title, true); + if (subtitle == null || subtitle.isBlank()) subtitle = subtitleFallback; + if (subtitle != null) cell.setSubtitle(subtitle); + cell.setEnabled(enabled); + } + + public void onClick(BaseFragment fragment) { + if (enabled && onClick != null) onClick.accept(fragment); + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/NekoExperimentalSettingsActivity.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/NekoExperimentalSettingsActivity.java index 4ad1273fc0..617b01649b 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/NekoExperimentalSettingsActivity.java +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/NekoExperimentalSettingsActivity.java @@ -28,6 +28,7 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.ThemeDescription; import org.telegram.ui.Cells.EmptyCell; @@ -60,6 +61,7 @@ import tw.nekomimi.nekogram.config.cell.AbstractConfigCell; import tw.nekomimi.nekogram.config.cell.*; import xyz.nextalone.nagram.NaConfig; +import xyz.nextalone.nagram.helper.ExternalStickerCacheHelper; @SuppressLint("RtlHardcoded") public class NekoExperimentalSettingsActivity extends BaseNekoXSettingsActivity { @@ -102,10 +104,27 @@ public class NekoExperimentalSettingsActivity extends BaseNekoXSettingsActivity private final AbstractConfigCell customArtworkApiRow = cellGroup.appendCell(new ConfigCellTextInput(null, NaConfig.INSTANCE.getCustomArtworkApi(), "", null)); private final AbstractConfigCell fakeHighPerformanceDeviceRow = cellGroup.appendCell(new ConfigCellTextCheck(NaConfig.INSTANCE.getFakeHighPerformanceDevice())); private final AbstractConfigCell disableEmojiDrawLimitRow = cellGroup.appendCell(new ConfigCellTextCheck(NaConfig.INSTANCE.getDisableEmojiDrawLimit())); + private final AbstractConfigCell externalStickerCacheRow = cellGroup.appendCell(new ConfigCellButton( + LocaleController.getString(R.string.ExternalStickerCache), LocaleController.getString(R.string.ExternalStickerCacheHint), NekoExperimentalSettingsActivity::onExternalStickerCacheButtonClick)); private final AbstractConfigCell divider1 = cellGroup.appendCell(new ConfigCellDivider()); private UndoView tooltip; + private static final int INTENT_PICK_CUSTOM_EMOJI_PACK = 114; + private static final int INTENT_PICK_EXTERNAL_STICKER_DIRECTORY = 514; + + private void refreshExternalStickerStorageState() { + ConfigCellButton cell = (ConfigCellButton) externalStickerCacheRow; + cell.setSubtitle("Loading..."); + Context context = ApplicationLoader.applicationContext; + ExternalStickerCacheHelper.checkUri(cell, context); + } + + private static void onExternalStickerCacheButtonClick(BaseFragment fragment) { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + fragment.startActivityForResult(intent, 514); + } + @Override public boolean onFragmentCreate() { super.onFragmentCreate(); @@ -133,6 +152,8 @@ public void onItemClick(int id) { } }); + refreshExternalStickerStorageState(); // Cell (externalStickerCacheRow): Refresh state + listAdapter = new ListAdapter(context); fragmentView = new FrameLayout(context); @@ -154,6 +175,8 @@ public void onItemClick(int id) { ((ConfigCellSelectBox) a).onClick(view); } else if (a instanceof ConfigCellTextInput) { ((ConfigCellTextInput) a).onClick(); + } else if (a instanceof ConfigCellButton) { + ((ConfigCellButton) a).onClick(this); } else if (a instanceof ConfigCellTextDetail) { RecyclerListView.OnItemClickListener o = ((ConfigCellTextDetail) a).onItemClickListener; if (o != null) { @@ -251,7 +274,7 @@ public void onItemClick(int id) { intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("application/zip"); Activity act = getParentActivity(); - act.startActivityFromChild(act, intent, 114); + act.startActivityFromChild(act, intent, INTENT_PICK_CUSTOM_EMOJI_PACK); } }; @@ -266,7 +289,7 @@ public void onItemClick(int id) { @Override public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { - if (requestCode == 114 && resultCode == Activity.RESULT_OK) { + if (requestCode == INTENT_PICK_CUSTOM_EMOJI_PACK && resultCode == Activity.RESULT_OK) { try { // copy emoji zip Uri uri = data.getData(); @@ -299,6 +322,15 @@ public void onActivityResultFragment(int requestCode, int resultCode, Intent dat } tooltip.showWithAction(0, UndoView.ACTION_NEED_RESATRT, null, null); // listAdapter.notifyItemChanged(cellGroup.rows.indexOf(useCustomEmojiRow)); + } else if (requestCode == INTENT_PICK_EXTERNAL_STICKER_DIRECTORY && resultCode == Activity.RESULT_OK) { + Uri uri = data.getData(); + // reserve permissions + int takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION; + ApplicationLoader.applicationContext.getContentResolver().takePersistableUriPermission(uri, takeFlags); + // save config + NaConfig.INSTANCE.setExternalStickerCacheUri(uri); + refreshExternalStickerStorageState(); + tooltip.showWithAction(0, UndoView.ACTION_NEED_RESATRT, null, null); } } diff --git a/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/NaConfig.kt b/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/NaConfig.kt index eb1ed83f15..66be111d03 100644 --- a/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/NaConfig.kt +++ b/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/NaConfig.kt @@ -2,6 +2,7 @@ package xyz.nextalone.nagram import android.content.Context import android.content.SharedPreferences +import android.net.Uri import android.util.Base64 import org.telegram.messenger.ApplicationLoader import org.telegram.messenger.LocaleController @@ -478,6 +479,15 @@ object NaConfig { ConfigItem.configTypeBool, false ) + val externalStickerCache = + addConfig( + "ExternalStickerCache", + ConfigItem.configTypeString, + "" + ) + var externalStickerCacheUri + get() = Uri.parse(externalStickerCache.String()) + set(value) = externalStickerCache.setConfigString(value.toString()) private fun addConfig( k: String, diff --git a/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/helper/ExternalStickerCacheHelper.kt b/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/helper/ExternalStickerCacheHelper.kt new file mode 100644 index 0000000000..4175d8b638 --- /dev/null +++ b/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/helper/ExternalStickerCacheHelper.kt @@ -0,0 +1,120 @@ +package xyz.nextalone.nagram.helper + +import android.app.Application +import android.content.Context +import android.content.SharedPreferences +import androidx.documentfile.provider.DocumentFile +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.telegram.messenger.ApplicationLoader +import org.telegram.messenger.BuildVars +import org.telegram.messenger.MediaDataController +import org.telegram.tgnet.TLRPC.TL_messages_stickerSet +import tw.nekomimi.nekogram.config.cell.ConfigCellButton +import xyz.nextalone.nagram.NaConfig +import java.io.File + +object ExternalStickerCacheHelper { + @JvmStatic + fun checkUri(configCell: ConfigCellButton, context: Context) { + CoroutineScope(Dispatchers.IO).launch { + val uri = NaConfig.externalStickerCacheUri + val dir = DocumentFile.fromTreeUri(context, uri) + if (dir == null) { + configCell.setSubtitle("Error: failed to access document") + } else { + if (dir.isDirectory) { + val testFile = dir.createFile("text/plain", "test file") + if (testFile == null) { + configCell.setSubtitle("Error: cannot create file") + } else { + if (testFile.canRead() && testFile.canWrite()) { + configCell.setSubtitle("Currently using: ${dir.name}") + } else { + configCell.setSubtitle("Error: read/write is not supported") + } + if (!testFile.delete()) configCell.setSubtitle("Error: cannot delete file") + } + } else { + configCell.setSubtitle("Error: not a directory") + } + } + } + } + + private val SYNC get() = "" + + @JvmStatic + fun onCacheStickers(type: Int, stickerSets: ArrayList, context: Context) { + if (NaConfig.externalStickerCache.String().isEmpty()) return + when (type) { + MediaDataController.TYPE_EMOJI, + MediaDataController.TYPE_EMOJIPACKS, + MediaDataController.TYPE_FEATURED_EMOJIPACKS, + -> return + else -> CoroutineScope(Dispatchers.IO).launch { + synchronized(SYNC) { + try { + val uri = NaConfig.externalStickerCacheUri + val resolver = context.contentResolver + DocumentFile.fromTreeUri(context, uri)?.let { dir -> + val cachePath = getCachePath(context) + if (dir.isDirectory) { + val stickerSetDirMap = dir.listFiles().run { + val map = mutableMapOf() + forEach { it.name?.let { name -> map[name] = it } } + map + } + stickerSets.forEach { stickerSetObject -> + val stickerSet = stickerSetObject.set + val stickers = stickerSetObject.documents + val avatarId = stickerSet.thumb_document_id + val idString = stickerSet.id.toString() + (stickerSetDirMap[idString] ?: dir.createDirectory(idString))?.let { stickerSetDir -> + if (stickerSetDir.isDirectory) { + val stickerFileMap = stickerSetDir.listFiles().run { + val map = mutableMapOf() + forEach { it.name?.let { name -> map[name] = it } } + map + } + for (sticker in stickers) { + val webp = "image/webp" + val localPath = "${sticker.dc_id}_${sticker.id}.webp" + val localPathLowQuality = "-${sticker.id}_1109.webp" + val stickerFile = File(cachePath, localPath) + val stickerFileLowQuality = File(cachePath, localPathLowQuality) + val destName = "${sticker.id}_high" + val destNameLowQuality = "${sticker.id}_low" + + if (stickerFile.exists()) { + if (stickerFileMap[destName] == null) { + stickerSetDir.createFile(webp, destName)?.let { destFile -> + resolver.openOutputStream(destFile.uri)?.let { stickerFile.inputStream().copyTo(it) } + stickerFileMap[destNameLowQuality]?.delete() + } + } + } else if (stickerFileLowQuality.exists()) { + if (stickerFileMap[destNameLowQuality] == null) { + stickerSetDir.createFile(webp, destNameLowQuality)?.let { destFileLowQuality -> + resolver.openOutputStream(destFileLowQuality.uri)?.let { stickerFile.inputStream().copyTo(it) } + stickerFileMap[destName]?.delete() + } + } + } + } + } + } + } + } + } + } catch (e: Exception) { + if (BuildVars.DEBUG_VERSION) throw RuntimeException(e) + } + } + } + } + } + + private fun getCachePath(context: Context) = context.getExternalFilesDir(null)!!.resolve("caches") +} diff --git a/TMessagesProj/src/main/res/values-zh-rCN/strings_na.xml b/TMessagesProj/src/main/res/values-zh-rCN/strings_na.xml index 46181258e5..ffa19d2e9a 100644 --- a/TMessagesProj/src/main/res/values-zh-rCN/strings_na.xml +++ b/TMessagesProj/src/main/res/values-zh-rCN/strings_na.xml @@ -94,4 +94,6 @@ 显示方形头像 禁用私聊的自定义背景 禁用频道的自定义背景 + 在外部存储缓存贴纸文件 + 用于与其他应用共享贴纸包 (点击选择存储位置) diff --git a/TMessagesProj/src/main/res/values/strings_na.xml b/TMessagesProj/src/main/res/values/strings_na.xml index efcb79f01a..f8d0e1d5a1 100644 --- a/TMessagesProj/src/main/res/values/strings_na.xml +++ b/TMessagesProj/src/main/res/values/strings_na.xml @@ -97,4 +97,6 @@ Show square avatar Disable user custom wallpaper Disable channel custom wallpaper + External sticker cache + For sharing sticker packs with other apps. Click to pick storage location. From 2bee97ea8c2f29839bc7fa29dbc98ec50c9252af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=BB=E5=A4=A9=E5=A4=A9?= Date: Tue, 30 Jan 2024 15:42:23 +0800 Subject: [PATCH 2/4] refactor: feat: external sticker cache --- .../messenger/MediaDataController.java | 12 + .../ui/Components/StickerMasksAlert.java | 4 + .../telegram/ui/Components/StickersAlert.java | 14 + .../config/cell/ConfigCellAutoTextCheck.java | 69 +++++ .../config/cell/ConfigCellButton.java | 60 ----- .../NekoExperimentalSettingsActivity.java | 24 +- .../kotlin/xyz/nextalone/nagram/NaConfig.kt | 4 +- .../helper/ExternalStickerCacheHelper.kt | 251 ++++++++++++------ .../src/main/res/values-zh-rCN/strings_na.xml | 2 + .../src/main/res/values/strings_na.xml | 2 + 10 files changed, 294 insertions(+), 148 deletions(-) create mode 100644 TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellAutoTextCheck.java delete mode 100644 TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellButton.java diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java index 3509031f6f..39591bec87 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java @@ -1953,6 +1953,9 @@ public void addNewStickerSet(TLRPC.TL_messages_stickerSet set) { loadHash[type] = calcStickersHash(stickerSets[type]); getNotificationCenter().postNotificationName(NotificationCenter.stickersDidLoad, type, true); loadStickers(type, false, true); + + // Na: [ExternalStickerCache] cache sticker sets + ExternalStickerCacheHelper.cacheStickers(); } public void loadFeaturedStickers(boolean emoji, boolean cache, boolean force) { @@ -2723,6 +2726,9 @@ public void loadStickers(int type, boolean cache, boolean force, boolean schedul })); } } + + // Na: [ExternalStickerCache] cache sticker sets + ExternalStickerCacheHelper.cacheStickers(); } private void putStickersToCache(int type, ArrayList stickers, int date, long hash) { @@ -2758,6 +2764,9 @@ private void putStickersToCache(int type, ArrayList sets) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerMasksAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerMasksAlert.java index d6e74e7862..456b0abe74 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerMasksAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerMasksAlert.java @@ -64,6 +64,7 @@ import org.telegram.ui.Cells.StickerEmojiCell; import org.telegram.ui.Cells.StickerSetNameCell; import org.telegram.ui.ContentPreviewViewer; +import xyz.nextalone.nagram.helper.ExternalStickerCacheHelper; import java.util.ArrayList; import java.util.Arrays; @@ -1165,6 +1166,9 @@ private void updateStickerTabs() { stickersTab.onPageScrolled(lastPosition, lastPosition); } checkPanels(); + + // Na: [ExternalStickerCache] cache sticker sets + ExternalStickerCacheHelper.cacheStickers(); } private void checkPanels() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java index 90a45305ec..75d7db6c24 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java @@ -109,6 +109,8 @@ import tw.nekomimi.nekogram.NekoConfig; import tw.nekomimi.nekogram.utils.ProxyUtil; +import xyz.nextalone.nagram.NaConfig; +import xyz.nextalone.nagram.helper.ExternalStickerCacheHelper; public class StickersAlert extends BottomSheet implements NotificationCenter.NotificationCenterDelegate { @@ -160,6 +162,8 @@ public interface StickersAlertCustomButtonDelegate { public boolean probablyEmojis; private int menu_archive = 4; + private int menu_refreshExternalCache = 5; + private int menu_deleteExternalCache = 6; private TLRPC.TL_messages_stickerSet stickerSet; private TLRPC.Document selectedSticker; @@ -865,6 +869,10 @@ public void requestLayout() { optionsButton.addSubItem(2, R.drawable.msg_link, LocaleController.getString("CopyLink", R.string.CopyLink)); optionsButton.addSubItem(3, R.drawable.msg_qrcode, LocaleController.getString("ShareQRCode", R.string.ShareQRCode)); optionsButton.addSubItem(menu_archive, R.drawable.msg_archive, LocaleController.getString("Archive", R.string.Archive)); + if (!NaConfig.INSTANCE.getExternalStickerCache().String().isBlank()) { + optionsButton.addSubItem(menu_refreshExternalCache, R.drawable.menu_views_reposts, LocaleController.getString(R.string.ExternalStickerCacheRefresh)); + optionsButton.addSubItem(menu_deleteExternalCache, R.drawable.msg_delete, LocaleController.getString(R.string.ExternalStickerCacheDelete)); + } optionsButton.setOnClickListener(v -> optionsButton.toggleSubMenu()); optionsButton.setDelegate(this::onSubItemClick); @@ -1092,6 +1100,12 @@ protected void onSend(LongSparseArray dids, int count, TLRPC.TL_fo } else if (id == menu_archive) { dismiss(); MediaDataController.getInstance(currentAccount).toggleStickerSet(parentActivity, stickerSet, 1, parentFragment, false, true); + } else if (id == menu_refreshExternalCache) { + // Na: [ExternalStickerCache] force refresh cache files + ExternalStickerCacheHelper.refreshCacheFiles(stickerSet); + } else if (id == menu_deleteExternalCache) { + // Na: [ExternalStickerCache] delete cache files + ExternalStickerCacheHelper.deleteCacheFiles(stickerSet); } } diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellAutoTextCheck.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellAutoTextCheck.java new file mode 100644 index 0000000000..a28961a74f --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellAutoTextCheck.java @@ -0,0 +1,69 @@ +package tw.nekomimi.nekogram.config.cell; + +import androidx.recyclerview.widget.RecyclerView; +import org.telegram.ui.Cells.TextCheckCell; +import tw.nekomimi.nekogram.config.CellGroup; + +import java.util.function.Consumer; + +public class ConfigCellAutoTextCheck extends AbstractConfigCell { + private final Consumer onClick; + private final String title; + private String subtitle = null; + private final String subtitleFallback; + private boolean enabled = true; + public TextCheckCell cell; + + public ConfigCellAutoTextCheck(String title, String subtitleFallback, Consumer onClick) { + this.title = title; + this.subtitleFallback = subtitleFallback; + this.onClick = onClick; + } + + public ConfigCellAutoTextCheck(String title, Consumer onClick) { + this(title, null, onClick); + } + + public int getType() { + return CellGroup.ITEM_TYPE_TEXT_CHECK; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + if (this.cell != null) + this.cell.setEnabled(this.enabled); + } + + public void setSubtitle(String subtitle) { + this.subtitle = subtitle; + boolean checked = true; + if (subtitle == null || subtitle.isBlank()) { + subtitle = subtitleFallback; + checked = false; + } + if (cell != null) { + if (subtitle == null) { + cell.setTextAndCheck(title, checked, cellGroup.needSetDivider(this)); + } else { + cell.setTextAndValueAndCheck(title, subtitle, checked, true, cellGroup.needSetDivider(this)); + } + } + } + + public void onBindViewHolder(RecyclerView.ViewHolder holder) { + TextCheckCell cell = (TextCheckCell) holder.itemView; + this.cell = cell; + setSubtitle(subtitle); + cell.setEnabled(enabled, null); + } + + public void onClick() { + if (enabled) { + if (onClick != null) onClick.accept(cell.isChecked()); + } + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellButton.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellButton.java deleted file mode 100644 index 903f58c724..0000000000 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellButton.java +++ /dev/null @@ -1,60 +0,0 @@ -package tw.nekomimi.nekogram.config.cell; - -import androidx.recyclerview.widget.RecyclerView; -import org.telegram.ui.ActionBar.BaseFragment; -import org.telegram.ui.Cells.TextCell; -import tw.nekomimi.nekogram.config.CellGroup; - -import java.util.function.Consumer; - -public class ConfigCellButton extends AbstractConfigCell { - private final Consumer onClick; - private final String title; - private String subtitle = null; - private final String subtitleFallback; - private boolean enabled = true; - public TextCell cell; - - public ConfigCellButton(String title, String subtitleFallback, Consumer onClick) { - this.title = title; - this.subtitleFallback = subtitleFallback; - this.onClick = onClick; - } - - public ConfigCellButton(String title, Consumer onClick) { - this(title, null, onClick); - } - - public int getType() { - return CellGroup.ITEM_TYPE_TEXT; - } - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - if (this.cell != null) - this.cell.setEnabled(this.enabled); - } - - public void setSubtitle(String subtitle) { - if (subtitle == null || subtitle.isBlank()) return; - this.subtitle = subtitle; - cell.setSubtitle(subtitle); - } - - public void onBindViewHolder(RecyclerView.ViewHolder holder) { - TextCell cell = (TextCell) holder.itemView; - this.cell = cell; - cell.setText(title, true); - if (subtitle == null || subtitle.isBlank()) subtitle = subtitleFallback; - if (subtitle != null) cell.setSubtitle(subtitle); - cell.setEnabled(enabled); - } - - public void onClick(BaseFragment fragment) { - if (enabled && onClick != null) onClick.accept(fragment); - } -} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/NekoExperimentalSettingsActivity.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/NekoExperimentalSettingsActivity.java index 617b01649b..495e2d1c52 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/NekoExperimentalSettingsActivity.java +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/NekoExperimentalSettingsActivity.java @@ -104,8 +104,8 @@ public class NekoExperimentalSettingsActivity extends BaseNekoXSettingsActivity private final AbstractConfigCell customArtworkApiRow = cellGroup.appendCell(new ConfigCellTextInput(null, NaConfig.INSTANCE.getCustomArtworkApi(), "", null)); private final AbstractConfigCell fakeHighPerformanceDeviceRow = cellGroup.appendCell(new ConfigCellTextCheck(NaConfig.INSTANCE.getFakeHighPerformanceDevice())); private final AbstractConfigCell disableEmojiDrawLimitRow = cellGroup.appendCell(new ConfigCellTextCheck(NaConfig.INSTANCE.getDisableEmojiDrawLimit())); - private final AbstractConfigCell externalStickerCacheRow = cellGroup.appendCell(new ConfigCellButton( - LocaleController.getString(R.string.ExternalStickerCache), LocaleController.getString(R.string.ExternalStickerCacheHint), NekoExperimentalSettingsActivity::onExternalStickerCacheButtonClick)); + private final AbstractConfigCell externalStickerCacheRow = cellGroup.appendCell(new ConfigCellAutoTextCheck( + LocaleController.getString(R.string.ExternalStickerCache), LocaleController.getString(R.string.ExternalStickerCacheHint), this::onExternalStickerCacheButtonClick)); private final AbstractConfigCell divider1 = cellGroup.appendCell(new ConfigCellDivider()); private UndoView tooltip; @@ -114,15 +114,21 @@ public class NekoExperimentalSettingsActivity extends BaseNekoXSettingsActivity private static final int INTENT_PICK_EXTERNAL_STICKER_DIRECTORY = 514; private void refreshExternalStickerStorageState() { - ConfigCellButton cell = (ConfigCellButton) externalStickerCacheRow; - cell.setSubtitle("Loading..."); + ConfigCellAutoTextCheck cell = (ConfigCellAutoTextCheck) externalStickerCacheRow; Context context = ApplicationLoader.applicationContext; ExternalStickerCacheHelper.checkUri(cell, context); } - private static void onExternalStickerCacheButtonClick(BaseFragment fragment) { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); - fragment.startActivityForResult(intent, 514); + private void onExternalStickerCacheButtonClick(boolean isChecked) { + if (isChecked) { + // clear config + ConfigCellAutoTextCheck cell = (ConfigCellAutoTextCheck) externalStickerCacheRow; + cell.setSubtitle(null); + NaConfig.INSTANCE.getExternalStickerCache().setConfigString(""); + } else { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + startActivityForResult(intent, INTENT_PICK_EXTERNAL_STICKER_DIRECTORY); + } } @Override @@ -175,8 +181,8 @@ public void onItemClick(int id) { ((ConfigCellSelectBox) a).onClick(view); } else if (a instanceof ConfigCellTextInput) { ((ConfigCellTextInput) a).onClick(); - } else if (a instanceof ConfigCellButton) { - ((ConfigCellButton) a).onClick(this); + } else if (a instanceof ConfigCellAutoTextCheck) { + ((ConfigCellAutoTextCheck) a).onClick(); } else if (a instanceof ConfigCellTextDetail) { RecyclerListView.OnItemClickListener o = ((ConfigCellTextDetail) a).onItemClickListener; if (o != null) { diff --git a/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/NaConfig.kt b/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/NaConfig.kt index 66be111d03..30b2accfaf 100644 --- a/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/NaConfig.kt +++ b/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/NaConfig.kt @@ -485,8 +485,8 @@ object NaConfig { ConfigItem.configTypeString, "" ) - var externalStickerCacheUri - get() = Uri.parse(externalStickerCache.String()) + var externalStickerCacheUri: Uri? + get() = externalStickerCache.String().let { if (it.isBlank()) return null else return Uri.parse(it) } set(value) = externalStickerCache.setConfigString(value.toString()) private fun addConfig( diff --git a/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/helper/ExternalStickerCacheHelper.kt b/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/helper/ExternalStickerCacheHelper.kt index 4175d8b638..c2f21709a3 100644 --- a/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/helper/ExternalStickerCacheHelper.kt +++ b/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/helper/ExternalStickerCacheHelper.kt @@ -1,120 +1,217 @@ package xyz.nextalone.nagram.helper -import android.app.Application import android.content.Context -import android.content.SharedPreferences +import android.util.Log import androidx.documentfile.provider.DocumentFile import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import org.telegram.messenger.AndroidUtilities import org.telegram.messenger.ApplicationLoader -import org.telegram.messenger.BuildVars import org.telegram.messenger.MediaDataController +import org.telegram.messenger.UserConfig import org.telegram.tgnet.TLRPC.TL_messages_stickerSet -import tw.nekomimi.nekogram.config.cell.ConfigCellButton +import tw.nekomimi.nekogram.config.cell.ConfigCellAutoTextCheck import xyz.nextalone.nagram.NaConfig import java.io.File object ExternalStickerCacheHelper { + const val TAG = "ExternalStickerCache" + + private val cachePath = ApplicationLoader.applicationContext.getExternalFilesDir(null)!!.resolve("caches") + @JvmStatic - fun checkUri(configCell: ConfigCellButton, context: Context) { + fun checkUri(configCell: ConfigCellAutoTextCheck, context: Context) { CoroutineScope(Dispatchers.IO).launch { - val uri = NaConfig.externalStickerCacheUri - val dir = DocumentFile.fromTreeUri(context, uri) - if (dir == null) { - configCell.setSubtitle("Error: failed to access document") - } else { - if (dir.isDirectory) { - val testFile = dir.createFile("text/plain", "test file") - if (testFile == null) { - configCell.setSubtitle("Error: cannot create file") - } else { - if (testFile.canRead() && testFile.canWrite()) { - configCell.setSubtitle("Currently using: ${dir.name}") + NaConfig.externalStickerCacheUri?.let { uri -> + AndroidUtilities.runOnUIThread { configCell.setSubtitle("Loading...") } + val dir = DocumentFile.fromTreeUri(context, uri) + var subtitle: String + if (dir == null) { + subtitle = "Error: failed to access document" + } else { + if (dir.isDirectory) { + val testFile = dir.findFile("test") ?: dir.createFile("text/plain", "test") + if (testFile == null) { + subtitle = "Error: cannot create file" } else { - configCell.setSubtitle("Error: read/write is not supported") + if (testFile.canRead() && testFile.canWrite()) { + subtitle = "Currently using: ${dir.name}" + } else { + subtitle = "Error: read/write is not supported" + } + if (!testFile.delete()) subtitle = "Error: cannot delete file" } - if (!testFile.delete()) configCell.setSubtitle("Error: cannot delete file") + } else { + subtitle = "Error: not a directory" } - } else { - configCell.setSubtitle("Error: not a directory") } + AndroidUtilities.runOnUIThread { configCell.setSubtitle(subtitle) } } } } - private val SYNC get() = "" + private var caching = false + private var cacheAgain = false @JvmStatic - fun onCacheStickers(type: Int, stickerSets: ArrayList, context: Context) { + fun cacheStickers() { + if (caching) { + cacheAgain = true + return + } if (NaConfig.externalStickerCache.String().isEmpty()) return - when (type) { - MediaDataController.TYPE_EMOJI, - MediaDataController.TYPE_EMOJIPACKS, - MediaDataController.TYPE_FEATURED_EMOJIPACKS, - -> return - else -> CoroutineScope(Dispatchers.IO).launch { - synchronized(SYNC) { - try { - val uri = NaConfig.externalStickerCacheUri - val resolver = context.contentResolver - DocumentFile.fromTreeUri(context, uri)?.let { dir -> - val cachePath = getCachePath(context) - if (dir.isDirectory) { - val stickerSetDirMap = dir.listFiles().run { - val map = mutableMapOf() - forEach { it.name?.let { name -> map[name] = it } } - map - } - stickerSets.forEach { stickerSetObject -> - val stickerSet = stickerSetObject.set - val stickers = stickerSetObject.documents - val avatarId = stickerSet.thumb_document_id - val idString = stickerSet.id.toString() - (stickerSetDirMap[idString] ?: dir.createDirectory(idString))?.let { stickerSetDir -> - if (stickerSetDir.isDirectory) { - val stickerFileMap = stickerSetDir.listFiles().run { - val map = mutableMapOf() - forEach { it.name?.let { name -> map[name] = it } } - map - } - for (sticker in stickers) { - val webp = "image/webp" - val localPath = "${sticker.dc_id}_${sticker.id}.webp" - val localPathLowQuality = "-${sticker.id}_1109.webp" - val stickerFile = File(cachePath, localPath) - val stickerFileLowQuality = File(cachePath, localPathLowQuality) - val destName = "${sticker.id}_high" - val destNameLowQuality = "${sticker.id}_low" + CoroutineScope(Dispatchers.IO).launch { + caching = true + val stickerSets = MediaDataController.getInstance(UserConfig.selectedAccount).getStickerSets(MediaDataController.TYPE_IMAGE) + val context = ApplicationLoader.applicationContext + try { + val uri = NaConfig.externalStickerCacheUri ?: return@launch + val resolver = context.contentResolver + DocumentFile.fromTreeUri(context, uri)?.let { dir -> + logD("Caching ${stickerSets.size} sticker set(s)...") + if (dir.isDirectory) { + val stickerSetDirMap = dir.listFiles().run { + val map = mutableMapOf() + forEach { it.name?.let { name -> map[name] = it } } + map + } + stickerSets.forEach { stickerSetObject -> + val stickerSet = stickerSetObject.set + val stickers = stickerSetObject.documents + val idString = stickerSet.id.toString() + (stickerSetDirMap[idString] ?: dir.createDirectory(idString))?.let { stickerSetDir -> + if (stickerSetDir.isDirectory) { + val stickerFileMap = stickerSetDir.listFiles().run { + val map = mutableMapOf() + forEach { it.name?.let { name -> map[name] = it } } + map + } + for (sticker in stickers) { + val webp = "image/webp" + val webpExt = ".webp" + val localPath = "${sticker.dc_id}_${sticker.id}.webp" + val localPathLowQuality = "-${sticker.id}_1109.webp" + val stickerFile = File(cachePath, localPath) + val stickerFileLowQuality = File(cachePath, localPathLowQuality) + val destName = "${sticker.id}_high" + val destNameExt = destName + webpExt + val destNameNotFound = stickerFileMap[destNameExt] == null + val destNameLowQuality = "${sticker.id}_low" + val destNameLowQualityExt = destNameLowQuality + webpExt - if (stickerFile.exists()) { - if (stickerFileMap[destName] == null) { - stickerSetDir.createFile(webp, destName)?.let { destFile -> - resolver.openOutputStream(destFile.uri)?.let { stickerFile.inputStream().copyTo(it) } - stickerFileMap[destNameLowQuality]?.delete() - } - } - } else if (stickerFileLowQuality.exists()) { - if (stickerFileMap[destNameLowQuality] == null) { - stickerSetDir.createFile(webp, destNameLowQuality)?.let { destFileLowQuality -> - resolver.openOutputStream(destFileLowQuality.uri)?.let { stickerFile.inputStream().copyTo(it) } - stickerFileMap[destName]?.delete() - } + if (stickerFile.exists()) { + if (destNameNotFound) { + stickerSetDir.createFile(webp, destName)?.let { destFile -> + resolver.openOutputStream(destFile.uri)?.let { stickerFile.inputStream().copyTo(it) } + logV("Created file ${destFile.name}") + stickerFileMap[destNameLowQualityExt]?.let { + it.delete() + logV("Deleted low quality file") } } } + } else if (stickerFileLowQuality.exists()) { + if (stickerFileMap[destNameLowQualityExt] == null && destNameNotFound) { + stickerSetDir.createFile(webp, destNameLowQuality)?.let { destFileLowQuality -> + resolver.openOutputStream(destFileLowQuality.uri)?.let { stickerFileLowQuality.inputStream().copyTo(it) } + logV("Created low quality file ${destFileLowQuality.name}") + } + } } } } } } - } catch (e: Exception) { - if (BuildVars.DEBUG_VERSION) throw RuntimeException(e) } } + } catch (e: Exception) { + logException(e, "caching stickers") + } + if (cacheAgain) { + delay(30000) + cacheAgain = false + cacheStickers() + } else { + caching = false + } + } + } + + @JvmStatic + fun refreshCacheFiles(set: TL_messages_stickerSet) { + CoroutineScope(Dispatchers.IO).launch { + val uri = NaConfig.externalStickerCacheUri ?: return@launch + val context = ApplicationLoader.applicationContext + try { + DocumentFile.fromTreeUri(context, uri)?.let { dir -> + val stickerSet = set.set + val idString = stickerSet.id.toString() + logD("Refreshing cache $idString...") + dir.findFile(idString)?.let { + it.delete() + logD("Deleting exist files...") + while (true) { + delay(500) + if (dir.findFile(idString) == null) break + } + } + dir.createDirectory(idString)?.let { stickerSetDir -> + val stickers = set.documents + val resolver = context.contentResolver + for (sticker in stickers) { + val localPath = "${sticker.dc_id}_${sticker.id}.webp" + val localPathLowQuality = "-${sticker.id}_1109.webp" + val stickerFile = File(cachePath, localPath) + val stickerFileLowQuality = File(cachePath, localPathLowQuality) + fun create(name: String, type: String) = stickerSetDir.createFile(type, name) + val webp = "image/webp" + val destName = "${sticker.id}_high" + val destNameLowQuality = "${sticker.id}_low" + + if (stickerFile.exists()) { + create(destName, webp)?.let { destFile -> + resolver.openOutputStream(destFile.uri)?.let { stickerFile.inputStream().copyTo(it) } + logV("Created file ${destFile.name}") + } + } else if (stickerFileLowQuality.exists()) { + create(destNameLowQuality, webp)?.let { destFileLowQuality -> + resolver.openOutputStream(destFileLowQuality.uri)?.let { stickerFileLowQuality.inputStream().copyTo(it) } + logV("Created low quality file ${destFileLowQuality.name}") + } + } + } + } + } + } catch (e: Exception) { + logException(e, "refreshing specific cache") } } } - private fun getCachePath(context: Context) = context.getExternalFilesDir(null)!!.resolve("caches") + @JvmStatic + fun deleteCacheFiles(set: TL_messages_stickerSet) { + CoroutineScope(Dispatchers.IO).launch { + val uri = NaConfig.externalStickerCacheUri ?: return@launch + val context = ApplicationLoader.applicationContext + try { + DocumentFile.fromTreeUri(context, uri)?.let { dir -> + val stickerSet = set.set + val idString = stickerSet.id.toString() + dir.findFile(idString)?.delete() + } + } catch (e: Exception) { + logException(e, "deleting specific cache") + } + } + } + + private fun logException(e: Exception, s: String) { + val exception = e.javaClass.canonicalName + val message = e.message + Log.e(TAG, "Exception while $s: $exception: $message") + } + private fun logV(message: String) = Log.v(TAG, message) + private fun logD(message: String) = Log.d(TAG, message) } diff --git a/TMessagesProj/src/main/res/values-zh-rCN/strings_na.xml b/TMessagesProj/src/main/res/values-zh-rCN/strings_na.xml index ffa19d2e9a..5c134231bb 100644 --- a/TMessagesProj/src/main/res/values-zh-rCN/strings_na.xml +++ b/TMessagesProj/src/main/res/values-zh-rCN/strings_na.xml @@ -96,4 +96,6 @@ 禁用频道的自定义背景 在外部存储缓存贴纸文件 用于与其他应用共享贴纸包 (点击选择存储位置) + 刷新外部缓存 + 删除外部缓存 diff --git a/TMessagesProj/src/main/res/values/strings_na.xml b/TMessagesProj/src/main/res/values/strings_na.xml index f8d0e1d5a1..669db1eaf9 100644 --- a/TMessagesProj/src/main/res/values/strings_na.xml +++ b/TMessagesProj/src/main/res/values/strings_na.xml @@ -99,4 +99,6 @@ Disable channel custom wallpaper External sticker cache For sharing sticker packs with other apps. Click to pick storage location. + Refresh External Cache + Delete External Cache From a102cd6e8c88e00c0cfbaf1119ef687dc13c4591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=BB=E5=A4=A9=E5=A4=A9?= Date: Tue, 30 Jan 2024 16:46:46 +0800 Subject: [PATCH 3/4] fix: delete a non-existent invoke in MediaDataController --- .../main/java/org/telegram/messenger/MediaDataController.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java index 39591bec87..32e8b9c15b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java @@ -2950,8 +2950,6 @@ private void processLoadedStickers(int type, ArrayList loadDate[type] = date); putStickersToCache(type, null, date, 0); From 4103863fab9c30417a24f1b52b3158bfac419c16 Mon Sep 17 00:00:00 2001 From: xtaodada Date: Tue, 30 Jan 2024 17:50:09 +0800 Subject: [PATCH 4/4] improve --- .../telegram/ui/Components/StickersAlert.java | 12 +++++----- .../config/cell/ConfigCellAutoTextCheck.java | 21 +++++++++++++----- .../settings/BaseNekoXSettingsActivity.java | 3 +++ .../NekoExperimentalSettingsActivity.java | 2 +- .../helper/ExternalStickerCacheHelper.kt | 22 ++++++++++++++++++- 5 files changed, 46 insertions(+), 14 deletions(-) diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java index 75d7db6c24..ca94da0889 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java @@ -162,8 +162,8 @@ public interface StickersAlertCustomButtonDelegate { public boolean probablyEmojis; private int menu_archive = 4; - private int menu_refreshExternalCache = 5; - private int menu_deleteExternalCache = 6; + private final int menuRefreshExternalCache = 100; + private final int menuDeleteExternalCache = 101; private TLRPC.TL_messages_stickerSet stickerSet; private TLRPC.Document selectedSticker; @@ -870,8 +870,8 @@ public void requestLayout() { optionsButton.addSubItem(3, R.drawable.msg_qrcode, LocaleController.getString("ShareQRCode", R.string.ShareQRCode)); optionsButton.addSubItem(menu_archive, R.drawable.msg_archive, LocaleController.getString("Archive", R.string.Archive)); if (!NaConfig.INSTANCE.getExternalStickerCache().String().isBlank()) { - optionsButton.addSubItem(menu_refreshExternalCache, R.drawable.menu_views_reposts, LocaleController.getString(R.string.ExternalStickerCacheRefresh)); - optionsButton.addSubItem(menu_deleteExternalCache, R.drawable.msg_delete, LocaleController.getString(R.string.ExternalStickerCacheDelete)); + optionsButton.addSubItem(menuRefreshExternalCache, R.drawable.menu_views_reposts, LocaleController.getString(R.string.ExternalStickerCacheRefresh)); + optionsButton.addSubItem(menuDeleteExternalCache, R.drawable.msg_delete, LocaleController.getString(R.string.ExternalStickerCacheDelete)); } optionsButton.setOnClickListener(v -> optionsButton.toggleSubMenu()); @@ -1100,10 +1100,10 @@ protected void onSend(LongSparseArray dids, int count, TLRPC.TL_fo } else if (id == menu_archive) { dismiss(); MediaDataController.getInstance(currentAccount).toggleStickerSet(parentActivity, stickerSet, 1, parentFragment, false, true); - } else if (id == menu_refreshExternalCache) { + } else if (id == menuRefreshExternalCache) { // Na: [ExternalStickerCache] force refresh cache files ExternalStickerCacheHelper.refreshCacheFiles(stickerSet); - } else if (id == menu_deleteExternalCache) { + } else if (id == menuDeleteExternalCache) { // Na: [ExternalStickerCache] delete cache files ExternalStickerCacheHelper.deleteCacheFiles(stickerSet); } diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellAutoTextCheck.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellAutoTextCheck.java index a28961a74f..884b8dfbf4 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellAutoTextCheck.java +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/config/cell/ConfigCellAutoTextCheck.java @@ -1,12 +1,16 @@ package tw.nekomimi.nekogram.config.cell; import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.LocaleController; import org.telegram.ui.Cells.TextCheckCell; import tw.nekomimi.nekogram.config.CellGroup; +import tw.nekomimi.nekogram.config.ConfigItem; import java.util.function.Consumer; public class ConfigCellAutoTextCheck extends AbstractConfigCell { + private final ConfigItem bindConfig; private final Consumer onClick; private final String title; private String subtitle = null; @@ -14,20 +18,25 @@ public class ConfigCellAutoTextCheck extends AbstractConfigCell { private boolean enabled = true; public TextCheckCell cell; - public ConfigCellAutoTextCheck(String title, String subtitleFallback, Consumer onClick) { - this.title = title; + public ConfigCellAutoTextCheck(ConfigItem bindConfig, String subtitleFallback, Consumer onClick) { + this.bindConfig = bindConfig; + this.title = LocaleController.getString(bindConfig.getKey()); this.subtitleFallback = subtitleFallback; this.onClick = onClick; } - public ConfigCellAutoTextCheck(String title, Consumer onClick) { - this(title, null, onClick); - } - public int getType() { return CellGroup.ITEM_TYPE_TEXT_CHECK; } + public String getKey() { + return bindConfig == null ? null : bindConfig.getKey(); + } + + public ConfigItem getBindConfig() { + return bindConfig; + } + public boolean isEnabled() { return enabled; } diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/BaseNekoXSettingsActivity.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/BaseNekoXSettingsActivity.java index f5929236bd..e475e8357f 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/BaseNekoXSettingsActivity.java +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/BaseNekoXSettingsActivity.java @@ -19,6 +19,7 @@ import tw.nekomimi.nekogram.config.CellGroup; import tw.nekomimi.nekogram.config.ConfigItem; import tw.nekomimi.nekogram.config.cell.AbstractConfigCell; +import tw.nekomimi.nekogram.config.cell.ConfigCellAutoTextCheck; import tw.nekomimi.nekogram.config.cell.ConfigCellCustom; import tw.nekomimi.nekogram.config.cell.ConfigCellSelectBox; import tw.nekomimi.nekogram.config.cell.ConfigCellTextCheck; @@ -88,6 +89,8 @@ protected String getRowKey(AbstractConfigCell row) { return ((ConfigCellTextInput) row).getKey(); } else if (row instanceof ConfigCellCustom) { return ((ConfigCellCustom) row).getKey(); + } else if (row instanceof ConfigCellAutoTextCheck) { + return ((ConfigCellAutoTextCheck) row).getKey(); } return null; } diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/NekoExperimentalSettingsActivity.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/NekoExperimentalSettingsActivity.java index 495e2d1c52..8fdafff6fa 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/NekoExperimentalSettingsActivity.java +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/NekoExperimentalSettingsActivity.java @@ -105,7 +105,7 @@ public class NekoExperimentalSettingsActivity extends BaseNekoXSettingsActivity private final AbstractConfigCell fakeHighPerformanceDeviceRow = cellGroup.appendCell(new ConfigCellTextCheck(NaConfig.INSTANCE.getFakeHighPerformanceDevice())); private final AbstractConfigCell disableEmojiDrawLimitRow = cellGroup.appendCell(new ConfigCellTextCheck(NaConfig.INSTANCE.getDisableEmojiDrawLimit())); private final AbstractConfigCell externalStickerCacheRow = cellGroup.appendCell(new ConfigCellAutoTextCheck( - LocaleController.getString(R.string.ExternalStickerCache), LocaleController.getString(R.string.ExternalStickerCacheHint), this::onExternalStickerCacheButtonClick)); + NaConfig.INSTANCE.getExternalStickerCache(), LocaleController.getString(R.string.ExternalStickerCacheHint), this::onExternalStickerCacheButtonClick)); private final AbstractConfigCell divider1 = cellGroup.appendCell(new ConfigCellDivider()); private UndoView tooltip; diff --git a/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/helper/ExternalStickerCacheHelper.kt b/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/helper/ExternalStickerCacheHelper.kt index c2f21709a3..1fe6f1ce00 100644 --- a/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/helper/ExternalStickerCacheHelper.kt +++ b/TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/helper/ExternalStickerCacheHelper.kt @@ -9,10 +9,13 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.telegram.messenger.AndroidUtilities import org.telegram.messenger.ApplicationLoader +import org.telegram.messenger.LocaleController import org.telegram.messenger.MediaDataController +import org.telegram.messenger.R import org.telegram.messenger.UserConfig import org.telegram.tgnet.TLRPC.TL_messages_stickerSet import tw.nekomimi.nekogram.config.cell.ConfigCellAutoTextCheck +import tw.nekomimi.nekogram.utils.AlertUtil import xyz.nextalone.nagram.NaConfig import java.io.File @@ -184,6 +187,7 @@ object ExternalStickerCacheHelper { } } } + showToast(null) } catch (e: Exception) { logException(e, "refreshing specific cache") } @@ -201,16 +205,32 @@ object ExternalStickerCacheHelper { val idString = stickerSet.id.toString() dir.findFile(idString)?.delete() } + showToast(null) } catch (e: Exception) { logException(e, "deleting specific cache") } } } + @JvmStatic + private fun showToast(msg: String?) { + var realMessage = msg + if (realMessage == null) { + realMessage = LocaleController.getString("Done", R.string.Done) + } + AndroidUtilities.runOnUIThread { + if (realMessage != null) { + AlertUtil.showToast(realMessage) + } + } + } + private fun logException(e: Exception, s: String) { val exception = e.javaClass.canonicalName val message = e.message - Log.e(TAG, "Exception while $s: $exception: $message") + val realMessage = "Exception while $s: $exception: $message" + Log.e(TAG, realMessage) + showToast(realMessage) } private fun logV(message: String) = Log.v(TAG, message) private fun logD(message: String) = Log.d(TAG, message)