on main help sign in
Forkgram 13.0.5 snapshot
ad9770fgithub-actions[bot]
TMessagesProj/src/main/java/org/telegram/messenger/forkgram/VoiceTimecodesConfig.kt+156−109
@@ -1,14 +1,14 @@
1package org.telegram.messenger.forkgram
1package org.telegram.messenger.forkgram
2
2
3import android.content.SharedPreferences
3import android.content.SharedPreferences
4−import android.os.Environment
4+import android.util.Base64
5import org.json.JSONObject
5import org.json.JSONObject
6−import org.telegram.messenger.ApplicationLoader
7import org.telegram.messenger.FileLog
6import org.telegram.messenger.FileLog
8import org.telegram.messenger.MessagesController
7import org.telegram.messenger.MessagesController
9import org.telegram.messenger.UserConfig
8import org.telegram.messenger.UserConfig
10−import java.io.File
9+import java.io.ByteArrayOutputStream
10+import java.util.zip.Deflater
11−import java.net.URL
11+import java.util.zip.Inflater
12
12
13object VoiceTimecodesConfig {
13object VoiceTimecodesConfig {
14
14
@@ -21,6 +21,20 @@ object VoiceTimecodesConfig {
21 const val PREF_MIN_DURATION = "voice_timecodes_min_duration"
21 const val PREF_MIN_DURATION = "voice_timecodes_min_duration"
22 const val PREF_PROMPT = "voice_timecodes_prompt"
22 const val PREF_PROMPT = "voice_timecodes_prompt"
23
23
24+ private const val CODE_PREFIX = "FVT1:"
25+ private const val MAX_DECODED_SIZE = 256 * 1024
26+ private val BASE64_FLAGS = Base64.URL_SAFE or Base64.NO_WRAP or Base64.NO_PADDING
27+ private val PREF_KEYS = setOf(
28+ PREF_ENABLED,
29+ PREF_STT_URL,
30+ PREF_STT_TOKEN,
31+ PREF_LLM_URL,
32+ PREF_LLM_TOKEN,
33+ PREF_LLM_MODEL,
34+ PREF_MIN_DURATION,
35+ PREF_PROMPT
36+ )
37+
24 const val DEFAULT_LLM_MODEL = "gpt-4o-mini"
38 const val DEFAULT_LLM_MODEL = "gpt-4o-mini"
25 const val DEFAULT_MIN_DURATION = 10
39 const val DEFAULT_MIN_DURATION = 10
26
40
@@ -40,7 +54,6 @@ object VoiceTimecodesConfig {
40
54
41 @JvmStatic
55 @JvmStatic
42 fun isEnabled(): Boolean {
56 fun isEnabled(): Boolean {
43− ensureAutoImport()
44 return prefs().getBoolean(PREF_ENABLED, false)
57 return prefs().getBoolean(PREF_ENABLED, false)
45 }
58 }
46
59
@@ -51,31 +64,26 @@ object VoiceTimecodesConfig {
51
64
52 @JvmStatic
65 @JvmStatic
53 fun sttUrl(): String {
66 fun sttUrl(): String {
54− ensureAutoImport()
55 return prefs().getString(PREF_STT_URL, "")?.trim() ?: ""
67 return prefs().getString(PREF_STT_URL, "")?.trim() ?: ""
56 }
68 }
57
69
58 @JvmStatic
70 @JvmStatic
59 fun sttToken(): String {
71 fun sttToken(): String {
60− ensureAutoImport()
61 return prefs().getString(PREF_STT_TOKEN, "")?.trim() ?: ""
72 return prefs().getString(PREF_STT_TOKEN, "")?.trim() ?: ""
62 }
73 }
63
74
64 @JvmStatic
75 @JvmStatic
65 fun llmUrl(): String {
76 fun llmUrl(): String {
66− ensureAutoImport()
67 return prefs().getString(PREF_LLM_URL, "")?.trim() ?: ""
77 return prefs().getString(PREF_LLM_URL, "")?.trim() ?: ""
68 }
78 }
69
79
70 @JvmStatic
80 @JvmStatic
71 fun llmToken(): String {
81 fun llmToken(): String {
72− ensureAutoImport()
73 return prefs().getString(PREF_LLM_TOKEN, "")?.trim() ?: ""
82 return prefs().getString(PREF_LLM_TOKEN, "")?.trim() ?: ""
74 }
83 }
75
84
76 @JvmStatic
85 @JvmStatic
77 fun llmModel(): String {
86 fun llmModel(): String {
78− ensureAutoImport()
79 val m = prefs().getString(PREF_LLM_MODEL, DEFAULT_LLM_MODEL)?.trim() ?: ""
87 val m = prefs().getString(PREF_LLM_MODEL, DEFAULT_LLM_MODEL)?.trim() ?: ""
80 return if (m.isEmpty()) DEFAULT_LLM_MODEL else m
88 return if (m.isEmpty()) DEFAULT_LLM_MODEL else m
81 }
89 }
@@ -125,62 +133,57 @@ object VoiceTimecodesConfig {
125 .apply()
133 .apply()
126 }
134 }
127
135
128− @Volatile
129− private var autoImportChecked = false
130−
131 @JvmStatic
136 @JvmStatic
132− fun ensureAutoImport() {
137+ fun exportToString(): String {
133− if (autoImportChecked) return
138+ return exportToString(
134− autoImportChecked = true
139+ isEnabled(), sttUrl(), sttToken(), llmUrl(), llmToken(), llmModel(), minDuration(), promptTemplate()
140+ )
141+ }
135
142
136− val currentStt = prefs().getString(PREF_STT_URL, null)
143+ @JvmStatic
137− val currentLlm = prefs().getString(PREF_LLM_URL, null)
144+ fun exportToString(
138− if (!currentStt.isNullOrEmpty() && !currentLlm.isNullOrEmpty()) {
145+ enabled: Boolean,
146+ sttUrl: String,
147+ sttToken: String,
148+ llmUrl: String,
149+ llmToken: String,
150+ llmModel: String,
151+ minDuration: Int,
152+ prompt: String
153+ ): String {
154+ val root = JSONObject()
155+ root.put("v", 1)
156+ root.put("enabled", enabled)
157+ root.put("stt_url", sttUrl.trim())
158+ root.put("stt_token", sttToken.trim())
159+ root.put("llm_url", llmUrl.trim())
160+ root.put("llm_token", llmToken.trim())
161+ root.put("llm_model", llmModel.trim())
162+ root.put("min_duration", if (minDuration <= 0) DEFAULT_MIN_DURATION else minDuration)
163+ root.put("prompt", if (prompt.isBlank()) DEFAULT_PROMPT else prompt)
164+ val encoded = Base64.encodeToString(compress(root.toString().toByteArray(Charsets.UTF_8)), BASE64_FLAGS)
139− return
165+ return CODE_PREFIX + encoded
140− }
166+ }
141
167
142− val candidateDirs = mutableListOf<File>()
168+ @JvmStatic
143− try {
169+ fun importFromString(text: String): Boolean {
144− val downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
170+ val trimmed = text.trim()
145− if (downloadDir != null) {
171+ if (trimmed.isEmpty()) {
146− candidateDirs.add(downloadDir)
172+ return false
147− }
148− } catch (e: Exception) {
149− FileLog.e(e)
150 }
173 }
151− val context = ApplicationLoader.applicationContext
174+ val prefixIndex = trimmed.indexOf(CODE_PREFIX, ignoreCase = true)
152− if (context != null) {
175+ if (prefixIndex >= 0) {
153− try {
176+ return try {
154− val extFiles = context.getExternalFilesDir(null)
177+ val payload = trimmed.substring(prefixIndex + CODE_PREFIX.length)
155− if (extFiles != null) {
178+ .filter { it.isLetterOrDigit() || it == '-' || it == '_' }
156− candidateDirs.add(extFiles)
179+ val json = String(inflate(Base64.decode(payload, BASE64_FLAGS)), Charsets.UTF_8)
157− }
180+ importFromJson(json)
158 } catch (e: Exception) {
181 } catch (e: Exception) {
159− FileLog.e(e)
182+ FileLog.e("VoiceTimecodesConfig code import error: $e")
160− }
183+ false
161− }
162−
163− for (dir in candidateDirs) {
164− val f1 = File(dir, "voice_timecodes.json")
165− if (f1.exists() && f1.canRead() && importFromFile(f1)) {
166− return
167 }
184 }
168− val f2 = File(dir, "voice_timecodes_backup.json")
169− if (f2.exists() && f2.canRead() && importFromFile(f2)) {
170− return
171− }
172− }
173− }
174−
175− @JvmStatic
176− fun importFromFile(file: File): Boolean {
177− return try {
178− val text = file.readText(Charsets.UTF_8)
179− importFromJson(text)
180− } catch (e: Exception) {
181− FileLog.e("VoiceTimecodesConfig import error from ${file.absolutePath}: $e")
182− false
183 }
185 }
186+ return importFromJson(trimmed)
184 }
187 }
185
188
186 @JvmStatic
189 @JvmStatic
@@ -188,68 +191,112 @@ object VoiceTimecodesConfig {
188 return try {
191 return try {
189 val root = JSONObject(jsonString)
192 val root = JSONObject(jsonString)
190 val editor = prefs().edit()
193 val editor = prefs().edit()
194+ var updated = false
191
195
192− if (root.has("prefs")) {
193− val prefsObj = root.optJSONObject("prefs")
196+ val mainConfig = root.optJSONObject("prefs")?.optJSONObject("mainconfig")
194− val mainConfig = prefsObj?.optJSONObject("mainconfig")
195− if (mainConfig != null) {
197+ if (mainConfig != null) {
196− val it = mainConfig.keys()
198+ val keys = mainConfig.keys()
197− while (it.hasNext()) {
199+ while (keys.hasNext()) {
198− val key = it.next()
200+ val key = keys.next()
201+ if (key !in PREF_KEYS) {
202+ continue
203+ }
199− val item = mainConfig.optJSONObject(key) ?: continue
204+ val item = mainConfig.optJSONObject(key) ?: continue
200− val t = item.optString("t")
201− when (t) {
205+ when (item.optString("t")) {
206+ "b" -> {
202− "b" -> editor.putBoolean(key, item.optBoolean("v"))
207+ editor.putBoolean(key, item.optBoolean("v"))
208+ updated = true
209+ }
210+ "s" -> {
203− "s" -> editor.putString(key, item.optString("v"))
211+ editor.putString(key, item.optString("v"))
212+ updated = true
213+ }
214+ "i" -> {
204− "i" -> editor.putInt(key, item.optInt("v"))
215+ editor.putInt(key, item.optInt("v"))
216+ updated = true
205 }
217 }
206 }
218 }
207− editor.apply()
219+ }
208− return true
220+ } else {
221+ if (root.has("enabled")) {
222+ editor.putBoolean(PREF_ENABLED, root.optBoolean("enabled"))
223+ updated = true
224+ }
225+ if (root.has("stt_url")) {
226+ editor.putString(PREF_STT_URL, root.optString("stt_url"))
227+ updated = true
228+ }
229+ if (root.has("stt_token")) {
230+ editor.putString(PREF_STT_TOKEN, root.optString("stt_token"))
231+ updated = true
232+ }
233+ if (root.has("llm_url")) {
234+ editor.putString(PREF_LLM_URL, root.optString("llm_url"))
235+ updated = true
236+ }
237+ if (root.has("llm_token")) {
238+ editor.putString(PREF_LLM_TOKEN, root.optString("llm_token"))
239+ updated = true
240+ }
241+ if (root.has("llm_model")) {
242+ editor.putString(PREF_LLM_MODEL, root.optString("llm_model"))
243+ updated = true
244+ }
245+ if (root.has("min_duration")) {
246+ editor.putInt(PREF_MIN_DURATION, root.optInt("min_duration", DEFAULT_MIN_DURATION))
247+ updated = true
248+ }
249+ if (root.has("prompt")) {
250+ editor.putString(PREF_PROMPT, root.optString("prompt"))
251+ updated = true
209 }
252 }
210 }
253 }
211
254
212− var updated = false
213− if (root.has("enabled")) {
214− editor.putBoolean(PREF_ENABLED, root.getBoolean("enabled"))
215− updated = true
216− }
217− if (root.has("stt_url")) {
218− editor.putString(PREF_STT_URL, root.getString("stt_url"))
219− updated = true
220− }
221− if (root.has("stt_token")) {
222− editor.putString(PREF_STT_TOKEN, root.getString("stt_token"))
223− updated = true
224− }
225− if (root.has("llm_url")) {
226− editor.putString(PREF_LLM_URL, root.getString("llm_url"))
227− updated = true
228− }
229− if (root.has("llm_token")) {
230− editor.putString(PREF_LLM_TOKEN, root.getString("llm_token"))
231− updated = true
232− }
233− if (root.has("llm_model")) {
234− editor.putString(PREF_LLM_MODEL, root.getString("llm_model"))
235− updated = true
236− }
237− if (root.has("min_duration")) {
238− editor.putInt(PREF_MIN_DURATION, root.getInt("min_duration"))
239− updated = true
240− }
241− if (root.has("prompt")) {
242− editor.putString(PREF_PROMPT, root.getString("prompt"))
243− updated = true
244− }
245 if (updated) {
255 if (updated) {
246 editor.apply()
256 editor.apply()
247− return true
248 }
257 }
249− false
258+ updated
250 } catch (e: Exception) {
259 } catch (e: Exception) {
251 FileLog.e(e)
260 FileLog.e(e)
252 false
261 false
253 }
262 }
254 }
263 }
264+
265+ private fun compress(data: ByteArray): ByteArray {
266+ val deflater = Deflater(Deflater.BEST_COMPRESSION)
267+ val out = ByteArrayOutputStream()
268+ val buffer = ByteArray(4096)
269+ try {
270+ deflater.setInput(data)
271+ deflater.finish()
272+ while (!deflater.finished()) {
273+ out.write(buffer, 0, deflater.deflate(buffer))
274+ }
275+ } finally {
276+ deflater.end()
277+ }
278+ return out.toByteArray()
279+ }
280+
281+ private fun inflate(data: ByteArray): ByteArray {
282+ val inflater = Inflater()
283+ val out = ByteArrayOutputStream()
284+ val buffer = ByteArray(4096)
285+ try {
286+ inflater.setInput(data)
287+ while (!inflater.finished()) {
288+ val count = inflater.inflate(buffer)
289+ if (count == 0) {
290+ throw IllegalArgumentException("Truncated settings code")
291+ }
292+ out.write(buffer, 0, count)
293+ if (out.size() > MAX_DECODED_SIZE) {
294+ throw IllegalArgumentException("Settings code is too large")
295+ }
296+ }
297+ } finally {
298+ inflater.end()
299+ }
300+ return out.toByteArray()
301+ }
255}
302}
TMessagesProj/src/main/java/org/telegram/ui/VoiceTimecodesSettingsActivity.java+101−43
@@ -1,10 +1,11 @@
1package org.telegram.ui;
1package org.telegram.ui;
2
2
3+import android.content.ClipData;
4+import android.content.ClipboardManager;
3import android.content.Context;
5import android.content.Context;
4−import android.content.SharedPreferences;
5−import android.os.Environment;
6import android.text.InputType;
6import android.text.InputType;
7import android.text.method.PasswordTransformationMethod;
7import android.text.method.PasswordTransformationMethod;
8+import android.util.TypedValue;
8import android.view.Gravity;
9import android.view.Gravity;
9import android.view.View;
10import android.view.View;
10import android.view.inputmethod.EditorInfo;
11import android.view.inputmethod.EditorInfo;
@@ -16,7 +17,6 @@ import android.widget.Toast;
16
17
17import org.telegram.messenger.AndroidUtilities;
18import org.telegram.messenger.AndroidUtilities;
18import org.telegram.messenger.LocaleController;
19import org.telegram.messenger.LocaleController;
19−import org.telegram.messenger.MessagesController;
20import org.telegram.messenger.R;
20import org.telegram.messenger.R;
21import org.telegram.messenger.forkgram.VoiceTimecodesConfig;
21import org.telegram.messenger.forkgram.VoiceTimecodesConfig;
22import org.telegram.ui.ActionBar.ActionBar;
22import org.telegram.ui.ActionBar.ActionBar;
@@ -27,8 +27,6 @@ import org.telegram.ui.Components.EditTextBoldCursor;
27import org.telegram.ui.Components.LayoutHelper;
27import org.telegram.ui.Components.LayoutHelper;
28import org.telegram.ui.Components.OutlineEditText;
28import org.telegram.ui.Components.OutlineEditText;
29
29
30−import java.io.File;
31−
32public class VoiceTimecodesSettingsActivity extends BaseFragment {
30public class VoiceTimecodesSettingsActivity extends BaseFragment {
33
31
34 private static final int DONE_BUTTON = 1;
32 private static final int DONE_BUTTON = 1;
@@ -41,6 +39,7 @@ public class VoiceTimecodesSettingsActivity extends BaseFragment {
41 private EditTextBoldCursor llmModelField;
39 private EditTextBoldCursor llmModelField;
42 private EditTextBoldCursor minDurationField;
40 private EditTextBoldCursor minDurationField;
43 private EditTextBoldCursor promptField;
41 private EditTextBoldCursor promptField;
42+ private EditTextBoldCursor exportCodeField;
44
43
45 private EditTextBoldCursor lastField;
44 private EditTextBoldCursor lastField;
46
45
@@ -138,13 +137,44 @@ public class VoiceTimecodesSettingsActivity extends BaseFragment {
138 resetPrompt.setOnClickListener(v -> promptField.setText(VoiceTimecodesConfig.DEFAULT_PROMPT));
137 resetPrompt.setOnClickListener(v -> promptField.setText(VoiceTimecodesConfig.DEFAULT_PROMPT));
139 content.addView(resetPrompt, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
138 content.addView(resetPrompt, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
140
139
140+ TextView codeInfo = new TextView(context);
141+ codeInfo.setText(LocaleController.getString(R.string.VoiceTimecodesCodeInfo));
142+ codeInfo.setTextSize(14);
143+ codeInfo.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2));
144+ content.addView(codeInfo, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 8));
145+
146+ OutlineEditText codeOutline = new OutlineEditText(context);
147+ codeOutline.setHint(LocaleController.getString(R.string.VoiceTimecodesCodeTitle));
148+ exportCodeField = codeOutline.getEditText();
149+ exportCodeField.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
150+ exportCodeField.setHint(LocaleController.getString(R.string.VoiceTimecodesCodeHint));
151+ exportCodeField.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
152+ exportCodeField.setImeOptions(EditorInfo.IME_ACTION_DONE);
153+ exportCodeField.setPadding(AndroidUtilities.dp(15), AndroidUtilities.dp(16), AndroidUtilities.dp(15), AndroidUtilities.dp(16));
154+ content.addView(codeOutline, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 12));
155+
156+ LinearLayout codeActions = new LinearLayout(context);
157+ codeActions.setOrientation(LinearLayout.HORIZONTAL);
158+
159+ TextView exportButton = new TextView(context);
160+ exportButton.setText(LocaleController.getString(R.string.VoiceTimecodesCodeExport));
161+ exportButton.setTextSize(14);
162+ exportButton.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText4));
163+ exportButton.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
164+ exportButton.setPadding(0, 0, 0, AndroidUtilities.dp(16));
165+ exportButton.setOnClickListener(v -> exportCode());
166+ codeActions.addView(exportButton, LayoutHelper.createLinear(0, LayoutHelper.WRAP_CONTENT, 1f));
167+
141 TextView importButton = new TextView(context);
168 TextView importButton = new TextView(context);
142− importButton.setText(LocaleController.getString(R.string.VoiceTimecodesImportFile));
169+ importButton.setText(LocaleController.getString(R.string.VoiceTimecodesCodeImport));
143 importButton.setTextSize(14);
170 importButton.setTextSize(14);
144 importButton.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText4));
171 importButton.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText4));
172+ importButton.setGravity(LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT);
145− importButton.setPadding(0, AndroidUtilities.dp(6), 0, AndroidUtilities.dp(24));
173+ importButton.setPadding(0, 0, 0, AndroidUtilities.dp(16));
146− importButton.setOnClickListener(v -> tryImportFromFile());
174+ importButton.setOnClickListener(v -> importCode());
175+ codeActions.addView(importButton, LayoutHelper.createLinear(0, LayoutHelper.WRAP_CONTENT, 1f));
176+
147− content.addView(importButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
177+ content.addView(codeActions, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
148
178
149 return fragmentView;
179 return fragmentView;
150 }
180 }
@@ -172,52 +202,80 @@ public class VoiceTimecodesSettingsActivity extends BaseFragment {
172 lastField = field;
202 lastField = field;
173 }
203 }
174
204
175− private void tryImportFromFile() {
205+ private void exportCode() {
176− java.util.List<File> candidates = new java.util.ArrayList<>();
206+ int minDuration = VoiceTimecodesConfig.DEFAULT_MIN_DURATION;
177 try {
207 try {
178− File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
208+ minDuration = Integer.parseInt(minDurationField.getText().toString().trim());
179− if (downloadDir != null) {
180− candidates.add(new File(downloadDir, "voice_timecodes.json"));
181− candidates.add(new File(downloadDir, "voice_timecodes_backup.json"));
182− }
183 } catch (Exception ignore) {
209 } catch (Exception ignore) {
184 }
210 }
185− if (getContext() != null) {
211+ String code = VoiceTimecodesConfig.exportToString(
186− try {
212+ enabledCell.isChecked(),
187− File ext = getContext().getExternalFilesDir(null);
213+ sttUrlField.getText().toString(),
214+ sttTokenField.getText().toString(),
215+ llmUrlField.getText().toString(),
216+ llmTokenField.getText().toString(),
217+ llmModelField.getText().toString(),
218+ minDuration,
219+ promptField.getText().toString()
220+ );
221+ exportCodeField.setText(code);
222+ exportCodeField.setSelection(0);
223+ if (getParentActivity() != null) {
224+ ClipboardManager clipboard = (ClipboardManager) getParentActivity().getSystemService(Context.CLIPBOARD_SERVICE);
188− if (ext != null) {
225+ if (clipboard != null) {
189− candidates.add(new File(ext, "voice_timecodes.json"));
226+ clipboard.setPrimaryClip(ClipData.newPlainText("Forkgram voice timecodes", code));
190− candidates.add(new File(ext, "voice_timecodes_backup.json"));
191− }
192− } catch (Exception ignore) {
193 }
227 }
228+ Toast.makeText(getParentActivity(), LocaleController.getString(R.string.VoiceTimecodesCodeCopied), Toast.LENGTH_SHORT).show();
194 }
229 }
195− boolean success = false;
230+ }
196− for (File target : candidates) {
231+
197− if (target.exists() && target.canRead() && VoiceTimecodesConfig.importFromFile(target)) {
232+ private void importCode() {
198− success = true;
233+ String clipboardText = readClipboard();
199− break;
234+ String fieldText = exportCodeField.getText().toString().trim();
200− }
235+ boolean success = !clipboardText.isEmpty() && VoiceTimecodesConfig.importFromString(clipboardText);
236+ if (!success && !fieldText.isEmpty()) {
237+ success = VoiceTimecodesConfig.importFromString(fieldText);
201 }
238 }
202 if (success) {
239 if (success) {
203− enabledCell.setChecked(VoiceTimecodesConfig.isEnabled());
240+ refreshFromConfig();
204− sttUrlField.setText(VoiceTimecodesConfig.sttUrl());
205− sttTokenField.setText(VoiceTimecodesConfig.sttToken());
206− llmUrlField.setText(VoiceTimecodesConfig.llmUrl());
207− llmTokenField.setText(VoiceTimecodesConfig.llmToken());
208− llmModelField.setText(VoiceTimecodesConfig.llmModel());
209− minDurationField.setText(String.valueOf(VoiceTimecodesConfig.minDuration()));
241+ String code = VoiceTimecodesConfig.exportToString();
210− promptField.setText(VoiceTimecodesConfig.promptTemplate());
242+ exportCodeField.setText(code);
211− if (getParentActivity() != null) {
243+ exportCodeField.setSelection(0);
212− Toast.makeText(getParentActivity(), LocaleController.getString(R.string.VoiceTimecodesImportSuccess), Toast.LENGTH_SHORT).show();
213− }
214− } else {
215 if (getParentActivity() != null) {
244 if (getParentActivity() != null) {
216− Toast.makeText(getParentActivity(), LocaleController.getString(R.string.VoiceTimecodesImportFailed), Toast.LENGTH_SHORT).show();
245+ Toast.makeText(getParentActivity(), LocaleController.getString(R.string.VoiceTimecodesCodeImported), Toast.LENGTH_SHORT).show();
217 }
246 }
247+ } else if (getParentActivity() != null) {
248+ Toast.makeText(getParentActivity(), LocaleController.getString(R.string.VoiceTimecodesCodeImportFailed), Toast.LENGTH_SHORT).show();
218 }
249 }
219 }
250 }
220
251
252+ private String readClipboard() {
253+ if (getParentActivity() == null) {
254+ return "";
255+ }
256+ ClipboardManager clipboard = (ClipboardManager) getParentActivity().getSystemService(Context.CLIPBOARD_SERVICE);
257+ if (clipboard == null || !clipboard.hasPrimaryClip() || clipboard.getPrimaryClip() == null) {
258+ return "";
259+ }
260+ ClipData clipData = clipboard.getPrimaryClip();
261+ if (clipData.getItemCount() == 0) {
262+ return "";
263+ }
264+ CharSequence text = clipData.getItemAt(0).coerceToText(getParentActivity());
265+ return text != null ? text.toString().trim() : "";
266+ }
267+
268+ private void refreshFromConfig() {
269+ enabledCell.setChecked(VoiceTimecodesConfig.isEnabled());
270+ sttUrlField.setText(VoiceTimecodesConfig.sttUrl());
271+ sttTokenField.setText(VoiceTimecodesConfig.sttToken());
272+ llmUrlField.setText(VoiceTimecodesConfig.llmUrl());
273+ llmTokenField.setText(VoiceTimecodesConfig.llmToken());
274+ llmModelField.setText(VoiceTimecodesConfig.llmModel());
275+ minDurationField.setText(String.valueOf(VoiceTimecodesConfig.minDuration()));
276+ promptField.setText(VoiceTimecodesConfig.promptTemplate());
277+ }
278+
221 private void save() {
279 private void save() {
222 boolean enabled = enabledCell.isChecked();
280 boolean enabled = enabledCell.isChecked();
223 String sttUrl = sttUrlField.getText().toString().trim();
281 String sttUrl = sttUrlField.getText().toString().trim();
TMessagesProj/src/main/res/values-ar/strings.xml+8−3
@@ -1520,9 +1520,14 @@
1520 <string name="VoiceTimecodesMinDuration">الحد الأدنى لمدة الرسالة الصوتية (بالثواني)</string>
1520 <string name="VoiceTimecodesMinDuration">الحد الأدنى لمدة الرسالة الصوتية (بالثواني)</string>
1521 <string name="VoiceTimecodesPrompt">قالب التوجيه ({transcript} = النص)</string>
1521 <string name="VoiceTimecodesPrompt">قالب التوجيه ({transcript} = النص)</string>
1522 <string name="VoiceTimecodesResetPrompt">إعادة التوجيه إلى الوضع الافتراضي</string>
1522 <string name="VoiceTimecodesResetPrompt">إعادة التوجيه إلى الوضع الافتراضي</string>
1523− <string name="VoiceTimecodesImportFile">استيراد من /sdcard/Download/voice_timecodes.json</string>
1523+ <string name="VoiceTimecodesCodeTitle">رمز الإعدادات</string>
1524− <string name="VoiceTimecodesImportSuccess">تم استيراد الإعدادات بنجاح</string>
1524+ <string name="VoiceTimecodesCodeInfo">رمز من سطر واحد يحتوي على جميع إعدادات الطوابع الزمنية للصوت، بما في ذلك مفاتيح API. انسخه إلى الرسائل المحفوظة، ثم على جهاز آخر انسخه مرة أخرى واضغط على «استيراد الرمز». احتفظ بالرمز خاصًا.</string>
1525− <string name="VoiceTimecodesImportFailed">الملف غير موجود أو التنسيق غير صالح</string>
1525+ <string name="VoiceTimecodesCodeHint">اضغط على «تصدير» أو الصق رمزًا من جهاز آخر</string>
1526+ <string name="VoiceTimecodesCodeExport">تصدير إلى الحافظة</string>
1527+ <string name="VoiceTimecodesCodeImport">استيراد الرمز</string>
1528+ <string name="VoiceTimecodesCodeCopied">تم نسخ رمز الإعدادات</string>
1529+ <string name="VoiceTimecodesCodeImported">تم استيراد الإعدادات</string>
1530+ <string name="VoiceTimecodesCodeImportFailed">رمز الإعدادات فارغ أو غير صالح</string>
1526 <string name="VoiceTimecodesSaved">تم حفظ إعدادات أكواد توقيت الصوت</string>
1531 <string name="VoiceTimecodesSaved">تم حفظ إعدادات أكواد توقيت الصوت</string>
1527 <string name="VoiceTimecodesDone">تمت إضافة أكواد التوقيت</string>
1532 <string name="VoiceTimecodesDone">تمت إضافة أكواد التوقيت</string>
1528 <string name="VoiceTimecodesGenerate">إضافة أكواد التوقيت</string>
1533 <string name="VoiceTimecodesGenerate">إضافة أكواد التوقيت</string>
TMessagesProj/src/main/res/values-de/strings.xml+8−3
@@ -1652,9 +1652,14 @@
1652 <string name="VoiceTimecodesMinDuration">Mindestdauer der Sprachnachricht (Sekunden)</string>
1652 <string name="VoiceTimecodesMinDuration">Mindestdauer der Sprachnachricht (Sekunden)</string>
1653 <string name="VoiceTimecodesPrompt">Prompt-Vorlage ({transcript} = Text)</string>
1653 <string name="VoiceTimecodesPrompt">Prompt-Vorlage ({transcript} = Text)</string>
1654 <string name="VoiceTimecodesResetPrompt">Prompt zurücksetzen</string>
1654 <string name="VoiceTimecodesResetPrompt">Prompt zurücksetzen</string>
1655− <string name="VoiceTimecodesImportFile">Aus /sdcard/Download/voice_timecodes.json importieren</string>
1655+ <string name="VoiceTimecodesCodeTitle">Einstellungscode</string>
1656− <string name="VoiceTimecodesImportSuccess">Einstellungen erfolgreich importiert</string>
1656+ <string name="VoiceTimecodesCodeInfo">Ein einzeiliger Code mit allen Einstellungen für Sprach-Zeitstempel, einschließlich API-Schlüsseln. Kopieren Sie ihn in die Gespeicherten Nachrichten und kopieren Sie ihn auf einem anderen Gerät erneut und tippen Sie auf „Code importieren“. Halten Sie den Code geheim.</string>
1657− <string name="VoiceTimecodesImportFailed">Datei nicht gefunden oder ungültiges Format</string>
1657+ <string name="VoiceTimecodesCodeHint">Tippen Sie auf „Exportieren“ oder fügen Sie einen Code von einem anderen Gerät ein</string>
1658+ <string name="VoiceTimecodesCodeExport">In Zwischenablage exportieren</string>
1659+ <string name="VoiceTimecodesCodeImport">Code importieren</string>
1660+ <string name="VoiceTimecodesCodeCopied">Einstellungscode kopiert</string>
1661+ <string name="VoiceTimecodesCodeImported">Einstellungen importiert</string>
1662+ <string name="VoiceTimecodesCodeImportFailed">Ungültiger oder leerer Einstellungscode</string>
1658 <string name="VoiceTimecodesSaved">Zeitmarken-Einstellungen gespeichert</string>
1663 <string name="VoiceTimecodesSaved">Zeitmarken-Einstellungen gespeichert</string>
1659 <string name="VoiceTimecodesDone">Zeitmarken hinzugefügt</string>
1664 <string name="VoiceTimecodesDone">Zeitmarken hinzugefügt</string>
1660 <string name="VoiceTimecodesGenerate">Zeitmarken hinzufügen</string>
1665 <string name="VoiceTimecodesGenerate">Zeitmarken hinzufügen</string>
TMessagesProj/src/main/res/values-es/strings.xml+8−3
@@ -1592,9 +1592,14 @@
1592 <string name="VoiceTimecodesMinDuration">Duración mínima del mensaje de voz (segundos)</string>
1592 <string name="VoiceTimecodesMinDuration">Duración mínima del mensaje de voz (segundos)</string>
1593 <string name="VoiceTimecodesPrompt">Plantilla del prompt ({transcript} = texto)</string>
1593 <string name="VoiceTimecodesPrompt">Plantilla del prompt ({transcript} = texto)</string>
1594 <string name="VoiceTimecodesResetPrompt">Restablecer el prompt</string>
1594 <string name="VoiceTimecodesResetPrompt">Restablecer el prompt</string>
1595− <string name="VoiceTimecodesImportFile">Importar desde /sdcard/Download/voice_timecodes.json</string>
1595+ <string name="VoiceTimecodesCodeTitle">Código de ajustes</string>
1596− <string name="VoiceTimecodesImportSuccess">Ajustes importados correctamente</string>
1596+ <string name="VoiceTimecodesCodeInfo">Un código de una sola línea con todos los ajustes de marcas de tiempo de voz, incluidas las claves de API. Cópialo en Mensajes guardados y, en otro dispositivo, vuelve a copiarlo y pulsa «Importar código». Mantén el código en privado.</string>
1597− <string name="VoiceTimecodesImportFailed">Archivo no encontrado o formato no válido</string>
1597+ <string name="VoiceTimecodesCodeHint">Toca «Exportar» o pega un código de otro dispositivo</string>
1598+ <string name="VoiceTimecodesCodeExport">Exportar al portapapeles</string>
1599+ <string name="VoiceTimecodesCodeImport">Importar código</string>
1600+ <string name="VoiceTimecodesCodeCopied">Código de ajustes copiado</string>
1601+ <string name="VoiceTimecodesCodeImported">Ajustes importados</string>
1602+ <string name="VoiceTimecodesCodeImportFailed">Código de ajustes no válido o vacío</string>
1598 <string name="VoiceTimecodesSaved">Ajustes de marcas de tiempo guardados</string>
1603 <string name="VoiceTimecodesSaved">Ajustes de marcas de tiempo guardados</string>
1599 <string name="VoiceTimecodesDone">Marcas de tiempo añadidas</string>
1604 <string name="VoiceTimecodesDone">Marcas de tiempo añadidas</string>
1600 <string name="VoiceTimecodesGenerate">Añadir marcas de tiempo</string>
1605 <string name="VoiceTimecodesGenerate">Añadir marcas de tiempo</string>
TMessagesProj/src/main/res/values-fa/strings.xml+8−3
@@ -281,9 +281,14 @@
281 <string name="VoiceTimecodesMinDuration">حداقل مدت پیام صوتی (ثانیه)</string>
281 <string name="VoiceTimecodesMinDuration">حداقل مدت پیام صوتی (ثانیه)</string>
282 <string name="VoiceTimecodesPrompt">قالب دستور ({transcript} = متن)</string>
282 <string name="VoiceTimecodesPrompt">قالب دستور ({transcript} = متن)</string>
283 <string name="VoiceTimecodesResetPrompt">بازنشانی دستور به حالت پیش‌فرض</string>
283 <string name="VoiceTimecodesResetPrompt">بازنشانی دستور به حالت پیش‌فرض</string>
284− <string name="VoiceTimecodesImportFile">درون‌ریزی از /sdcard/Download/voice_timecodes.json</string>
284+ <string name="VoiceTimecodesCodeTitle">کد تنظیمات</string>
285− <string name="VoiceTimecodesImportSuccess">تنظیمات با موفقیت درون‌ریزی شد</string>
285+ <string name="VoiceTimecodesCodeInfo">کدی تک‌خطی با همهٔ تنظیمات مهرهای زمانی صدا، شامل کلیدهای API. آن را در پیام‌های ذخیره‌شده کپی کنید، سپس در دستگاه دیگر دوباره کپی کنید و «درون‌ریزی کد» را بزنید. کد را خصوصی نگه دارید.</string>
286+ <string name="VoiceTimecodesCodeHint">روی «برون‌ریزی» بزنید یا کدی از دستگاه دیگر بچسبانید</string>
287+ <string name="VoiceTimecodesCodeExport">برون‌ریزی به کلیپ‌بورد</string>
288+ <string name="VoiceTimecodesCodeImport">درون‌ریزی کد</string>
289+ <string name="VoiceTimecodesCodeCopied">کد تنظیمات کپی شد</string>
290+ <string name="VoiceTimecodesCodeImported">تنظیمات درون‌ریزی شد</string>
286− <string name="VoiceTimecodesImportFailed">فایل یافت نشد یا قالب نامعتبر است</string>
291+ <string name="VoiceTimecodesCodeImportFailed">کد تنظیمات خالی یا نامعتبر است</string>
287 <string name="VoiceTimecodesSaved">تنظیمات کدهای زمانی صدا ذخیره شد</string>
292 <string name="VoiceTimecodesSaved">تنظیمات کدهای زمانی صدا ذخیره شد</string>
288 <string name="VoiceTimecodesDone">کدهای زمانی افزوده شد</string>
293 <string name="VoiceTimecodesDone">کدهای زمانی افزوده شد</string>
289 <string name="VoiceTimecodesGenerate">افزودن کدهای زمانی</string>
294 <string name="VoiceTimecodesGenerate">افزودن کدهای زمانی</string>
TMessagesProj/src/main/res/values-it/strings.xml+8−3
@@ -1814,9 +1814,14 @@
1814 <string name="VoiceTimecodesMinDuration">Durata minima del vocale (secondi)</string>
1814 <string name="VoiceTimecodesMinDuration">Durata minima del vocale (secondi)</string>
1815 <string name="VoiceTimecodesPrompt">Modello di prompt ({transcript} = testo)</string>
1815 <string name="VoiceTimecodesPrompt">Modello di prompt ({transcript} = testo)</string>
1816 <string name="VoiceTimecodesResetPrompt">Ripristina il prompt</string>
1816 <string name="VoiceTimecodesResetPrompt">Ripristina il prompt</string>
1817− <string name="VoiceTimecodesImportFile">Importa da /sdcard/Download/voice_timecodes.json</string>
1817+ <string name="VoiceTimecodesCodeTitle">Codice impostazioni</string>
1818− <string name="VoiceTimecodesImportSuccess">Impostazioni importate correttamente</string>
1818+ <string name="VoiceTimecodesCodeInfo">Un codice su una sola riga con tutte le impostazioni dei timestamp vocali, incluse le chiavi API. Copialo nei Messaggi salvati, poi su un altro dispositivo copialo di nuovo e tocca «Importa codice». Mantieni il codice privato.</string>
1819− <string name="VoiceTimecodesImportFailed">File non trovato o formato non valido</string>
1819+ <string name="VoiceTimecodesCodeHint">Tocca «Esporta» o incolla un codice da un altro dispositivo</string>
1820+ <string name="VoiceTimecodesCodeExport">Esporta negli appunti</string>
1821+ <string name="VoiceTimecodesCodeImport">Importa codice</string>
1822+ <string name="VoiceTimecodesCodeCopied">Codice impostazioni copiato</string>
1823+ <string name="VoiceTimecodesCodeImported">Impostazioni importate</string>
1824+ <string name="VoiceTimecodesCodeImportFailed">Codice impostazioni non valido o vuoto</string>
1820 <string name="VoiceTimecodesSaved">Impostazioni dei timecode vocali salvate</string>
1825 <string name="VoiceTimecodesSaved">Impostazioni dei timecode vocali salvate</string>
1821 <string name="VoiceTimecodesDone">Timecode aggiunti</string>
1826 <string name="VoiceTimecodesDone">Timecode aggiunti</string>
1822 <string name="VoiceTimecodesGenerate">Aggiungi timecode</string>
1827 <string name="VoiceTimecodesGenerate">Aggiungi timecode</string>
TMessagesProj/src/main/res/values-ko/strings.xml+8−3
@@ -1413,9 +1413,14 @@
1413 <string name="VoiceTimecodesMinDuration">최소 음성 길이(초)</string>
1413 <string name="VoiceTimecodesMinDuration">최소 음성 길이(초)</string>
1414 <string name="VoiceTimecodesPrompt">프롬프트 템플릿 ({transcript} = 텍스트)</string>
1414 <string name="VoiceTimecodesPrompt">프롬프트 템플릿 ({transcript} = 텍스트)</string>
1415 <string name="VoiceTimecodesResetPrompt">프롬프트 기본값으로 재설정</string>
1415 <string name="VoiceTimecodesResetPrompt">프롬프트 기본값으로 재설정</string>
1416− <string name="VoiceTimecodesImportFile">/sdcard/Download/voice_timecodes.json에서 가져오기</string>
1416+ <string name="VoiceTimecodesCodeTitle">설정 코드</string>
1417+ <string name="VoiceTimecodesCodeInfo">API 키를 포함한 모든 음성 타임코드 설정이 담긴 한 줄짜리 코드입니다. 저장된 메시지에 복사한 뒤 다른 기기에서 다시 복사하여 ‘코드 가져오기’를 누르세요. 코드를 비공개로 유지하세요.</string>
1418+ <string name="VoiceTimecodesCodeHint">‘내보내기’를 누르거나 다른 기기의 코드를 붙여넣으세요</string>
1419+ <string name="VoiceTimecodesCodeExport">클립보드로 내보내기</string>
1420+ <string name="VoiceTimecodesCodeImport">코드 가져오기</string>
1421+ <string name="VoiceTimecodesCodeCopied">설정 코드가 복사되었습니다</string>
1417− <string name="VoiceTimecodesImportSuccess">설정을 가져왔습니다</string>
1422+ <string name="VoiceTimecodesCodeImported">설정을 가져왔습니다</string>
1418− <string name="VoiceTimecodesImportFailed">파일을 찾을 수 없거나 형식이 올바르지 않습니다</string>
1423+ <string name="VoiceTimecodesCodeImportFailed">설정 코드가 비어 있거나 잘못되었습니다</string>
1419 <string name="VoiceTimecodesSaved">음성 타임코드 설정을 저장했습니다</string>
1424 <string name="VoiceTimecodesSaved">음성 타임코드 설정을 저장했습니다</string>
1420 <string name="VoiceTimecodesDone">타임코드를 추가했습니다</string>
1425 <string name="VoiceTimecodesDone">타임코드를 추가했습니다</string>
1421 <string name="VoiceTimecodesGenerate">타임코드 추가</string>
1426 <string name="VoiceTimecodesGenerate">타임코드 추가</string>
TMessagesProj/src/main/res/values-nl/strings.xml+8−3
@@ -1361,9 +1361,14 @@
1361 <string name="VoiceTimecodesMinDuration">Minimale duur van spraakbericht (seconden)</string>
1361 <string name="VoiceTimecodesMinDuration">Minimale duur van spraakbericht (seconden)</string>
1362 <string name="VoiceTimecodesPrompt">Promptsjabloon ({transcript} = tekst)</string>
1362 <string name="VoiceTimecodesPrompt">Promptsjabloon ({transcript} = tekst)</string>
1363 <string name="VoiceTimecodesResetPrompt">Prompt terugzetten naar standaard</string>
1363 <string name="VoiceTimecodesResetPrompt">Prompt terugzetten naar standaard</string>
1364− <string name="VoiceTimecodesImportFile">Importeren uit /sdcard/Download/voice_timecodes.json</string>
1364+ <string name="VoiceTimecodesCodeTitle">Instellingencode</string>
1365+ <string name="VoiceTimecodesCodeInfo">Een code van één regel met alle instellingen voor spraaktijdstempels, inclusief API-sleutels. Kopieer hem naar Opgeslagen berichten en kopieer hem op een ander apparaat opnieuw en tik op ‘Code importeren’. Houd de code privé.</string>
1366+ <string name="VoiceTimecodesCodeHint">Tik op ‘Exporteren’ of plak een code van een ander apparaat</string>
1367+ <string name="VoiceTimecodesCodeExport">Naar klembord exporteren</string>
1368+ <string name="VoiceTimecodesCodeImport">Code importeren</string>
1369+ <string name="VoiceTimecodesCodeCopied">Instellingencode gekopieerd</string>
1365− <string name="VoiceTimecodesImportSuccess">Instellingen geïmporteerd</string>
1370+ <string name="VoiceTimecodesCodeImported">Instellingen geïmporteerd</string>
1366− <string name="VoiceTimecodesImportFailed">Bestand niet gevonden of ongeldige indeling</string>
1371+ <string name="VoiceTimecodesCodeImportFailed">Ongeldige of lege instellingencode</string>
1367 <string name="VoiceTimecodesSaved">Instellingen voor tijdmarkeringen opgeslagen</string>
1372 <string name="VoiceTimecodesSaved">Instellingen voor tijdmarkeringen opgeslagen</string>
1368 <string name="VoiceTimecodesDone">Tijdmarkeringen toegevoegd</string>
1373 <string name="VoiceTimecodesDone">Tijdmarkeringen toegevoegd</string>
1369 <string name="VoiceTimecodesGenerate">Tijdmarkeringen toevoegen</string>
1374 <string name="VoiceTimecodesGenerate">Tijdmarkeringen toevoegen</string>
TMessagesProj/src/main/res/values-pt-rBR/strings.xml+8−3
@@ -1543,9 +1543,14 @@
1543 <string name="VoiceTimecodesMinDuration">Duração mínima do áudio (segundos)</string>
1543 <string name="VoiceTimecodesMinDuration">Duração mínima do áudio (segundos)</string>
1544 <string name="VoiceTimecodesPrompt">Modelo de prompt ({transcript} = texto)</string>
1544 <string name="VoiceTimecodesPrompt">Modelo de prompt ({transcript} = texto)</string>
1545 <string name="VoiceTimecodesResetPrompt">Restaurar prompt padrão</string>
1545 <string name="VoiceTimecodesResetPrompt">Restaurar prompt padrão</string>
1546− <string name="VoiceTimecodesImportFile">Importar de /sdcard/Download/voice_timecodes.json</string>
1546+ <string name="VoiceTimecodesCodeTitle">Código de configurações</string>
1547+ <string name="VoiceTimecodesCodeInfo">Um código de uma linha com todas as configurações de marcações de tempo de voz, incluindo chaves de API. Copie-o para Mensagens salvas e, em outro dispositivo, copie-o novamente e toque em «Importar código». Mantenha o código privado.</string>
1548+ <string name="VoiceTimecodesCodeHint">Toque em «Exportar» ou cole um código de outro dispositivo</string>
1549+ <string name="VoiceTimecodesCodeExport">Exportar para a área de transferência</string>
1550+ <string name="VoiceTimecodesCodeImport">Importar código</string>
1551+ <string name="VoiceTimecodesCodeCopied">Código de configurações copiado</string>
1547− <string name="VoiceTimecodesImportSuccess">Configurações importadas com sucesso</string>
1552+ <string name="VoiceTimecodesCodeImported">Configurações importadas</string>
1548− <string name="VoiceTimecodesImportFailed">Arquivo não encontrado ou formato inválido</string>
1553+ <string name="VoiceTimecodesCodeImportFailed">Código de configurações inválido ou vazio</string>
1549 <string name="VoiceTimecodesSaved">Configurações de timecodes salvas</string>
1554 <string name="VoiceTimecodesSaved">Configurações de timecodes salvas</string>
1550 <string name="VoiceTimecodesDone">Timecodes adicionados</string>
1555 <string name="VoiceTimecodesDone">Timecodes adicionados</string>
1551 <string name="VoiceTimecodesGenerate">Adicionar timecodes</string>
1556 <string name="VoiceTimecodesGenerate">Adicionar timecodes</string>
TMessagesProj/src/main/res/values-ru/strings.xml+8−3
@@ -552,9 +552,14 @@
552 <string name="VoiceTimecodesMinDuration">Мин. длительность голосового (сек)</string>
552 <string name="VoiceTimecodesMinDuration">Мин. длительность голосового (сек)</string>
553 <string name="VoiceTimecodesPrompt">Шаблон промпта ({transcript} = текст)</string>
553 <string name="VoiceTimecodesPrompt">Шаблон промпта ({transcript} = текст)</string>
554 <string name="VoiceTimecodesResetPrompt">Сбросить промпт по умолчанию</string>
554 <string name="VoiceTimecodesResetPrompt">Сбросить промпт по умолчанию</string>
555− <string name="VoiceTimecodesImportFile">Импортировать из /sdcard/Download/voice_timecodes.json</string>
555+ <string name="VoiceTimecodesCodeTitle">Код настроек</string>
556+ <string name="VoiceTimecodesCodeInfo">Однострочный код со всеми настройками автотаймкодов, включая API-ключи. Скопируйте его в «Избранное», а на другом устройстве скопируйте оттуда и нажмите «Импортировать код». Никому не показывайте этот код.</string>
557+ <string name="VoiceTimecodesCodeHint">Нажмите «Экспорт» или вставьте код с другого устройства</string>
558+ <string name="VoiceTimecodesCodeExport">Экспортировать в буфер</string>
559+ <string name="VoiceTimecodesCodeImport">Импортировать код</string>
560+ <string name="VoiceTimecodesCodeCopied">Код настроек скопирован</string>
556− <string name="VoiceTimecodesImportSuccess">Настройки успешно импортированы</string>
561+ <string name="VoiceTimecodesCodeImported">Настройки импортированы</string>
557− <string name="VoiceTimecodesImportFailed">Файл не найден или содержит ошибку</string>
562+ <string name="VoiceTimecodesCodeImportFailed">Код настроек пуст или повреждён</string>
558 <string name="VoiceTimecodesSaved">Настройки таймкодов сохранены</string>
563 <string name="VoiceTimecodesSaved">Настройки таймкодов сохранены</string>
559 <string name="VoiceTimecodesDone">Таймкоды добавлены</string>
564 <string name="VoiceTimecodesDone">Таймкоды добавлены</string>
560 <string name="VoiceTimecodesGenerate">Добавить таймкоды</string>
565 <string name="VoiceTimecodesGenerate">Добавить таймкоды</string>
TMessagesProj/src/main/res/values-uk/strings.xml+8−3
@@ -553,9 +553,14 @@
553 <string name="VoiceTimecodesMinDuration">Мінімальна тривалість голосового (секунди)</string>
553 <string name="VoiceTimecodesMinDuration">Мінімальна тривалість голосового (секунди)</string>
554 <string name="VoiceTimecodesPrompt">Шаблон промпту ({transcript} = текст)</string>
554 <string name="VoiceTimecodesPrompt">Шаблон промпту ({transcript} = текст)</string>
555 <string name="VoiceTimecodesResetPrompt">Скинути промпт до типового</string>
555 <string name="VoiceTimecodesResetPrompt">Скинути промпт до типового</string>
556− <string name="VoiceTimecodesImportFile">Імпортувати з /sdcard/Download/voice_timecodes.json</string>
556+ <string name="VoiceTimecodesCodeTitle">Код налаштувань</string>
557+ <string name="VoiceTimecodesCodeInfo">Однорядковий код з усіма налаштуваннями автотаймкодів, включно з API-ключами. Скопіюйте його у «Вибране», а на іншому пристрої скопіюйте звідти й натисніть «Імпортувати код». Нікому не показуйте цей код.</string>
558+ <string name="VoiceTimecodesCodeHint">Натисніть «Експорт» або вставте код з іншого пристрою</string>
559+ <string name="VoiceTimecodesCodeExport">Експортувати в буфер</string>
560+ <string name="VoiceTimecodesCodeImport">Імпортувати код</string>
561+ <string name="VoiceTimecodesCodeCopied">Код налаштувань скопійовано</string>
557− <string name="VoiceTimecodesImportSuccess">Налаштування успішно імпортовано</string>
562+ <string name="VoiceTimecodesCodeImported">Налаштування імпортовано</string>
558− <string name="VoiceTimecodesImportFailed">Файл не знайдено або недійсний формат</string>
563+ <string name="VoiceTimecodesCodeImportFailed">Код налаштувань порожній або пошкоджений</string>
559 <string name="VoiceTimecodesSaved">Налаштування таймкодів збережено</string>
564 <string name="VoiceTimecodesSaved">Налаштування таймкодів збережено</string>
560 <string name="VoiceTimecodesDone">Таймкоди додано</string>
565 <string name="VoiceTimecodesDone">Таймкоди додано</string>
561 <string name="VoiceTimecodesGenerate">Додати таймкоди</string>
566 <string name="VoiceTimecodesGenerate">Додати таймкоди</string>
TMessagesProj/src/main/res/values/strings.xml+8−3
@@ -11835,9 +11835,14 @@
11835 <string name="VoiceTimecodesMinDuration">Minimum voice duration (seconds)</string>
11835 <string name="VoiceTimecodesMinDuration">Minimum voice duration (seconds)</string>
11836 <string name="VoiceTimecodesPrompt">Prompt template ({transcript} = text)</string>
11836 <string name="VoiceTimecodesPrompt">Prompt template ({transcript} = text)</string>
11837 <string name="VoiceTimecodesResetPrompt">Reset prompt to default</string>
11837 <string name="VoiceTimecodesResetPrompt">Reset prompt to default</string>
11838− <string name="VoiceTimecodesImportFile">Import from /sdcard/Download/voice_timecodes.json</string>
11838+ <string name="VoiceTimecodesCodeTitle">Settings code</string>
11839+ <string name="VoiceTimecodesCodeInfo">A single-line code with all voice timecode settings, including API keys. Copy it to Saved Messages, then on another device copy it again and tap Import code. Keep the code private.</string>
11840+ <string name="VoiceTimecodesCodeHint">Tap Export or paste a code from another device</string>
11841+ <string name="VoiceTimecodesCodeExport">Export to clipboard</string>
11842+ <string name="VoiceTimecodesCodeImport">Import code</string>
11843+ <string name="VoiceTimecodesCodeCopied">Settings code copied</string>
11839− <string name="VoiceTimecodesImportSuccess">Settings imported successfully</string>
11844+ <string name="VoiceTimecodesCodeImported">Settings imported</string>
11840− <string name="VoiceTimecodesImportFailed">File not found or invalid format</string>
11845+ <string name="VoiceTimecodesCodeImportFailed">Invalid or empty settings code</string>
11841 <string name="VoiceTimecodesSaved">Voice timecodes settings saved</string>
11846 <string name="VoiceTimecodesSaved">Voice timecodes settings saved</string>
11842 <string name="VoiceTimecodesDone">Timecodes added</string>
11847 <string name="VoiceTimecodesDone">Timecodes added</string>
11843 <string name="VoiceTimecodesGenerate">Add timecodes</string>
11848 <string name="VoiceTimecodesGenerate">Add timecodes</string>
gradle.properties+2−2
@@ -13,8 +13,8 @@
13# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
14# org.gradle.parallel=true
14# org.gradle.parallel=true
15#Sat Mar 12 05:53:50 MSK 2016
15#Sat Mar 12 05:53:50 MSK 2016
16−APP_VERSION_CODE=7184
16+APP_VERSION_CODE=7185
17−APP_VERSION_NAME=13.0.4
17+APP_VERSION_NAME=13.0.5
18APP_PACKAGE=org.telegram.messenger
18APP_PACKAGE=org.telegram.messenger
19IS_PRIVATE=false
19IS_PRIVATE=false
20RELEASE_KEY_PASSWORD=android
20RELEASE_KEY_PASSWORD=android