diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index 800d5026b..d0dbed6ae 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -80,7 +80,7 @@ android { defaultConfig { minSdkVersion 8 targetSdkVersion 19 - versionCode 363 + versionCode 371 versionName "1.9.5" } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java b/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java index b4f28c26a..ed9a09e90 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java @@ -425,7 +425,7 @@ public class AndroidUtilities { final NumberPicker numberPicker = new NumberPicker(context); numberPicker.setMinValue(0); numberPicker.setMaxValue(20); - if (encryptedChat.ttl >= 0 && encryptedChat.ttl < 16) { + if (encryptedChat.ttl > 0 && encryptedChat.ttl < 16) { numberPicker.setValue(encryptedChat.ttl); } else if (encryptedChat.ttl == 30) { numberPicker.setValue(16); @@ -437,6 +437,8 @@ public class AndroidUtilities { numberPicker.setValue(19); } else if (encryptedChat.ttl == 60 * 60 * 24 * 7) { numberPicker.setValue(20); + } else if (encryptedChat.ttl == 0) { + numberPicker.setValue(5); } numberPicker.setFormatter(new NumberPicker.Formatter() { @Override @@ -479,7 +481,7 @@ public class AndroidUtilities { encryptedChat.ttl = 60 * 60 * 24 * 7; } if (oldValue != encryptedChat.ttl) { - SendMessagesHelper.getInstance().sendTTLMessage(encryptedChat); + SendMessagesHelper.getInstance().sendTTLMessage(encryptedChat, null); MessagesStorage.getInstance().updateEncryptedChatTTL(encryptedChat); } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/MediaController.java b/TMessagesProj/src/main/java/org/telegram/android/MediaController.java index 1464050f8..ad08add77 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MediaController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MediaController.java @@ -791,7 +791,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel photoW = bmOptions.outWidth; photoH = bmOptions.outHeight; } - if (photoW == 0 || photoH == 0 || (photoW == width && photoH == height || photoH == width && photoW == height)) { + if (photoW <= 0 || photoH <= 0 || (photoW == width && photoH == height || photoH == width && photoW == height)) { screenshotDates.add(date); } } catch (Exception e) { @@ -834,7 +834,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } } if (send) { - SendMessagesHelper.getInstance().sendScreenshotMessage(lastSecretChat, lastSecretChatVisibleMessages); + SendMessagesHelper.getInstance().sendScreenshotMessage(lastSecretChat, lastSecretChatVisibleMessages, null); } } @@ -2200,6 +2200,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel File inputFile = new File(videoPath); if (!inputFile.canRead()) { + didWriteData(messageObject, cacheFile, true, true); return false; } @@ -2577,6 +2578,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel FileLog.e("tmessages", "time = " + (System.currentTimeMillis() - time)); } } else { + didWriteData(messageObject, cacheFile, true, true); return false; } didWriteData(messageObject, cacheFile, true, error); diff --git a/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java index dea61ff84..953bdb110 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java @@ -232,7 +232,7 @@ public class MessageObject { } else { messageText = LocaleController.formatString("NotificationContactNewPhoto", R.string.NotificationContactNewPhoto, ""); } - } else if (message.action instanceof TLRPC.TL_messageEcryptedAction) { + } else if (message.action instanceof TLRPC.TL_messageEncryptedAction) { if (message.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages) { if (isFromMe()) { messageText = LocaleController.formatString("ActionTakeScreenshootYou", R.string.ActionTakeScreenshootYou); @@ -243,6 +243,29 @@ public class MessageObject { messageText = LocaleController.formatString("ActionTakeScreenshoot", R.string.ActionTakeScreenshoot).replace("un1", ""); } } + } else if (message.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) { + TLRPC.TL_decryptedMessageActionSetMessageTTL action = (TLRPC.TL_decryptedMessageActionSetMessageTTL) message.action.encryptedAction; + if (action.ttl_seconds != 0) { + if (isFromMe()) { + messageText = LocaleController.formatString("MessageLifetimeChangedOutgoing", R.string.MessageLifetimeChangedOutgoing, AndroidUtilities.formatTTLString(action.ttl_seconds)); + } else { + if (fromUser != null) { + messageText = LocaleController.formatString("MessageLifetimeChanged", R.string.MessageLifetimeChanged, fromUser.first_name, AndroidUtilities.formatTTLString(action.ttl_seconds)); + } else { + messageText = LocaleController.formatString("MessageLifetimeChanged", R.string.MessageLifetimeChanged, "", AndroidUtilities.formatTTLString(action.ttl_seconds)); + } + } + } else { + if (isFromMe()) { + messageText = LocaleController.getString("MessageLifetimeYouRemoved", R.string.MessageLifetimeYouRemoved); + } else { + if (fromUser != null) { + messageText = LocaleController.formatString("MessageLifetimeRemoved", R.string.MessageLifetimeRemoved, fromUser.first_name); + } else { + messageText = LocaleController.formatString("MessageLifetimeRemoved", R.string.MessageLifetimeRemoved, ""); + } + } + } } } else if (message.action instanceof TLRPC.TL_messageActionCreatedBroadcastList) { messageText = LocaleController.formatString("YouCreatedBroadcastList", R.string.YouCreatedBroadcastList); @@ -301,6 +324,14 @@ public class MessageObject { } else if (message.action instanceof TLRPC.TL_messageActionChatEditPhoto || message.action instanceof TLRPC.TL_messageActionUserUpdatedPhoto) { contentType = 4; type = 11; + } else if (message.action instanceof TLRPC.TL_messageEncryptedAction) { + if (message.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages || message.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) { + contentType = 4; + type = 10; + } else { + contentType = -1; + type = -1; + } } else { contentType = 4; type = 10; diff --git a/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java index 205034341..eb95c9a56 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java @@ -980,7 +980,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesDeleted, messages); if (randoms != null && encryptedChat != null && !randoms.isEmpty()) { - SendMessagesHelper.getInstance().sendMessagesDeleteMessage(randoms, encryptedChat); + SendMessagesHelper.getInstance().sendMessagesDeleteMessage(encryptedChat, randoms, null); } ArrayList toSend = new ArrayList(); @@ -1071,7 +1071,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter }); } else { if (onlyHistory) { - SendMessagesHelper.getInstance().sendClearHistoryMessage(getEncryptedChat(high_id)); + SendMessagesHelper.getInstance().sendClearHistoryMessage(getEncryptedChat(high_id), null); } else { declineSecretChat(high_id); } @@ -1643,6 +1643,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter putChats(dialogsRes.chats, isCache); if (encChats != null) { for (TLRPC.EncryptedChat encryptedChat : encChats) { + if (encryptedChat instanceof TLRPC.TL_encryptedChat && AndroidUtilities.getMyLayerVersion(encryptedChat.layer) < SendMessagesHelper.CURRENT_SECRET_CHAT_LAYER) { + SendMessagesHelper.getInstance().sendNotifyLayerMessage(encryptedChat, null); + } putEncryptedChat(encryptedChat, true); } } @@ -1707,7 +1710,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter }); } - public void markMessageAsRead(final long dialog_id, final long random_id) { + public void markMessageAsRead(final long dialog_id, final long random_id, int ttl) { if (random_id == 0 || dialog_id == 0) { return; } @@ -1722,12 +1725,11 @@ public class MessagesController implements NotificationCenter.NotificationCenter } ArrayList random_ids = new ArrayList(); random_ids.add(random_id); - SendMessagesHelper.getInstance().sendMessagesReadMessage(random_ids, chat); - if (chat.ttl > 0) { + SendMessagesHelper.getInstance().sendMessagesReadMessage(chat, random_ids, null); + if (ttl > 0) { int time = ConnectionsManager.getInstance().getCurrentTime(); MessagesStorage.getInstance().createTaskForSecretChat(chat.id, time, time, 0, random_ids); } - //TODO resend request } public void markDialogAsRead(final long dialog_id, final int max_id, final int max_positive_id, final int offset, final int max_date, final boolean was, final boolean popup) { @@ -3558,7 +3560,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter public TLRPC.Message decryptMessage(TLRPC.EncryptedMessage message) { final TLRPC.EncryptedChat chat = getEncryptedChatDB(message.chat_id); - if (chat == null) { + if (chat == null || chat instanceof TLRPC.TL_encryptedChatDiscarded) { return null; } ByteBufferDesc is = BuffersStorage.getInstance().getFreeBuffer(message.bytes.length); @@ -3582,13 +3584,38 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (object instanceof TLRPC.TL_decryptedMessageLayer) { final TLRPC.TL_decryptedMessageLayer layer = (TLRPC.TL_decryptedMessageLayer)object; - AndroidUtilities.RunOnUIThread(new Runnable() { - @Override - public void run() { - chat.seq_in = layer.out_seq_no; - MessagesStorage.getInstance().updateEncryptedChatSeq(chat); + if (chat.seq_in == 0 && chat.seq_out == 0) { + if (chat.admin_id == UserConfig.getClientUserId()) { + chat.seq_out = 1; + } else { + chat.seq_in = 1; } - }); + } + if (chat.seq_in != layer.out_seq_no && chat.seq_in != layer.out_seq_no - 2) { + AndroidUtilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + final TLRPC.TL_encryptedChatDiscarded newChat = new TLRPC.TL_encryptedChatDiscarded(); + newChat.id = chat.id; + newChat.user_id = chat.user_id; + newChat.auth_key = chat.auth_key; + newChat.seq_in = chat.seq_in; + newChat.seq_out = chat.seq_out; + MessagesStorage.getInstance().updateEncryptedChat(newChat); + AndroidUtilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + putEncryptedChat(newChat, false); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.encryptedChatUpdated, newChat); + } + }); + declineSecretChat(chat.id); + } + }); + return null; + } + chat.seq_in = layer.out_seq_no; + MessagesStorage.getInstance().updateEncryptedChatSeq(chat); object = layer.message; } @@ -3597,8 +3624,10 @@ public class MessagesController implements NotificationCenter.NotificationCenter TLRPC.TL_message newMessage = null; if (AndroidUtilities.getPeerLayerVersion(chat.layer) >= 17) { newMessage = new TLRPC.TL_message_secret(); + newMessage.ttl = decryptedMessage.ttl; } else { newMessage = new TLRPC.TL_message(); + newMessage.ttl = chat.ttl; } newMessage.message = decryptedMessage.message; newMessage.date = message.date; @@ -3610,7 +3639,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter newMessage.to_id.user_id = UserConfig.getClientUserId(); newMessage.flags = TLRPC.MESSAGE_FLAG_UNREAD; newMessage.dialog_id = ((long)chat.id) << 32; - newMessage.ttl = chat.ttl; if (decryptedMessage.media instanceof TLRPC.TL_decryptedMessageMediaEmpty) { newMessage.media = new TLRPC.TL_messageMediaEmpty(); } else if (decryptedMessage.media instanceof TLRPC.TL_decryptedMessageMediaContact) { @@ -3751,13 +3779,15 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL || serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages) { TLRPC.TL_messageService newMessage = new TLRPC.TL_messageService(); if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) { - newMessage.action = new TLRPC.TL_messageActionTTLChange(); + newMessage.action = new TLRPC.TL_messageEncryptedAction(); if (serviceMessage.action.ttl_seconds < 0 || serviceMessage.action.ttl_seconds > 60 * 60 * 24 * 365) { serviceMessage.action.ttl_seconds = 60 * 60 * 24 * 365; } - newMessage.action.ttl = chat.ttl = serviceMessage.action.ttl_seconds; + chat.ttl = serviceMessage.action.ttl_seconds; + newMessage.action.encryptedAction = serviceMessage.action; + MessagesStorage.getInstance().updateEncryptedChatTTL(chat); } else if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages) { - newMessage.action = new TLRPC.TL_messageEcryptedAction(); + newMessage.action = new TLRPC.TL_messageEncryptedAction(); newMessage.action.encryptedAction = serviceMessage.action; } newMessage.local_id = newMessage.id = UserConfig.getNewMessageId(); @@ -3768,7 +3798,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter newMessage.to_id = new TLRPC.TL_peerUser(); newMessage.to_id.user_id = UserConfig.getClientUserId(); newMessage.dialog_id = ((long)chat.id) << 32; - MessagesStorage.getInstance().updateEncryptedChatTTL(chat); return newMessage; } else if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionFlushHistory) { final long did = ((long)chat.id) << 32; @@ -3818,7 +3847,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter chat.layer = AndroidUtilities.setPeerLayerVersion(chat.layer, serviceMessage.action.layer); MessagesStorage.getInstance().updateEncryptedChatLayer(chat); if (currentPeerLayer < 17) { - SendMessagesHelper.getInstance().sendNotifyLayerMessage(chat); + SendMessagesHelper.getInstance().sendNotifyLayerMessage(chat, null); } } }); @@ -3878,7 +3907,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter public void run() { putEncryptedChat(encryptedChat, false); NotificationCenter.getInstance().postNotificationName(NotificationCenter.encryptedChatUpdated, encryptedChat); - SendMessagesHelper.getInstance().sendNotifyLayerMessage(encryptedChat); + SendMessagesHelper.getInstance().sendNotifyLayerMessage(encryptedChat, null); } }); } else { @@ -4003,7 +4032,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter public void run() { putEncryptedChat(newChat, false); NotificationCenter.getInstance().postNotificationName(NotificationCenter.encryptedChatUpdated, newChat); - SendMessagesHelper.getInstance().sendNotifyLayerMessage(newChat); + SendMessagesHelper.getInstance().sendNotifyLayerMessage(newChat, null); } }); } diff --git a/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java index 31e58ea8e..479401bf6 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java @@ -110,6 +110,7 @@ public class MessagesStorage { database.executeFast("CREATE TABLE download_queue(uid INTEGER, type INTEGER, date INTEGER, data BLOB, PRIMARY KEY (uid, type));").stepThis().dispose(); database.executeFast("CREATE TABLE dialog_settings(did INTEGER PRIMARY KEY, flags INTEGER);").stepThis().dispose(); database.executeFast("CREATE TABLE messages_seq(mid INTEGER PRIMARY KEY, seq_in INTEGER, seq_out INTEGER);").stepThis().dispose(); + //database.executeFast("CREATE TABLE attach_data(uid INTEGER, id INTEGER, data BLOB, PRIMARY KEY (uid, id))").stepThis().dispose(); database.executeFast("CREATE TABLE user_contacts_v6(uid INTEGER PRIMARY KEY, fname TEXT, sname TEXT)").stepThis().dispose(); @@ -137,6 +138,9 @@ public class MessagesStorage { database.executeFast("CREATE INDEX IF NOT EXISTS mid_out_idx_messages ON messages(mid, out);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS task_idx_messages ON messages(uid, out, read_state, ttl, date, send_state);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS send_state_idx_messages ON messages(mid, send_state, date) WHERE mid < 0 AND send_state = 1;").stepThis().dispose(); + + database.executeFast("CREATE INDEX IF NOT EXISTS seq_idx_messages_seq ON messages_seq(seq_in, seq_out);").stepThis().dispose(); + database.executeFast("PRAGMA user_version = 7").stepThis().dispose(); } else { try { @@ -292,6 +296,7 @@ public class MessagesStorage { } if (version == 6 && version < 7) { database.executeFast("CREATE TABLE IF NOT EXISTS messages_seq(mid INTEGER PRIMARY KEY, seq_in INTEGER, seq_out INTEGER);").stepThis().dispose(); + database.executeFast("CREATE INDEX IF NOT EXISTS seq_idx_messages_seq ON messages_seq(seq_in, seq_out);").stepThis().dispose(); database.executeFast("ALTER TABLE enc_chats ADD COLUMN layer INTEGER default 0").stepThis().dispose(); database.executeFast("ALTER TABLE enc_chats ADD COLUMN seq_in INTEGER default 0").stepThis().dispose(); database.executeFast("ALTER TABLE enc_chats ADD COLUMN seq_out INTEGER default 0").stepThis().dispose(); @@ -1559,9 +1564,9 @@ public class MessagesStorage { } } else { if (max_id != 0) { - cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media WHERE uid = %d AND mid > %d ORDER BY mid ASC LIMIT %d", uid, max_id, count)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, r.random_id FROM media as m LEFT JOIN randoms as r ON r.mid = m.mid WHERE m.uid = %d AND m.mid > %d ORDER BY m.mid ASC LIMIT %d", uid, max_id, count)); } else { - cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media WHERE uid = %d ORDER BY mid ASC LIMIT %d,%d", uid, offset, count)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.data, m.mid, r.random_id FROM media as m LEFT JOIN randoms as r ON r.mid = m.mid WHERE m.uid = %d ORDER BY m.mid ASC LIMIT %d,%d", uid, offset, count)); } } @@ -1571,6 +1576,9 @@ public class MessagesStorage { TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); message.id = cursor.intValue(1); message.dialog_id = uid; + if ((int)uid == 0) { + message.random_id = cursor.longValue(2); + } res.messages.add(message); fromUser.add(message.from_id); } @@ -1659,7 +1667,7 @@ public class MessagesStorage { ArrayList chatIds = new ArrayList(); ArrayList broadcastIds = new ArrayList(); ArrayList encryptedChatIds = new ArrayList(); - SQLiteCursor cursor = database.queryFinalized("SELECT m.read_state, m.data, m.send_state, m.mid, m.date, r.random_id, m.uid FROM messages as m LEFT JOIN randoms as r ON r.mid = m.mid WHERE m.mid < 0 AND m.send_state = 1 ORDER BY m.mid DESC LIMIT " + count); + SQLiteCursor cursor = database.queryFinalized("SELECT m.read_state, m.data, m.send_state, m.mid, m.date, r.random_id, m.uid, s.seq_in, s.seq_out FROM messages as m LEFT JOIN randoms as r ON r.mid = m.mid LEFT JOIN messages_seq as s ON m.mid = s.mid WHERE m.mid < 0 AND m.send_state = 1 ORDER BY m.mid DESC LIMIT " + count); while (cursor.next()) { ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1)); if (data != null && cursor.byteBufferValue(1, data.buffer) != 0) { @@ -1671,6 +1679,8 @@ public class MessagesStorage { message.random_id = cursor.longValue(5); } message.dialog_id = cursor.longValue(6); + message.seq_in = cursor.intValue(7); + message.seq_out = cursor.intValue(8); messages.add(message); int lower_id = (int)message.dialog_id; @@ -2629,10 +2639,19 @@ public class MessagesStorage { ByteBufferDesc data = buffersStorage.getFreeBuffer(message.getObjectSize()); message.serializeToStream(data); - TLRPC.Message lastMessage = messagesMap.get(dialog_id); - if (lastMessage == null || message.date > lastMessage.date) { - messagesMap.put(dialog_id, message); + + boolean updateDialog = true; + if (message.action != null && message.action instanceof TLRPC.TL_messageEncryptedAction && !(message.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL || message.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages)) { + updateDialog = false; } + + if (updateDialog) { + TLRPC.Message lastMessage = messagesMap.get(dialog_id); + if (lastMessage == null || message.date > lastMessage.date) { + messagesMap.put(dialog_id, message); + } + } + state.bindInteger(1, messageId); state.bindLong(2, dialog_id); state.bindInteger(3, (MessageObject.isUnread(message) ? 0 : 1)); @@ -2820,6 +2839,25 @@ public class MessagesStorage { }); } + public void setMessageSeq(final int mid, final int seq_in, final int seq_out) { + storageQueue.postRunnable(new Runnable() { + @Override + public void run() { + try { + SQLitePreparedStatement state = database.executeFast("REPLACE INTO messages_seq VALUES(?, ?, ?)"); + state.requery(); + state.bindInteger(1, mid); + state.bindInteger(2, seq_in); + state.bindInteger(3, seq_out); + state.step(); + state.dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + private Integer updateMessageStateAndIdInternal(long random_id, Integer _oldId, int newId, int date) { if (_oldId != null && _oldId == newId && date != 0) { SQLitePreparedStatement state = null; @@ -2835,6 +2873,7 @@ public class MessagesStorage { state.dispose(); } } + return newId; } else { Integer oldId = _oldId; @@ -2875,6 +2914,7 @@ public class MessagesStorage { } finally { if (state != null) { state.dispose(); + state = null; } } @@ -2888,6 +2928,7 @@ public class MessagesStorage { } finally { if (state != null) { state.dispose(); + state = null; } } @@ -2901,6 +2942,7 @@ public class MessagesStorage { } finally { if (state != null) { state.dispose(); + state = null; } } @@ -3113,10 +3155,63 @@ public class MessagesStorage { } try { String ids = TextUtils.join(",", messages); + SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT uid, data FROM messages WHERE mid IN(%s)", ids)); + ArrayList filesToDelete = new ArrayList(); + try { + while (cursor.next()) { + long did = cursor.longValue(0); + if ((int)did != 0) { + continue; + } + ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1)); + if (data != null && cursor.byteBufferValue(1, data.buffer) != 0) { + TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + if (message == null || message.media == null) { + continue; + } + if (message.media instanceof TLRPC.TL_messageMediaAudio) { + File file = FileLoader.getPathToAttach(message.media.audio); + if (file != null && file.toString().length() > 0) { + filesToDelete.add(file); + } + } else if (message.media instanceof TLRPC.TL_messageMediaPhoto) { + for (TLRPC.PhotoSize photoSize : message.media.photo.sizes) { + File file = FileLoader.getPathToAttach(photoSize); + if (file != null && file.toString().length() > 0) { + filesToDelete.add(file); + } + } + } else if (message.media instanceof TLRPC.TL_messageMediaVideo) { + File file = FileLoader.getPathToAttach(message.media.video); + if (file != null && file.toString().length() > 0) { + filesToDelete.add(file); + } + file = FileLoader.getPathToAttach(message.media.video.thumb); + if (file != null && file.toString().length() > 0) { + filesToDelete.add(file); + } + } else if (message.media instanceof TLRPC.TL_messageMediaDocument) { + File file = FileLoader.getPathToAttach(message.media.document); + if (file != null && file.toString().length() > 0) { + filesToDelete.add(file); + } + file = FileLoader.getPathToAttach(message.media.document.thumb); + if (file != null && file.toString().length() > 0) { + filesToDelete.add(file); + } + } + } + buffersStorage.reuseFreeBuffer(data); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + cursor.dispose(); + FileLoader.getInstance().deleteFiles(filesToDelete); database.executeFast(String.format(Locale.US, "DELETE FROM messages WHERE mid IN(%s)", ids)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "DELETE FROM messages_seq WHERE mid IN(%s)", ids)).stepThis().dispose(); database.executeFast(String.format(Locale.US, "DELETE FROM media WHERE mid IN(%s)", ids)).stepThis().dispose(); database.executeFast("DELETE FROM media_counts WHERE 1").stepThis().dispose(); - } catch (Exception e) { FileLog.e("tmessages", e); } diff --git a/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java b/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java index bf7f6a070..27cdd0368 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java @@ -113,18 +113,18 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter if (file != null && media != null) { if (message.type == 0) { media.file = file; - performSendMessageRequest(message.sendRequest, message.obj, message.originalPath); + performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); } else if (message.type == 1) { if (media.file == null) { media.file = file; if (media.thumb == null && message.location != null) { performSendDelayedMessage(message); } else { - performSendMessageRequest(message.sendRequest, message.obj, message.originalPath); + performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); } } else { media.thumb = file; - performSendMessageRequest(message.sendRequest, message.obj, message.originalPath); + performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); } } else if (message.type == 2) { if (media.file == null) { @@ -132,22 +132,22 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter if (media.thumb == null && message.location != null) { performSendDelayedMessage(message); } else { - performSendMessageRequest(message.sendRequest, message.obj, message.originalPath); + performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); } } else { media.thumb = file; - performSendMessageRequest(message.sendRequest, message.obj, message.originalPath); + performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); } } else if (message.type == 3) { media.file = file; - performSendMessageRequest(message.sendRequest, message.obj, message.originalPath); + performSendMessageRequest(message.sendRequest, message.obj.messageOwner, message.originalPath); } arr.remove(a); a--; } else if (encryptedFile != null && message.sendEncryptedRequest != null) { message.sendEncryptedRequest.media.key = encryptedFile.key; message.sendEncryptedRequest.media.iv = encryptedFile.iv; - performSendEncryptedRequest(message.sendEncryptedRequest, message.obj, message.encryptedChat, encryptedFile, message.originalPath, null); + performSendEncryptedRequest(message.sendEncryptedRequest, message.obj.messageOwner, message.encryptedChat, encryptedFile, message.originalPath); arr.remove(a); a--; } @@ -288,6 +288,35 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter if (messageObject.messageOwner.id >= 0) { return false; } + if (messageObject.messageOwner.action instanceof TLRPC.TL_messageEncryptedAction) { + int enc_id = (int) (messageObject.getDialogId() >> 32); + TLRPC.EncryptedChat encryptedChat = MessagesController.getInstance().getEncryptedChat(enc_id); + if (encryptedChat == null) { + MessagesStorage.getInstance().markMessageAsSendError(messageObject.messageOwner.id); + messageObject.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR; + NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageSendError, messageObject.messageOwner.id); + processSentMessage(messageObject.messageOwner.id); + return false; + } + if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) { + sendTTLMessage(encryptedChat, messageObject.messageOwner); + } else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionDeleteMessages) { + sendMessagesDeleteMessage(encryptedChat, null, messageObject.messageOwner); + } else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionFlushHistory) { + sendClearHistoryMessage(encryptedChat, messageObject.messageOwner); + } else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionNotifyLayer) { + sendNotifyLayerMessage(encryptedChat, messageObject.messageOwner); + } else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionReadMessages) { + sendMessagesReadMessage(encryptedChat, null, messageObject.messageOwner); + } else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages) { + sendScreenshotMessage(encryptedChat, null, messageObject.messageOwner); + } else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionTyping) { + + } else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionResend) { + + } + return true; + } if (unsent) { unsentMessages.put(messageObject.messageOwner.id, messageObject); } @@ -623,13 +652,13 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend.message = message; reqSend.contacts = sendToPeers; reqSend.media = new TLRPC.TL_inputMediaEmpty(); - performSendMessageRequest(reqSend, newMsgObj, null); + performSendMessageRequest(reqSend, newMsgObj.messageOwner, null); } else { TLRPC.TL_messages_sendMessage reqSend = new TLRPC.TL_messages_sendMessage(); reqSend.message = message; reqSend.peer = sendToPeer; reqSend.random_id = newMsg.random_id; - performSendMessageRequest(reqSend, newMsgObj, null); + performSendMessageRequest(reqSend, newMsgObj.messageOwner, null); } } else { TLRPC.TL_decryptedMessage reqSend; @@ -644,7 +673,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend.random_id = newMsg.random_id; reqSend.message = message; reqSend.media = new TLRPC.TL_decryptedMessageMediaEmpty(); - performSendEncryptedRequest(reqSend, newMsgObj, encryptedChat, null, null, null); + performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, null, null); } } else if (type >= 1 && type <= 3 || type >= 5 && type <= 8) { if (encryptedChat == null) { @@ -761,32 +790,32 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend = request; } if (type == 1) { - performSendMessageRequest(reqSend, newMsgObj, null); + performSendMessageRequest(reqSend, newMsgObj.messageOwner, null); } else if (type == 2) { if (photo.access_hash == 0) { performSendDelayedMessage(delayedMessage); } else { - performSendMessageRequest(reqSend, newMsgObj, null); + performSendMessageRequest(reqSend, newMsgObj.messageOwner, null); } } else if (type == 3) { if (video.access_hash == 0) { performSendDelayedMessage(delayedMessage); } else { - performSendMessageRequest(reqSend, newMsgObj, null); + performSendMessageRequest(reqSend, newMsgObj.messageOwner, null); } } else if (type == 6) { - performSendMessageRequest(reqSend, newMsgObj, null); + performSendMessageRequest(reqSend, newMsgObj.messageOwner, null); } else if (type == 7) { if (document.access_hash == 0) { performSendDelayedMessage(delayedMessage); } else { - performSendMessageRequest(reqSend, newMsgObj, null); + performSendMessageRequest(reqSend, newMsgObj.messageOwner, null); } } else if (type == 8) { if (audio.access_hash == 0) { performSendDelayedMessage(delayedMessage); } else { - performSendMessageRequest(reqSend, newMsgObj, null); + performSendMessageRequest(reqSend, newMsgObj.messageOwner, null); } } } else { @@ -805,7 +834,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend.media = new TLRPC.TL_decryptedMessageMediaGeoPoint(); reqSend.media.lat = lat; reqSend.media._long = lon; - performSendEncryptedRequest(reqSend, newMsgObj, encryptedChat, null, null, null); + performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, null, null); } else if (type == 2) { TLRPC.PhotoSize small = photo.sizes.get(0); TLRPC.PhotoSize big = photo.sizes.get(photo.sizes.size() - 1); @@ -831,7 +860,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter encryptedFile.access_hash = big.location.secret; reqSend.media.key = big.location.key; reqSend.media.iv = big.location.iv; - performSendEncryptedRequest(reqSend, newMsgObj, encryptedChat, encryptedFile, null, null); + performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, encryptedFile, null); } } else if (type == 3) { if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { @@ -862,7 +891,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter encryptedFile.access_hash = video.access_hash; reqSend.media.key = video.key; reqSend.media.iv = video.iv; - performSendEncryptedRequest(reqSend, newMsgObj, encryptedChat, encryptedFile, null, null); + performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, encryptedFile, null); } } else if (type == 6) { reqSend.media = new TLRPC.TL_decryptedMessageMediaContact(); @@ -870,7 +899,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend.media.first_name = user.first_name; reqSend.media.last_name = user.last_name; reqSend.media.user_id = user.id; - performSendEncryptedRequest(reqSend, newMsgObj, encryptedChat, null, null, null); + performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, null, null); } else if (type == 7) { reqSend.media = new TLRPC.TL_decryptedMessageMediaDocument(); reqSend.media.size = document.size; @@ -900,7 +929,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter encryptedFile.access_hash = document.access_hash; reqSend.media.key = document.key; reqSend.media.iv = document.iv; - performSendEncryptedRequest(reqSend, newMsgObj, encryptedChat, encryptedFile, null, null); + performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, encryptedFile, null); } } else if (type == 8) { if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { @@ -930,7 +959,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } else { reqSend.id = msgObj.messageOwner.fwd_msg_id; } - performSendMessageRequest(reqSend, newMsgObj, null); + performSendMessageRequest(reqSend, newMsgObj.messageOwner, null); } } catch (Exception e) { FileLog.e("tmessages", e); @@ -1046,26 +1075,26 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter }); } - private void performSendMessageRequest(final TLObject req, final MessageObject newMsgObj, final String originalPath) { + private void performSendMessageRequest(final TLObject req, final TLRPC.Message newMsgObj, final String originalPath) { ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { if (error == null) { - final int oldId = newMsgObj.messageOwner.id; + final int oldId = newMsgObj.id; final boolean isBroadcast = req instanceof TLRPC.TL_messages_sendBroadcast; final ArrayList sentMessages = new ArrayList(); - final String attachPath = newMsgObj.messageOwner.attachPath; + final String attachPath = newMsgObj.attachPath; if (response instanceof TLRPC.messages_SentMessage) { TLRPC.messages_SentMessage res = (TLRPC.messages_SentMessage) response; - newMsgObj.messageOwner.id = res.id; - newMsgObj.messageOwner.date = res.date; + newMsgObj.id = res.id; + newMsgObj.date = res.date; MessagesController.getInstance().processNewDifferenceParams(res.seq, res.pts, res.date); } else if (response instanceof TLRPC.messages_StatedMessage) { TLRPC.messages_StatedMessage res = (TLRPC.messages_StatedMessage) response; sentMessages.add(res.message); - newMsgObj.messageOwner.id = res.message.id; - processSentMessage(newMsgObj.messageOwner, res.message, null, null, originalPath); + newMsgObj.id = res.message.id; + processSentMessage(newMsgObj, res.message, null, null, originalPath); MessagesController.getInstance().processNewDifferenceParams(res.seq, res.pts, res.message.date); } else if (response instanceof TLRPC.messages_StatedMessages) { TLRPC.messages_StatedMessages res = (TLRPC.messages_StatedMessages) response; @@ -1073,27 +1102,27 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter sentMessages.addAll(res.messages); TLRPC.Message message = res.messages.get(0); if (!isBroadcast) { - newMsgObj.messageOwner.id = message.id; + newMsgObj.id = message.id; } - processSentMessage(newMsgObj.messageOwner, message, null, null, originalPath); + processSentMessage(newMsgObj, message, null, null, originalPath); } MessagesController.getInstance().processNewDifferenceParams(res.seq, res.pts, -1); } MessagesStorage.getInstance().storageQueue.postRunnable(new Runnable() { @Override public void run() { - MessagesStorage.getInstance().updateMessageStateAndId(newMsgObj.messageOwner.random_id, oldId, (isBroadcast ? oldId : newMsgObj.messageOwner.id), 0, false); + MessagesStorage.getInstance().updateMessageStateAndId(newMsgObj.random_id, oldId, (isBroadcast ? oldId : newMsgObj.id), 0, false); MessagesStorage.getInstance().putMessages(sentMessages, true, false, isBroadcast, 0); if (isBroadcast) { ArrayList currentMessage = new ArrayList(); - currentMessage.add(newMsgObj.messageOwner); - newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; + currentMessage.add(newMsgObj); + newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; MessagesStorage.getInstance().putMessages(currentMessage, true, false, false, 0); } AndroidUtilities.RunOnUIThread(new Runnable() { @Override public void run() { - newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; + newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; if (isBroadcast) { for (TLRPC.Message message : sentMessages) { ArrayList arr = new ArrayList(); @@ -1103,25 +1132,25 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); } - NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, (isBroadcast ? oldId : newMsgObj.messageOwner.id), newMsgObj); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, (isBroadcast ? oldId : newMsgObj.id), newMsgObj); processSentMessage(oldId); } }); - if (newMsgObj.messageOwner.media instanceof TLRPC.TL_messageMediaVideo) { + if (newMsgObj.media instanceof TLRPC.TL_messageMediaVideo) { stopVideoService(attachPath); } } }); } else { - MessagesStorage.getInstance().markMessageAsSendError(newMsgObj.messageOwner.id); + MessagesStorage.getInstance().markMessageAsSendError(newMsgObj.id); AndroidUtilities.RunOnUIThread(new Runnable() { @Override public void run() { - newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR; - NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageSendError, newMsgObj.messageOwner.id); - processSentMessage(newMsgObj.messageOwner.id); - if (newMsgObj.messageOwner.media instanceof TLRPC.TL_messageMediaVideo) { - stopVideoService(newMsgObj.messageOwner.attachPath); + newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR; + NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageSendError, newMsgObj.id); + processSentMessage(newMsgObj.id); + if (newMsgObj.media instanceof TLRPC.TL_messageMediaVideo) { + stopVideoService(newMsgObj.attachPath); } } }); @@ -1130,11 +1159,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter }, (req instanceof TLRPC.TL_messages_forwardMessages ? null : new RPCRequest.RPCQuickAckDelegate() { @Override public void quickAck() { - final int msg_id = newMsgObj.messageOwner.id; + final int msg_id = newMsgObj.id; AndroidUtilities.RunOnUIThread(new Runnable() { @Override public void run() { - newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; + newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageReceivedByAck, msg_id); } }); @@ -1142,7 +1171,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter }), true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassCanCompress, ConnectionsManager.DEFAULT_DATACENTER_ID); } - private void performSendEncryptedRequest(final TLRPC.DecryptedMessage req, final MessageObject newMsgObj, final TLRPC.EncryptedChat chat, final TLRPC.InputEncryptedFile encryptedFile, final String originalPath, final Runnable callback) { + private void performSendEncryptedRequest(final TLRPC.DecryptedMessage req, final TLRPC.Message newMsgObj, final TLRPC.EncryptedChat chat, final TLRPC.InputEncryptedFile encryptedFile, final String originalPath) { if (req == null || chat.auth_key == null || chat instanceof TLRPC.TL_encryptedChatRequested || chat instanceof TLRPC.TL_encryptedChatWaiting) { return; } @@ -1150,15 +1179,33 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter if (AndroidUtilities.getPeerLayerVersion(chat.layer) >= 17) { TLRPC.TL_decryptedMessageLayer layer = new TLRPC.TL_decryptedMessageLayer(); layer.layer = CURRENT_SECRET_CHAT_LAYER; - layer.in_seq_no = chat.seq_in; - layer.out_seq_no = chat.seq_out; layer.message = req; layer.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))]; Utilities.random.nextBytes(layer.random_bytes); toEncryptObject = layer; - chat.seq_out += 2; - MessagesStorage.getInstance().updateEncryptedChatSeq(chat); + if (chat.seq_in == 0 && chat.seq_out == 0) { + if (chat.admin_id == UserConfig.getClientUserId()) { + chat.seq_out = 1; + } else { + chat.seq_in = 1; + } + } + + if (newMsgObj.seq_in == 0 && newMsgObj.seq_out == 0) { + layer.in_seq_no = chat.seq_in; + layer.out_seq_no = chat.seq_out; + chat.seq_out += 2; + MessagesStorage.getInstance().updateEncryptedChatSeq(chat); + if (newMsgObj != null) { + newMsgObj.seq_in = layer.in_seq_no; + newMsgObj.seq_out = layer.out_seq_no; + MessagesStorage.getInstance().setMessageSeq(newMsgObj.id, newMsgObj.seq_in, newMsgObj.seq_out); + } + } else { + layer.in_seq_no = newMsgObj.seq_in; + layer.out_seq_no = newMsgObj.seq_out; + } } else { toEncryptObject = req; } @@ -1199,13 +1246,23 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter TLObject reqToSend = null; if (encryptedFile == null) { - TLRPC.TL_messages_sendEncrypted req2 = new TLRPC.TL_messages_sendEncrypted(); - req2.data = data; - req2.random_id = req.random_id; - req2.peer = new TLRPC.TL_inputEncryptedChat(); - req2.peer.chat_id = chat.id; - req2.peer.access_hash = chat.access_hash; - reqToSend = req2; + if (req instanceof TLRPC.TL_decryptedMessageService) { + TLRPC.TL_messages_sendEncryptedService req2 = new TLRPC.TL_messages_sendEncryptedService(); + req2.data = data; + req2.random_id = req.random_id; + req2.peer = new TLRPC.TL_inputEncryptedChat(); + req2.peer.chat_id = chat.id; + req2.peer.access_hash = chat.access_hash; + reqToSend = req2; + } else { + TLRPC.TL_messages_sendEncrypted req2 = new TLRPC.TL_messages_sendEncrypted(); + req2.data = data; + req2.random_id = req.random_id; + req2.peer = new TLRPC.TL_inputEncryptedChat(); + req2.peer.chat_id = chat.id; + req2.peer.access_hash = chat.access_hash; + reqToSend = req2; + } } else { TLRPC.TL_messages_sendEncryptedFile req2 = new TLRPC.TL_messages_sendEncryptedFile(); req2.data = data; @@ -1219,28 +1276,47 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter ConnectionsManager.getInstance().performRpc(reqToSend, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { - if (error == null && callback != null) { - callback.run(); + if (error == null) { + if (req.action instanceof TLRPC.TL_decryptedMessageActionNotifyLayer) { + AndroidUtilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + TLRPC.EncryptedChat currentChat = MessagesController.getInstance().getEncryptedChat(chat.id); + sendingNotifyLayer.remove((Integer)currentChat.id); + currentChat.layer = AndroidUtilities.setMyLayerVersion(currentChat.layer, CURRENT_SECRET_CHAT_LAYER); + MessagesStorage.getInstance().updateEncryptedChatLayer(currentChat); + } + }); + } } if (newMsgObj != null) { if (error == null) { - final String attachPath = newMsgObj.messageOwner.attachPath; + final String attachPath = newMsgObj.attachPath; final TLRPC.messages_SentEncryptedMessage res = (TLRPC.messages_SentEncryptedMessage) response; - newMsgObj.messageOwner.date = res.date; + if (newMsgObj.action instanceof TLRPC.TL_messageEncryptedAction) { + if (newMsgObj.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages || newMsgObj.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) { + newMsgObj.date = res.date; + } + } if (res.file instanceof TLRPC.TL_encryptedFile) { - processSentMessage(newMsgObj.messageOwner, null, res.file, req, originalPath); + processSentMessage(newMsgObj, null, res.file, req, originalPath); } MessagesStorage.getInstance().storageQueue.postRunnable(new Runnable() { @Override public void run() { - MessagesStorage.getInstance().updateMessageStateAndId(newMsgObj.messageOwner.random_id, newMsgObj.messageOwner.id, newMsgObj.messageOwner.id, res.date, false); + if (newMsgObj.action instanceof TLRPC.TL_messageEncryptedAction) { + if (!(newMsgObj.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages || newMsgObj.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL)) { + res.date = 0; + } + } + MessagesStorage.getInstance().updateMessageStateAndId(newMsgObj.random_id, newMsgObj.id, newMsgObj.id, res.date, false); AndroidUtilities.RunOnUIThread(new Runnable() { @Override public void run() { - newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; - NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageReceivedByServer, newMsgObj.messageOwner.id, newMsgObj.messageOwner.id, newMsgObj); - processSentMessage(newMsgObj.messageOwner.id); - if (newMsgObj.messageOwner.media instanceof TLRPC.TL_messageMediaVideo) { + newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; + NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageReceivedByServer, newMsgObj.id, newMsgObj.id, newMsgObj); + processSentMessage(newMsgObj.id); + if (newMsgObj.media instanceof TLRPC.TL_messageMediaVideo) { stopVideoService(attachPath); } } @@ -1248,15 +1324,15 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } }); } else { - MessagesStorage.getInstance().markMessageAsSendError(newMsgObj.messageOwner.id); + MessagesStorage.getInstance().markMessageAsSendError(newMsgObj.id); AndroidUtilities.RunOnUIThread(new Runnable() { @Override public void run() { - newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR; - NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageSendError, newMsgObj.messageOwner.id); - processSentMessage(newMsgObj.messageOwner.id); - if (newMsgObj.messageOwner.media instanceof TLRPC.TL_messageMediaVideo) { - stopVideoService(newMsgObj.messageOwner.attachPath); + newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR; + NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageSendError, newMsgObj.id); + processSentMessage(newMsgObj.id); + if (newMsgObj.media instanceof TLRPC.TL_messageMediaVideo) { + stopVideoService(newMsgObj.attachPath); } } }); @@ -1492,7 +1568,38 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } } - public void sendMessagesReadMessage(ArrayList random_ids, TLRPC.EncryptedChat encryptedChat) { + private TLRPC.TL_messageService createServiceSecretMessage(final TLRPC.EncryptedChat encryptedChat, TLRPC.DecryptedMessageAction decryptedMessage) { + TLRPC.TL_messageService newMsg = new TLRPC.TL_messageService(); + + newMsg.action = new TLRPC.TL_messageEncryptedAction(); + newMsg.action.encryptedAction = decryptedMessage; + newMsg.local_id = newMsg.id = UserConfig.getNewMessageId(); + newMsg.from_id = UserConfig.getClientUserId(); + newMsg.flags = TLRPC.MESSAGE_FLAG_UNREAD | TLRPC.MESSAGE_FLAG_OUT; + newMsg.dialog_id = ((long)encryptedChat.id) << 32; + newMsg.to_id = new TLRPC.TL_peerUser(); + newMsg.send_state = MessageObject.MESSAGE_SEND_STATE_SENDING; + if (encryptedChat.participant_id == UserConfig.getClientUserId()) { + newMsg.to_id.user_id = encryptedChat.admin_id; + } else { + newMsg.to_id.user_id = encryptedChat.participant_id; + } + if (decryptedMessage instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages || decryptedMessage instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) { + newMsg.date = ConnectionsManager.getInstance().getCurrentTime(); + } else { + newMsg.date = 0; + } + newMsg.random_id = getNextRandomId(); + UserConfig.saveConfig(false); + + ArrayList arr = new ArrayList(); + arr.add(newMsg); + MessagesStorage.getInstance().putMessages(arr, false, true, true, 0); + + return newMsg; + } + + public void sendMessagesReadMessage(TLRPC.EncryptedChat encryptedChat, ArrayList random_ids, TLRPC.Message resendMessage) { if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) { return; } @@ -1504,13 +1611,23 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))]; Utilities.random.nextBytes(reqSend.random_bytes); } - reqSend.random_id = getNextRandomId(); - reqSend.action = new TLRPC.TL_decryptedMessageActionReadMessages(); - reqSend.action.random_ids = random_ids; - performSendEncryptedRequest(reqSend, null, encryptedChat, null, null, null); + + TLRPC.Message message = null; + + if (resendMessage != null) { + message = resendMessage; + reqSend.action = message.action.encryptedAction; + } else { + reqSend.action = new TLRPC.TL_decryptedMessageActionReadMessages(); + reqSend.action.random_ids = random_ids; + message = createServiceSecretMessage(encryptedChat, reqSend.action); + } + reqSend.random_id = message.random_id; + + performSendEncryptedRequest(reqSend, message, encryptedChat, null, null); } - public void sendMessagesDeleteMessage(ArrayList random_ids, TLRPC.EncryptedChat encryptedChat) { + public void sendMessagesDeleteMessage(TLRPC.EncryptedChat encryptedChat, ArrayList random_ids, TLRPC.Message resendMessage) { if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) { return; } @@ -1522,13 +1639,23 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))]; Utilities.random.nextBytes(reqSend.random_bytes); } - reqSend.random_id = getNextRandomId(); - reqSend.action = new TLRPC.TL_decryptedMessageActionDeleteMessages(); - reqSend.action.random_ids = random_ids; - performSendEncryptedRequest(reqSend, null, encryptedChat, null, null, null); + + TLRPC.Message message = null; + + if (resendMessage != null) { + message = resendMessage; + reqSend.action = message.action.encryptedAction; + } else { + reqSend.action = new TLRPC.TL_decryptedMessageActionDeleteMessages(); + reqSend.action.random_ids = random_ids; + message = createServiceSecretMessage(encryptedChat, reqSend.action); + } + reqSend.random_id = message.random_id; + + performSendEncryptedRequest(reqSend, message, encryptedChat, null, null); } - public void sendClearHistoryMessage(TLRPC.EncryptedChat encryptedChat) { + public void sendClearHistoryMessage(TLRPC.EncryptedChat encryptedChat, TLRPC.Message resendMessage) { if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) { return; } @@ -1540,12 +1667,22 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))]; Utilities.random.nextBytes(reqSend.random_bytes); } - reqSend.random_id = getNextRandomId(); - reqSend.action = new TLRPC.TL_decryptedMessageActionFlushHistory(); - performSendEncryptedRequest(reqSend, null, encryptedChat, null, null, null); + + TLRPC.Message message = null; + + if (resendMessage != null) { + message = resendMessage; + reqSend.action = message.action.encryptedAction; + } else { + reqSend.action = new TLRPC.TL_decryptedMessageActionFlushHistory(); + message = createServiceSecretMessage(encryptedChat, reqSend.action); + } + reqSend.random_id = message.random_id; + + performSendEncryptedRequest(reqSend, message, encryptedChat, null, null); } - public void sendNotifyLayerMessage(final TLRPC.EncryptedChat encryptedChat) { + public void sendNotifyLayerMessage(final TLRPC.EncryptedChat encryptedChat, TLRPC.Message resendMessage) { if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) { return; } @@ -1561,57 +1698,26 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))]; Utilities.random.nextBytes(reqSend.random_bytes); } - reqSend.random_id = getNextRandomId(); - reqSend.action = new TLRPC.TL_decryptedMessageActionNotifyLayer(); - reqSend.action.layer = CURRENT_SECRET_CHAT_LAYER; - Runnable callback = new Runnable() { - @Override - public void run() { - AndroidUtilities.RunOnUIThread(new Runnable() { - @Override - public void run() { - TLRPC.EncryptedChat chat = MessagesController.getInstance().getEncryptedChat(encryptedChat.id); - sendingNotifyLayer.remove((Integer)chat.id); - chat.layer = AndroidUtilities.setMyLayerVersion(chat.layer, CURRENT_SECRET_CHAT_LAYER); - MessagesStorage.getInstance().updateEncryptedChatLayer(chat); - } - }); - } - }; - performSendEncryptedRequest(reqSend, null, encryptedChat, null, null, callback); + + TLRPC.Message message = null; + + if (resendMessage != null) { + message = resendMessage; + reqSend.action = message.action.encryptedAction; + } else { + reqSend.action = new TLRPC.TL_decryptedMessageActionNotifyLayer(); + reqSend.action.layer = CURRENT_SECRET_CHAT_LAYER; + message = createServiceSecretMessage(encryptedChat, reqSend.action); + } + reqSend.random_id = message.random_id; + + performSendEncryptedRequest(reqSend, message, encryptedChat, null, null); } - public void sendTTLMessage(TLRPC.EncryptedChat encryptedChat) { + public void sendTTLMessage(TLRPC.EncryptedChat encryptedChat, TLRPC.Message resendMessage) { if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) { return; } - TLRPC.TL_messageService newMsg = new TLRPC.TL_messageService(); - - newMsg.action = new TLRPC.TL_messageActionTTLChange(); - newMsg.action.ttl = encryptedChat.ttl; - newMsg.local_id = newMsg.id = UserConfig.getNewMessageId(); - newMsg.from_id = UserConfig.getClientUserId(); - newMsg.flags = TLRPC.MESSAGE_FLAG_UNREAD | TLRPC.MESSAGE_FLAG_OUT; - newMsg.dialog_id = ((long)encryptedChat.id) << 32; - newMsg.to_id = new TLRPC.TL_peerUser(); - if (encryptedChat.participant_id == UserConfig.getClientUserId()) { - newMsg.to_id.user_id = encryptedChat.admin_id; - } else { - newMsg.to_id.user_id = encryptedChat.participant_id; - } - newMsg.date = ConnectionsManager.getInstance().getCurrentTime(); - newMsg.random_id = getNextRandomId(); - UserConfig.saveConfig(false); - final MessageObject newMsgObj = new MessageObject(newMsg, null); - newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENDING; - - final ArrayList objArr = new ArrayList(); - objArr.add(newMsgObj); - ArrayList arr = new ArrayList(); - arr.add(newMsg); - MessagesStorage.getInstance().putMessages(arr, false, true, false, 0); - MessagesController.getInstance().updateInterfaceWithMessages(newMsg.dialog_id, objArr); - NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); TLRPC.TL_decryptedMessageService reqSend = null; if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { @@ -1621,49 +1727,34 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))]; Utilities.random.nextBytes(reqSend.random_bytes); } - reqSend.random_id = newMsg.random_id; - reqSend.action = new TLRPC.TL_decryptedMessageActionSetMessageTTL(); - reqSend.action.ttl_seconds = encryptedChat.ttl; - performSendEncryptedRequest(reqSend, newMsgObj, encryptedChat, null, null, null); + + TLRPC.Message message = null; + + if (resendMessage != null) { + message = resendMessage; + reqSend.action = message.action.encryptedAction; + } else { + reqSend.action = new TLRPC.TL_decryptedMessageActionSetMessageTTL(); + reqSend.action.ttl_seconds = encryptedChat.ttl; + message = createServiceSecretMessage(encryptedChat, reqSend.action); + + MessageObject newMsgObj = new MessageObject(message, null); + newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENDING; + ArrayList objArr = new ArrayList(); + objArr.add(newMsgObj); + MessagesController.getInstance().updateInterfaceWithMessages(message.dialog_id, objArr); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); + } + reqSend.random_id = message.random_id; + + performSendEncryptedRequest(reqSend, message, encryptedChat, null, null); } - public void sendScreenshotMessage(TLRPC.EncryptedChat encryptedChat, ArrayList random_ids) { + public void sendScreenshotMessage(TLRPC.EncryptedChat encryptedChat, ArrayList random_ids, TLRPC.Message resendMessage) { if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) { return; } - TLRPC.TL_decryptedMessageActionScreenshotMessages action = new TLRPC.TL_decryptedMessageActionScreenshotMessages(); - action.random_ids = random_ids; - - TLRPC.TL_messageService newMsg = new TLRPC.TL_messageService(); - - newMsg.action = new TLRPC.TL_messageEcryptedAction(); - newMsg.action.encryptedAction = action; - - newMsg.local_id = newMsg.id = UserConfig.getNewMessageId(); - newMsg.from_id = UserConfig.getClientUserId(); - newMsg.flags = TLRPC.MESSAGE_FLAG_UNREAD | TLRPC.MESSAGE_FLAG_OUT; - newMsg.dialog_id = ((long)encryptedChat.id) << 32; - newMsg.to_id = new TLRPC.TL_peerUser(); - if (encryptedChat.participant_id == UserConfig.getClientUserId()) { - newMsg.to_id.user_id = encryptedChat.admin_id; - } else { - newMsg.to_id.user_id = encryptedChat.participant_id; - } - newMsg.date = ConnectionsManager.getInstance().getCurrentTime(); - newMsg.random_id = getNextRandomId(); - UserConfig.saveConfig(false); - final MessageObject newMsgObj = new MessageObject(newMsg, null); - newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENDING; - - final ArrayList objArr = new ArrayList(); - objArr.add(newMsgObj); - ArrayList arr = new ArrayList(); - arr.add(newMsg); - MessagesStorage.getInstance().putMessages(arr, false, true, false, 0); - MessagesController.getInstance().updateInterfaceWithMessages(newMsg.dialog_id, objArr); - NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); - TLRPC.TL_decryptedMessageService reqSend = null; if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) { reqSend = new TLRPC.TL_decryptedMessageService(); @@ -1672,9 +1763,27 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter reqSend.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))]; Utilities.random.nextBytes(reqSend.random_bytes); } - reqSend.random_id = newMsg.random_id; - reqSend.action = action; - performSendEncryptedRequest(reqSend, newMsgObj, encryptedChat, null, null, null); + + TLRPC.Message message = null; + + if (resendMessage != null) { + message = resendMessage; + reqSend.action = message.action.encryptedAction; + } else { + reqSend.action = new TLRPC.TL_decryptedMessageActionScreenshotMessages(); + reqSend.action.random_ids = random_ids; + message = createServiceSecretMessage(encryptedChat, reqSend.action); + + MessageObject newMsgObj = new MessageObject(message, null); + newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENDING; + ArrayList objArr = new ArrayList(); + objArr.add(newMsgObj); + MessagesController.getInstance().updateInterfaceWithMessages(message.dialog_id, objArr); + NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload); + } + reqSend.random_id = message.random_id; + + performSendEncryptedRequest(reqSend, message, encryptedChat, null, null); } private void putToDelayedMessages(String location, DelayedMessage message) { @@ -1695,7 +1804,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } public void checkUnsentMessages() { - MessagesStorage.getInstance().getUnsentMessages(10); + MessagesStorage.getInstance().getUnsentMessages(1000); } protected void processUnsentMessages(final ArrayList messages, final ArrayList users, final ArrayList chats, final ArrayList encryptedChats) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java index be06766b1..0a134c094 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java @@ -704,4 +704,26 @@ public class FileLoader { } return ""; } + + public void deleteFiles(final ArrayList files) { + if (files == null || files.isEmpty()) { + return; + } + fileLoaderQueue.postRunnable(new Runnable() { + @Override + public void run() { + for (File file : files) { + if (file.exists()) { + try { + if (!file.delete()) { + file.deleteOnExit(); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + } + }); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java b/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java index 67a050c2b..aa690ca1e 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java @@ -370,7 +370,7 @@ public class TLClassStore { classStore.put(TLRPC.TL_userRequest_old.constructor, TLRPC.TL_userRequest_old.class); classStore.put(TLRPC.TL_userForeign_old.constructor, TLRPC.TL_userForeign_old.class); classStore.put(TLRPC.TL_userDeleted_old.constructor, TLRPC.TL_userDeleted_old.class); - classStore.put(TLRPC.TL_messageEcryptedAction.constructor, TLRPC.TL_messageEcryptedAction.class); + classStore.put(TLRPC.TL_messageEncryptedAction.constructor, TLRPC.TL_messageEncryptedAction.class); } static TLClassStore store = null; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java b/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java index b358aca16..af8559cb4 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java @@ -8429,33 +8429,38 @@ public class TLRPC { } } + //manually created + public static class TL_messages_sendEncryptedService extends TLObject { public static int constructor = 0x32d439a4; public TL_inputEncryptedChat peer; public long random_id; - public byte[] data; + public ByteBufferDesc data; public Class responseClass () { return messages_SentEncryptedMessage.class; } - public void readParams(AbsSerializedData stream) { - peer = (TL_inputEncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - random_id = stream.readInt64(); - data = stream.readByteArray(); - } - public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeInt64(random_id); - stream.writeByteArray(data); + stream.writeByteBuffer(data); + } + + @Override + public void freeResources() { + if (disableFree) { + return; + } + if (data != null) { + BuffersStorage.getInstance().reuseFreeBuffer(data); + data = null; + } } } - //manually created - public static class TL_userDeleted_old extends TL_userDeleted { public static int constructor = 0xb29ad7cc; @@ -8990,6 +8995,8 @@ public class TLRPC { public int ttl; public int destroyTime; public int layer; + public int seq_in; + public int seq_out; public VideoEditedInfo videoEditedInfo = null; } @@ -9993,7 +10000,7 @@ public class TLRPC { } } - public static class TL_messageEcryptedAction extends MessageAction { + public static class TL_messageEncryptedAction extends MessageAction { public static int constructor = 0x555555F7; public void readParams(AbsSerializedData stream) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java index 7a9e7bbc8..a28e59ba2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -43,36 +43,41 @@ public class ChatMessageCell extends ChatBaseCell { y -= textY; int blockNum = Math.max(0, y / currentMessageObject.blockHeight); if (blockNum < currentMessageObject.textLayoutBlocks.size()) { - MessageObject.TextLayoutBlock block = currentMessageObject.textLayoutBlocks.get(blockNum); - x -= textX - (int)Math.ceil(block.textXOffset); - y -= block.textYOffset; - final int line = block.textLayout.getLineForVertical(y); - final int off = block.textLayout.getOffsetForHorizontal(line, x) + block.charactersOffset; + try { + MessageObject.TextLayoutBlock block = currentMessageObject.textLayoutBlocks.get(blockNum); + x -= textX - (int)Math.ceil(block.textXOffset); + y -= block.textYOffset; + final int line = block.textLayout.getLineForVertical(y); + final int off = block.textLayout.getOffsetForHorizontal(line, x) + block.charactersOffset; - final float left = block.textLayout.getLineLeft(line); - if (left <= x && left + block.textLayout.getLineWidth(line) >= x) { - Spannable buffer = (Spannable)currentMessageObject.messageText; - ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); + final float left = block.textLayout.getLineLeft(line); + if (left <= x && left + block.textLayout.getLineWidth(line) >= x) { + Spannable buffer = (Spannable)currentMessageObject.messageText; + ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); - if (link.length != 0) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - pressedLink = link[0]; - return true; - } else { - if (link[0] == pressedLink) { - try { - pressedLink.onClick(this); - } catch (Exception e) { - FileLog.e("tmessages", e); - } + if (link.length != 0) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + pressedLink = link[0]; return true; + } else { + if (link[0] == pressedLink) { + try { + pressedLink.onClick(this); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return true; + } } + } else { + pressedLink = null; } } else { pressedLink = null; } - } else { + } catch (Exception e) { pressedLink = null; + FileLog.e("tmessages", e); } } else { pressedLink = null; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index 7d5074797..e5918f4bc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -387,7 +387,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not typingDotsDrawable.setIsChat(currentChat != null); if (currentEncryptedChat != null && AndroidUtilities.getMyLayerVersion(currentEncryptedChat.layer) != SendMessagesHelper.CURRENT_SECRET_CHAT_LAYER) { - SendMessagesHelper.getInstance().sendNotifyLayerMessage(currentEncryptedChat); + SendMessagesHelper.getInstance().sendNotifyLayerMessage(currentEncryptedChat, null); } return true; @@ -651,7 +651,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not actionModeViews.clear(); - ActionBarMenu actionMode = actionBarLayer.createActionMode(); + final ActionBarMenu actionMode = actionBarLayer.createActionMode(); actionModeViews.add(actionMode.addItem(-2, R.drawable.ic_ab_done_gray, R.drawable.bar_selector_mode)); FrameLayout layout = new FrameLayout(actionMode.getContext()); @@ -822,6 +822,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not chatListView.setOnInterceptTouchEventListener(new LayoutListView.OnInterceptTouchEventListener() { @Override public boolean onInterceptTouchEvent(MotionEvent event) { + if (actionBarLayer.isActionModeShowed()) { + return false; + } if (event.getAction() == MotionEvent.ACTION_DOWN) { int x = (int)event.getX(); int y = (int)event.getY(); @@ -880,14 +883,14 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public boolean onTouch(View v, MotionEvent event) { if (openSecretPhotoRunnable != null || SecretPhotoViewer.getInstance().isVisible()) { if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_POINTER_UP) { + AndroidUtilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + chatListView.setOnItemClickListener(onItemClickListener); + } + }, 150); if (openSecretPhotoRunnable != null) { AndroidUtilities.CancelRunOnUIThread(openSecretPhotoRunnable); - AndroidUtilities.RunOnUIThread(new Runnable() { - @Override - public void run() { - chatListView.setOnItemClickListener(onItemClickListener); - } - }, 150); openSecretPhotoRunnable = null; try { Toast.makeText(v.getContext(), LocaleController.getString("PhotoTip", R.string.PhotoTip), Toast.LENGTH_SHORT).show(); @@ -899,14 +902,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not AndroidUtilities.RunOnUIThread(new Runnable() { @Override public void run() { - chatListView.setOnItemClickListener(onItemClickListener); chatListView.setOnItemLongClickListener(onItemLongClickListener); chatListView.setLongClickable(true); } }); SecretPhotoViewer.getInstance().closePhoto(); - } else { - chatListView.setOnItemClickListener(onItemClickListener); } } } else if (event.getAction() != MotionEvent.ACTION_DOWN) { @@ -1043,7 +1043,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (messageObject == null || messageObject.isOut() || !messageObject.isSecretMedia() || messageObject.messageOwner.destroyTime != 0) { return false; } - MessagesController.getInstance().markMessageAsRead(dialog_id, messageObject.messageOwner.random_id); + MessagesController.getInstance().markMessageAsRead(dialog_id, messageObject.messageOwner.random_id, messageObject.messageOwner.ttl); messageObject.messageOwner.destroyTime = messageObject.messageOwner.ttl + ConnectionsManager.getInstance().getCurrentTime(); return true; } @@ -1290,11 +1290,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else { return 6; } - } else if (messageObject.type == 10 || messageObject.type == 11 || messageObject.isSending()) { - if (messageObject.messageOwner.id == 0) { + } else if (messageObject.type == 10 || messageObject.type == 11) { + if (messageObject.isSending()) { return -1; + } else { + return 1; } - return 1; } else { if (!(messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaEmpty)) { if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVideo || @@ -1502,10 +1503,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not SendMessagesHelper.prepareSendingVideo(videoPath, estimatedSize, estimatedDuration, resultWidth, resultHeight, videoEditedInfo, dialog_id); } }); - if (parentLayout == null || !parentLayout.presentFragment(fragment, removeLast, true, true)) { + + if (parentLayout == null || !fragment.onFragmentCreate()) { SendMessagesHelper.prepareSendingVideo(videoPath, 0, 0, 0, 0, null, dialog_id); return false; } + parentLayout.presentFragment(fragment, removeLast, true, true); return true; } @@ -1686,6 +1689,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (minDate == 0 || obj.messageOwner.date < minDate) { minDate = obj.messageOwner.date; } + + if (obj.type < 0) { + continue; + } + if (!obj.isOut() && obj.isUnread()) { wasUnread = true; } @@ -1876,14 +1884,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ArrayList arr = (ArrayList)args[1]; if (currentEncryptedChat != null && arr.size() == 1) { - MessageObject messageObject = arr.get(0); + MessageObject obj = arr.get(0); - if (messageObject.isOut() && messageObject.messageOwner.action instanceof TLRPC.TL_messageActionTTLChange && getParentActivity() != null && AndroidUtilities.getPeerLayerVersion(currentEncryptedChat.layer) < 17 && currentEncryptedChat.ttl > 0 && currentEncryptedChat.ttl <= 60) { - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); - builder.setPositiveButton(R.string.OK, null); - builder.setMessage(LocaleController.formatString("CompatibilityChat", R.string.CompatibilityChat, currentUser.first_name, currentUser.first_name)); - showAlertDialog(builder); + if (currentEncryptedChat != null && obj.isOut() && obj.messageOwner.action != null && obj.messageOwner.action instanceof TLRPC.TL_messageEncryptedAction && + obj.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL && getParentActivity() != null) { + TLRPC.TL_decryptedMessageActionSetMessageTTL action = (TLRPC.TL_decryptedMessageActionSetMessageTTL)obj.messageOwner.action.encryptedAction; + if (AndroidUtilities.getPeerLayerVersion(currentEncryptedChat.layer) < 17 && currentEncryptedChat.ttl > 0 && currentEncryptedChat.ttl <= 60) { + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + builder.setPositiveButton(R.string.OK, null); + builder.setMessage(LocaleController.formatString("CompatibilityChat", R.string.CompatibilityChat, currentUser.first_name, currentUser.first_name)); + showAlertDialog(builder); + } } } @@ -1896,8 +1908,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not boolean currentMarkAsRead = false; for (MessageObject obj : arr) { - if (currentEncryptedChat != null && obj.messageOwner.action != null && obj.messageOwner.action instanceof TLRPC.TL_messageActionTTLChange && timerButton != null) { - timerButton.setTime(obj.messageOwner.action.ttl); + if (currentEncryptedChat != null && obj.messageOwner.action != null && obj.messageOwner.action instanceof TLRPC.TL_messageEncryptedAction && + obj.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL && timerButton != null) { + TLRPC.TL_decryptedMessageActionSetMessageTTL action = (TLRPC.TL_decryptedMessageActionSetMessageTTL)obj.messageOwner.action.encryptedAction; + timerButton.setTime(action.ttl_seconds); } if (obj.isOut() && obj.isSending()) { scrollToLastMessage(); @@ -1938,8 +1952,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not boolean markAsRead = false; int oldCount = messages.size(); for (MessageObject obj : arr) { - if (currentEncryptedChat != null && obj.messageOwner.action != null && obj.messageOwner.action instanceof TLRPC.TL_messageActionTTLChange && timerButton != null) { - timerButton.setTime(obj.messageOwner.action.ttl); + if (currentEncryptedChat != null && obj.messageOwner.action != null && obj.messageOwner.action instanceof TLRPC.TL_messageEncryptedAction && + obj.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL && timerButton != null) { + TLRPC.TL_decryptedMessageActionSetMessageTTL action = (TLRPC.TL_decryptedMessageActionSetMessageTTL)obj.messageOwner.action.encryptedAction; + timerButton.setTime(action.ttl_seconds); } if (messagesDict.containsKey(obj.messageOwner.id)) { continue; @@ -2108,9 +2124,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not MessageObject obj = messagesDict.get(msgId); if (obj != null) { Integer newMsgId = (Integer)args[1]; - MessageObject newMsgObj = (MessageObject)args[2]; + TLRPC.Message newMsgObj = (TLRPC.Message)args[2]; if (newMsgObj != null) { - obj.messageOwner.media = newMsgObj.messageOwner.media; + obj.messageOwner.media = newMsgObj.media; obj.generateThumbs(true, 1); } messagesDict.remove(msgId); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java index 3e70e6b9c..a0a349618 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java @@ -657,7 +657,16 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (obj.isSent()) { ArrayList arr = new ArrayList(); arr.add(obj.messageOwner.id); - MessagesController.getInstance().deleteMessages(arr, null, null); + + ArrayList random_ids = null; + TLRPC.EncryptedChat encryptedChat = null; + if ((int)obj.getDialogId() == 0 && obj.messageOwner.random_id != 0) { + random_ids = new ArrayList(); + random_ids.add(obj.messageOwner.random_id); + encryptedChat = MessagesController.getInstance().getEncryptedChat((int)(obj.getDialogId() >> 32)); + } + + MessagesController.getInstance().deleteMessages(arr, random_ids, encryptedChat); closePhoto(false); } } else if (!avatarsArr.isEmpty()) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsChangeUsernameActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsChangeUsernameActivity.java index f6cb32d16..9a4177047 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsChangeUsernameActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsChangeUsernameActivity.java @@ -71,9 +71,7 @@ public class SettingsChangeUsernameActivity extends BaseFragment { doneButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - if (firstNameField.getText().length() != 0) { - saveName(); - } + saveName(); } }); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/VideoEditorActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/VideoEditorActivity.java index 1de738432..6a9c7027c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/VideoEditorActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/VideoEditorActivity.java @@ -58,6 +58,7 @@ import java.util.List; @TargetApi(16) public class VideoEditorActivity extends BaseFragment implements TextureView.SurfaceTextureListener { + private boolean created = false; private MediaPlayer videoPlayer = null; private VideoTimelineView videoTimelineView = null; private View videoContainerView = null; @@ -161,6 +162,9 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur @Override public boolean onFragmentCreate() { + if (created) { + return true; + } if (videoPath == null || !processOpenVideo()) { return false; } @@ -191,6 +195,8 @@ public class VideoEditorActivity extends BaseFragment implements TextureView.Sur return false; } + created = true; + return super.onFragmentCreate(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/NumberPicker.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/NumberPicker.java index e91a71138..bd5e23b29 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/NumberPicker.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/NumberPicker.java @@ -36,7 +36,6 @@ import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.widget.LinearLayout; -import android.widget.Scroller; import android.widget.TextView; import org.telegram.messenger.R; @@ -726,6 +725,28 @@ public class NumberPicker extends LinearLayout { } } + public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { + int result = size; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + switch (specMode) { + case MeasureSpec.UNSPECIFIED: + result = size; + break; + case MeasureSpec.AT_MOST: + if (specSize < size) { + result = specSize | 16777216; + } else { + result = size; + } + break; + case MeasureSpec.EXACTLY: + result = specSize; + break; + } + return result | (childMeasuredState & (-16777216)); + } + private void initializeSelectorWheelIndices() { mSelectorIndexToStringCache.clear(); int[] selectorIndices = mSelectorIndices; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/Scroller.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/Scroller.java new file mode 100644 index 000000000..b87b9b7cb --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/Scroller.java @@ -0,0 +1,502 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.telegram.ui.Views; + +import android.content.Context; +import android.hardware.SensorManager; +import android.util.FloatMath; +import android.view.ViewConfiguration; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; + + +/** + * This class encapsulates scrolling. The duration of the scroll + * can be passed in the constructor and specifies the maximum time that + * the scrolling animation should take. Past this time, the scrolling is + * automatically moved to its final stage and computeScrollOffset() + * will always return false to indicate that scrolling is over. + */ +public class Scroller { + private int mMode; + + private int mStartX; + private int mStartY; + private int mFinalX; + private int mFinalY; + + private int mMinX; + private int mMaxX; + private int mMinY; + private int mMaxY; + + private int mCurrX; + private int mCurrY; + private long mStartTime; + private int mDuration; + private float mDurationReciprocal; + private float mDeltaX; + private float mDeltaY; + private boolean mFinished; + private Interpolator mInterpolator; + private boolean mFlywheel; + + private float mVelocity; + + private static final int DEFAULT_DURATION = 250; + private static final int SCROLL_MODE = 0; + private static final int FLING_MODE = 1; + + private static float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9)); + private static float START_TENSION = 0.4f; // Tension at start: (0.4 * total T, 1.0 * Distance) + private static float END_TENSION = 1.0f - START_TENSION; + private static final int NB_SAMPLES = 100; + private static final float[] SPLINE = new float[NB_SAMPLES + 1]; + + private float mDeceleration; + private final float mPpi; + + static { + float x_min = 0.0f; + for (int i = 0; i <= NB_SAMPLES; i++) { + final float t = (float) i / NB_SAMPLES; + float x_max = 1.0f; + float x, tx, coef; + while (true) { + x = x_min + (x_max - x_min) / 2.0f; + coef = 3.0f * x * (1.0f - x); + tx = coef * ((1.0f - x) * START_TENSION + x * END_TENSION) + x * x * x; + if (Math.abs(tx - t) < 1E-5) break; + if (tx > t) x_max = x; + else x_min = x; + } + final float d = coef + x * x * x; + SPLINE[i] = d; + } + SPLINE[NB_SAMPLES] = 1.0f; + + // This controls the viscous fluid effect (how much of it) + sViscousFluidScale = 8.0f; + // must be set to 1.0 (used in viscousFluid()) + sViscousFluidNormalize = 1.0f; + sViscousFluidNormalize = 1.0f / viscousFluid(1.0f); + } + + private static float sViscousFluidScale; + private static float sViscousFluidNormalize; + + /** + * Create a Scroller with the default duration and interpolator. + */ + public Scroller(Context context) { + this(context, null); + } + + /** + * Create a Scroller with the specified interpolator. If the interpolator is + * null, the default (viscous) interpolator will be used. "Flywheel" behavior will + * be in effect for apps targeting Honeycomb or newer. + */ + public Scroller(Context context, Interpolator interpolator) { + this(context, interpolator, true); + } + + /** + * Create a Scroller with the specified interpolator. If the interpolator is + * null, the default (viscous) interpolator will be used. Specify whether or + * not to support progressive "flywheel" behavior in flinging. + */ + public Scroller(Context context, Interpolator interpolator, boolean flywheel) { + mFinished = true; + mInterpolator = interpolator; + mPpi = context.getResources().getDisplayMetrics().density * 160.0f; + mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction()); + mFlywheel = flywheel; + } + + /** + * The amount of friction applied to flings. The default value + * is {@link android.view.ViewConfiguration#getScrollFriction}. + * + * @param friction A scalar dimension-less value representing the coefficient of + * friction. + */ + public final void setFriction(float friction) { + mDeceleration = computeDeceleration(friction); + } + + private float computeDeceleration(float friction) { + return SensorManager.GRAVITY_EARTH // g (m/s^2) + * 39.37f // inch/meter + * mPpi // pixels per inch + * friction; + } + + /** + * + * Returns whether the scroller has finished scrolling. + * + * @return True if the scroller has finished scrolling, false otherwise. + */ + public final boolean isFinished() { + return mFinished; + } + + /** + * Force the finished field to a particular value. + * + * @param finished The new finished value. + */ + public final void forceFinished(boolean finished) { + mFinished = finished; + } + + /** + * Returns how long the scroll event will take, in milliseconds. + * + * @return The duration of the scroll in milliseconds. + */ + public final int getDuration() { + return mDuration; + } + + /** + * Returns the current X offset in the scroll. + * + * @return The new X offset as an absolute distance from the origin. + */ + public final int getCurrX() { + return mCurrX; + } + + /** + * Returns the current Y offset in the scroll. + * + * @return The new Y offset as an absolute distance from the origin. + */ + public final int getCurrY() { + return mCurrY; + } + + /** + * Returns the current velocity. + * + * @return The original velocity less the deceleration. Result may be + * negative. + */ + public float getCurrVelocity() { + return mVelocity - mDeceleration * timePassed() / 2000.0f; + } + + /** + * Returns the start X offset in the scroll. + * + * @return The start X offset as an absolute distance from the origin. + */ + public final int getStartX() { + return mStartX; + } + + /** + * Returns the start Y offset in the scroll. + * + * @return The start Y offset as an absolute distance from the origin. + */ + public final int getStartY() { + return mStartY; + } + + /** + * Returns where the scroll will end. Valid only for "fling" scrolls. + * + * @return The final X offset as an absolute distance from the origin. + */ + public final int getFinalX() { + return mFinalX; + } + + /** + * Returns where the scroll will end. Valid only for "fling" scrolls. + * + * @return The final Y offset as an absolute distance from the origin. + */ + public final int getFinalY() { + return mFinalY; + } + + /** + * Call this when you want to know the new location. If it returns true, + * the animation is not yet finished. loc will be altered to provide the + * new location. + */ + public boolean computeScrollOffset() { + if (mFinished) { + return false; + } + + int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); + + if (timePassed < mDuration) { + switch (mMode) { + case SCROLL_MODE: + float x = timePassed * mDurationReciprocal; + + if (mInterpolator == null) + x = viscousFluid(x); + else + x = mInterpolator.getInterpolation(x); + + mCurrX = mStartX + Math.round(x * mDeltaX); + mCurrY = mStartY + Math.round(x * mDeltaY); + break; + case FLING_MODE: + final float t = (float) timePassed / mDuration; + final int index = (int) (NB_SAMPLES * t); + final float t_inf = (float) index / NB_SAMPLES; + final float t_sup = (float) (index + 1) / NB_SAMPLES; + final float d_inf = SPLINE[index]; + final float d_sup = SPLINE[index + 1]; + final float distanceCoef = d_inf + (t - t_inf) / (t_sup - t_inf) * (d_sup - d_inf); + + mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX)); + // Pin to mMinX <= mCurrX <= mMaxX + mCurrX = Math.min(mCurrX, mMaxX); + mCurrX = Math.max(mCurrX, mMinX); + + mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY)); + // Pin to mMinY <= mCurrY <= mMaxY + mCurrY = Math.min(mCurrY, mMaxY); + mCurrY = Math.max(mCurrY, mMinY); + + if (mCurrX == mFinalX && mCurrY == mFinalY) { + mFinished = true; + } + + break; + } + } + else { + mCurrX = mFinalX; + mCurrY = mFinalY; + mFinished = true; + } + return true; + } + + /** + * Start scrolling by providing a starting point and the distance to travel. + * The scroll will use the default value of 250 milliseconds for the + * duration. + * + * @param startX Starting horizontal scroll offset in pixels. Positive + * numbers will scroll the content to the left. + * @param startY Starting vertical scroll offset in pixels. Positive numbers + * will scroll the content up. + * @param dx Horizontal distance to travel. Positive numbers will scroll the + * content to the left. + * @param dy Vertical distance to travel. Positive numbers will scroll the + * content up. + */ + public void startScroll(int startX, int startY, int dx, int dy) { + startScroll(startX, startY, dx, dy, DEFAULT_DURATION); + } + + /** + * Start scrolling by providing a starting point and the distance to travel. + * + * @param startX Starting horizontal scroll offset in pixels. Positive + * numbers will scroll the content to the left. + * @param startY Starting vertical scroll offset in pixels. Positive numbers + * will scroll the content up. + * @param dx Horizontal distance to travel. Positive numbers will scroll the + * content to the left. + * @param dy Vertical distance to travel. Positive numbers will scroll the + * content up. + * @param duration Duration of the scroll in milliseconds. + */ + public void startScroll(int startX, int startY, int dx, int dy, int duration) { + mMode = SCROLL_MODE; + mFinished = false; + mDuration = duration; + mStartTime = AnimationUtils.currentAnimationTimeMillis(); + mStartX = startX; + mStartY = startY; + mFinalX = startX + dx; + mFinalY = startY + dy; + mDeltaX = dx; + mDeltaY = dy; + mDurationReciprocal = 1.0f / (float) mDuration; + } + + /** + * Start scrolling based on a fling gesture. The distance travelled will + * depend on the initial velocity of the fling. + * + * @param startX Starting point of the scroll (X) + * @param startY Starting point of the scroll (Y) + * @param velocityX Initial velocity of the fling (X) measured in pixels per + * second. + * @param velocityY Initial velocity of the fling (Y) measured in pixels per + * second + * @param minX Minimum X value. The scroller will not scroll past this + * point. + * @param maxX Maximum X value. The scroller will not scroll past this + * point. + * @param minY Minimum Y value. The scroller will not scroll past this + * point. + * @param maxY Maximum Y value. The scroller will not scroll past this + * point. + */ + public void fling(int startX, int startY, int velocityX, int velocityY, + int minX, int maxX, int minY, int maxY) { + // Continue a scroll or fling in progress + if (mFlywheel && !mFinished) { + float oldVel = getCurrVelocity(); + + float dx = (float) (mFinalX - mStartX); + float dy = (float) (mFinalY - mStartY); + float hyp = FloatMath.sqrt(dx * dx + dy * dy); + + float ndx = dx / hyp; + float ndy = dy / hyp; + + float oldVelocityX = ndx * oldVel; + float oldVelocityY = ndy * oldVel; + if (Math.signum(velocityX) == Math.signum(oldVelocityX) && + Math.signum(velocityY) == Math.signum(oldVelocityY)) { + velocityX += oldVelocityX; + velocityY += oldVelocityY; + } + } + + mMode = FLING_MODE; + mFinished = false; + + float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY); + + mVelocity = velocity; + float ALPHA = 800; + final double l = Math.log(START_TENSION * velocity / ALPHA); + mDuration = (int) (1000.0 * Math.exp(l / (DECELERATION_RATE - 1.0))); + mStartTime = AnimationUtils.currentAnimationTimeMillis(); + mStartX = startX; + mStartY = startY; + + float coeffX = velocity == 0 ? 1.0f : velocityX / velocity; + float coeffY = velocity == 0 ? 1.0f : velocityY / velocity; + + int totalDistance = + (int) (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l)); + + mMinX = minX; + mMaxX = maxX; + mMinY = minY; + mMaxY = maxY; + + mFinalX = startX + Math.round(totalDistance * coeffX); + // Pin to mMinX <= mFinalX <= mMaxX + mFinalX = Math.min(mFinalX, mMaxX); + mFinalX = Math.max(mFinalX, mMinX); + + mFinalY = startY + Math.round(totalDistance * coeffY); + // Pin to mMinY <= mFinalY <= mMaxY + mFinalY = Math.min(mFinalY, mMaxY); + mFinalY = Math.max(mFinalY, mMinY); + } + + static float viscousFluid(float x) + { + x *= sViscousFluidScale; + if (x < 1.0f) { + x -= (1.0f - (float)Math.exp(-x)); + } else { + float start = 0.36787944117f; // 1/e == exp(-1) + x = 1.0f - (float)Math.exp(1.0f - x); + x = start + x * (1.0f - start); + } + x *= sViscousFluidNormalize; + return x; + } + + /** + * Stops the animation. Contrary to {@link #forceFinished(boolean)}, + * aborting the animating cause the scroller to move to the final x and y + * position + * + * @see #forceFinished(boolean) + */ + public void abortAnimation() { + mCurrX = mFinalX; + mCurrY = mFinalY; + mFinished = true; + } + + /** + * Extend the scroll animation. This allows a running animation to scroll + * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}. + * + * @param extend Additional time to scroll in milliseconds. + * @see #setFinalX(int) + * @see #setFinalY(int) + */ + public void extendDuration(int extend) { + int passed = timePassed(); + mDuration = passed + extend; + mDurationReciprocal = 1.0f / mDuration; + mFinished = false; + } + + /** + * Returns the time elapsed since the beginning of the scrolling. + * + * @return The elapsed time in milliseconds. + */ + public int timePassed() { + return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); + } + + /** + * Sets the final position (X) for this scroller. + * + * @param newX The new X offset as an absolute distance from the origin. + * @see #extendDuration(int) + * @see #setFinalY(int) + */ + public void setFinalX(int newX) { + mFinalX = newX; + mDeltaX = mFinalX - mStartX; + mFinished = false; + } + + /** + * Sets the final position (Y) for this scroller. + * + * @param newY The new Y offset as an absolute distance from the origin. + * @see #extendDuration(int) + * @see #setFinalX(int) + */ + public void setFinalY(int newY) { + mFinalY = newY; + mDeltaY = mFinalY - mStartY; + mFinished = false; + } + + public boolean isScrollingInDirection(float xvel, float yvel) { + return !mFinished && Math.signum(xvel) == Math.signum(mFinalX - mStartX) && + Math.signum(yvel) == Math.signum(mFinalY - mStartY); + } +} diff --git a/TMessagesProj/src/main/res/values-ar/strings.xml b/TMessagesProj/src/main/res/values-ar/strings.xml index 9e24a7e9f..64851a0e8 100644 --- a/TMessagesProj/src/main/res/values-ar/strings.xml +++ b/TMessagesProj/src/main/res/values-ar/strings.xml @@ -51,6 +51,7 @@ حذف وخروج الاسم مخفي اختر محادثة + إضغط بإستمرار على المستخدم العرض %1$s يستخدم إصدار قديم من تيليجرام، لذلك، الصور السرية ستظهر في وضع الموافقة.\n\nعندما يقوم %2$s بتحديث تيليجرام، الصور التي بها عداد دقيقة أو أقل ستعمل بطريقة \"الاستمرار بالضغط للإستعراض\"، وسيتم إخبارك عندما يلتقط المستقبل صورة من شاشته. قائمة الرسالة الجماعية @@ -201,6 +202,7 @@ غير معروف معلومات هاتف + اسم مستخدم معرّفك المعذرة، اسم المستخدم تم اختياره مسبقًا. @@ -211,8 +213,8 @@ يمكنك اختيار اسم مستخدم في ]]>تيليجرام]]>. إذا قمت بذلك، سيستطيع الناس إيجادك باستخدام الاسم المستخدم والتواصل معك من دون معرفة رقمك.
]]>يمكنك استخدام ]]>حروف اللغة الإنجليزية]]>, ]]>وأرقامها]]> و كذلك الخط. لا بد من استخدام ]]>٥]]> حروف على الأقل.
جاري مراجعة اسم المستخدم... %1$s متاح. - None - An error occurred + لا يوجد + حدث خطأ. تم تعيين كافة الإشعارات افتراضيا حجم نص الرسائل @@ -384,6 +386,7 @@ أعد الإرسال باستخدام اسمي هل ترغب في إرسال رسالة إلى %1$s؟ ؟%1$s هل تريد إعادة توجيه الرسائل إلى + .Sorry, this feature is currently not available in your country تيليجرام سريع diff --git a/TMessagesProj/src/main/res/values-de/strings.xml b/TMessagesProj/src/main/res/values-de/strings.xml index 3fe8b822f..9d8888043 100644 --- a/TMessagesProj/src/main/res/values-de/strings.xml +++ b/TMessagesProj/src/main/res/values-de/strings.xml @@ -51,6 +51,7 @@ Löschen und beenden Versteckter Name Chat auswählen + Tippen und Halten %1$s benutzt eine ältere Version von Telegram, sodass Fotos in Geheimen Chats im Kompatibilitätsmodus angezeigt werden.\n\nSobald %2$s Telegram aktualisiert, werden Fotos mit Timern von 1 Minute und kürzer per \"Tippen und Halten\" angezeigt. Du wirst benachrichtigt, sobald dein Chatpartner ein Bildschirmfoto macht. Broadcast Liste @@ -201,6 +202,7 @@ Unbekannt INFO Telefon + Benutzername Dein Benutzername Leider ist dieser Benutzername vergeben. @@ -208,11 +210,11 @@ Ein Benutzername benötigt mindestens 5 Zeichen. Ein Benutzername darf maximal 32 Zeichen haben. Benutzernamen dürfen leider nicht mit einer Zahl anfangen. - Wähle einen Benutzernamen, wenn du von anderen bei]]>Telegram]]>gefunden werden willst – ohne, dass sie deine Nummer kennen müssen.
]]>Erlaubt sind ]]>a–z]]>, ]]>0–9]]> und Unterstriche. Die Mindestlänge beträgt ]]>5]]> Zeichen.
+ Wähle einen Benutzernamen, wenn du von anderen bei]]>Telegram]]>gefunden werden willst — ohne, dass sie deine Nummer kennen müssen.
]]>Erlaubt sind ]]>a-z]]>, ]]>0-9]]> und Unterstriche. Die Mindestlänge beträgt ]]>5]]> Zeichen.
Prüfe Benutzername... %1$s ist verfügbar. - None - An error occurred + Keiner + Es ist ein Fehler aufgetreten. Alle Einstellungen für Mitteilungen zurücksetzen Textgröße für Nachrichten @@ -384,6 +386,7 @@ mit meinem Namen weiterleiten Nachricht an %1$s senden? Weiterleiten an %1$s? + Sorry, this feature is currently not available in your country. Telegram Schnell diff --git a/TMessagesProj/src/main/res/values-es/strings.xml b/TMessagesProj/src/main/res/values-es/strings.xml index 2eecba922..ce8ca4e7f 100644 --- a/TMessagesProj/src/main/res/values-es/strings.xml +++ b/TMessagesProj/src/main/res/values-es/strings.xml @@ -22,10 +22,10 @@ ¿No recibiste el código? Tu nombre - Ingresa tu nombre y apellido + Ingresa tu nombre y apellidos Nombre (requerido) - Apellido (opcional) + Apellidos (opcional) Cancelar registro Chats @@ -51,6 +51,7 @@ Eliminar y salir Nombre oculto Elige el chat + Mantén pulsado para ver %1$s usa una versión antigua de Telegram, así que las fotos secretas serán mostradas en un modo de compatibilidad.\n\nCuando %2$s actualice Telegram, las fotos con autodestrucción de 1 minuto o menos funcionarán con el modo \'Mantén pulsado para ver\', y te notificaremos siempre que la otra parte haga una captura de pantalla. Lista de difusión @@ -201,6 +202,7 @@ Desconocido INFORMACIÓN Teléfono + Apodo Tu apodo Lo siento, este apodo ya está ocupado. @@ -211,8 +213,8 @@ Puedes elegir un apodo en ]]>Telegram]]>. Si lo haces, otras personas te podrán encontrar por ese apodo y contactarte sin saber tu número de teléfono.
]]>Puedes usar ]]>a–z]]>, ]]>0–9]]> y guiones bajos. La longitud mínima es de ]]>5]]> caracteres.
Verificando apodo... %1$s está disponible. - None - An error occurred + Ninguno + Ocurrió un error. Restablecer las notificaciones Tamaño del texto @@ -238,7 +240,7 @@ Usuarios bloqueados Guardar fotos entrantes Cerrar sesión - TU NOMBRE Y APELLIDO + TU NOMBRE Y APELLIDOS Sin sonido Por defecto SOPORTE @@ -264,7 +266,7 @@ Importar contactos Sólo vía Wi-Fi Nombre - Apellido + Apellidos Color del LED Notificaciones emergentes Desactivadas @@ -360,7 +362,7 @@ Muchos intentos. Por favor, inténtalo más tarde. Código inválido Nombre inválido - Apellido inválido + Apellidos inválidos Cargando... No tienes reproductor de vídeo. Por favor, instala uno para continuar. Por favor, envíanos un correo electrónico a sms@telegram.org y cuéntanos tu problema. @@ -384,6 +386,7 @@ reenviar desde mi nombre ¿Enviar mensajes a %1$s? ¿Reenviar mensajes a %1$s? + Sorry, this feature is currently not available in your country. Telegram Rápida diff --git a/TMessagesProj/src/main/res/values-it/strings.xml b/TMessagesProj/src/main/res/values-it/strings.xml index 0b37c2682..835962588 100644 --- a/TMessagesProj/src/main/res/values-it/strings.xml +++ b/TMessagesProj/src/main/res/values-it/strings.xml @@ -51,6 +51,7 @@ Elimina ed esci Nome nascosto Seleziona chat + Tieni premuto per vedere %1$s sta usando una versione vecchia di Telegram, quindi le foto segrete verranno visualizzate in modalità di compatibilità.\n\nUna volta che %2$s avrà aggiornato Telegram, le foto con il timer minore di 1 minuto funzioneranno in modalità \'Tieni premuto per vedere\' , e verrai notificato ogni volta che l\'altro esegue uno screenshot. Lista broadcast @@ -201,6 +202,7 @@ Sconosciuto INFO Telefono + Nome utente Il tuo Nome Utente Nome utente già preso. @@ -211,8 +213,8 @@ Puoi scegliere un nome utente su ]]>Telegram]]>. Se lo fai, le altre persone potranno trovarti tramite questo nome utente e contattarti senza conoscere il tuo numero di telefono.
]]>Puoi usare ]]>a–z]]>, ]]>0–9]]> e underscore. La lunghezza minima è di ]]>5]]> caratteri.
Controllando il nome utente... %1$s è disponibile. - None - An error occurred + Nessuno + Si è verificato un errore. Ripristina tutte le impostazioni di notifica predefinite Dimensione testo messaggi @@ -384,6 +386,7 @@ inoltra dal mio nome Inviare messaggi a %1$s? Inoltra messaggi a %1$s? + Ci spiace, questa funzione non è disponibile nel tuo paese. Telegram Veloce diff --git a/TMessagesProj/src/main/res/values-ko/strings.xml b/TMessagesProj/src/main/res/values-ko/strings.xml index 3aa93cccd..c6314c7f0 100644 --- a/TMessagesProj/src/main/res/values-ko/strings.xml +++ b/TMessagesProj/src/main/res/values-ko/strings.xml @@ -38,7 +38,7 @@ 결과 없음 채팅방이 없습니다... 대화를 시작하려면 우측 상단의\n초대하기 버튼을 누르거나\n메뉴 버튼을 눌러 보세요. - 네트워크 연결을 기다리는 중... + 대기 중... 연결 중... 업데이트 중... 비밀대화 시작 @@ -51,6 +51,8 @@ 채팅방 나가기 숨긴 이름 채팅방 선택 + 꾹 눌러서 보기 + %1$s님의 텔레그램 버전이 낮아 비밀 사진을 호환성 모드로 표시합니다.\n\n%2$s님이 텔레그램을 업데이트하고 나면, 자동삭제 시간이 1분 이하인 사진은 \"탭하고 누르고 있어야 보임\" 상태가 되며, 상대방이 화면을 캡처할 때 마다 알림을 받습니다. 단체 메시지 리스트 새 단체 메시지 리스트 @@ -160,7 +162,7 @@ 마지막 접속: 마지막 접속: 친구 초대 - GLOBAL SEARCH + 전체 검색 메시지 보내기... 그룹 이름 입력 @@ -197,21 +199,22 @@ 자동삭제 타이머 해제 이 이미지는 ]]>%1$s]]>님과의 비밀대화에 사용 중인 암호화 키의 모습입니다.
]]>이 이미지가 ]]>%2$s]]>님의 암호화 키와 똑같다면 대화는 200%% 안전합니다.
]]>더 자세한 사항은 telegram.org 를 참고해 주세요.
- Unknown - INFO + 알 수 없음 + 정보 전화번호 - Username - Your Username - Sorry, this username is already taken. - Sorry, this username is invalid. - A username must have at least 5 characters. - A username must have maximum 32 characters. - Sorry, a username can\'t start with a number. - You can choose a username on ]]>Telegram]]>. If you do, other people will be able to find you by this username and contact you without knowing your phone number.
]]>You can use ]]>a–z]]>, ]]>0–9]]> and underscores. Minimum length is ]]>5]]> characters.
- Checking username... - %1$s is available. - None - An error occurred + + 아이디 + 아이디 + 이미 사용 중인 아이디입니다. + 올바른 아이디를 입력하세요. + 아이디는 최소 다섯 글자 이상 입력해야 합니다. + 아이디는 최대 32자까지만 가능합니다. + 아이디는 숫자로 시작할 수 없습니다. + 텔레그램 아이디를 설정할 수 있습니다. 아이디를 설정하면 회원님의 전화번호를 몰라도 아이디로 회원님을 찾아 대화를 나눌 수 있습니다.
]]>아이디는 영문, 밑줄, 숫자로 (]]>a~z]]>, ]]>_]]>, ]]>0~9]]>) ]]>다섯 글자]]> 이상으로 설정해 주세요.
+ 아이디 확인 중... + %1$s: 사용 가능합니다. + 없음 + 오류가 발생했습니다. 모든 알림 설정이 초기화되었습니다 채팅 글자 크기 @@ -383,6 +386,7 @@ 내 이름으로 전달 %1$s 그룹에 메시지를 보낼까요? %1$s 그룹에 메시지를 전달할까요? + 이 기능은 회원님의 국가에서는 사용할 수 없습니다. 텔레그램 눈부신 속도 @@ -436,36 +440,36 @@ 채팅방 %1$d개에서 채팅방 %1$d개에서 채팅방 %1$d개에서 - %1$d seconds - %1$d second - %1$d seconds - %1$d seconds - %1$d seconds - %1$d seconds - %1$d minutes - %1$d minute - %1$d minutes - %1$d minutes - %1$d minutes - %1$d minutes - %1$d hours - %1$d hour - %1$d hours - %1$d hours - %1$d hours - %1$d hours - %1$d days - %1$d day - %1$d days - %1$d days - %1$d days - %1$d days - %1$d weeks - %1$d week - %1$d weeks - %1$d weeks - %1$d weeks - %1$d weeks + %1$d초 + %1$d초 + %1$d초 + %1$d초 + %1$d초 + %1$d초 + %1$d분 + %1$d분 + %1$d분 + %1$d분 + %1$d분 + %1$d분 + %1$d시간 + %1$d시간 + %1$d시간 + %1$d시간 + %1$d시간 + %1$d시간 + %1$d일 + %1$d일 + %1$d일 + %1$d일 + %1$d일 + %1$d일 + %1$d주 + %1$d주 + %1$d주 + %1$d주 + %1$d주 + %1$d주 M\'월\' d\'일\' yyyy.MM.dd. diff --git a/TMessagesProj/src/main/res/values-nl/strings.xml b/TMessagesProj/src/main/res/values-nl/strings.xml index 7916a2fbf..eafe28483 100644 --- a/TMessagesProj/src/main/res/values-nl/strings.xml +++ b/TMessagesProj/src/main/res/values-nl/strings.xml @@ -51,7 +51,8 @@ Verwijderen en verlaten Verborgen naam Kies een gesprek - %1$s gebruikt een oudere versie van Telegram, dus worden geheime foto\'s weergegeven in de compatibiliteitsmodus.\n\nZodra %2$s Telegram update werken foto\'s met timers voor 1 minuut of minder in de \'Houd ingedrukt om te bekijken\'-modus en krijg je een bericht wanneer de andere partij een schermafbeelding maakt. + Druk en houd ingedrukt + %1$s gebruikt een oudere versie van Telegram, dus worden geheime foto\'s weergegeven in de compatibiliteitsmodus.\n\nZodra %2$s Telegram updatet werken foto\'s met timers voor 1 minuut of minder in de \'Druk en houd ingedrukt\'-modus en krijg je een bericht wanneer de andere partij een schermafbeelding maakt. Verzendlijst Nieuwe verzendlijst @@ -201,6 +202,7 @@ Onbekend INFORMATIE Telefoon + Gebruikersnaam Kies een naam Sorry, deze gebruikersnaam is al bezet. @@ -211,8 +213,8 @@ Je kan een gebruikersnaam kiezen voor ]]>Telegram]]>. Hiermee kunnen anderen je vinden en contact met je opnemen zonder je telefoonnummer te weten.
]]>Je mag ]]>a–z]]>, ]]>0–9]]> en liggend streepje gebruiken. De minimale lengte is ]]>5]]> tekens.
Gebruikersnaam controleren. %1$s is beschikbaar. - None - An error occurred + Geen + Er is een fout opgetreden. Alle meldingsinstellingen herstellen Tekstgrootte berichten @@ -384,6 +386,7 @@ doorsturen via mijn eigen naam Berichten naar %1$s verzenden? Berichten naar %1$s doorsturen? + Sorry, deze functie is momenteel niet beschikbaar in jouw land. Telegram Snel diff --git a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml index a6270eed6..df6d5c489 100644 --- a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml @@ -51,6 +51,7 @@ Apagar e sair Nome oculto Selecione uma Conversa + Toque e segure para ver %1$s está usando uma versão mais antiga do Telegram, por isso fotos secretas serão mostradas em modo de compatibilidade.\n\nAssim que %2$s atualize o Telegram, fotos com timers de 1 minuto ou menos passarão a funcionar no modo ‘Toque e segure para ver’, e você será notificado caso a outra pessoa salve a tela. Lista de Broadcast @@ -201,6 +202,7 @@ Desconhecido INFO Telefone + Nome de Usuário Seu nome de usuário Desculpe, este usuário já existe. @@ -211,8 +213,8 @@ Você pode escolher um nome de usuário no ]]>Telegram]]>. Assim, outras pessoas poderão te encontrar pelo nome de usuário e entrar em contato sem precisar saber seu telefone.
]]>Você pode usar ]]>a–z]]>, ]]>0–9]]> e underline. O tamanho mínimo é ]]>5]]> caracteres.
Verificando nome de usuário... %1$s está disponível. - None - An error occurred + Nenhum + Ocorreu um erro. Restaurar todas as configurações de notificação Tamanho do texto nas mensagens @@ -384,6 +386,7 @@ encaminhar pelo meu nome Enviar mensagens para %1$s? Encaminhar mensagem para %1$s? + Sorry, this feature is currently not available in your country. Telegram Rápido diff --git a/TMessagesProj/src/main/res/values-pt-rPT/strings.xml b/TMessagesProj/src/main/res/values-pt-rPT/strings.xml index b11dad27f..f79e0ff23 100644 --- a/TMessagesProj/src/main/res/values-pt-rPT/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rPT/strings.xml @@ -51,6 +51,7 @@ Apagar e sair Nome oculto Selecione uma Conversa + Toque e segure para ver %1$s está usando uma versão mais antiga do Telegram, por isso fotos secretas serão mostradas em modo de compatibilidade.\n\nAssim que %2$s atualize o Telegram, fotos com timers de 1 minuto ou menos passarão a funcionar no modo ‘Toque e segure para ver’, e você será notificado caso a outra pessoa salve a tela. Lista de Broadcast @@ -201,6 +202,7 @@ Desconhecido INFO Telefone + Nome de Usuário Seu nome de usuário Desculpe, este usuário já existe. @@ -211,8 +213,8 @@ Você pode escolher um nome de usuário no ]]>Telegram]]>. Assim, outras pessoas poderão te encontrar pelo nome de usuário e entrar em contato sem precisar saber seu telefone.
]]>Você pode usar ]]>a–z]]>, ]]>0–9]]> e underline. O tamanho mínimo é ]]>5]]> caracteres.
Verificando nome de usuário... %1$s está disponível. - None - An error occurred + Nenhum + Ocorreu um erro. Restaurar todas as configurações de notificação Tamanho do texto nas mensagens @@ -384,6 +386,7 @@ encaminhar pelo meu nome Enviar mensagens para %1$s? Encaminhar mensagem para %1$s? + Sorry, this feature is currently not available in your country. Telegram Rápido